From 35a0c41cf59ee6f3641e03e82e4cb2b888958eff Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 Jun 2017 10:54:03 -0700 Subject: [PATCH 01/13] Move Ktx processing to thread pool. --- libraries/gpu/src/gpu/Texture_ktx.cpp | 5 + .../src/model-networking/TextureCache.cpp | 195 +++++++++++++----- .../src/model-networking/TextureCache.h | 2 +- libraries/networking/src/FileCache.cpp | 7 +- libraries/shared/src/shared/Storage.cpp | 7 + 5 files changed, 157 insertions(+), 59 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index f455fde009..8c185e0b22 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "GPULogging.h" @@ -242,6 +243,7 @@ uint16 KtxStorage::minAvailableMipLevel() const { } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { + PROFILE_RANGE(app, __FUNCTION__) if (level != _minMipLevelAvailable - 1) { qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level << " " << _filename.c_str(); return; @@ -283,6 +285,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } memcpy(imageData, storage->data(), storage->size()); + _minMipLevelAvailable = level; if (_offsetToMinMipKV > 0) { auto minMipKeyData = fileData + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV; @@ -311,6 +314,7 @@ void Texture::setKtxBacking(const std::string& filename) { ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { + PROFILE_RANGE(app, __FUNCTION__) ktx::Header header; // From texture format to ktx format description @@ -453,6 +457,7 @@ TexturePointer Texture::unserialize(const std::string& ktxfile) { } TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor) { + PROFILE_RANGE(app, __FUNCTION__) const auto& header = descriptor.header; Format mipFormat = Format::COLOR_BGRA_32; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 8683d56b6b..3158ab386d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -300,16 +300,23 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, _textureSource->resetTexture(texture); if (texture) { + if (_sourceIsKTX) { + _ktxResourceState = WAITING_FOR_MIP_REQUEST; + } + _width = texture->getWidth(); _height = texture->getHeight(); setSize(texture->getStoredSize()); + finishedLoading(true); } else { // FIXME: If !gpuTexture, we failed to load! - _width = _height = 0; - qWarning() << "Texture did not load"; - } + if (_sourceIsKTX) { + _ktxResourceState = FAILED_TO_LOAD; + } - finishedLoading(true); + _width = _height = 0; + finishedLoading(false); + } emit networkTextureCreated(qWeakPointerCast (_self)); } @@ -407,6 +414,7 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::startRequestForNextMipLevel() { + PROFILE_RANGE(app, __FUNCTION__); if (_lowestKnownPopulatedMip == 0) { qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip << " " << _textureSource->getGPUTexture()->minAvailableMipLevel() << " " << _url; @@ -434,6 +442,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { if (_ktxMipRequest) { return; } + PROFILE_RANGE(app, __FUNCTION__); bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; @@ -470,41 +479,101 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { + PROFILE_RANGE(app, __FUNCTION__); + + Q_ASSERT_X(_ktxHeaderRequest, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - if (!_ktxHeaderRequest) { + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { + { "from_cache", _ktxHeaderRequest->loadedFromCache() }, + { "size_mb", _bytesTotal / 1000000.0 } + }); + + setSize(_bytesTotal); + + if (!_ktxHeaderRequest || _ktxHeaderRequest != sender()) { + // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. + qWarning(networking) << "Received signal NetworkTexture::ktxHeaderRequestFinished from ResourceRequest that is not the current" + << " request: " << sender() << ", " << _ktxHeaderRequest; return; } - _ktxHeaderRequestFinished = true; - maybeHandleFinishedInitialLoad(); + ResourceCache::requestCompleted(_self); + + auto result = _ktxHeaderRequest->getResult(); + if (result == ResourceRequest::Success) { + auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); + qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); + + auto data = _ktxHeaderRequest->getData(); + + //emit loaded(data); + //downloadFinished(data); + _ktxHeaderRequestFinished = true; + maybeHandleFinishedInitialLoad(); + + } else { + handleFailedRequest(result); + } + + _ktxHeaderRequest->disconnect(this); + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; } void NetworkTexture::ktxMipRequestFinished() { + PROFILE_RANGE(app, __FUNCTION__); + + Q_ASSERT_X(_ktxMipRequest, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); - if (!_ktxMipRequest) { + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { + { "from_cache", _ktxMipRequest->loadedFromCache() }, + { "size_mb", _bytesTotal / 1000000.0 } + }); + + setSize(_bytesTotal); + + if (!_ktxMipRequest || _ktxMipRequest != sender()) { + // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. + qWarning(networking) << "Received signal NetworkTexture::ktxHeaderRequestFinished from ResourceRequest that is not the current" + << " request: " << sender() << ", " << _ktxMipRequest; return; } - if (_ktxResourceState == LOADING_INITIAL_DATA) { - _ktxHighMipRequestFinished = true; - maybeHandleFinishedInitialLoad(); - } else if (_ktxResourceState == REQUESTING_MIP) { - Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); - TextureCache::requestCompleted(_self); + ResourceCache::requestCompleted(_self); - if (_ktxMipRequest->getResult() == ResourceRequest::Success) { + auto result = _ktxMipRequest->getResult(); + if (result == ResourceRequest::Success) { + auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); + qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); + + auto data = _ktxMipRequest->getData(); + + //emit loaded(data); + //downloadFinished(data); + + if (_ktxResourceState == LOADING_INITIAL_DATA) { + _ktxHighMipRequestFinished = true; + + //// + maybeHandleFinishedInitialLoad(); + } else if (_ktxResourceState == REQUESTING_MIP) { + Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; + _ktxResourceState = WAITING_FOR_MIP_REQUEST; + if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { + startRequestForNextMipLevel(); + } + + //// auto texture = _textureSource->getGPUTexture(); if (texture) { - texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, - _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); - + texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, + _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); if (texture->minAvailableMipLevel() <= _ktxMipLevelRangeInFlight.first) { - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - _ktxResourceState = WAITING_FOR_MIP_REQUEST; } else { qWarning(networking) << "Failed to load mip: " << _url << ":" << _ktxMipLevelRangeInFlight.first; _ktxResourceState = FAILED_TO_LOAD; @@ -514,29 +583,29 @@ void NetworkTexture::ktxMipRequestFinished() { qWarning(networking) << "Trying to update mips but texture is null"; } finishedLoading(true); + } else { - finishedLoading(false); - if (handleFailedRequest(_ktxMipRequest->getResult())) { - _ktxResourceState = PENDING_MIP_REQUEST; - } else { - qWarning(networking) << "Failed to load mip: " << _url; - _ktxResourceState = FAILED_TO_LOAD; - } + qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; - if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { - startRequestForNextMipLevel(); - } } else { - qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; + if (handleFailedRequest(result)) { + _ktxResourceState = PENDING_MIP_REQUEST; + } else { + _ktxResourceState = FAILED_TO_LOAD; + } } + + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } // This is called when the header or top mips have been loaded void NetworkTexture::maybeHandleFinishedInitialLoad() { + PROFILE_RANGE(app, __FUNCTION__); + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { @@ -546,15 +615,9 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { if (handleFailedRequest(_ktxMipRequest->getResult())) { _ktxResourceState = PENDING_INITIAL_LOAD; - } - else { + } else { _ktxResourceState = FAILED_TO_LOAD; } - - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; } else { // create ktx... auto ktxHeaderData = _ktxHeaderRequest->getData(); @@ -564,16 +627,20 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (!ktx::checkIdentifier(header->identifier)) { qWarning() << "Cannot load " << _url << ", invalid header identifier"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); return; } auto kvSize = header->bytesOfKeyValueData; if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); return; } @@ -582,8 +649,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto imageDescriptors = header->generateImageDescriptors(); if (imageDescriptors.size() == 0) { qWarning(networking) << "Failed to process ktx file " << _url; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; } _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); @@ -595,8 +665,10 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { std::string hash; if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { qWarning("Invalid source hash key found, bailing"); - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); return; } else { // at this point the source hash is in binary 16-byte form @@ -624,7 +696,10 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto memKtx = ktx::KTX::createBare(*header, keyValues); if (!memKtx) { qWarning() << " Ktx could not be created, bailing"; - finishedLoading(false); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); return; } @@ -635,8 +710,10 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto& ktxCache = textureCache->_ktxCache; if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { qCWarning(modelnetworking) << _url << " failed to write cache file"; - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); return; } else { _file = file; @@ -672,14 +749,18 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; + QMetaObject::invokeMethod(resource.data(), "ktxRequestProcessed", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); } + + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; + startRequestForNextMipLevel(); } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 7dab18d457..a17fbd353e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -74,7 +74,7 @@ protected: virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; - + Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 95304e3866..c3cc55cbae 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -21,8 +21,9 @@ #include #include -#include #include +#include +#include #ifdef Q_OS_WIN #include @@ -87,6 +88,7 @@ FileCache::~FileCache() { } void FileCache::initialize() { + PROFILE_RANGE(app, __FUNCTION__) QDir dir(_dirpath.c_str()); if (dir.exists()) { @@ -127,6 +129,7 @@ FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) } FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bool overwrite) { + PROFILE_RANGE(app, __FUNCTION__) assert(_initialized); std::string filepath = getFilepath(metadata.key); @@ -319,6 +322,7 @@ File::File(Metadata&& metadata, const std::string& filepath) : } File::~File() { + PROFILE_RANGE(app, __FUNCTION__) QFile file(getFilepath().c_str()); if (file.exists() && !_shouldPersist) { qCInfo(file_cache, "Unlinked %s", getFilepath().c_str()); @@ -327,6 +331,7 @@ File::~File() { } void File::touch() { + PROFILE_RANGE(app, __FUNCTION__) utime(_filepath.c_str(), nullptr); _modified = std::max(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch(), _modified); } \ No newline at end of file diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index b07f896df0..3e1b357019 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -12,6 +12,8 @@ #include #include +#include "../Profile.h" + Q_LOGGING_CATEGORY(storagelogging, "hifi.core.storage") using namespace storage; @@ -48,6 +50,7 @@ MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { } StoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { + PROFILE_RANGE(app, "FileStorage::create()"); QFile file(filename); if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { throw std::runtime_error("Unable to open file for writing"); @@ -70,6 +73,8 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u } FileStorage::FileStorage(const QString& filename) : _file(filename) { + PROFILE_RANGE(app, "FileStorage()"); + bool opened = _file.open(QFile::ReadWrite); if (opened) { _hasWriteAccess = true; @@ -79,6 +84,7 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } if (opened) { + PROFILE_RANGE(app, "FileStorage() map"); _mapped = _file.map(0, _file.size()); if (_mapped) { _valid = true; @@ -91,6 +97,7 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } FileStorage::~FileStorage() { + PROFILE_RANGE(app, "~FileStorage()"); if (_mapped) { if (!_file.unmap(_mapped)) { throw std::runtime_error("Unable to unmap file"); From 3110dd72bf4cc7d03cea48bff93a1ca5087da89c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 Jun 2017 13:20:19 -0700 Subject: [PATCH 02/13] More ktx work --- .../src/model-networking/TextureCache.cpp | 313 ++++++++---------- 1 file changed, 141 insertions(+), 172 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3158ab386d..1cd5298df6 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -498,17 +498,13 @@ void NetworkTexture::ktxHeaderRequestFinished() { return; } - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); auto result = _ktxHeaderRequest->getResult(); if (result == ResourceRequest::Success) { auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); - auto data = _ktxHeaderRequest->getData(); - - //emit loaded(data); - //downloadFinished(data); _ktxHeaderRequestFinished = true; maybeHandleFinishedInitialLoad(); @@ -541,51 +537,39 @@ void NetworkTexture::ktxMipRequestFinished() { return; } - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); auto result = _ktxMipRequest->getResult(); if (result == ResourceRequest::Success) { auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); - auto data = _ktxMipRequest->getData(); - - //emit loaded(data); - //downloadFinished(data); - if (_ktxResourceState == LOADING_INITIAL_DATA) { _ktxHighMipRequestFinished = true; - - //// maybeHandleFinishedInitialLoad(); } else if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; _ktxResourceState = WAITING_FOR_MIP_REQUEST; - if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { + if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { startRequestForNextMipLevel(); } - //// + //// Move to other thread auto texture = _textureSource->getGPUTexture(); if (texture) { - texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, - _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); - if (texture->minAvailableMipLevel() <= _ktxMipLevelRangeInFlight.first) { - } else { - qWarning(networking) << "Failed to load mip: " << _url << ":" << _ktxMipLevelRangeInFlight.first; - _ktxResourceState = FAILED_TO_LOAD; - } - } else { - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - qWarning(networking) << "Trying to update mips but texture is null"; + auto data = _ktxMipRequest->getData(); + texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, + data.size(), reinterpret_cast(data.data())); } finishedLoading(true); } else { - qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; + qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState; + finishedLoading(false); } @@ -610,158 +594,143 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { - TextureCache::requestCompleted(_self); + auto resource = _self; - if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { - if (handleFailedRequest(_ktxMipRequest->getResult())) { - _ktxResourceState = PENDING_INITIAL_LOAD; - } else { - _ktxResourceState = FAILED_TO_LOAD; - } - } else { - // create ktx... - auto ktxHeaderData = _ktxHeaderRequest->getData(); - auto ktxHighMipData = _ktxMipRequest->getData(); + // create ktx... + auto ktxHeaderData = _ktxHeaderRequest->getData(); + auto ktxHighMipData = _ktxMipRequest->getData(); - auto header = reinterpret_cast(ktxHeaderData.data()); + auto header = reinterpret_cast(ktxHeaderData.data()); - if (!ktx::checkIdentifier(header->identifier)) { - qWarning() << "Cannot load " << _url << ", invalid header identifier"; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - - auto kvSize = header->bytesOfKeyValueData; - if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); - - auto imageDescriptors = header->generateImageDescriptors(); - if (imageDescriptors.size() == 0) { - qWarning(networking) << "Failed to process ktx file " << _url; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); - - // Create bare ktx in memory - auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { - return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; - }); - std::string filename; - std::string hash; - if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { - qWarning("Invalid source hash key found, bailing"); - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } else { - // at this point the source hash is in binary 16-byte form - // and we need it in a hexadecimal string - auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); - hash = filename = binaryHash.toHex().toStdString(); - } - - auto textureCache = DependencyManager::get(); - - gpu::TexturePointer texture = textureCache->getTextureByHash(hash); - - if (!texture) { - KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); - if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile->getFilepath()); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); - } - } - } - - if (!texture) { - - auto memKtx = ktx::KTX::createBare(*header, keyValues); - if (!memKtx) { - qWarning() << " Ktx could not be created, bailing"; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - - // Move ktx to file - const char* data = reinterpret_cast(memKtx->_storage->data()); - size_t length = memKtx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = textureCache->_ktxCache; - if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << " failed to write cache file"; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } else { - _file = file; - } - - auto newKtxDescriptor = memKtx->toDescriptor(); - - texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); - texture->setKtxBacking(file->getFilepath()); - texture->setSource(filename); - - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); - ktxData += ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - for (int level = static_cast(images.size()) - 1; level >= 0; --level) { - auto& image = images[level]; - if (image._imageSize > imageSizeRemaining) { - break; - } - ktxData -= image._imageSize; - texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); - ktxData -= ktx::IMAGE_SIZE_WIDTH; - imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); - } - - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different - // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will - // be the winner - texture = textureCache->cacheTextureByHash(filename, texture); - } - - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - - - QMetaObject::invokeMethod(resource.data(), "ktxRequestProcessed", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + if (!ktx::checkIdentifier(header->identifier)) { + qWarning() << "Cannot load " << _url << ", invalid header identifier"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; } - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { + qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } - startRequestForNextMipLevel(); + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + auto imageDescriptors = header->generateImageDescriptors(); + if (imageDescriptors.size() == 0) { + qWarning(networking) << "Failed to process ktx file " << _url; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); + + // Create bare ktx in memory + auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { + return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; + }); + std::string filename; + std::string hash; + if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { + qWarning("Invalid source hash key found, bailing"); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } else { + // at this point the source hash is in binary 16-byte form + // and we need it in a hexadecimal string + auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); + hash = filename = binaryHash.toHex().toStdString(); + } + + auto textureCache = DependencyManager::get(); + + gpu::TexturePointer texture = textureCache->getTextureByHash(hash); + + if (!texture) { + KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); + if (ktxFile) { + texture = gpu::Texture::unserialize(ktxFile->getFilepath()); + if (texture) { + texture = textureCache->cacheTextureByHash(hash, texture); + } + } + } + + if (!texture) { + + auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << _url << " failed to write cache file"; + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } else { + _file = file; + } + + auto newKtxDescriptor = memKtx->toDescriptor(); + + texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); + texture->setKtxBacking(file->getFilepath()); + texture->setSource(filename); + + auto& images = _originalKtxDescriptor->images; + size_t imageSizeRemaining = ktxHighMipData.size(); + uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + for (int level = static_cast(images.size()) - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; + } + ktxData -= image._imageSize; + texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); + ktxData -= ktx::IMAGE_SIZE_WIDTH; + imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); + } + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + texture = textureCache->cacheTextureByHash(filename, texture); + } + + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + + + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); } } From d2251bb8402ba15b2b12b973175ffec4cc8435ea Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 Jun 2017 15:44:34 -0700 Subject: [PATCH 03/13] Offload ktx processing to threadpool --- .../src/model-networking/TextureCache.cpp | 315 +++++++++--------- .../src/model-networking/TextureCache.h | 6 +- 2 files changed, 169 insertions(+), 152 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1cd5298df6..7ccf081183 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -13,6 +13,8 @@ #include +#include + #include #include #include @@ -300,20 +302,11 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, _textureSource->resetTexture(texture); if (texture) { - if (_sourceIsKTX) { - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - } - _width = texture->getWidth(); _height = texture->getHeight(); setSize(texture->getStoredSize()); finishedLoading(true); } else { - // FIXME: If !gpuTexture, we failed to load! - if (_sourceIsKTX) { - _ktxResourceState = FAILED_TO_LOAD; - } - _width = _height = 0; finishedLoading(false); } @@ -484,10 +477,10 @@ void NetworkTexture::ktxHeaderRequestFinished() { Q_ASSERT_X(_ktxHeaderRequest, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { - { "from_cache", _ktxHeaderRequest->loadedFromCache() }, - { "size_mb", _bytesTotal / 1000000.0 } - }); + //PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { + // { "from_cache", _ktxHeaderRequest->loadedFromCache() }, + // { "size_mb", _bytesTotal / 1000000.0 } + //}); setSize(_bytesTotal); @@ -505,9 +498,8 @@ void NetworkTexture::ktxHeaderRequestFinished() { auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); - _ktxHeaderRequestFinished = true; + _ktxHeaderData = _ktxHeaderRequest->getData(); maybeHandleFinishedInitialLoad(); - } else { handleFailedRequest(result); } @@ -545,7 +537,7 @@ void NetworkTexture::ktxMipRequestFinished() { qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); if (_ktxResourceState == LOADING_INITIAL_DATA) { - _ktxHighMipRequestFinished = true; + _ktxHighMipData = _ktxMipRequest->getData(); maybeHandleFinishedInitialLoad(); } else if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); @@ -558,21 +550,37 @@ void NetworkTexture::ktxMipRequestFinished() { startRequestForNextMipLevel(); } - //// Move to other thread - auto texture = _textureSource->getGPUTexture(); - if (texture) { - auto data = _ktxMipRequest->getData(); - texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, - data.size(), reinterpret_cast(data.data())); - } - finishedLoading(true); + + auto self = _self; + auto data = _ktxMipRequest->getData(); + auto mipLevel = _ktxMipLevelRangeInFlight.first; + QtConcurrent::run(QThreadPool::globalInstance(), [this, self, data, mipLevel] { + auto that = self.lock(); + if (!that) { + // Resource no longer exists, bail + return; + } + + auto texture = _textureSource->getGPUTexture(); + if (texture) { + texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast(data.data())); + + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); + } else { + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + } + }); } else { qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState; finishedLoading(false); } - - } else { if (handleFailedRequest(result)) { _ktxResourceState = PENDING_MIP_REQUEST; @@ -588,149 +596,158 @@ void NetworkTexture::ktxMipRequestFinished() { // This is called when the header or top mips have been loaded void NetworkTexture::maybeHandleFinishedInitialLoad() { - PROFILE_RANGE(app, __FUNCTION__); - Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { - - auto resource = _self; + PROFILE_RANGE(app, __FUNCTION__); + if (!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty()) { // create ktx... - auto ktxHeaderData = _ktxHeaderRequest->getData(); - auto ktxHighMipData = _ktxMipRequest->getData(); + auto ktxHeaderData = _ktxHeaderData; + auto ktxHighMipData = _ktxHighMipData; + _ktxHeaderData.clear(); + _ktxHighMipData.clear(); - auto header = reinterpret_cast(ktxHeaderData.data()); - - if (!ktx::checkIdentifier(header->identifier)) { - qWarning() << "Cannot load " << _url << ", invalid header identifier"; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - - auto kvSize = header->bytesOfKeyValueData; - if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); - - auto imageDescriptors = header->generateImageDescriptors(); - if (imageDescriptors.size() == 0) { - qWarning(networking) << "Failed to process ktx file " << _url; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); - - // Create bare ktx in memory - auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { - return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; - }); - std::string filename; - std::string hash; - if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { - qWarning("Invalid source hash key found, bailing"); - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } else { - // at this point the source hash is in binary 16-byte form - // and we need it in a hexadecimal string - auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); - hash = filename = binaryHash.toHex().toStdString(); - } - - auto textureCache = DependencyManager::get(); - - gpu::TexturePointer texture = textureCache->getTextureByHash(hash); - - if (!texture) { - KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); - if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile->getFilepath()); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); - } - } - } - - if (!texture) { - - auto memKtx = ktx::KTX::createBare(*header, keyValues); - if (!memKtx) { - qWarning() << " Ktx could not be created, bailing"; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); + auto self = _self; + QtConcurrent::run(QThreadPool::globalInstance(), [=] { + auto that = self.lock(); + if (!that) { + // Resource no longer exists, bail return; } - // Move ktx to file - const char* data = reinterpret_cast(memKtx->_storage->data()); - size_t length = memKtx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = textureCache->_ktxCache; - if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << " failed to write cache file"; - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); + auto header = reinterpret_cast(ktxHeaderData.data()); + + if (!ktx::checkIdentifier(header->identifier)) { + qWarning() << "Cannot load " << _url << ", invalid header identifier"; + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { + qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + auto imageDescriptors = header->generateImageDescriptors(); + if (imageDescriptors.size() == 0) { + qWarning(networking) << "Failed to process ktx file " << _url; + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); + + // Create bare ktx in memory + auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { + return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; + }); + std::string filename; + std::string hash; + if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { + qWarning("Invalid source hash key found, bailing"); + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); return; } else { - _file = file; + // at this point the source hash is in binary 16-byte form + // and we need it in a hexadecimal string + auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); + hash = filename = binaryHash.toHex().toStdString(); } - auto newKtxDescriptor = memKtx->toDescriptor(); + auto textureCache = DependencyManager::get(); - texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); - texture->setKtxBacking(file->getFilepath()); - texture->setSource(filename); + gpu::TexturePointer texture = textureCache->getTextureByHash(hash); - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); - ktxData += ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - for (int level = static_cast(images.size()) - 1; level >= 0; --level) { - auto& image = images[level]; - if (image._imageSize > imageSizeRemaining) { - break; + if (!texture) { + KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); + if (ktxFile) { + texture = gpu::Texture::unserialize(ktxFile->getFilepath()); + if (texture) { + texture = textureCache->cacheTextureByHash(hash, texture); + } } - ktxData -= image._imageSize; - texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); - ktxData -= ktx::IMAGE_SIZE_WIDTH; - imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); } - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different - // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will - // be the winner - texture = textureCache->cacheTextureByHash(filename, texture); - } + if (!texture) { - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << _url << " failed to write cache file"; + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } else { + _file = file; + } + + auto newKtxDescriptor = memKtx->toDescriptor(); + + texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); + texture->setKtxBacking(file->getFilepath()); + texture->setSource(filename); + + auto& images = _originalKtxDescriptor->images; + size_t imageSizeRemaining = ktxHighMipData.size(); + const uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + for (int level = static_cast(images.size()) - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; + } + ktxData -= image._imageSize; + texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); + ktxData -= ktx::IMAGE_SIZE_WIDTH; + imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); + } + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + texture = textureCache->cacheTextureByHash(filename, texture); + } + + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); + QMetaObject::invokeMethod(this, "startRequestForNextMipLevel"); + }); } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index a17fbd353e..a93d45dac9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -78,7 +78,7 @@ protected: Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); - void startRequestForNextMipLevel(); + Q_INVOKABLE void startRequestForNextMipLevel(); void startMipRangeRequest(uint16_t low, uint16_t high); void maybeHandleFinishedInitialLoad(); @@ -110,8 +110,8 @@ private: ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; - bool _ktxHeaderRequestFinished{ false }; - bool _ktxHighMipRequestFinished{ false }; + QByteArray _ktxHeaderData; + QByteArray _ktxHighMipData; uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; From 31d2592e7dbce5ef96bf3072d19160a329aca643 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 Jun 2017 16:39:29 -0700 Subject: [PATCH 04/13] Handle header and high mips in the same callback --- .../src/model-networking/TextureCache.cpp | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7ccf081183..2f7121bbf1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -472,48 +472,65 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { - PROFILE_RANGE(app, __FUNCTION__); - - Q_ASSERT_X(_ktxHeaderRequest, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); - Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - - //PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { - // { "from_cache", _ktxHeaderRequest->loadedFromCache() }, - // { "size_mb", _bytesTotal / 1000000.0 } - //}); - - setSize(_bytesTotal); - - if (!_ktxHeaderRequest || _ktxHeaderRequest != sender()) { - // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. - qWarning(networking) << "Received signal NetworkTexture::ktxHeaderRequestFinished from ResourceRequest that is not the current" - << " request: " << sender() << ", " << _ktxHeaderRequest; + if (!_ktxHeaderRequest || _ktxHeaderRequest->getState() != ResourceRequest::Finished || + !_ktxMipRequest || _ktxMipRequest->getState() != ResourceRequest::Finished) { + // Wait for both request to be finished return; } + PROFILE_RANGE(app, __FUNCTION__); + + Q_ASSERT_X(_ktxHeaderRequest, __FUNCTION__, "Request should not be null while in handleReplyFinished"); + Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in handleReplyFinished"); + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { + { "from_cache", _ktxHeaderRequest->loadedFromCache() }, + { "size_mb", _bytesTotal / 1000000.0 } + }); + + setSize(_bytesTotal); TextureCache::requestCompleted(_self); auto result = _ktxHeaderRequest->getResult(); + if (result == ResourceRequest::Success) { + result = _ktxMipRequest->getResult(); + } + if (result == ResourceRequest::Success) { auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); _ktxHeaderData = _ktxHeaderRequest->getData(); + _ktxHighMipData = _ktxMipRequest->getData(); maybeHandleFinishedInitialLoad(); } else { - handleFailedRequest(result); + if (handleFailedRequest(result)) { + _ktxResourceState = PENDING_INITIAL_LOAD; + } else { + _ktxResourceState = FAILED_TO_LOAD; + } } _ktxHeaderRequest->disconnect(this); _ktxHeaderRequest->deleteLater(); _ktxHeaderRequest = nullptr; + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } void NetworkTexture::ktxMipRequestFinished() { + // If this is the high mips request, redirect it to ktxHeaderRequestFinished + if (_ktxResourceState == LOADING_INITIAL_DATA) { + ktxHeaderRequestFinished(); + return; + } + PROFILE_RANGE(app, __FUNCTION__); Q_ASSERT_X(_ktxMipRequest, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); - Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); + Q_ASSERT(_ktxResourceState == REQUESTING_MIP); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { { "from_cache", _ktxMipRequest->loadedFromCache() }, @@ -536,10 +553,7 @@ void NetworkTexture::ktxMipRequestFinished() { auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); - if (_ktxResourceState == LOADING_INITIAL_DATA) { - _ktxHighMipData = _ktxMipRequest->getData(); - maybeHandleFinishedInitialLoad(); - } else if (_ktxResourceState == REQUESTING_MIP) { + if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); @@ -576,7 +590,6 @@ void NetworkTexture::ktxMipRequestFinished() { Q_ARG(int, 0)); } }); - } else { qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState; finishedLoading(false); From 57e8d8e1fd691cf10c6c17a7696fc8834567fd4e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 Jun 2017 16:53:30 -0700 Subject: [PATCH 05/13] Fix download retries --- .../src/model-networking/TextureCache.cpp | 274 +++++++++--------- .../src/model-networking/TextureCache.h | 7 +- 2 files changed, 135 insertions(+), 146 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 2f7121bbf1..c31f8f464b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -13,7 +13,7 @@ #include -#include +#include #include #include @@ -382,8 +382,7 @@ void NetworkTexture::makeRequest() { emit loading(); - connect(_ktxHeaderRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxHeaderRequestProgress); - connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxHeaderRequestFinished); + connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxInitialDataRequestFinished); _bytesReceived = _bytesTotal = _bytes = 0; @@ -455,6 +454,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { ByteRange range; range.fromInclusive = -HIGH_MIP_MAX_SIZE; _ktxMipRequest->setByteRange(range); + + connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxInitialDataRequestFinished); } else { ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData @@ -462,16 +463,15 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.toExclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[high + 1]._imageOffset; _ktxMipRequest->setByteRange(range); - } - connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress); - connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished); + connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished); + } _ktxMipRequest->send(); } -void NetworkTexture::ktxHeaderRequestFinished() { +void NetworkTexture::ktxInitialDataRequestFinished() { if (!_ktxHeaderRequest || _ktxHeaderRequest->getState() != ResourceRequest::Finished || !_ktxMipRequest || _ktxMipRequest->getState() != ResourceRequest::Finished) { // Wait for both request to be finished @@ -479,9 +479,8 @@ void NetworkTexture::ktxHeaderRequestFinished() { } PROFILE_RANGE(app, __FUNCTION__); - Q_ASSERT_X(_ktxHeaderRequest, __FUNCTION__, "Request should not be null while in handleReplyFinished"); - Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in handleReplyFinished"); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + Q_ASSERT_X(_ktxHeaderRequest && _ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxInitialDataRequestFinished"); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { { "from_cache", _ktxHeaderRequest->loadedFromCache() }, @@ -503,7 +502,7 @@ void NetworkTexture::ktxHeaderRequestFinished() { _ktxHeaderData = _ktxHeaderRequest->getData(); _ktxHighMipData = _ktxMipRequest->getData(); - maybeHandleFinishedInitialLoad(); + handleFinishedInitialLoad(); } else { if (handleFailedRequest(result)) { _ktxResourceState = PENDING_INITIAL_LOAD; @@ -521,15 +520,9 @@ void NetworkTexture::ktxHeaderRequestFinished() { } void NetworkTexture::ktxMipRequestFinished() { - // If this is the high mips request, redirect it to ktxHeaderRequestFinished - if (_ktxResourceState == LOADING_INITIAL_DATA) { - ktxHeaderRequestFinished(); - return; - } - PROFILE_RANGE(app, __FUNCTION__); - Q_ASSERT_X(_ktxMipRequest, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); + Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxMipRequestFinished"); Q_ASSERT(_ktxResourceState == REQUESTING_MIP); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { @@ -541,7 +534,7 @@ void NetworkTexture::ktxMipRequestFinished() { if (!_ktxMipRequest || _ktxMipRequest != sender()) { // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. - qWarning(networking) << "Received signal NetworkTexture::ktxHeaderRequestFinished from ResourceRequest that is not the current" + qWarning(networking) << "Received signal NetworkTexture::ktxMipRequestFinished from ResourceRequest that is not the current" << " request: " << sender() << ", " << _ktxMipRequest; return; } @@ -608,30 +601,99 @@ void NetworkTexture::ktxMipRequestFinished() { } // This is called when the header or top mips have been loaded -void NetworkTexture::maybeHandleFinishedInitialLoad() { +void NetworkTexture::handleFinishedInitialLoad() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + Q_ASSERT(!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty()); PROFILE_RANGE(app, __FUNCTION__); - if (!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty()) { - // create ktx... - auto ktxHeaderData = _ktxHeaderData; - auto ktxHighMipData = _ktxHighMipData; - _ktxHeaderData.clear(); - _ktxHighMipData.clear(); + // create ktx... + auto ktxHeaderData = _ktxHeaderData; + auto ktxHighMipData = _ktxHighMipData; + _ktxHeaderData.clear(); + _ktxHighMipData.clear(); - auto self = _self; - QtConcurrent::run(QThreadPool::globalInstance(), [=] { - auto that = self.lock(); - if (!that) { - // Resource no longer exists, bail - return; + auto self = _self; + QtConcurrent::run(QThreadPool::globalInstance(), [=] { + auto that = self.lock(); + if (!that) { + // Resource no longer exists, bail + return; + } + + auto header = reinterpret_cast(ktxHeaderData.data()); + + if (!ktx::checkIdentifier(header->identifier)) { + qWarning() << "Cannot load " << _url << ", invalid header identifier"; + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { + qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + auto imageDescriptors = header->generateImageDescriptors(); + if (imageDescriptors.size() == 0) { + qWarning(networking) << "Failed to process ktx file " << _url; + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } + _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); + + // Create bare ktx in memory + auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { + return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; + }); + std::string filename; + std::string hash; + if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { + qWarning("Invalid source hash key found, bailing"); + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, nullptr), + Q_ARG(int, 0), + Q_ARG(int, 0)); + return; + } else { + // at this point the source hash is in binary 16-byte form + // and we need it in a hexadecimal string + auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); + hash = filename = binaryHash.toHex().toStdString(); + } + + auto textureCache = DependencyManager::get(); + + gpu::TexturePointer texture = textureCache->getTextureByHash(hash); + + if (!texture) { + KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); + if (ktxFile) { + texture = gpu::Texture::unserialize(ktxFile->getFilepath()); + if (texture) { + texture = textureCache->cacheTextureByHash(hash, texture); + } } + } - auto header = reinterpret_cast(ktxHeaderData.data()); + if (!texture) { - if (!ktx::checkIdentifier(header->identifier)) { - qWarning() << "Cannot load " << _url << ", invalid header identifier"; + auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; QMetaObject::invokeMethod(this, "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), @@ -639,129 +701,59 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { return; } - auto kvSize = header->bytesOfKeyValueData; - if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); - - auto imageDescriptors = header->generateImageDescriptors(); - if (imageDescriptors.size() == 0) { - qWarning(networking) << "Failed to process ktx file " << _url; - QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); - - // Create bare ktx in memory - auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { - return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; - }); - std::string filename; - std::string hash; - if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { - qWarning("Invalid source hash key found, bailing"); + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << _url << " failed to write cache file"; QMetaObject::invokeMethod(this, "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); return; } else { - // at this point the source hash is in binary 16-byte form - // and we need it in a hexadecimal string - auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); - hash = filename = binaryHash.toHex().toStdString(); + _file = file; } - auto textureCache = DependencyManager::get(); + auto newKtxDescriptor = memKtx->toDescriptor(); - gpu::TexturePointer texture = textureCache->getTextureByHash(hash); + texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); + texture->setKtxBacking(file->getFilepath()); + texture->setSource(filename); - if (!texture) { - KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); - if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile->getFilepath()); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); - } + auto& images = _originalKtxDescriptor->images; + size_t imageSizeRemaining = ktxHighMipData.size(); + const uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + for (int level = static_cast(images.size()) - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; } + ktxData -= image._imageSize; + texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); + ktxData -= ktx::IMAGE_SIZE_WIDTH; + imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); } - if (!texture) { + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + texture = textureCache->cacheTextureByHash(filename, texture); + } - auto memKtx = ktx::KTX::createBare(*header, keyValues); - if (!memKtx) { - qWarning() << " Ktx could not be created, bailing"; - QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } - - // Move ktx to file - const char* data = reinterpret_cast(memKtx->_storage->data()); - size_t length = memKtx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = textureCache->_ktxCache; - if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << " failed to write cache file"; - QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - return; - } else { - _file = file; - } - - auto newKtxDescriptor = memKtx->toDescriptor(); - - texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); - texture->setKtxBacking(file->getFilepath()); - texture->setSource(filename); - - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = ktxHighMipData.size(); - const uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); - ktxData += ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - for (int level = static_cast(images.size()) - 1; level >= 0; --level) { - auto& image = images[level]; - if (image._imageSize > imageSizeRemaining) { - break; - } - ktxData -= image._imageSize; - texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); - ktxData -= ktx::IMAGE_SIZE_WIDTH; - imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); - } - - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different - // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will - // be the winner - texture = textureCache->cacheTextureByHash(filename, texture); - } - - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); - QMetaObject::invokeMethod(this, "startRequestForNextMipLevel"); - }); - } + QMetaObject::invokeMethod(this, "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); + QMetaObject::invokeMethod(this, "startRequestForNextMipLevel"); + }); } void NetworkTexture::downloadFinished(const QByteArray& data) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index a93d45dac9..0b55839319 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -62,10 +62,7 @@ signals: void networkTextureCreated(const QWeakPointer& self); public slots: - void ktxHeaderRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } - void ktxHeaderRequestFinished(); - - void ktxMipRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } + void ktxInitialDataRequestFinished(); void ktxMipRequestFinished(); protected: @@ -81,7 +78,7 @@ protected: Q_INVOKABLE void startRequestForNextMipLevel(); void startMipRangeRequest(uint16_t low, uint16_t high); - void maybeHandleFinishedInitialLoad(); + void handleFinishedInitialLoad(); private: friend class KTXReader; From 306b55e45d64df869bd0d0f49e47120c1bfd41e3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 Jun 2017 13:46:29 -0700 Subject: [PATCH 06/13] Synchronous mip loading --- .../src/model-networking/TextureCache.cpp | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c31f8f464b..6c5fef7765 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -550,14 +550,6 @@ void NetworkTexture::ktxMipRequestFinished() { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); - _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - - if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { - startRequestForNextMipLevel(); - } - - auto self = _self; auto data = _ktxMipRequest->getData(); auto mipLevel = _ktxMipLevelRangeInFlight.first; @@ -572,10 +564,17 @@ void NetworkTexture::ktxMipRequestFinished() { if (texture) { texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast(data.data())); + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + _ktxResourceState = WAITING_FOR_MIP_REQUEST; + QMetaObject::invokeMethod(this, "setImage", Q_ARG(gpu::TexturePointer, texture), Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); + + if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { + QMetaObject::invokeMethod(this, "startRequestForNextMipLevel"); + } } else { QMetaObject::invokeMethod(this, "setImage", Q_ARG(gpu::TexturePointer, nullptr), @@ -746,13 +745,17 @@ void NetworkTexture::handleFinishedInitialLoad() { } _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + _ktxResourceState = WAITING_FOR_MIP_REQUEST; QMetaObject::invokeMethod(this, "setImage", Q_ARG(gpu::TexturePointer, texture), Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); - QMetaObject::invokeMethod(this, "startRequestForNextMipLevel"); + + if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { + QMetaObject::invokeMethod(this, "startRequestForNextMipLevel"); + } }); } From fb8aa96a57468169f54ed9cc645bbd75cbc03d0b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 Jun 2017 14:42:18 -0700 Subject: [PATCH 07/13] Add resource processing tracking to KTX --- .../src/model-networking/TextureCache.cpp | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 6c5fef7765..607338d32e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -553,7 +553,19 @@ void NetworkTexture::ktxMipRequestFinished() { auto self = _self; auto data = _ktxMipRequest->getData(); auto mipLevel = _ktxMipLevelRangeInFlight.first; + DependencyManager::get()->incrementStat("PendingProcessing"); QtConcurrent::run(QThreadPool::globalInstance(), [this, self, data, mipLevel] { + PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", _url.toString() } }); + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); + + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; + } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); + auto that = self.lock(); if (!that) { // Resource no longer exists, bail @@ -613,7 +625,19 @@ void NetworkTexture::handleFinishedInitialLoad() { _ktxHighMipData.clear(); auto self = _self; - QtConcurrent::run(QThreadPool::globalInstance(), [=] { + DependencyManager::get()->incrementStat("PendingProcessing"); + QtConcurrent::run(QThreadPool::globalInstance(), [this, self, ktxHeaderData, ktxHighMipData] { + PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", _url.toString() } }); + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); + + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; + } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); + auto that = self.lock(); if (!that) { // Resource no longer exists, bail From 4d3fc39ab62fa61c79a272ea69b83a47cb0d4c5d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 Jun 2017 17:20:38 -0700 Subject: [PATCH 08/13] Make KTX async tasks thread safe --- .../src/model-networking/TextureCache.cpp | 112 ++++++++---------- .../src/model-networking/TextureCache.h | 2 + 2 files changed, 53 insertions(+), 61 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 607338d32e..1d280aecc9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -406,19 +406,18 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::startRequestForNextMipLevel() { - PROFILE_RANGE(app, __FUNCTION__); - if (_lowestKnownPopulatedMip == 0) { - qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip - << " " << _textureSource->getGPUTexture()->minAvailableMipLevel() << " " << _url; + auto self = _self.lock(); + if (!self) { return; } - if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { - auto self = _self.lock(); - if (!self) { - return; - } + auto texture = _textureSource->getGPUTexture(); + if (!texture || _ktxResourceState != WAITING_FOR_MIP_REQUEST) { + return; + } + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { _ktxResourceState = PENDING_MIP_REQUEST; init(false); @@ -434,7 +433,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { if (_ktxMipRequest) { return; } - PROFILE_RANGE(app, __FUNCTION__); bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; @@ -477,7 +475,6 @@ void NetworkTexture::ktxInitialDataRequestFinished() { // Wait for both request to be finished return; } - PROFILE_RANGE(app, __FUNCTION__); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); Q_ASSERT_X(_ktxHeaderRequest && _ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxInitialDataRequestFinished"); @@ -487,6 +484,8 @@ void NetworkTexture::ktxInitialDataRequestFinished() { { "size_mb", _bytesTotal / 1000000.0 } }); + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + setSize(_bytesTotal); TextureCache::requestCompleted(_self); @@ -520,8 +519,6 @@ void NetworkTexture::ktxInitialDataRequestFinished() { } void NetworkTexture::ktxMipRequestFinished() { - PROFILE_RANGE(app, __FUNCTION__); - Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxMipRequestFinished"); Q_ASSERT(_ktxResourceState == REQUESTING_MIP); @@ -530,6 +527,9 @@ void NetworkTexture::ktxMipRequestFinished() { { "size_mb", _bytesTotal / 1000000.0 } }); + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + setSize(_bytesTotal); if (!_ktxMipRequest || _ktxMipRequest != sender()) { @@ -550,12 +550,16 @@ void NetworkTexture::ktxMipRequestFinished() { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + _ktxResourceState = WAITING_FOR_MIP_REQUEST; + auto self = _self; + auto url = _url; auto data = _ktxMipRequest->getData(); auto mipLevel = _ktxMipLevelRangeInFlight.first; + auto texture = _textureSource->getGPUTexture(); DependencyManager::get()->incrementStat("PendingProcessing"); - QtConcurrent::run(QThreadPool::globalInstance(), [this, self, data, mipLevel] { - PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", _url.toString() } }); + QtConcurrent::run(QThreadPool::globalInstance(), [self, data, mipLevel, url, texture] { + PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", url.toString() } }); DependencyManager::get()->decrementStat("PendingProcessing"); CounterStat counter("Processing"); @@ -566,33 +570,22 @@ void NetworkTexture::ktxMipRequestFinished() { QThread::currentThread()->setPriority(QThread::LowPriority); Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); - auto that = self.lock(); - if (!that) { + auto resource = self.lock(); + if (!resource) { // Resource no longer exists, bail return; } - auto texture = _textureSource->getGPUTexture(); - if (texture) { - texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast(data.data())); + Q_ASSERT_X(texture, "Async - NetworkTexture::ktxMipRequestFinished", "NetworkTexture should have been assigned a GPU texture by now."); - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - _ktxResourceState = WAITING_FOR_MIP_REQUEST; + texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast(data.data())); - QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), + Q_ARG(int, texture->getHeight())); - if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { - QMetaObject::invokeMethod(this, "startRequestForNextMipLevel"); - } - } else { - QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, nullptr), - Q_ARG(int, 0), - Q_ARG(int, 0)); - } + QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); }); } else { qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState; @@ -616,18 +609,19 @@ void NetworkTexture::handleFinishedInitialLoad() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); Q_ASSERT(!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty()); - PROFILE_RANGE(app, __FUNCTION__); - // create ktx... auto ktxHeaderData = _ktxHeaderData; auto ktxHighMipData = _ktxHighMipData; _ktxHeaderData.clear(); _ktxHighMipData.clear(); + _ktxResourceState = WAITING_FOR_MIP_REQUEST; + auto self = _self; + auto url = _url; DependencyManager::get()->incrementStat("PendingProcessing"); - QtConcurrent::run(QThreadPool::globalInstance(), [this, self, ktxHeaderData, ktxHighMipData] { - PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", _url.toString() } }); + QtConcurrent::run(QThreadPool::globalInstance(), [self, ktxHeaderData, ktxHighMipData, url] { + PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", url.toString() } }); DependencyManager::get()->decrementStat("PendingProcessing"); CounterStat counter("Processing"); @@ -638,8 +632,8 @@ void NetworkTexture::handleFinishedInitialLoad() { QThread::currentThread()->setPriority(QThread::LowPriority); Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); - auto that = self.lock(); - if (!that) { + auto resource = self.lock(); + if (!resource) { // Resource no longer exists, bail return; } @@ -647,8 +641,8 @@ void NetworkTexture::handleFinishedInitialLoad() { auto header = reinterpret_cast(ktxHeaderData.data()); if (!ktx::checkIdentifier(header->identifier)) { - qWarning() << "Cannot load " << _url << ", invalid header identifier"; - QMetaObject::invokeMethod(this, "setImage", + qWarning() << "Cannot load " << url << ", invalid header identifier"; + QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -657,8 +651,8 @@ void NetworkTexture::handleFinishedInitialLoad() { auto kvSize = header->bytesOfKeyValueData; if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - QMetaObject::invokeMethod(this, "setImage", + qWarning() << "Cannot load " << url << ", did not receive all kv data with initial request"; + QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -669,14 +663,16 @@ void NetworkTexture::handleFinishedInitialLoad() { auto imageDescriptors = header->generateImageDescriptors(); if (imageDescriptors.size() == 0) { - qWarning(networking) << "Failed to process ktx file " << _url; - QMetaObject::invokeMethod(this, "setImage", + qWarning(networking) << "Failed to process ktx file " << url; + QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); return; } - _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); + auto originalKtxDescriptor = new ktx::KTXDescriptor(*header, keyValues, imageDescriptors); + QMetaObject::invokeMethod(resource.data(), "setOriginalDescriptor", + Q_ARG(ktx::KTXDescriptor*, originalKtxDescriptor)); // Create bare ktx in memory auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { @@ -686,7 +682,7 @@ void NetworkTexture::handleFinishedInitialLoad() { std::string hash; if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { qWarning("Invalid source hash key found, bailing"); - QMetaObject::invokeMethod(this, "setImage", + QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -717,7 +713,7 @@ void NetworkTexture::handleFinishedInitialLoad() { auto memKtx = ktx::KTX::createBare(*header, keyValues); if (!memKtx) { qWarning() << " Ktx could not be created, bailing"; - QMetaObject::invokeMethod(this, "setImage", + QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -730,8 +726,8 @@ void NetworkTexture::handleFinishedInitialLoad() { KTXFilePointer file; auto& ktxCache = textureCache->_ktxCache; if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << " failed to write cache file"; - QMetaObject::invokeMethod(this, "setImage", + qCWarning(modelnetworking) << url << " failed to write cache file"; + QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -746,7 +742,7 @@ void NetworkTexture::handleFinishedInitialLoad() { texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); - auto& images = _originalKtxDescriptor->images; + auto& images = originalKtxDescriptor->images; size_t imageSizeRemaining = ktxHighMipData.size(); const uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); ktxData += ktxHighMipData.size(); @@ -768,18 +764,12 @@ void NetworkTexture::handleFinishedInitialLoad() { texture = textureCache->cacheTextureByHash(filename, texture); } - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - _ktxResourceState = WAITING_FOR_MIP_REQUEST; - - - QMetaObject::invokeMethod(this, "setImage", + QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, texture), Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); - if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { - QMetaObject::invokeMethod(this, "startRequestForNextMipLevel"); - } + QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); }); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 0b55839319..555c43636e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -58,6 +58,8 @@ public: void refresh() override; + Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); } + signals: void networkTextureCreated(const QWeakPointer& self); From 54fc8085f38273ef1694f72ab79291fa3c450a63 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 Jun 2017 17:35:59 -0700 Subject: [PATCH 09/13] Remove pointer to file in NetworkTexture --- .../src/model-networking/TextureCache.cpp | 10 ++++------ .../src/model-networking/TextureCache.h | 3 --- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1d280aecc9..6772f5e3bd 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -732,13 +732,11 @@ void NetworkTexture::handleFinishedInitialLoad() { Q_ARG(int, 0), Q_ARG(int, 0)); return; - } else { - _file = file; } auto newKtxDescriptor = memKtx->toDescriptor(); - texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); + texture = gpu::Texture::unserialize(file->getFilepath(), newKtxDescriptor); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); @@ -933,11 +931,11 @@ void ImageReader::read() { const char* data = reinterpret_cast(memKtx->_storage->data()); size_t length = memKtx->_storage->size(); auto& ktxCache = textureCache->_ktxCache; - networkTexture->_file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length)); // - if (!networkTexture->_file) { + auto file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length)); + if (!file) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { - texture->setKtxBacking(networkTexture->_file->getFilepath()); + texture->setKtxBacking(file->getFilepath()); } } else { qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 555c43636e..b86a685dcd 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -101,9 +101,6 @@ private: bool _sourceIsKTX { false }; KTXResourceState _ktxResourceState { PENDING_INITIAL_LOAD }; - // TODO Can this be removed? - KTXFilePointer _file; - // The current mips that are currently being requested w/ _ktxMipRequest std::pair _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; From c2650c2b712929c6001a6a78ce3be32db988227c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 Jun 2017 18:14:39 -0700 Subject: [PATCH 10/13] Declare KTXDescriptor* metatype --- libraries/ktx/src/ktx/KTX.cpp | 2 ++ libraries/ktx/src/ktx/KTX.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 788ec54a47..3b1a12cba7 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -16,6 +16,8 @@ using namespace ktx; +int ktxDescriptorMetaTypeId = qRegisterMetaType(); + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index b02e2ada75..8dc4ec7a47 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -387,4 +387,6 @@ namespace ktx { } +Q_DECLARE_METATYPE(ktx::KTXDescriptor*); + #endif // hifi_ktx_KTX_h From 097e75285a94883906be3f54a93c34211fad79ca Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 16 Jun 2017 14:31:31 -0700 Subject: [PATCH 11/13] Remove extra profiling --- libraries/gpu/src/gpu/Texture_ktx.cpp | 5 ----- libraries/networking/src/FileCache.cpp | 7 +------ libraries/shared/src/shared/Storage.cpp | 7 ------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 8c185e0b22..f455fde009 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -15,7 +15,6 @@ #include #include -#include #include "GPULogging.h" @@ -243,7 +242,6 @@ uint16 KtxStorage::minAvailableMipLevel() const { } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { - PROFILE_RANGE(app, __FUNCTION__) if (level != _minMipLevelAvailable - 1) { qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level << " " << _filename.c_str(); return; @@ -285,7 +283,6 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } memcpy(imageData, storage->data(), storage->size()); - _minMipLevelAvailable = level; if (_offsetToMinMipKV > 0) { auto minMipKeyData = fileData + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV; @@ -314,7 +311,6 @@ void Texture::setKtxBacking(const std::string& filename) { ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { - PROFILE_RANGE(app, __FUNCTION__) ktx::Header header; // From texture format to ktx format description @@ -457,7 +453,6 @@ TexturePointer Texture::unserialize(const std::string& ktxfile) { } TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor) { - PROFILE_RANGE(app, __FUNCTION__) const auto& header = descriptor.header; Format mipFormat = Format::COLOR_BGRA_32; diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index c3cc55cbae..95304e3866 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -21,9 +21,8 @@ #include #include -#include #include -#include +#include #ifdef Q_OS_WIN #include @@ -88,7 +87,6 @@ FileCache::~FileCache() { } void FileCache::initialize() { - PROFILE_RANGE(app, __FUNCTION__) QDir dir(_dirpath.c_str()); if (dir.exists()) { @@ -129,7 +127,6 @@ FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) } FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bool overwrite) { - PROFILE_RANGE(app, __FUNCTION__) assert(_initialized); std::string filepath = getFilepath(metadata.key); @@ -322,7 +319,6 @@ File::File(Metadata&& metadata, const std::string& filepath) : } File::~File() { - PROFILE_RANGE(app, __FUNCTION__) QFile file(getFilepath().c_str()); if (file.exists() && !_shouldPersist) { qCInfo(file_cache, "Unlinked %s", getFilepath().c_str()); @@ -331,7 +327,6 @@ File::~File() { } void File::touch() { - PROFILE_RANGE(app, __FUNCTION__) utime(_filepath.c_str(), nullptr); _modified = std::max(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch(), _modified); } \ No newline at end of file diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 3e1b357019..b07f896df0 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -12,8 +12,6 @@ #include #include -#include "../Profile.h" - Q_LOGGING_CATEGORY(storagelogging, "hifi.core.storage") using namespace storage; @@ -50,7 +48,6 @@ MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { } StoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { - PROFILE_RANGE(app, "FileStorage::create()"); QFile file(filename); if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { throw std::runtime_error("Unable to open file for writing"); @@ -73,8 +70,6 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u } FileStorage::FileStorage(const QString& filename) : _file(filename) { - PROFILE_RANGE(app, "FileStorage()"); - bool opened = _file.open(QFile::ReadWrite); if (opened) { _hasWriteAccess = true; @@ -84,7 +79,6 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } if (opened) { - PROFILE_RANGE(app, "FileStorage() map"); _mapped = _file.map(0, _file.size()); if (_mapped) { _valid = true; @@ -97,7 +91,6 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } FileStorage::~FileStorage() { - PROFILE_RANGE(app, "~FileStorage()"); if (_mapped) { if (!_file.unmap(_mapped)) { throw std::runtime_error("Unable to unmap file"); From d090ac4396693426fc08a4d3d40f89ca4f39f3f9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 16 Jun 2017 17:04:57 -0700 Subject: [PATCH 12/13] CR --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 6772f5e3bd..864e79220f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -546,7 +546,7 @@ void NetworkTexture::ktxMipRequestFinished() { auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); - if (_ktxResourceState == REQUESTING_MIP) { + if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); From 8d45c4da7c596b138b714668168cdfe9736293b4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 19 Jun 2017 11:02:31 -0700 Subject: [PATCH 13/13] Fix comments --- .../model-networking/src/model-networking/TextureCache.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 864e79220f..982d0138b9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -469,6 +469,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } +// This is called when the header or top mips have been loaded void NetworkTexture::ktxInitialDataRequestFinished() { if (!_ktxHeaderRequest || _ktxHeaderRequest->getState() != ResourceRequest::Finished || !_ktxMipRequest || _ktxMipRequest->getState() != ResourceRequest::Finished) { @@ -604,7 +605,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxMipRequest = nullptr; } -// This is called when the header or top mips have been loaded +// This is called when the header and top mips have been loaded void NetworkTexture::handleFinishedInitialLoad() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); Q_ASSERT(!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty());