From e9258ec97a6f9718d424ffb075aea456e1ab101b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 28 Aug 2017 15:37:15 -0700 Subject: [PATCH] Fix loading standalone baked textures from Asset Server NetworkTexture was not properly handling redirected ATP files. For instance, if going from .jpg -> .ktx, the NetworkTexture class needs to be aware of this so it can stop the current request and make multiple requests for the individual mip levels. --- assignment-client/src/assets/AssetServer.cpp | 9 ++++--- libraries/baking/src/TextureBaker.cpp | 8 ++++++ libraries/baking/src/TextureBaker.h | 3 +++ .../src/model-networking/TextureCache.cpp | 21 +++++++++++++-- .../src/model-networking/TextureCache.h | 2 ++ .../networking/src/AssetResourceRequest.cpp | 27 ++++++++++++------- libraries/networking/src/ResourceCache.cpp | 3 ++- libraries/networking/src/ResourceCache.h | 3 ++- libraries/networking/src/ResourceRequest.h | 5 +++- 9 files changed, 63 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 212b9d8d19..ce9e49326d 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -49,7 +49,7 @@ static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000; const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" }; -static const QList BAKEABLE_TEXTURE_EXTENSIONS = QImageReader::supportedImageFormats(); +static QStringList BAKEABLE_TEXTURE_EXTENSIONS; static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; @@ -71,7 +71,8 @@ void BakeAssetTask::run() { }; } else { baker = std::unique_ptr { - new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, PathUtils::generateTemporaryDir()) + new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, + PathUtils::generateTemporaryDir()) }; } @@ -198,7 +199,7 @@ bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHa auto extension = path.mid(dotIndex + 1); QString bakedFilename; - + if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { bakedFilename = BAKED_MODEL_SIMPLE_NAME; } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(assetHash)) { @@ -247,6 +248,8 @@ AssetServer::AssetServer(ReceivedMessage& message) : _transferTaskPool(this), _bakingTaskPool(this) { + BAKEABLE_TEXTURE_EXTENSIONS = TextureBaker::getSupportedFormats(); + qDebug() << "Supported baking texture formats:" << BAKEABLE_MODEL_EXTENSIONS; // Most of the work will be I/O bound, reading from disk and constructing packet objects, // so the ideal is greater than the number of cores on the system. diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 44995fa026..f80af66aa2 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -43,6 +43,14 @@ void TextureBaker::bake() { loadTexture(); } +const QStringList TextureBaker::getSupportedFormats() { + auto formats = QImageReader::supportedImageFormats(); + QStringList stringFormats; + std::transform(formats.begin(), formats.end(), std::back_inserter(stringFormats), + [](auto& format) -> QString { return format; }); + return stringFormats; +} + void TextureBaker::loadTexture() { // check if the texture is local or first needs to be downloaded if (_textureURL.isLocalFile()) { diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h index ee1e968f20..76d0a69823 100644 --- a/libraries/baking/src/TextureBaker.h +++ b/libraries/baking/src/TextureBaker.h @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -28,6 +29,8 @@ class TextureBaker : public Baker { public: TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory); + static const QStringList getSupportedFormats(); + const QByteArray& getOriginalTexture() const { return _originalTexture; } QUrl getTextureURL() const { return _textureURL; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 5b43a4090f..cc29e841ce 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -299,6 +299,8 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _textureSource = std::make_shared(); _lowestRequestedMipLevel = 0; + _shouldFailOnRedirect = !_sourceIsKTX; + if (type == image::TextureUsage::CUBE_TEXTURE) { setLoadPriority(this, SKYBOX_LOAD_PRIORITY); } else if (_sourceIsKTX) { @@ -428,6 +430,21 @@ void NetworkTexture::makeRequest() { } +bool NetworkTexture::handleFailedRequest(ResourceRequest::Result result) { + if (!_sourceIsKTX && result == ResourceRequest::Result::RedirectFail) { + auto newPath = _request->getRelativePathUrl(); + if (newPath.fileName().endsWith(".ktx")) { + qDebug() << "Redirecting to" << newPath << "from" << _url; + _sourceIsKTX = true; + _activeUrl = newPath; + _shouldFailOnRedirect = false; + makeRequest(); + return true; + } + } + return Resource::handleFailedRequest(result); +} + void NetworkTexture::startRequestForNextMipLevel() { auto self = _self.lock(); if (!self) { @@ -527,7 +544,7 @@ void NetworkTexture::ktxInitialDataRequestFinished() { _ktxHighMipData = _ktxMipRequest->getData(); handleFinishedInitialLoad(); } else { - if (handleFailedRequest(result)) { + if (Resource::handleFailedRequest(result)) { _ktxResourceState = PENDING_INITIAL_LOAD; } else { _ktxResourceState = FAILED_TO_LOAD; @@ -616,7 +633,7 @@ void NetworkTexture::ktxMipRequestFinished() { finishedLoading(false); } } else { - if (handleFailedRequest(result)) { + if (Resource::handleFailedRequest(result)) { _ktxResourceState = PENDING_MIP_REQUEST; } else { _ktxResourceState = FAILED_TO_LOAD; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index f5a0ec5215..5bc5aa7d96 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -75,6 +75,8 @@ protected: virtual void downloadFinished(const QByteArray& data) override; + bool handleFailedRequest(ResourceRequest::Result result) 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/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 01fe971125..c2e0bc1215 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -82,19 +82,23 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { Q_ASSERT(_state == InProgress); Q_ASSERT(request == _assetMappingRequest); + bool failed = false; + switch (request->getError()) { case MappingRequest::NoError: // we have no error, we should have a resulting hash - use that to send of a request for that asset qCDebug(networking) << "Got mapping for:" << path << "=>" << request->getHash(); - requestHash(request->getHash()); - statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_SUCCESS); // if we got a redirected path we need to store that with the resource request as relative path URL - if (request->wasRedirected()) { - qDebug() << "Request was redirected"; + if (request->wasRedirected() && _failOnRedirect) { _relativePathURL = ATP_SCHEME + request->getRedirectedPath(); + _result = RedirectFail; + + failed = true; + } else { + requestHash(request->getHash()); } break; @@ -113,17 +117,20 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { break; } - // since we've failed we know we are finished - _state = Finished; - emit finished(); - - statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_FAILED); - statTracker->incrementStat(STAT_ATP_REQUEST_FAILED); + failed = true; break; } } + if (failed) { + _state = Finished; + emit finished(); + + statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_FAILED); + statTracker->incrementStat(STAT_ATP_REQUEST_FAILED); + } + _assetMappingRequest->deleteLater(); _assetMappingRequest = nullptr; }); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index b7ab1269f9..fbe7c05801 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -687,8 +687,9 @@ void Resource::makeRequest() { PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; } - + _request->setByteRange(_requestByteRange); + _request->setFailOnRedirect(_shouldFailOnRedirect); qCDebug(resourceLog).noquote() << "Starting request for:" << _url.toDisplayString(); emit loading(); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 40df6418a5..17531f45b0 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -449,12 +449,13 @@ protected: Q_INVOKABLE void allReferencesCleared(); /// Return true if the resource will be retried - bool handleFailedRequest(ResourceRequest::Result result); + virtual bool handleFailedRequest(ResourceRequest::Result result); QUrl _url; QUrl _effectiveBaseURL{ _url }; QUrl _activeUrl; ByteRange _requestByteRange; + bool _shouldFailOnRedirect { false }; // _loaded == true means we are in a loaded and usable state. It is possible that there may still be // active requests/loading while in this state. Example: Progressive KTX downloads, where higher resolution diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 0a4dc12293..cf852c1e1b 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -57,7 +57,8 @@ public: AccessDenied, InvalidByteRange, InvalidURL, - NotFound + NotFound, + RedirectFail }; Q_ENUM(Result) @@ -70,6 +71,7 @@ public: bool loadedFromCache() const { return _loadedFromCache; } bool getRangeRequestSuccessful() const { return _rangeRequestSuccessful; } bool getTotalSizeOfResource() const { return _totalSizeOfResource; } + void setFailOnRedirect(bool failOnRedirect) { _failOnRedirect = failOnRedirect; } void setCacheEnabled(bool value) { _cacheEnabled = value; } void setByteRange(ByteRange byteRange) { _byteRange = byteRange; } @@ -89,6 +91,7 @@ protected: State _state { NotStarted }; Result _result; QByteArray _data; + bool _failOnRedirect { false }; bool _cacheEnabled { true }; bool _loadedFromCache { false }; ByteRange _byteRange;