From 105d17e85ef36a70208b1a354b5d3ff6d0f7609f Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Fri, 31 Mar 2017 15:07:34 -0700 Subject: [PATCH 01/85] Add byte range support to HTTPResourceRequest --- interface/src/Application.cpp | 9 +++++++++ interface/src/ui/overlays/Image3DOverlay.cpp | 1 + libraries/ktx/src/ktx/KTX.h | 2 ++ .../src/model-networking/TextureCache.cpp | 8 +++++++- .../model-networking/src/model-networking/TextureCache.h | 1 + libraries/networking/src/HTTPResourceRequest.cpp | 6 ++++++ libraries/networking/src/ResourceCache.cpp | 2 ++ libraries/networking/src/ResourceCache.h | 1 + libraries/networking/src/ResourceManager.h | 1 + libraries/networking/src/ResourceRequest.h | 9 +++++++++ libraries/script-engine/src/ScriptEngine.cpp | 6 ++++-- 11 files changed, 43 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f51c3d2b46..886487603e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -188,6 +188,7 @@ #include <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h> #include <EntityScriptClient.h> #include <ModelScriptingInterface.h> +#include <QtNetwork/QNetworkProxy> // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -604,6 +605,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo { const QString TEST_SCRIPT = "--testScript"; const QString TRACE_FILE = "--traceFile"; + const QString HTTP_PROXY = "--httpProxy"; const QStringList args = arguments(); for (int i = 0; i < args.size() - 1; ++i) { if (args.at(i) == TEST_SCRIPT) { @@ -615,10 +617,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QString traceFilePath = args.at(i + 1); setProperty(hifi::properties::TRACING, traceFilePath); DependencyManager::get<tracing::Tracer>()->startTracing(); + } else if (args.at(i) == HTTP_PROXY) { } } } + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("127.0.0.1"); + proxy.setPort(8888); + QNetworkProxy::setApplicationProxy(proxy); + // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 45d63d9cf1..f06efcef1c 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -53,6 +53,7 @@ void Image3DOverlay::update(float deltatime) { } void Image3DOverlay::render(RenderArgs* args) { + qDebug() << _url; if (!_isLoaded) { _isLoaded = true; _texture = DependencyManager::get<TextureCache>()->getTexture(_url); diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 043de573ed..7eab67c0db 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -379,6 +379,8 @@ namespace ktx { void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); } }; + static const size_t KTX_HEADER_SIZE = 64; + static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static"); // Key Values struct KeyValue { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f6e256bb06..45000b30a8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -266,7 +266,8 @@ QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSh NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : Resource(url), _type(type), - _maxNumPixels(maxNumPixels) + _maxNumPixels(maxNumPixels), + _sourceIsKTX(url.path().endsWith(".ktx")) { _textureSource = std::make_shared<gpu::TextureSource>(); @@ -274,6 +275,11 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _loaded = true; } + if (_sourceIsKTX) { + _requestByteRange.fromInclusive = 0; + _requestByteRange.toExclusive = 1000; + } + // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index d0600c3dce..d0916d61eb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -73,6 +73,7 @@ private: image::TextureUsage::Type _type; KTXFilePointer _file; + bool _sourceIsKTX { false }; int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 85da5de5b8..28d0485383 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -59,6 +59,12 @@ void HTTPResourceRequest::doSend() { networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); } + if (_byteRange.isSet()) { + auto byteRange = QString("bytes={}-{}").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + networkRequest.setRawHeader("Range", byteRange.toLatin1()); + } + networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + _reply = NetworkAccessManager::getInstance().get(networkRequest); connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 4031ff8bf7..7fdbc7885b 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -676,6 +676,8 @@ void Resource::makeRequest() { return; } + _request->setByteRange(_requestByteRange); + 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 53ccd2c386..32364bf71c 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -442,6 +442,7 @@ protected: QUrl _url; QUrl _activeUrl; + ByteRange _requestByteRange; bool _startedLoading = false; bool _failedToLoad = false; bool _loaded = false; diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index 162892abaf..d193c39cae 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -26,6 +26,7 @@ const QString URL_SCHEME_ATP = "atp"; class ResourceManager { public: + static void setUrlPrefixOverride(const QString& prefix, const QString& replacement); static QString normalizeURL(const QString& urlString); static QUrl normalizeURL(const QUrl& url); diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 7588fca046..03b46e715d 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -17,6 +17,13 @@ #include <cstdint> +struct ByteRange { + int64_t fromInclusive { 0 }; + int64_t toExclusive { 0 }; + + bool isSet() { return fromInclusive < -1 || fromInclusive < toExclusive; } +}; + class ResourceRequest : public QObject { Q_OBJECT public: @@ -48,6 +55,7 @@ public: bool loadedFromCache() const { return _loadedFromCache; } void setCacheEnabled(bool value) { _cacheEnabled = value; } + void setByteRange(ByteRange byteRange) { _byteRange = byteRange; } public slots: void send(); @@ -65,6 +73,7 @@ protected: QByteArray _data; bool _cacheEnabled { true }; bool _loadedFromCache { false }; + ByteRange _byteRange; }; #endif diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 067c7c1412..575d23c4c4 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2317,6 +2317,8 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR if (_entityScripts.contains(entityID)) { const EntityScriptDetails &oldDetails = _entityScripts[entityID]; + auto scriptText = oldDetails.scriptText; + if (isEntityScriptRunning(entityID)) { callEntityScriptMethod(entityID, "unload"); } @@ -2334,14 +2336,14 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR newDetails.status = EntityScriptStatus::UNLOADED; newDetails.lastModified = QDateTime::currentMSecsSinceEpoch(); // keep scriptText populated for the current need to "debouce" duplicate calls to unloadEntityScript - newDetails.scriptText = oldDetails.scriptText; + newDetails.scriptText = scriptText; setEntityScriptDetails(entityID, newDetails); } stopAllTimersForEntityScript(entityID); { // FIXME: shouldn't have to do this here, but currently something seems to be firing unloads moments after firing initial load requests - processDeferredEntityLoads(oldDetails.scriptText, entityID); + processDeferredEntityLoads(scriptText, entityID); } } } From 00cbfa0f70c0776a0e38739e7e9d692ecfa5e9ec Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Fri, 7 Apr 2017 16:48:22 -0700 Subject: [PATCH 02/85] Add start of progressive ktx-loading --- interface/src/ui/overlays/Image3DOverlay.cpp | 1 - libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 8 ++- libraries/gpu/src/gpu/Texture.h | 10 ++- libraries/gpu/src/gpu/Texture_ktx.cpp | 11 +++ libraries/ktx/src/ktx/KTX.cpp | 23 +++++- libraries/ktx/src/ktx/KTX.h | 11 ++- libraries/ktx/src/ktx/Writer.cpp | 62 ++++++++++++++++ .../src/model-networking/TextureCache.cpp | 72 +++++++++++++++++++ .../src/model-networking/TextureCache.h | 8 +++ .../networking/src/HTTPResourceRequest.cpp | 53 +++++++++++++- .../networking/src/NetworkAccessManager.cpp | 10 ++- libraries/networking/src/ResourceRequest.h | 4 ++ libraries/shared/src/shared/Storage.cpp | 1 + libraries/shared/src/shared/Storage.h | 1 + 14 files changed, 264 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index f06efcef1c..45d63d9cf1 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -53,7 +53,6 @@ void Image3DOverlay::update(float deltatime) { } void Image3DOverlay::render(RenderArgs* args) { - qDebug() << _url; if (!_isLoaded) { _isLoaded = true; _texture = DependencyManager::get<TextureCache>()->getTexture(_url); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index a6e6bf4fa3..b7d2ee0b0f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -217,8 +217,12 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t _transferSize = mipSize; _bufferingLambda = [=] { auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData(), _transferSize); + if (!mipData) { + qWarning() << "Mip not available: " << sourceMip; + } else { + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData(), _transferSize); + } _bufferingCompleted = true; }; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 2f63bd6719..756748497d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -28,6 +28,8 @@ namespace ktx { struct KTXDescriptor; using KTXDescriptorPointer = std::unique_ptr<KTXDescriptor>; struct Header; + struct KeyValue; + using KeyValues = std::list<KeyValue>; } namespace gpu { @@ -503,9 +505,15 @@ public: ExternalUpdates getUpdates() const; - // Textures can be serialized directly to ktx data file, here is how + // Serialize ktx header and keyvalues directly to a file, and return a Texture representing that file + static Texture* serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues); + + // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); + static TexturePointer unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); + static TexturePointer unserialize(const std::string& ktxFile, const ktx::KTXDescriptor& descriptor, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); + static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 50e9cb6d07..db6808a866 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -207,6 +207,10 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, TextureUsageType } ktx::KTXDescriptor descriptor { ktxPointer->toDescriptor() }; + return unserialize(ktxfile, ktxPointer->toDescriptor(), usageType, usage, sampler); +} + +TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { const auto& header = descriptor.header; Format mipFormat = Format::COLOR_BGRA_32; @@ -256,6 +260,13 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, TextureUsageType return tex; } +Texture* Texture::serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues) { + // Create a memory-backed KTX object + auto ktxBuffer = ktx::KTX::createBare(header, keyValues); + + return unserialize(ktxfile, ktxBuffer->toDescriptor()); +} + bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 6fca39788b..0580d9a8c3 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -45,7 +45,7 @@ uint32_t Header::evalPixelDepth(uint32_t level) const { } size_t Header::evalPixelSize() const { - return glTypeSize; // Really we should generate the size from the FOrmat etc + return 4;//glTypeSize; // Really we should generate the size from the FOrmat etc } size_t Header::evalRowSize(uint32_t level) const { @@ -70,6 +70,25 @@ size_t Header::evalImageSize(uint32_t level) const { } } +ImageDescriptors Header::generateImageDescriptors() const { + ImageDescriptors descriptors; + + for (auto level = 0; level < numberOfMipmapLevels; ++level) { + ImageHeader header { + numberOfFaces == NUM_CUBEMAPFACES, + static_cast<uint32_t>(evalImageSize(level)), + 0 + }; + ImageHeader::FaceOffsets offsets; + for (auto i = 0; i < numberOfFaces; ++i) { + offsets.push_back(0); + } + descriptors.push_back(ImageDescriptor(header, offsets)); + } + + return descriptors; +} + KeyValue::KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value) : _byteSize((uint32_t) key.size() + 1 + valueByteSize), // keyString size + '\0' ending char + the value size @@ -209,4 +228,4 @@ KTXDescriptor KTX::toDescriptor() const { KTX::KTX(const StoragePointer& storage, const Header& header, const KeyValues& keyValues, const Images& images) : _header(header), _storage(storage), _keyValues(keyValues), _images(images) { -} \ No newline at end of file +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 7eab67c0db..fb8927eca0 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -292,6 +292,9 @@ namespace ktx { using Storage = storage::Storage; using StoragePointer = std::shared_ptr<Storage>; + struct ImageDescriptor; + using ImageDescriptors = std::vector<ImageDescriptor>; + // Header struct Header { static const size_t IDENTIFIER_LENGTH = 12; @@ -378,6 +381,7 @@ namespace ktx { void setCube(uint32_t width, uint32_t height) { setDimensions(width, height, 0, 0, NUM_CUBEMAPFACES); } void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); } + ImageDescriptors generateImageDescriptors() const; }; static const size_t KTX_HEADER_SIZE = 64; static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static"); @@ -421,14 +425,14 @@ namespace ktx { struct Image; + // Image without the image data itself struct ImageDescriptor : public ImageHeader { const FaceOffsets _faceOffsets; ImageDescriptor(const ImageHeader& header, const FaceOffsets& offsets) : ImageHeader(header), _faceOffsets(offsets) {} Image toImage(const ktx::StoragePointer& storage) const; }; - using ImageDescriptors = std::vector<ImageDescriptor>; - + // Image with the image data itself struct Image : public ImageHeader { FaceBytes _faceBytes; Image(const ImageHeader& header, const FaceBytes& faces) : ImageHeader(header), _faceBytes(faces) {} @@ -473,6 +477,7 @@ namespace ktx { // This path allocate the Storage where to store header, keyvalues and copy mips // Then COPY all the data static std::unique_ptr<KTX> create(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static std::unique_ptr<KTX> createBare(const Header& header, const KeyValues& keyValues = KeyValues()); // Instead of creating a full Copy of the src data in a KTX object, the write serialization can be performed with the // following two functions @@ -486,7 +491,9 @@ namespace ktx { // // This is exactly what is done in the create function static size_t evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static size_t evalStorageSize(const Header& header, const ImageDescriptors& images, const KeyValues& keyValues = KeyValues()); static size_t write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static size_t writeWithoutImages(Byte* destBytes, size_t destByteSize, const Header& header, const ImageDescriptors& descriptors, const KeyValues& keyValues = KeyValues()); static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues); static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 25b363d31b..396aa09f9a 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -40,6 +40,20 @@ namespace ktx { return create(storagePointer); } + std::unique_ptr<KTX> KTX::createBare(const Header& header, const KeyValues& keyValues) { + auto descriptors = header.generateImageDescriptors(); + + StoragePointer storagePointer; + { + auto storageSize = ktx::KTX::evalStorageSize(header, descriptors, keyValues); + auto memoryStorage = new storage::MemoryStorage(storageSize); + qDebug() << "Memory storage size is: " << storageSize; + ktx::KTX::writeWithoutImages(memoryStorage->data(), memoryStorage->size(), header, descriptors, keyValues); + storagePointer.reset(memoryStorage); + } + return create(storagePointer); + } + size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { size_t storageSize = sizeof(Header); @@ -59,6 +73,25 @@ namespace ktx { return storageSize; } + size_t KTX::evalStorageSize(const Header& header, const ImageDescriptors& imageDescriptors, const KeyValues& keyValues) { + size_t storageSize = sizeof(Header); + + if (!keyValues.empty()) { + size_t keyValuesSize = KeyValue::serializedKeyValuesByteSize(keyValues); + storageSize += keyValuesSize; + } + + auto numMips = header.getNumberOfLevels(); + for (uint32_t l = 0; l < numMips; l++) { + if (imageDescriptors.size() > l) { + storageSize += sizeof(uint32_t); + storageSize += imageDescriptors[l]._imageSize; + storageSize += Header::evalPadding(imageDescriptors[l]._imageSize); + } + } + return storageSize; + } + size_t KTX::write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& srcImages, const KeyValues& keyValues) { // Check again that we have enough destination capacity if (!destBytes || (destByteSize < evalStorageSize(header, srcImages, keyValues))) { @@ -87,6 +120,35 @@ namespace ktx { return destByteSize; } + size_t KTX::writeWithoutImages(Byte* destBytes, size_t destByteSize, const Header& header, const ImageDescriptors& descriptors, const KeyValues& keyValues) { + // Check again that we have enough destination capacity + if (!destBytes || (destByteSize < evalStorageSize(header, descriptors, keyValues))) { + return 0; + } + + auto currentDestPtr = destBytes; + // Header + auto destHeader = reinterpret_cast<Header*>(currentDestPtr); + memcpy(currentDestPtr, &header, sizeof(Header)); + currentDestPtr += sizeof(Header); + + // KeyValues + if (!keyValues.empty()) { + destHeader->bytesOfKeyValueData = (uint32_t) writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); + } else { + // Make sure the header contains the right bytesOfKeyValueData size + destHeader->bytesOfKeyValueData = 0; + } + currentDestPtr += destHeader->bytesOfKeyValueData; + + for (int i = 0; i < descriptors.size(); ++i) { + *currentDestPtr = descriptors[i]._imageSize; + currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t); + } + + return destByteSize; + } + uint32_t KeyValue::writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval) { uint32_t keyvalSize = keyval.serializedByteSize(); if (keyvalSize > destByteSize) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 45000b30a8..6b6fc09975 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -335,6 +335,77 @@ void NetworkTexture::downloadFinished(const QByteArray& data) { } void NetworkTexture::loadContent(const QByteArray& content) { + if (_sourceIsKTX) { + if (_ktxLoadState == LOADING_HEADER) { + // TODO Handle case where we already have the source hash texture on disk + // TODO Handle case where data isn't as large as the ktx header + _ktxLoadState = LOADING_LOWEST_SIX; + auto header = reinterpret_cast<const ktx::Header*>(content.data()); + qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); + qDebug() << "Type:" << header->glType; + qDebug() << "TypeSize:" << header->glTypeSize; + qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; + qDebug() << "numberOfFaces:" << header->numberOfFaces; + qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > content.size() - ktx::KTX_HEADER_SIZE) { + qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(content.data()) + ktx::KTX_HEADER_SIZE); + + // Create bare ktx in memory + std::string filename = "test"; + auto memKtx = ktx::KTX::createBare(*header, keyValues); + + + auto textureCache = DependencyManager::get<TextureCache>(); + + // Move ktx to file + const char* data = reinterpret_cast<const char*>(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 << "file cache failed"; + } else { + _file = file; + } + + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); + gpu::TexturePointer texture; + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor())); + texture->setKtxBacking(file->getFilepath()); + + // 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 + if (textureCache) { + texture = textureCache->cacheTextureByHash(filename, texture); + } + + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + + + auto desc = memKtx->toDescriptor(); + int numMips = desc.images.size(); + auto numMipsToGet = glm::min(numMips, 6); + auto sizeOfTopMips = 0; + for (int i = 0; i < numMipsToGet; ++i) { + auto& img = desc.images[i]; + sizeOfTopMips += img._imageSize; + } + _requestByteRange.fromInclusive = length - sizeOfTopMips; + _requestByteRange.toExclusive = length; + attemptRequest(); + + } else { + qDebug() << "Got highest 6 mips"; + } + return; + } + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); } @@ -457,6 +528,7 @@ void ImageReader::read() { if (texture && textureCache) { auto memKtx = gpu::Texture::serialize(*texture); + // Move the texture into a memory mapped file if (memKtx) { const char* data = reinterpret_cast<const char*>(memKtx->_storage->data()); size_t length = memKtx->_storage->size(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index d0916d61eb..40eb29de35 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -72,6 +72,14 @@ private: friend class ImageReader; image::TextureUsage::Type _type; + + enum KTXLoadState { + LOADING_HEADER, + LOADING_LOWEST_SIX, + DONE_LOADING + }; + + KTXLoadState _ktxLoadState { LOADING_HEADER }; KTXFilePointer _file; bool _sourceIsKTX { false }; int _originalWidth { 0 }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 28d0485383..5932193830 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -60,7 +60,7 @@ void HTTPResourceRequest::doSend() { } if (_byteRange.isSet()) { - auto byteRange = QString("bytes={}-{}").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + auto byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); networkRequest.setRawHeader("Range", byteRange.toLatin1()); } networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); @@ -78,12 +78,61 @@ void HTTPResourceRequest::onRequestFinished() { Q_ASSERT(_reply); cleanupTimer(); - + + // Content-Range headers have the form: + // + // Content-Range: <unit> <range-start>-<range-end>/<size> + // Content-Range: <unit> <range-start>-<range-end>/* + // Content-Range: <unit> */<size> + // + auto parseContentRangeHeader = [](QString contentRangeHeader) -> std::pair<bool, uint64_t> { + auto unitRangeParts = contentRangeHeader.split(' '); + if (unitRangeParts.size() != 2) { + return { false, 0 }; + } + + auto rangeSizeParts = unitRangeParts[1].split('/'); + if (rangeSizeParts.size() != 2) { + return { false, 0 }; + } + + auto sizeStr = rangeSizeParts[1]; + if (sizeStr == "*") { + return { true, 0 }; + } else { + bool ok; + auto size = sizeStr.toLong(&ok); + return { ok, size }; + } + }; + switch(_reply->error()) { case QNetworkReply::NoError: _data = _reply->readAll(); _loadedFromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); _result = Success; + + if (_byteRange.isSet()) { + auto statusCode = _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 206) { + _rangeRequestSuccessful = true; + auto contentRangeHeader = _reply->rawHeader("Content-Range"); + bool success; + uint64_t size; + std::tie(success, size) = parseContentRangeHeader(contentRangeHeader); + if (success) { + qWarning(networking) << "Total http resource size is: " << size; + _totalSizeOfResource = size; + } else { + qWarning(networking) << "Error parsing content-range header: " << contentRangeHeader; + _totalSizeOfResource = 0; + } + } else { + _rangeRequestSuccessful = false; + _totalSizeOfResource = _data.size(); + } + } + break; case QNetworkReply::TimeoutError: diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 73096825e0..6895118be5 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -13,12 +13,20 @@ #include "AtpReply.h" #include "NetworkAccessManager.h" +#include <QtNetwork/QNetworkProxy> QThreadStorage<QNetworkAccessManager*> networkAccessManagers; QNetworkAccessManager& NetworkAccessManager::getInstance() { if (!networkAccessManagers.hasLocalData()) { - networkAccessManagers.setLocalData(new QNetworkAccessManager()); + auto nm = new QNetworkAccessManager(); + networkAccessManagers.setLocalData(nm); + + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("127.0.0.1"); + proxy.setPort(8888); + nm->setProxy(proxy); } return *networkAccessManagers.localData(); diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 03b46e715d..25db34ab0a 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -53,6 +53,8 @@ public: QString getResultString() const; QUrl getUrl() const { return _url; } bool loadedFromCache() const { return _loadedFromCache; } + bool getRangeRequestSuccessful() const { return _rangeRequestSuccessful; } + bool getTotalSizeOfResource() const { return _totalSizeOfResource; } void setCacheEnabled(bool value) { _cacheEnabled = value; } void setByteRange(ByteRange byteRange) { _byteRange = byteRange; } @@ -74,6 +76,8 @@ protected: bool _cacheEnabled { true }; bool _loadedFromCache { false }; ByteRange _byteRange; + bool _rangeRequestSuccessful { false }; + uint64_t _totalSizeOfResource { 0 }; }; #endif diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 3c46347a49..8999caf1e8 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -67,6 +67,7 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u return std::make_shared<FileStorage>(filename); } +// Represents a memory mapped file FileStorage::FileStorage(const QString& filename) : _file(filename) { if (_file.open(QFile::ReadOnly)) { _mapped = _file.map(0, _file.size()); diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 306984040f..46f45cfdc5 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -20,6 +20,7 @@ namespace storage { class Storage; using StoragePointer = std::shared_ptr<const Storage>; + // Abstract class to represent memory that stored _somewhere_ (in system memory or in a file, for example) class Storage : public std::enable_shared_from_this<Storage> { public: virtual ~Storage() {} From 1fec531c68418b6320924cb85c522c4457a9f25f Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Mon, 10 Apr 2017 07:12:49 -0700 Subject: [PATCH 03/85] Add basic mip writing to gpu::Texture --- interface/src/Application.cpp | 2 +- libraries/gpu/src/gpu/Texture.h | 2 ++ libraries/ktx/src/ktx/Writer.cpp | 9 +++++++-- .../src/model-networking/TextureCache.cpp | 3 ++- libraries/networking/src/ResourceCache.cpp | 1 + 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 886487603e..8703c09497 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -626,7 +626,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo proxy.setType(QNetworkProxy::HttpProxy); proxy.setHostName("127.0.0.1"); proxy.setPort(8888); - QNetworkProxy::setApplicationProxy(proxy); + //QNetworkProxy::setApplicationProxy(proxy); // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 756748497d..b01ec8f0bc 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -270,6 +270,7 @@ public: virtual void reset() = 0; virtual PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; virtual Size getMipFaceSize(uint16 level, uint8 face = 0) const = 0; + virtual void assignMipData(uint16 level, const char* data, const size_t length) = 0; virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; @@ -296,6 +297,7 @@ public: void reset() override; PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; + void assignMipData(uint16 level, const char* data, const size_t length) override; void assignMipData(uint16 level, const storage::StoragePointer& storage) override; void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; bool isMipAvailable(uint16 level, uint8 face = 0) const override; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 396aa09f9a..e2803a2258 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -141,8 +141,13 @@ namespace ktx { } currentDestPtr += destHeader->bytesOfKeyValueData; - for (int i = 0; i < descriptors.size(); ++i) { - *currentDestPtr = descriptors[i]._imageSize; + for (size_t i = 0; i < descriptors.size(); ++i) { + auto ptr = reinterpret_cast<uint32_t*>(currentDestPtr); + *ptr = descriptors[i]._imageSize; + ptr++; + for (size_t k = 0; k < descriptors[i]._imageSize/4; k++) { + *(ptr + k) = 0xFFFF0000; + } currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 6b6fc09975..593a7c163e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -393,7 +393,8 @@ void NetworkTexture::loadContent(const QByteArray& content) { auto numMipsToGet = glm::min(numMips, 6); auto sizeOfTopMips = 0; for (int i = 0; i < numMipsToGet; ++i) { - auto& img = desc.images[i]; + auto mipLevel = numMips - 1 - i; + auto& img = desc.images[mipLevel]; sizeOfTopMips += img._imageSize; } _requestByteRange.fromInclusive = length - sizeOfTopMips; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 7fdbc7885b..8373a9cf99 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -665,6 +665,7 @@ void Resource::makeRequest() { } PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); + qDebug() << "Making request to " << _url << " for byte range " << _requestByteRange.fromInclusive << "-" << _requestByteRange.toExclusive; _request = ResourceManager::createResourceRequest(this, _activeUrl); From ccd9c4697b2bf67f1c6912effcd5f0a40f9144aa Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 11 Apr 2017 22:44:43 -0700 Subject: [PATCH 04/85] Add extended ktx header/high-mip request handling to NetworkTexture --- interface/src/Application.cpp | 2 +- libraries/gpu/src/gpu/Texture.h | 13 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 20 +++ .../src/model-networking/TextureCache.cpp | 160 ++++++++++++++++++ .../src/model-networking/TextureCache.h | 21 +++ .../networking/src/HTTPResourceRequest.cpp | 7 +- libraries/networking/src/ResourceCache.cpp | 60 ++++--- libraries/networking/src/ResourceCache.h | 19 ++- libraries/networking/src/ResourceRequest.h | 2 +- 9 files changed, 257 insertions(+), 47 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8703c09497..886487603e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -626,7 +626,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo proxy.setType(QNetworkProxy::HttpProxy); proxy.setHostName("127.0.0.1"); proxy.setPort(8888); - //QNetworkProxy::setApplicationProxy(proxy); + QNetworkProxy::setApplicationProxy(proxy); // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index b01ec8f0bc..e82566b852 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -270,7 +270,6 @@ public: virtual void reset() = 0; virtual PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; virtual Size getMipFaceSize(uint16 level, uint8 face = 0) const = 0; - virtual void assignMipData(uint16 level, const char* data, const size_t length) = 0; virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; @@ -297,7 +296,6 @@ public: void reset() override; PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; - void assignMipData(uint16 level, const char* data, const size_t length) override; void assignMipData(uint16 level, const storage::StoragePointer& storage) override; void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; bool isMipAvailable(uint16 level, uint8 face = 0) const override; @@ -313,15 +311,12 @@ public: PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; // By convention, all mip levels and faces MUST be populated when using KTX backing - bool isMipAvailable(uint16 level, uint8 face = 0) const override { return true; } + bool isMipAvailable(uint16 level, uint8 face = 0) const override; - void assignMipData(uint16 level, const storage::StoragePointer& storage) override { - throw std::runtime_error("Invalid call"); - } + void assignMipData(uint16 level, const storage::StoragePointer& storage) override; + + void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; - void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override { - throw std::runtime_error("Invalid call"); - } void reset() override { } protected: diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index db6808a866..487d32f91d 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -72,6 +72,25 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { return _ktxDescriptor->getMipFaceTexelsSize(level, face); } + +bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { + auto numLevels = _ktxDescriptor->header.numberOfMipmapLevels; + auto minLevel = 7 > numLevels ? 0 : numLevels - 7; + auto avail = level >= minLevel; + qDebug() << "isMipAvailable: " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; + //return true; + return level > _ktxDescriptor->header.numberOfMipmapLevels - 7; +} + +void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { + throw std::runtime_error("Invalid call"); +} + +void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) { + throw std::runtime_error("Invalid call"); +} + + void Texture::setKtxBacking(const std::string& filename) { // Check the KTX file for validity before using it as backing storage { @@ -86,6 +105,7 @@ void Texture::setKtxBacking(const std::string& filename) { setStorage(newBacking); } + ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { ktx::Header header; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 593a7c163e..f4fe192a4a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -330,6 +330,166 @@ private: int _maxNumPixels; }; +const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits<uint16_t>::max(); +void NetworkTexture::makeRequest() { + if (!_sourceIsKTX) { + Resource::makeRequest(); + return; + } + + // We special-handle ktx requests to run 2 concurrent requests right off the bat + PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); + + if (!_ktxHeaderLoaded) { + qDebug() << ">>> Making request to " << _url << " for header"; + _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); + + if (!_ktxHeaderRequest) { + //qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + ResourceCache::requestCompleted(_self); + finishedLoading(false); + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); + return; + } + + ByteRange range; + range.fromInclusive = 0; + range.toExclusive = 1000; + _ktxHeaderRequest->setByteRange(range); + + //qCDebug(resourceLog).noquote() << "Starting request for:" << _url.toDisplayString(); + emit loading(); + + connect(_ktxHeaderRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxHeaderRequestProgress); + //connect(this, &Resource::onProgress, this, &NetworkTexture::ktxHeaderRequestFinished); + + connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxHeaderRequestFinished); + + _bytesReceived = _bytesTotal = _bytes = 0; + + _ktxHeaderRequest->send(); + } + + startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); +} + +// Load mips in the range [low, high] (inclusive) +void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { + if (_ktxMipRequest) { + return; + } + + bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; + + if (!isHighMipRequest && !_ktxHeaderLoaded) { + return; + } + + _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); + qDebug() << ">>> Making request to " << _url << " for " << low << " to " << high; + + if (isHighMipRequest) { + // This is a special case where we load the high 7 mips + ByteRange range; + range.fromInclusive = -15000; + _ktxMipRequest->setByteRange(range); + } else { + // TODO: Discover range for other mips + } + + connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress); + connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished); + + _ktxMipRequest->send(); +} + + +void NetworkTexture::ktxHeaderRequestFinished() { + assert(!_ktxHeaderLoaded); + + if (_ktxHeaderRequest->getResult() == ResourceRequest::Success) { + _ktxHeaderLoaded = true; + _ktxHeaderData = _ktxHeaderRequest->getData(); + maybeCreateKTX(); + } else { + handleFailedRequest(_ktxHeaderRequest->getResult()); + } + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; +} + +void NetworkTexture::ktxMipRequestFinished() { + bool isHighMipRequest = _ktxMipLevelRangeInFlight.first == NULL_MIP_LEVEL + && _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL; + + if (_ktxMipRequest->getResult() == ResourceRequest::Success) { + _ktxHighMipData = _ktxMipRequest->getData(); + maybeCreateKTX(); + } else { + handleFailedRequest(_ktxHeaderRequest->getResult()); + } + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; +} + +// This is called when the header or top mips have been loaded +void NetworkTexture::maybeCreateKTX() { + qDebug() << "Maybe create ktx..."; + if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { + // create ktx... + auto header = reinterpret_cast<const ktx::Header*>(_ktxHeaderData.data()); + + qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); + qDebug() << "Type:" << header->glType; + qDebug() << "TypeSize:" << header->glTypeSize; + qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; + qDebug() << "numberOfFaces:" << header->numberOfFaces; + qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + 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"; + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + // Create bare ktx in memory + std::string filename = "test"; + auto memKtx = ktx::KTX::createBare(*header, keyValues); + + auto d = const_cast<uint8_t*>(memKtx->getStorage()->data()); + memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); + + auto textureCache = DependencyManager::get<TextureCache>(); + + // Move ktx to file + const char* data = reinterpret_cast<const char*>(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 << "file cache failed"; + } else { + _file = file; + } + + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); + gpu::TexturePointer texture; + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor())); + texture->setKtxBacking(file->getFilepath()); + + // 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 + if (textureCache) { + texture = textureCache->cacheTextureByHash(filename, texture); + } + + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + + } +} + void NetworkTexture::downloadFinished(const QByteArray& data) { loadContent(data); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 40eb29de35..3998ba3408 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -59,7 +59,16 @@ public: signals: void networkTextureCreated(const QWeakPointer<NetworkTexture>& self); +public slots: + void ktxHeaderRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } + void ktxHeaderRequestFinished(); + + void ktxMipRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } + void ktxMipRequestFinished(); + protected: + void makeRequest() override; + virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; @@ -67,6 +76,9 @@ protected: Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); + void startMipRangeRequest(uint16_t low, uint16_t high); + void maybeCreateKTX(); + private: friend class KTXReader; friend class ImageReader; @@ -79,9 +91,18 @@ private: DONE_LOADING }; + KTXLoadState _ktxLoadState { LOADING_HEADER }; KTXFilePointer _file; + static const uint16_t NULL_MIP_LEVEL; bool _sourceIsKTX { false }; + bool _ktxHeaderLoaded { false }; + std::pair<uint16_t, uint16_t> _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; + ResourceRequest* _ktxHeaderRequest { nullptr }; + ResourceRequest* _ktxMipRequest { nullptr }; + QByteArray _ktxHeaderData; + QByteArray _ktxHighMipData; + int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 5932193830..1bb292a3ef 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -60,7 +60,12 @@ void HTTPResourceRequest::doSend() { } if (_byteRange.isSet()) { - auto byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + QString byteRange; + if (_byteRange.fromInclusive < 0) { + auto byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive); + } else { + auto byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + } networkRequest.setRawHeader("Range", byteRange.toLatin1()); } networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 8373a9cf99..62ea85bc86 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -725,34 +725,7 @@ void Resource::handleReplyFinished() { emit loaded(data); downloadFinished(data); } else { - switch (result) { - case ResourceRequest::Result::Timeout: { - qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; - // Fall through to other cases - } - case ResourceRequest::Result::ServerUnavailable: { - // retry with increasing delays - const int BASE_DELAY_MS = 1000; - if (_attempts++ < MAX_ATTEMPTS) { - auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); - - qCDebug(networking).noquote() << "Server unavailable for" << _url << "- may retry in" << waitTime << "ms" - << "if resource is still needed"; - - QTimer::singleShot(waitTime, this, &Resource::attemptRequest); - break; - } - // fall through to final failure - } - default: { - qCDebug(networking) << "Error loading " << _url; - auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError - : QNetworkReply::UnknownNetworkError; - emit failed(error); - finishedLoading(false); - break; - } - } + handleFailedRequest(result); } _request->disconnect(this); @@ -760,6 +733,37 @@ void Resource::handleReplyFinished() { _request = nullptr; } +void Resource::handleFailedRequest(ResourceRequest::Result result) { + switch (result) { + case ResourceRequest::Result::Timeout: { + qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; + // Fall through to other cases + } + case ResourceRequest::Result::ServerUnavailable: { + // retry with increasing delays + const int BASE_DELAY_MS = 1000; + if (_attempts++ < MAX_ATTEMPTS) { + auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); + + qCDebug(networking).noquote() << "Server unavailable for" << _url << "- may retry in" << waitTime << "ms" + << "if resource is still needed"; + + QTimer::singleShot(waitTime, this, &Resource::attemptRequest); + break; + } + // fall through to final failure + } + default: { + qCDebug(networking) << "Error loading " << _url; + auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError + : QNetworkReply::UnknownNetworkError; + emit failed(error); + finishedLoading(false); + break; + } + } +} + uint qHash(const QPointer<QObject>& value, uint seed) { return qHash(value.data(), seed); } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 32364bf71c..e699c3d198 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -424,6 +424,8 @@ protected slots: protected: virtual void init(); + virtual void makeRequest(); + /// Checks whether the resource is cacheable. virtual bool isCacheable() const { return true; } @@ -440,6 +442,8 @@ protected: Q_INVOKABLE void allReferencesCleared(); + void handleFailedRequest(ResourceRequest::Result result); + QUrl _url; QUrl _activeUrl; ByteRange _requestByteRange; @@ -449,8 +453,15 @@ protected: QHash<QPointer<QObject>, float> _loadPriorities; QWeakPointer<Resource> _self; QPointer<ResourceCache> _cache; + + qint64 _bytesReceived{ 0 }; + qint64 _bytesTotal{ 0 }; + qint64 _bytes{ 0 }; + + int _requestID; + ResourceRequest* _request{ nullptr }; -private slots: +public slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); void handleReplyFinished(); @@ -460,20 +471,14 @@ private: void setLRUKey(int lruKey) { _lruKey = lruKey; } - void makeRequest(); void retry(); void reinsert(); bool isInScript() const { return _isInScript; } void setInScript(bool isInScript) { _isInScript = isInScript; } - int _requestID; - ResourceRequest* _request{ nullptr }; int _lruKey{ 0 }; QTimer* _replyTimer{ nullptr }; - qint64 _bytesReceived{ 0 }; - qint64 _bytesTotal{ 0 }; - qint64 _bytes{ 0 }; int _attempts{ 0 }; bool _isInScript{ false }; }; diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 25db34ab0a..01ca62cf05 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -21,7 +21,7 @@ struct ByteRange { int64_t fromInclusive { 0 }; int64_t toExclusive { 0 }; - bool isSet() { return fromInclusive < -1 || fromInclusive < toExclusive; } + bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } }; class ResourceRequest : public QObject { From b20fcbfcdb7d0b19cff97295dc27d46f6542efec Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 12 Apr 2017 09:40:08 -0700 Subject: [PATCH 05/85] Add a way to write data to a storage object --- .../gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 4 +++- libraries/gpu/src/gpu/Texture.cpp | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 4 +++- libraries/ktx/src/ktx/KTX.cpp | 1 + libraries/ktx/src/ktx/KTX.h | 4 +++- libraries/ktx/src/ktx/Writer.cpp | 7 +++++++ .../src/model-networking/TextureCache.cpp | 17 +++++++++++++++-- .../networking/src/HTTPResourceRequest.cpp | 6 ++++++ libraries/networking/src/ResourceCache.cpp | 1 + libraries/shared/src/shared/Storage.cpp | 4 +++- libraries/shared/src/shared/Storage.h | 4 ++++ 11 files changed, 47 insertions(+), 6 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 1d1f92b297..008b658205 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -259,7 +259,9 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(target, to); - (void) CHECK_GL_ERROR(); + if (CHECK_GL_ERROR()) { + qDebug() << "slot: " << slot << ", target: " << target << ", to: " << to; + } _resource._textures[slot] = resourceTexture; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index ebde9d4d27..6a1e5ca699 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -411,6 +411,7 @@ const Element& Texture::getStoredMipFormat() const { } void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { + // TODO Skip the extra allocation here storage::StoragePointer storage = std::make_shared<storage::MemoryStorage>(size, bytes); assignStoredMip(level, storage); } diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 487d32f91d..1e6fe1115e 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -45,6 +45,7 @@ std::string GPUKTXPayload::KEY { "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { + // We are doing a lot of work here just to get descriptor data ktx::StoragePointer storage { new storage::FileStorage(_filename.c_str()) }; auto ktxPointer = ktx::KTX::create(storage); _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); @@ -74,8 +75,9 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { + return true; auto numLevels = _ktxDescriptor->header.numberOfMipmapLevels; - auto minLevel = 7 > numLevels ? 0 : numLevels - 7; + auto minLevel = 7 > numLevels ? 0 : numLevels - 10; auto avail = level >= minLevel; qDebug() << "isMipAvailable: " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 0580d9a8c3..1f22514226 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -211,6 +211,7 @@ Image ImageDescriptor::toImage(const ktx::StoragePointer& storage) const { FaceBytes faces; faces.resize(_faceOffsets.size()); for (size_t face = 0; face < _numFaces; ++face) { + // TODO Should we be storing pointers to unowned data? faces[face] = storage->data() + _faceOffsets[face]; } // Note, implicit cast of *this to const ImageHeader& diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index fb8927eca0..988c921ce9 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -384,7 +384,7 @@ namespace ktx { ImageDescriptors generateImageDescriptors() const; }; static const size_t KTX_HEADER_SIZE = 64; - static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static"); + static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static and should not change from the spec"); // Key Values struct KeyValue { @@ -497,6 +497,8 @@ namespace ktx { static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues); static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); + void writeMipData(uint16_t level, const Byte* sourceBytes, size_t source_size); + // Parse a block of memory and create a KTX object from it static std::unique_ptr<KTX> create(const StoragePointer& src); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index e2803a2258..0d6cd737b6 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -235,4 +235,11 @@ namespace ktx { return destImages; } + void KTX::writeMipData(uint16_t level, const Byte* sourceBytes, size_t sourceSize) { + Q_ASSERT(level > 0); + Q_ASSERT(level < _images.size()); + Q_ASSERT(sourceSize == _images[level]._imageSize); + + //memcpy(reinterpret_cast<void*>(_images[level]._faceBytes[0]), sourceBytes, sourceSize); + } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f4fe192a4a..706c62c8a1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -545,7 +545,6 @@ void NetworkTexture::loadContent(const QByteArray& content) { texture = textureCache->cacheTextureByHash(filename, texture); } - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); auto desc = memKtx->toDescriptor(); @@ -559,10 +558,24 @@ void NetworkTexture::loadContent(const QByteArray& content) { } _requestByteRange.fromInclusive = length - sizeOfTopMips; _requestByteRange.toExclusive = length; - attemptRequest(); + QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection); + + + //texture->setMinMip(desc.images.size() - 1); + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); } else { qDebug() << "Got highest 6 mips"; + + ktx::StoragePointer storage { new storage::FileStorage(QString::fromStdString(_file->getFilepath())) }; + auto data = storage->mutableData(); + auto size = storage->getSize(); + //*data = 'H'; + memcpy(data + _requestByteRange.fromInclusive, content.data(), content.size()); + //getGPUTexture()->setMinMip(getGPUTexture()->getMinMip() - 6); + //auto ktxPointer = ktx::KTX::create(storage); + + //ktxPointer->writeMipData(level, data, size); } return; } diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 1bb292a3ef..499708b12d 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -22,6 +22,7 @@ #include "NetworkLogging.h" HTTPResourceRequest::~HTTPResourceRequest() { + qDebug() << "Cleaning up:" << _url << " " << _byteRange.fromInclusive << "-" << _byteRange.toExclusive; if (_reply) { _reply->disconnect(this); _reply->deleteLater(); @@ -76,9 +77,12 @@ void HTTPResourceRequest::doSend() { connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress); setupTimer(); + qDebug() << "Sent: " << _url; } void HTTPResourceRequest::onRequestFinished() { + qDebug() << "On request finished: " << _url; + Q_ASSERT(_state == InProgress); Q_ASSERT(_reply); @@ -181,6 +185,7 @@ void HTTPResourceRequest::onRequestFinished() { } void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + qDebug() << "Progress: " << _url; Q_ASSERT(_state == InProgress); // We've received data, so reset the timer @@ -190,6 +195,7 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT } void HTTPResourceRequest::onTimeout() { + qDebug() << "Timeout: " << _url << ":" << _reply->isFinished(); Q_ASSERT(_state == InProgress); _reply->disconnect(this); _reply->abort(); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 62ea85bc86..038ee7fb53 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -698,6 +698,7 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota } void Resource::handleReplyFinished() { + qDebug() << "Got response for " << _activeUrl; Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 8999caf1e8..0f2b696a66 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -69,7 +69,8 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u // Represents a memory mapped file FileStorage::FileStorage(const QString& filename) : _file(filename) { - if (_file.open(QFile::ReadOnly)) { + if (_file.open(QFile::ReadWrite)) { + qDebug() << ">>> Opening mmapped file: " << filename; _mapped = _file.map(0, _file.size()); if (_mapped) { _valid = true; @@ -82,6 +83,7 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } FileStorage::~FileStorage() { + qDebug() << ">>> Closing mmapped file: " << _file.fileName(); if (_mapped) { if (!_file.unmap(_mapped)) { throw std::runtime_error("Unable to unmap file"); diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 46f45cfdc5..3983387c15 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -25,6 +25,7 @@ namespace storage { public: virtual ~Storage() {} virtual const uint8_t* data() const = 0; + virtual uint8_t* mutableData() = 0; virtual size_t size() const = 0; virtual operator bool() const { return true; } @@ -42,6 +43,7 @@ namespace storage { MemoryStorage(size_t size, const uint8_t* data = nullptr); const uint8_t* data() const override { return _data.data(); } uint8_t* data() { return _data.data(); } + uint8_t* mutableData() override { return 0; } size_t size() const override { return _data.size(); } operator bool() const override { return true; } private: @@ -58,6 +60,7 @@ namespace storage { FileStorage& operator=(const FileStorage& other) = delete; const uint8_t* data() const override { return _mapped; } + uint8_t* mutableData() override { return _mapped; } size_t size() const override { return _file.size(); } operator bool() const override { return _valid; } private: @@ -70,6 +73,7 @@ namespace storage { public: ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); const uint8_t* data() const override { return _data; } + uint8_t* mutableData() override { return 0; } size_t size() const override { return _size; } operator bool() const override { return *_owner; } private: From aca7ad27c661a7e11c59bd50cbddee9274ef7c1e Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 12 Apr 2017 17:01:13 -0700 Subject: [PATCH 06/85] Fix ktx dependencies --- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 1 + libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 4 ++-- .../model-networking/src/model-networking/TextureCache.cpp | 6 ++++-- .../model-networking/src/model-networking/TextureCache.h | 3 +++ libraries/procedural/CMakeLists.txt | 2 +- libraries/script-engine/CMakeLists.txt | 2 +- plugins/openvr/CMakeLists.txt | 2 +- 8 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index a453d4207d..f87240f9d0 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -171,6 +171,7 @@ void GL45ResourceTexture::populateTransferQueue() { auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); for (uint8_t face = 0; face < maxFace; ++face) { + qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { continue; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index e82566b852..832be610ba 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -321,6 +321,7 @@ public: protected: std::string _filename; + //storage::FileStorage _cacheFile; ktx::KTXDescriptorPointer _ktxDescriptor; friend class Texture; }; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 1e6fe1115e..306d72e58a 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -60,6 +60,7 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { } PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { + qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; storage::StoragePointer result; auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); @@ -75,11 +76,10 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { - return true; auto numLevels = _ktxDescriptor->header.numberOfMipmapLevels; auto minLevel = 7 > numLevels ? 0 : numLevels - 10; auto avail = level >= minLevel; - qDebug() << "isMipAvailable: " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; + qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; return level > _ktxDescriptor->header.numberOfMipmapLevels - 7; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 706c62c8a1..404c44d454 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -30,7 +30,6 @@ #include <gpu/Batch.h> -#include <ktx/KTX.h> #include <image/Image.h> @@ -473,9 +472,11 @@ void NetworkTexture::maybeCreateKTX() { _file = file; } + _ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor())); + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor)); texture->setKtxBacking(file->getFilepath()); // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different @@ -496,6 +497,7 @@ void NetworkTexture::downloadFinished(const QByteArray& data) { void NetworkTexture::loadContent(const QByteArray& content) { if (_sourceIsKTX) { + assert(false); if (_ktxLoadState == LOADING_HEADER) { // TODO Handle case where we already have the source hash texture on disk // TODO Handle case where data isn't as large as the ktx header diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 3998ba3408..a86c20c145 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -23,6 +23,7 @@ #include <ResourceCache.h> #include <model/TextureMap.h> #include <image/Image.h> +#include <ktx/KTX.h> #include "KTXCache.h" @@ -102,6 +103,8 @@ private: ResourceRequest* _ktxMipRequest { nullptr }; QByteArray _ktxHeaderData; QByteArray _ktxHighMipData; + ktx::KTXDescriptorPointer _ktxDescriptor; + int _originalWidth { 0 }; int _originalHeight { 0 }; diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt index 8c66442c59..3ebd0f3d14 100644 --- a/libraries/procedural/CMakeLists.txt +++ b/libraries/procedural/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME procedural) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared gpu gpu-gl networking model model-networking image) +link_hifi_libraries(shared gpu gpu-gl networking model model-networking ktx image) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 7b176a6973..39338fd767 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -16,6 +16,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics image) +link_hifi_libraries(shared networking octree gpu ui procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 2300a38e56..bc62117e70 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -13,7 +13,7 @@ if (WIN32) setup_hifi_plugin(OpenGL Script Qml Widgets) link_hifi_libraries(shared gl networking controllers ui plugins display-plugins ui-plugins input-plugins script-engine - render-utils model gpu gpu-gl render model-networking fbx image) + render-utils model gpu gpu-gl render model-networking fbx ktx image) include_hifi_library_headers(octree) From cf3dc12542452310b48bc48b43917cce1032f8a7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 13 Apr 2017 15:54:25 -0700 Subject: [PATCH 07/85] Add object labels to GL objects and add ktx min mip kv --- .../src/gpu/gl45/GL45BackendTexture.cpp | 2 ++ .../gpu/gl45/GL45BackendVariableTexture.cpp | 35 +++++++++++++++---- libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 20 +++++++---- libraries/ktx/src/ktx/KTX.h | 3 ++ libraries/ktx/src/ktx/Writer.cpp | 16 +++++++-- .../src/model-networking/TextureCache.cpp | 4 +-- .../src/model-networking/TextureCache.h | 5 +++ .../networking/src/HTTPResourceRequest.cpp | 5 +-- 9 files changed, 72 insertions(+), 19 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index c6f1ef41ae..a539b76b6c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -109,6 +109,8 @@ GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; glCreateTextures(getGLTextureType(texture), 1, &result); + auto source = texture.source(); + glObjectLabel(GL_TEXTURE, result, source.length(), source.data()); return result; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index f87240f9d0..320d694473 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -41,16 +41,25 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { + if (texture.source().find_first_of("box.ktx") != std::string::npos) { + qDebug() << "In box.ktx ctor"; + } auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; + + _maxAllocatedMip = _populatedMip = mipLevels; + uvec3 mipDimensions; for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) + && texture.isStoredMipFaceAvailable(mip)) { _maxAllocatedMip = _populatedMip = mip; break; } } + //_maxAllocatedMip = _populatedMip = mipLevels; + //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t allocatedMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); allocateStorage(allocatedMip); copyMipsFromTexture(); @@ -87,6 +96,10 @@ void GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); + qDebug() << "glTextureParameteri " << QString::fromStdString(_source) << _populatedMip << _populatedMip - _allocatedMip; + if (_source == "test" && _populatedMip == 0) { + qDebug() << "here"; + } glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); } @@ -97,6 +110,7 @@ void GL45ResourceTexture::promote() { auto oldSize = _size; // create new texture const_cast<GLuint&>(_id) = allocate(_gpuObject); + //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min<uint16_t>(_allocatedMip, 2)); @@ -130,6 +144,7 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast<GLuint&>(_id) = allocate(_gpuObject); + //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); uint16_t mips = _gpuObject.getNumMips(); @@ -166,20 +181,24 @@ void GL45ResourceTexture::populateTransferQueue() { const uint8_t maxFace = GLTexture::getFaceCount(_target); uint16_t sourceMip = _populatedMip; + qDebug() << "populateTransferQueue info : " << _populatedMip << " " << _maxAllocatedMip << " " << _allocatedMip; do { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + bool transferQueued = false; + qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; for (uint8_t face = 0; face < maxFace; ++face) { - qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { continue; } // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { + qDebug() << "mip is less than max transfer size"; // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); + transferQueued = true; continue; } @@ -191,18 +210,22 @@ void GL45ResourceTexture::populateTransferQueue() { Q_ASSERT(0 == (mipSize % lines)); uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); uint32_t lineOffset = 0; + qDebug() << "queing up single line transfers " << linesPerTransfer << " " << lineOffset; while (lineOffset < lines) { uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; + transferQueued = true; } } // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); + if (transferQueued) { + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); + } } while (sourceMip != _allocatedMip); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 832be610ba..83f1f154d3 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -321,6 +321,7 @@ public: protected: std::string _filename; + uint8_t _minMipLevelAvailable; //storage::FileStorage _cacheFile; ktx::KTXDescriptorPointer _ktxDescriptor; friend class Texture; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 306d72e58a..65dc48f634 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -30,7 +30,7 @@ struct GPUKTXPayload { } static bool findInKeyValues(const ktx::KeyValues& keyValues, GPUKTXPayload& payload) { - auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); + auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); if (found != keyValues.end()) { if ((*found)._value.size() == sizeof(GPUKTXPayload)) { memcpy(&payload, (*found)._value.data(), sizeof(GPUKTXPayload)); @@ -41,14 +41,23 @@ struct GPUKTXPayload { } }; -std::string GPUKTXPayload::KEY { "hifi.gpu" }; +std::string GPUKTXPayload::KEY{ "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { // We are doing a lot of work here just to get descriptor data - ktx::StoragePointer storage { new storage::FileStorage(_filename.c_str()) }; + ktx::StoragePointer storage{ new storage::FileStorage(_filename.c_str()) }; auto ktxPointer = ktx::KTX::create(storage); _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); + auto& keyValues = _ktxDescriptor->keyValues; + auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { + return val._key.compare(ktx::HIFI_MIN_POPULATED_MIP_KEY) == 0; + }); + if (found != keyValues.end()) { + _minMipLevelAvailable = found->_value[0]; + } else { + _minMipLevelAvailable = 4;// _ktxDescriptor->header.numberOfMipmapLevels; + } } // now that we know the ktx, let's get the header info to configure this Texture::Storage: @@ -76,12 +85,11 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { - auto numLevels = _ktxDescriptor->header.numberOfMipmapLevels; - auto minLevel = 7 > numLevels ? 0 : numLevels - 10; + auto minLevel = _minMipLevelAvailable; auto avail = level >= minLevel; qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; - return level > _ktxDescriptor->header.numberOfMipmapLevels - 7; + return avail; } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 988c921ce9..8b5a62ebb3 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -70,6 +70,9 @@ end namespace ktx { + const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifiMinMip"; + + const uint32_t PACKING_SIZE { sizeof(uint32_t) }; using Byte = uint8_t; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 0d6cd737b6..20d4d598a9 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -43,12 +43,19 @@ namespace ktx { std::unique_ptr<KTX> KTX::createBare(const Header& header, const KeyValues& keyValues) { auto descriptors = header.generateImageDescriptors(); + auto newHeader = header; + + Byte minMip = header.numberOfMipmapLevels - 6; + auto newKeyValues = keyValues; + //newKeyValues.emplace_back(KeyValue(HIFI_MIN_POPULATED_MIP_KEY, sizeof(Byte), &minMip)); + //newHeader.bytesOfKeyValueData = KeyValue::serializedKeyValuesByteSize(newKeyValues); + StoragePointer storagePointer; { - auto storageSize = ktx::KTX::evalStorageSize(header, descriptors, keyValues); + auto storageSize = ktx::KTX::evalStorageSize(header, descriptors, newKeyValues); auto memoryStorage = new storage::MemoryStorage(storageSize); qDebug() << "Memory storage size is: " << storageSize; - ktx::KTX::writeWithoutImages(memoryStorage->data(), memoryStorage->size(), header, descriptors, keyValues); + ktx::KTX::writeWithoutImages(memoryStorage->data(), memoryStorage->size(), header, descriptors, newKeyValues); storagePointer.reset(memoryStorage); } return create(storagePointer); @@ -132,6 +139,7 @@ namespace ktx { memcpy(currentDestPtr, &header, sizeof(Header)); currentDestPtr += sizeof(Header); + // KeyValues if (!keyValues.empty()) { destHeader->bytesOfKeyValueData = (uint32_t) writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); @@ -145,9 +153,11 @@ namespace ktx { auto ptr = reinterpret_cast<uint32_t*>(currentDestPtr); *ptr = descriptors[i]._imageSize; ptr++; +#ifdef DEBUG for (size_t k = 0; k < descriptors[i]._imageSize/4; k++) { - *(ptr + k) = 0xFFFF0000; + *(ptr + k) = 0xFFFF00FF; } +#endif currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 404c44d454..26691db083 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -425,7 +425,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxHighMipData = _ktxMipRequest->getData(); maybeCreateKTX(); } else { - handleFailedRequest(_ktxHeaderRequest->getResult()); + handleFailedRequest(_ktxMipRequest->getResult()); } _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; @@ -433,7 +433,6 @@ void NetworkTexture::ktxMipRequestFinished() { // This is called when the header or top mips have been loaded void NetworkTexture::maybeCreateKTX() { - qDebug() << "Maybe create ktx..."; if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { // create ktx... auto header = reinterpret_cast<const ktx::Header*>(_ktxHeaderData.data()); @@ -478,6 +477,7 @@ void NetworkTexture::maybeCreateKTX() { gpu::TexturePointer texture; texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor)); texture->setKtxBacking(file->getFilepath()); + texture->setSource(filename); // 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 diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index a86c20c145..a029b5b147 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -103,6 +103,11 @@ private: ResourceRequest* _ktxMipRequest { nullptr }; QByteArray _ktxHeaderData; QByteArray _ktxHighMipData; + + // This is a copy of the original KTX descriptor from the source url. + // We need this because the KTX that will be cached will likely include extra data + // in its key/value data, and so will not match up with the original, causing + // mip offsets to change. ktx::KTXDescriptorPointer _ktxDescriptor; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 499708b12d..f07ab4450b 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -63,10 +63,11 @@ void HTTPResourceRequest::doSend() { if (_byteRange.isSet()) { QString byteRange; if (_byteRange.fromInclusive < 0) { - auto byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive); + byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive); } else { - auto byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); } + qDebug() << "Setting http range to " << byteRange; networkRequest.setRawHeader("Range", byteRange.toLatin1()); } networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); From e9bb895bff8c122fa5f73dee722e92dfa835ff68 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 13 Apr 2017 17:03:59 -0700 Subject: [PATCH 08/85] Implement KTXStorage::assignMipData and add writing of mips to TextureCache --- libraries/gpu/src/gpu/Texture.cpp | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 30 +++++++++++++++++-- libraries/ktx/src/ktx/Writer.cpp | 6 ++-- .../src/model-networking/TextureCache.cpp | 18 +++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 6a1e5ca699..205cf3a65a 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -118,6 +118,7 @@ Texture::Size Texture::getAllowedGPUMemoryUsage() { return _allowedCPUMemoryUsage; } + void Texture::setAllowedGPUMemoryUsage(Size size) { qCDebug(gpulogging) << "New MAX texture memory " << BYTES_TO_MB(size) << " MB"; _allowedCPUMemoryUsage = size; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 65dc48f634..ede1f21c1f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -56,7 +56,8 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { if (found != keyValues.end()) { _minMipLevelAvailable = found->_value[0]; } else { - _minMipLevelAvailable = 4;// _ktxDescriptor->header.numberOfMipmapLevels; + // Assume all mip levels are available + _minMipLevelAvailable = 0; } } @@ -93,7 +94,32 @@ bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { - throw std::runtime_error("Invalid call"); + if (level != _minMipLevelAvailable - 1) { + qWarning() << "Invalid level to be stored"; + return; + } + + if (level >= _ktxDescriptor->images.size()) { + throw std::runtime_error("Invalid level"); + } + + if (storage->size() != _ktxDescriptor->images[level]._imageSize) { + throw std::runtime_error("Invalid image size for level"); + } + + + ktx::StoragePointer file { new storage::FileStorage(_filename.c_str()) }; + auto data = file->mutableData(); + data += file->size(); + + // TODO Cache this data inside Image or ImageDescriptor? + for (auto i = _ktxDescriptor->header.numberOfMipmapLevels - 1; i >= level; --i) { + data -= _ktxDescriptor->images[i]._imageSize; + data -= 4; + } + data += 4; + memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); + _minMipLevelAvailable = level; } void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 20d4d598a9..d149d559f9 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -45,10 +45,10 @@ namespace ktx { auto newHeader = header; - Byte minMip = header.numberOfMipmapLevels - 6; + Byte minMip = header.numberOfMipmapLevels; auto newKeyValues = keyValues; - //newKeyValues.emplace_back(KeyValue(HIFI_MIN_POPULATED_MIP_KEY, sizeof(Byte), &minMip)); - //newHeader.bytesOfKeyValueData = KeyValue::serializedKeyValuesByteSize(newKeyValues); + newKeyValues.emplace_back(KeyValue(HIFI_MIN_POPULATED_MIP_KEY, sizeof(Byte), &minMip)); + newHeader.bytesOfKeyValueData = KeyValue::serializedKeyValuesByteSize(newKeyValues); StoragePointer storagePointer; { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 26691db083..93a25330b1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -387,6 +387,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); qDebug() << ">>> Making request to " << _url << " for " << low << " to " << high; + _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { // This is a special case where we load the high 7 mips ByteRange range; @@ -479,6 +480,23 @@ void NetworkTexture::maybeCreateKTX() { texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); + auto& images = _ktxDescriptor->images; + size_t imageSizeRemaining = _ktxHighMipData.size(); + uint8_t* ktxData = reinterpret_cast<uint8_t*>(_ktxHighMipData.data()); + ktxData += _ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + for (uint16_t i = images.size() - 1; i >= 0; --i) { + auto& image = images[i]; + if (image._imageSize > imageSizeRemaining) { + break; + } + qDebug() << "Transferring " << i; + ktxData -= image._imageSize; + texture->assignStoredMip(i, image._imageSize, ktxData); + ktxData -= 4; + imageSizeRemaining - image._imageSize - 4; + } + // 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 From 39c3fee838acde023a40a5dc80ab66816d57f42a Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 13 Apr 2017 23:05:23 -0700 Subject: [PATCH 09/85] Add caching of imageOffset to ktx ImageDescriptor --- libraries/gpu/src/gpu/Texture_ktx.cpp | 9 +- libraries/ktx/src/ktx/KTX.cpp | 8 +- libraries/ktx/src/ktx/KTX.h | 15 +- libraries/ktx/src/ktx/Reader.cpp | 5 +- libraries/ktx/src/ktx/Writer.cpp | 5 +- .../src/model-networking/TextureCache.cpp | 135 ++++++------------ .../src/model-networking/TextureCache.h | 3 +- .../networking/src/HTTPResourceRequest.cpp | 3 +- 8 files changed, 79 insertions(+), 104 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index ede1f21c1f..d9a0348e54 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -108,7 +108,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } - ktx::StoragePointer file { new storage::FileStorage(_filename.c_str()) }; + auto fileStorage = new storage::FileStorage(_filename.c_str()); + ktx::StoragePointer file { fileStorage }; auto data = file->mutableData(); data += file->size(); @@ -197,19 +198,21 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { header.numberOfMipmapLevels = texture.getNumMips(); ktx::Images images; + uint32_t imageOffset = 0; for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { auto mip = texture.accessStoredMipFace(level); if (mip) { if (numFaces == 1) { - images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); + images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, mip->readData())); } else { ktx::Image::FaceBytes cubeFaces(Texture::CUBE_FACE_COUNT); cubeFaces[0] = mip->readData(); for (uint32_t face = 1; face < Texture::CUBE_FACE_COUNT; face++) { cubeFaces[face] = texture.accessStoredMipFace(level, face)->readData(); } - images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, cubeFaces)); + images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, cubeFaces)); } + imageOffset += mip->getSize() + 4; } } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 1f22514226..73751bdef2 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -73,12 +73,18 @@ size_t Header::evalImageSize(uint32_t level) const { ImageDescriptors Header::generateImageDescriptors() const { ImageDescriptors descriptors; + uint32_t imageOffset = 0; for (auto level = 0; level < numberOfMipmapLevels; ++level) { + auto imageSize = static_cast<uint32_t>(evalImageSize(level)); ImageHeader header { numberOfFaces == NUM_CUBEMAPFACES, - static_cast<uint32_t>(evalImageSize(level)), + imageOffset, + imageSize, 0 }; + + imageOffset += imageSize + 4; + ImageHeader::FaceOffsets offsets; for (auto i = 0; i < numberOfFaces; ++i) { offsets.push_back(0); diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 8b5a62ebb3..7056f22ba8 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -414,12 +414,17 @@ namespace ktx { struct ImageHeader { using FaceOffsets = std::vector<size_t>; using FaceBytes = std::vector<const Byte*>; + // This is the byte offset from the _start_ of the image region. For example, level 0 + // will have a byte offset of 0. + const uint32_t _imageOffset; + const uint32_t _numFaces; const uint32_t _imageSize; const uint32_t _faceSize; const uint32_t _padding; - ImageHeader(bool cube, uint32_t imageSize, uint32_t padding) : + ImageHeader(bool cube, uint32_t imageOffset, uint32_t imageSize, uint32_t padding) : _numFaces(cube ? NUM_CUBEMAPFACES : 1), + _imageOffset(imageOffset), _imageSize(imageSize * _numFaces), _faceSize(imageSize), _padding(padding) { @@ -439,11 +444,11 @@ namespace ktx { struct Image : public ImageHeader { FaceBytes _faceBytes; Image(const ImageHeader& header, const FaceBytes& faces) : ImageHeader(header), _faceBytes(faces) {} - Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : - ImageHeader(false, imageSize, padding), + Image(uint32_t imageOffset, uint32_t imageSize, uint32_t padding, const Byte* bytes) : + ImageHeader(false, imageOffset, imageSize, padding), _faceBytes(1, bytes) {} - Image(uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : - ImageHeader(true, pageSize, padding) + Image(uint32_t imageOffset, uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : + ImageHeader(true, imageOffset, pageSize, padding) { if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { _faceBytes = cubeFaceBytes; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index bf72faeba5..b22f262e85 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -144,6 +144,7 @@ namespace ktx { while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) { // Grab the imageSize coming up + uint32_t imageOffset = currentPtr - srcBytes; size_t imageSize = *reinterpret_cast<const uint32_t*>(currentPtr); currentPtr += sizeof(uint32_t); @@ -158,10 +159,10 @@ namespace ktx { faces[face] = currentPtr; currentPtr += faceSize; } - images.emplace_back(Image((uint32_t) faceSize, padding, faces)); + images.emplace_back(Image(imageOffset, (uint32_t) faceSize, padding, faces)); currentPtr += padding; } else { - images.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); + images.emplace_back(Image(imageOffset, (uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize + padding; } } else { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index d149d559f9..4596bf00c0 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -211,6 +211,7 @@ namespace ktx { for (uint32_t l = 0; l < srcImages.size(); l++) { if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { + uint32_t imageOffset = currentPtr - destBytes; size_t imageSize = srcImages[l]._imageSize; *(reinterpret_cast<uint32_t*> (currentPtr)) = (uint32_t) imageSize; currentPtr += sizeof(uint32_t); @@ -223,7 +224,7 @@ namespace ktx { // Single face vs cubes if (srcImages[l]._numFaces == 1) { memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); - destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); + destImages.emplace_back(Image(imageOffset, (uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize; } else { Image::FaceBytes faceBytes(NUM_CUBEMAPFACES); @@ -233,7 +234,7 @@ namespace ktx { faceBytes[face] = currentPtr; currentPtr += faceSize; } - destImages.emplace_back(Image(faceSize, padding, faceBytes)); + destImages.emplace_back(Image(imageOffset, faceSize, padding, faceBytes)); } currentPtr += padding; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 93a25330b1..592413e2bc 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -30,7 +30,6 @@ #include <gpu/Batch.h> - #include <image/Image.h> #include <NumericalConstants.h> @@ -395,6 +394,12 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipRequest->setByteRange(range); } else { // TODO: Discover range for other mips + ByteRange range; + range.fromInclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + + _ktxDescriptor->images[low]._imageOffset + 4; + range.toExclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + + _ktxDescriptor->images[high + 1]._imageOffset; + _ktxMipRequest->setByteRange(range); } connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress); @@ -423,8 +428,17 @@ void NetworkTexture::ktxMipRequestFinished() { && _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL; if (_ktxMipRequest->getResult() == ResourceRequest::Success) { - _ktxHighMipData = _ktxMipRequest->getData(); - maybeCreateKTX(); + if (_initialKtxLoaded) { + assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + + _textureSource->getGPUTexture()->assignStoredMip(_ktxMipLevelRangeInFlight.first, + _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); + //texture->assignStoredMip(level, image._imageSize, ktxData); + } else { + _ktxHighMipData = _ktxMipRequest->getData(); + maybeCreateKTX(); + } + } else { handleFailedRequest(_ktxMipRequest->getResult()); } @@ -457,7 +471,7 @@ void NetworkTexture::maybeCreateKTX() { auto memKtx = ktx::KTX::createBare(*header, keyValues); auto d = const_cast<uint8_t*>(memKtx->getStorage()->data()); - memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); + ///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); auto textureCache = DependencyManager::get<TextureCache>(); @@ -485,18 +499,21 @@ void NetworkTexture::maybeCreateKTX() { uint8_t* ktxData = reinterpret_cast<uint8_t*>(_ktxHighMipData.data()); ktxData += _ktxHighMipData.size(); // TODO Move image offset calculation to ktx ImageDescriptor - for (uint16_t i = images.size() - 1; i >= 0; --i) { - auto& image = images[i]; + uint16_t level; + for (level = images.size() - 1; level >= 0; --level) { + auto& image = images[level]; if (image._imageSize > imageSizeRemaining) { break; } - qDebug() << "Transferring " << i; + qDebug() << "Transferring " << level; ktxData -= image._imageSize; - texture->assignStoredMip(i, image._imageSize, ktxData); + texture->assignStoredMip(level, image._imageSize, ktxData); ktxData -= 4; imageSizeRemaining - image._imageSize - 4; } + _initialKtxLoaded = true; + // 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 @@ -506,6 +523,27 @@ void NetworkTexture::maybeCreateKTX() { setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + + // Force load the next two levels + { + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + startMipRangeRequest(level, level); + }); + timer->setSingleShot(true); + timer->setInterval(4000); + timer->start(); + } + + { + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + startMipRangeRequest(level - 1, level - 1); + }); + timer->setSingleShot(true); + timer->setInterval(6000); + timer->start(); + } } } @@ -516,87 +554,6 @@ void NetworkTexture::downloadFinished(const QByteArray& data) { void NetworkTexture::loadContent(const QByteArray& content) { if (_sourceIsKTX) { assert(false); - if (_ktxLoadState == LOADING_HEADER) { - // TODO Handle case where we already have the source hash texture on disk - // TODO Handle case where data isn't as large as the ktx header - _ktxLoadState = LOADING_LOWEST_SIX; - auto header = reinterpret_cast<const ktx::Header*>(content.data()); - qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); - qDebug() << "Type:" << header->glType; - qDebug() << "TypeSize:" << header->glTypeSize; - qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; - qDebug() << "numberOfFaces:" << header->numberOfFaces; - qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; - auto kvSize = header->bytesOfKeyValueData; - if (kvSize > content.size() - ktx::KTX_HEADER_SIZE) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - return; - } - - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(content.data()) + ktx::KTX_HEADER_SIZE); - - // Create bare ktx in memory - std::string filename = "test"; - auto memKtx = ktx::KTX::createBare(*header, keyValues); - - - auto textureCache = DependencyManager::get<TextureCache>(); - - // Move ktx to file - const char* data = reinterpret_cast<const char*>(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 << "file cache failed"; - } else { - _file = file; - } - - //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); - gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor())); - texture->setKtxBacking(file->getFilepath()); - - // 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 - if (textureCache) { - texture = textureCache->cacheTextureByHash(filename, texture); - } - - - - auto desc = memKtx->toDescriptor(); - int numMips = desc.images.size(); - auto numMipsToGet = glm::min(numMips, 6); - auto sizeOfTopMips = 0; - for (int i = 0; i < numMipsToGet; ++i) { - auto mipLevel = numMips - 1 - i; - auto& img = desc.images[mipLevel]; - sizeOfTopMips += img._imageSize; - } - _requestByteRange.fromInclusive = length - sizeOfTopMips; - _requestByteRange.toExclusive = length; - QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection); - - - //texture->setMinMip(desc.images.size() - 1); - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - - } else { - qDebug() << "Got highest 6 mips"; - - ktx::StoragePointer storage { new storage::FileStorage(QString::fromStdString(_file->getFilepath())) }; - auto data = storage->mutableData(); - auto size = storage->getSize(); - //*data = 'H'; - memcpy(data + _requestByteRange.fromInclusive, content.data(), content.size()); - //getGPUTexture()->setMinMip(getGPUTexture()->getMinMip() - 6); - //auto ktxPointer = ktx::KTX::create(storage); - - //ktxPointer->writeMipData(level, data, size); - } return; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index a029b5b147..c032a9c29d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -93,7 +93,8 @@ private: }; - KTXLoadState _ktxLoadState { LOADING_HEADER }; + bool _initialKtxLoaded { false }; + //KTXLoadState _ktxLoadState; KTXFilePointer _file; static const uint16_t NULL_MIP_LEVEL; bool _sourceIsKTX { false }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index f07ab4450b..8958eeaf3b 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -65,7 +65,8 @@ void HTTPResourceRequest::doSend() { if (_byteRange.fromInclusive < 0) { byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive); } else { - byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + // HTTP byte ranges are inclusive on the `to` end: [from, to] + byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive - 1); } qDebug() << "Setting http range to " << byteRange; networkRequest.setRawHeader("Range", byteRange.toLatin1()); From ab7099b3eb28b95773ce650ab075c61a8e3cecc2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Sun, 16 Apr 2017 23:16:23 -0700 Subject: [PATCH 10/85] Add loading of lower mips to NetworkTexture --- .../gpu/gl45/GL45BackendVariableTexture.cpp | 1 + libraries/gpu/src/gpu/Texture.cpp | 26 +++++++++++++ libraries/gpu/src/gpu/Texture.h | 26 ++++++++++--- libraries/gpu/src/gpu/Texture_ktx.cpp | 36 ++++++++++++++---- libraries/ktx/src/ktx/KTX.h | 4 +- .../src/model-networking/TextureCache.cpp | 37 ++++++++++++++++++- .../src/model-networking/TextureCache.h | 11 ++++-- 7 files changed, 121 insertions(+), 20 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 320d694473..c37ee46bfd 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -190,6 +190,7 @@ void GL45ResourceTexture::populateTransferQueue() { qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); continue; } diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 205cf3a65a..1dbe9db2b4 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -476,6 +476,32 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin } } +void Texture::requestInterestInMip(uint16 level) { + if (!_storage->isMipAvailable(level, 0)) { + std::lock_guard<std::mutex> lock(_mipInterestListenersMutex); + for (auto& callback : _mipInterestListeners) { + callback->handleMipInterestCallback(level); + } + } +} + +bool Texture::isStoredMipFaceAvailable(uint16 level, uint8 face) const { + return _storage->isMipAvailable(level, face); +} + +void Texture::registerMipInterestListener(MipInterestListener* listener) { + std::lock_guard<std::mutex> lock(_mipInterestListenersMutex); + _mipInterestListeners.push_back(listener); +} + +void Texture::unregisterMipInterestListener(MipInterestListener* listener) { + std::lock_guard<std::mutex> lock(_mipInterestListenersMutex); + auto it = find(_mipInterestListeners.begin(), _mipInterestListeners.end(), listener); + if (it != _mipInterestListeners.end()) { + _mipInterestListeners.erase(it); + } +} + void Texture::setAutoGenerateMips(bool enable) { bool changed = false; if (!_autoGenerateMips) { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 83f1f154d3..8c9d62e3b8 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -310,19 +310,22 @@ public: KtxStorage(const std::string& filename); PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; - // By convention, all mip levels and faces MUST be populated when using KTX backing bool isMipAvailable(uint16 level, uint8 face = 0) const override; - void assignMipData(uint16 level, const storage::StoragePointer& storage) override; - void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; void reset() override { } protected: + std::shared_ptr<storage::FileStorage> maybeOpenFile(); + + std::mutex _cacheFileCreateMutex; + std::mutex _cacheFileWriteMutex; + std::weak_ptr<storage::FileStorage> _cacheFile; + std::string _filename; - uint8_t _minMipLevelAvailable; - //storage::FileStorage _cacheFile; + std::atomic<uint8_t> _minMipLevelAvailable; + ktx::KTXDescriptorPointer _ktxDescriptor; friend class Texture; }; @@ -470,7 +473,7 @@ public: // Access the stored mips and faces const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } - bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } + bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const;// { return _storage->isMipAvailable(level, face); } Size getStoredMipFaceSize(uint16 level, uint8 face = 0) const { return _storage->getMipFaceSize(level, face); } Size getStoredMipSize(uint16 level) const; Size getStoredSize() const; @@ -478,6 +481,17 @@ public: void setStorage(std::unique_ptr<Storage>& newStorage); void setKtxBacking(const std::string& filename); + class MipInterestListener { + public: + virtual void handleMipInterestCallback(uint16 level) = 0; + }; + void registerMipInterestListener(MipInterestListener* listener); + void unregisterMipInterestListener(MipInterestListener* listener); + std::vector<MipInterestListener*> _mipInterestListeners; + std::mutex _mipInterestListenersMutex; + + void requestInterestInMip(uint16 level); + // Usage is a a set of flags providing Semantic about the usage of the Texture. void setUsage(const Usage& usage) { _usage = usage; } Usage getUsage() const { return _usage; } diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d9a0348e54..0ea9f2ce4d 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -69,6 +69,19 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { } } +std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() { + std::shared_ptr<storage::FileStorage> file = _cacheFile.lock(); + if (file) { + return file; + } + + std::lock_guard<std::mutex> lock { _cacheFileCreateMutex }; + file = std::make_shared<storage::FileStorage>(_filename.c_str()); + _cacheFile = file; + + return file; +} + PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; storage::StoragePointer result; @@ -86,9 +99,8 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { - auto minLevel = _minMipLevelAvailable; - auto avail = level >= minLevel; - qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; + auto avail = level >= _minMipLevelAvailable; + qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; return avail; } @@ -108,8 +120,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } - auto fileStorage = new storage::FileStorage(_filename.c_str()); - ktx::StoragePointer file { fileStorage }; + //auto fileStorage = new storage::FileStorage(_filename.c_str()); + //ktx::StoragePointer file { fileStorage }; + auto file = maybeOpenFile(); auto data = file->mutableData(); data += file->size(); @@ -119,8 +132,17 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor data -= 4; } data += 4; - memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); - _minMipLevelAvailable = level; + { + std::lock_guard<std::mutex> lock { _cacheFileWriteMutex }; + + if (level != _minMipLevelAvailable - 1) { + qWarning() << "Invalid level to be stored"; + return; + } + + memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); + _minMipLevelAvailable = level; + } } void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 7056f22ba8..e8ed7da8e3 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -70,7 +70,7 @@ end namespace ktx { - const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifiMinMip"; + const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifi.minMip"; const uint32_t PACKING_SIZE { sizeof(uint32_t) }; @@ -414,10 +414,10 @@ namespace ktx { struct ImageHeader { using FaceOffsets = std::vector<size_t>; using FaceBytes = std::vector<const Byte*>; + // This is the byte offset from the _start_ of the image region. For example, level 0 // will have a byte offset of 0. const uint32_t _imageOffset; - const uint32_t _numFaces; const uint32_t _imageSize; const uint32_t _faceSize; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 592413e2bc..e45c353aac 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -244,6 +244,10 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { return result; } +NetworkTexture::~NetworkTexture() { + _textureSource->getGPUTexture()->unregisterMipInterestListener(this); +} + /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) { QImage image = QImage(path); @@ -371,6 +375,21 @@ void NetworkTexture::makeRequest() { startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); } +void NetworkTexture::handleMipInterestCallback(uint16_t level) { + QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(uint16_t, level)); +} + +void NetworkTexture::handleMipInterestLevel(uint16_t level) { + _lowestRequestedMipLevel = std::min(level, _lowestRequestedMipLevel); + if (!_ktxMipRequest) { + startRequestForNextMipLevel(); + } +} + +void NetworkTexture::startRequestForNextMipLevel() { + startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1)); +} + // Load mips in the range [low, high] (inclusive) void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { if (_ktxMipRequest) { @@ -430,6 +449,8 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxMipRequest->getResult() == ResourceRequest::Success) { if (_initialKtxLoaded) { assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + + _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; _textureSource->getGPUTexture()->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); @@ -493,6 +514,7 @@ void NetworkTexture::maybeCreateKTX() { texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor)); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); + texture->registerMipInterestListener(this); auto& images = _ktxDescriptor->images; size_t imageSizeRemaining = _ktxHighMipData.size(); @@ -521,6 +543,15 @@ void NetworkTexture::maybeCreateKTX() { texture = textureCache->cacheTextureByHash(filename, texture); } + + _lowestKnownPopulatedMip = _ktxDescriptor->header.numberOfMipmapLevels; + for (uint16_t l = 0; l < 200; l++) { + if (texture->isStoredMipFaceAvailable(l)) { + _lowestKnownPopulatedMip = l; + break; + } + } + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); @@ -528,7 +559,8 @@ void NetworkTexture::maybeCreateKTX() { { QTimer* timer = new QTimer(); connect(timer, &QTimer::timeout, this, [=]() { - startMipRangeRequest(level, level); + //startMipRangeRequest(level, level); + startRequestForNextMipLevel(); }); timer->setSingleShot(true); timer->setInterval(4000); @@ -538,7 +570,8 @@ void NetworkTexture::maybeCreateKTX() { { QTimer* timer = new QTimer(); connect(timer, &QTimer::timeout, this, [=]() { - startMipRangeRequest(level - 1, level - 1); + //startMipRangeRequest(level - 1, level - 1); + startRequestForNextMipLevel(); }); timer->setSingleShot(true); timer->setInterval(6000); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index c032a9c29d..b11ae687a2 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -41,7 +41,7 @@ public: }; /// A texture loaded from the network. -class NetworkTexture : public Resource, public Texture { +class NetworkTexture : public Resource, public Texture, public gpu::Texture::MipInterestListener { Q_OBJECT public: @@ -57,6 +57,9 @@ public: gpu::TexturePointer getFallbackTexture() const; + void handleMipInterestCallback(uint16_t level) override; + Q_INVOKABLE void handleMipInterestLevel(uint16_t level); + signals: void networkTextureCreated(const QWeakPointer<NetworkTexture>& self); @@ -77,6 +80,8 @@ protected: Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); + void startRequestForNextMipLevel(); + void startMipRangeRequest(uint16_t low, uint16_t high); void maybeCreateKTX(); @@ -92,9 +97,7 @@ private: DONE_LOADING }; - bool _initialKtxLoaded { false }; - //KTXLoadState _ktxLoadState; KTXFilePointer _file; static const uint16_t NULL_MIP_LEVEL; bool _sourceIsKTX { false }; @@ -102,6 +105,8 @@ private: std::pair<uint16_t, uint16_t> _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; + uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; + uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; QByteArray _ktxHeaderData; QByteArray _ktxHighMipData; From b21dc12cc6e0f112134c498c9c846eacba09c10f Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Mon, 17 Apr 2017 10:37:21 -0700 Subject: [PATCH 11/85] Cleanup NetworkTexture ktx handling --- libraries/gpu/src/gpu/Texture_ktx.cpp | 21 ++++++++++++--- .../src/model-networking/TextureCache.cpp | 27 +++++++++++-------- .../src/model-networking/TextureCache.h | 4 +-- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 0ea9f2ce4d..1eb6d6b10f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -75,9 +75,17 @@ std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() { return file; } - std::lock_guard<std::mutex> lock { _cacheFileCreateMutex }; - file = std::make_shared<storage::FileStorage>(_filename.c_str()); - _cacheFile = file; + { + std::lock_guard<std::mutex> lock{ _cacheFileCreateMutex }; + + file = _cacheFile.lock(); + if (file) { + return file; + } + + file = std::make_shared<storage::FileStorage>(_filename.c_str()); + _cacheFile = file; + } return file; } @@ -127,11 +135,16 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor data += file->size(); // TODO Cache this data inside Image or ImageDescriptor? - for (auto i = _ktxDescriptor->header.numberOfMipmapLevels - 1; i >= level; --i) { + for (int i = _ktxDescriptor->header.numberOfMipmapLevels - 1; i >= level; --i) { data -= _ktxDescriptor->images[i]._imageSize; data -= 4; } data += 4; + + data = file->mutableData(); + data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; + data += 4; + { std::lock_guard<std::mutex> lock { _cacheFileWriteMutex }; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e45c353aac..abc7ecfc41 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -376,11 +376,11 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::handleMipInterestCallback(uint16_t level) { - QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(uint16_t, level)); + QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(int, level)); } -void NetworkTexture::handleMipInterestLevel(uint16_t level) { - _lowestRequestedMipLevel = std::min(level, _lowestRequestedMipLevel); +void NetworkTexture::handleMipInterestLevel(int level) { + _lowestRequestedMipLevel = std::min(static_cast<uint16_t>(level), _lowestRequestedMipLevel); if (!_ktxMipRequest) { startRequestForNextMipLevel(); } @@ -414,10 +414,10 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } else { // TODO: Discover range for other mips ByteRange range; - range.fromInclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData - + _ktxDescriptor->images[low]._imageOffset + 4; - range.toExclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData - + _ktxDescriptor->images[high + 1]._imageOffset; + range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + + _originalKtxDescriptor->images[low]._imageOffset + 4; + range.toExclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + + _originalKtxDescriptor->images[high + 1]._imageOffset; _ktxMipRequest->setByteRange(range); } @@ -487,6 +487,9 @@ void NetworkTexture::maybeCreateKTX() { auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + auto imageDescriptors = header->generateImageDescriptors(); + _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); + // Create bare ktx in memory std::string filename = "test"; auto memKtx = ktx::KTX::createBare(*header, keyValues); @@ -507,16 +510,17 @@ void NetworkTexture::maybeCreateKTX() { _file = file; } - _ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); + //_ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); + auto newKtxDescriptor = memKtx->toDescriptor(); //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor)); + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); texture->registerMipInterestListener(this); - auto& images = _ktxDescriptor->images; + auto& images = _originalKtxDescriptor->images; size_t imageSizeRemaining = _ktxHighMipData.size(); uint8_t* ktxData = reinterpret_cast<uint8_t*>(_ktxHighMipData.data()); ktxData += _ktxHighMipData.size(); @@ -544,7 +548,7 @@ void NetworkTexture::maybeCreateKTX() { } - _lowestKnownPopulatedMip = _ktxDescriptor->header.numberOfMipmapLevels; + _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; for (uint16_t l = 0; l < 200; l++) { if (texture->isStoredMipFaceAvailable(l)) { _lowestKnownPopulatedMip = l; @@ -554,6 +558,7 @@ void NetworkTexture::maybeCreateKTX() { setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + return; // Force load the next two levels { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index b11ae687a2..50c8ecf352 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -58,7 +58,7 @@ public: gpu::TexturePointer getFallbackTexture() const; void handleMipInterestCallback(uint16_t level) override; - Q_INVOKABLE void handleMipInterestLevel(uint16_t level); + Q_INVOKABLE void handleMipInterestLevel(int level); signals: void networkTextureCreated(const QWeakPointer<NetworkTexture>& self); @@ -114,7 +114,7 @@ private: // We need this because the KTX that will be cached will likely include extra data // in its key/value data, and so will not match up with the original, causing // mip offsets to change. - ktx::KTXDescriptorPointer _ktxDescriptor; + ktx::KTXDescriptorPointer _originalKtxDescriptor; int _originalWidth { 0 }; From 20f4d14e07eac0c4ba07e1c3256ebb87244d20cc Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Mon, 17 Apr 2017 17:45:02 -0700 Subject: [PATCH 12/85] Add compressed KTX size evaluation --- .../src/RenderableWebEntityItem.cpp | 4 +- .../gpu/gl45/GL45BackendVariableTexture.cpp | 4 +- libraries/gpu/src/gpu/Texture.h | 2 + libraries/gpu/src/gpu/Texture_ktx.cpp | 14 +- libraries/ktx/src/ktx/KTX.cpp | 67 ++++- libraries/ktx/src/ktx/KTX.h | 8 +- libraries/ktx/src/ktx/Writer.cpp | 2 +- .../src/model-networking/TextureCache.cpp | 248 ++++++++++-------- .../src/model-networking/TextureCache.h | 10 +- libraries/shared/src/shared/Storage.cpp | 4 +- 10 files changed, 220 insertions(+), 143 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 0d286c46eb..20e4f4bc18 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -31,7 +31,7 @@ const float METERS_TO_INCHES = 39.3701f; static uint32_t _currentWebCount { 0 }; // Don't allow more than 100 concurrent web views -static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20; +static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 0; // If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; @@ -71,7 +71,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { bool RenderableWebEntityItem::buildWebSurface(QSharedPointer<EntityTreeRenderer> renderer) { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { - qWarning() << "Too many concurrent web views to create new view"; + //qWarning() << "Too many concurrent web views to create new view"; return false; } QString javaScriptToInject; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index c37ee46bfd..09361689d8 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -181,13 +181,13 @@ void GL45ResourceTexture::populateTransferQueue() { const uint8_t maxFace = GLTexture::getFaceCount(_target); uint16_t sourceMip = _populatedMip; - qDebug() << "populateTransferQueue info : " << _populatedMip << " " << _maxAllocatedMip << " " << _allocatedMip; + //qDebug() << "populateTransferQueue info : " << _populatedMip << " " << _maxAllocatedMip << " " << _allocatedMip; do { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); bool transferQueued = false; - qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; + //qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 8c9d62e3b8..e71cd63fbd 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -34,6 +34,8 @@ namespace ktx { namespace gpu { +extern const std::string SOURCE_HASH_KEY; + // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated // with the cube texture class Texture; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 1eb6d6b10f..eb6bb169ce 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -11,6 +11,7 @@ #include "Texture.h" +#include <qdebug.h> #include <ktx/KTX.h> using namespace gpu; @@ -41,6 +42,7 @@ struct GPUKTXPayload { } }; +const std::string gpu::SOURCE_HASH_KEY { "hifi.sourceHash" }; std::string GPUKTXPayload::KEY{ "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { @@ -91,7 +93,7 @@ std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() { } PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { - qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; + //qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; storage::StoragePointer result; auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); @@ -108,14 +110,14 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { auto avail = level >= _minMipLevelAvailable; - qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels; + //qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; return avail; } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { if (level != _minMipLevelAvailable - 1) { - qWarning() << "Invalid level to be stored"; + qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level; return; } @@ -124,7 +126,10 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } if (storage->size() != _ktxDescriptor->images[level]._imageSize) { - throw std::runtime_error("Invalid image size for level"); + qDebug() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize + << ", filename: " << QString::fromStdString(_filename); + //throw std::runtime_error("Invalid image size for level"); + return; } @@ -258,7 +263,6 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { ktx::KeyValues keyValues; keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval)); - static const std::string SOURCE_HASH_KEY = "hifi.sourceHash"; auto hash = texture.sourceHash(); if (!hash.empty()) { keyValues.emplace_back(ktx::KeyValue(SOURCE_HASH_KEY, static_cast<uint32>(hash.size()), (ktx::Byte*) hash.c_str())); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 73751bdef2..45820b9351 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -34,30 +34,75 @@ uint32_t Header::evalMaxDimension() const { return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); } -uint32_t Header::evalPixelWidth(uint32_t level) const { - return std::max(getPixelWidth() >> level, 1U); +uint32_t Header::evalPixelOrBlockWidth(uint32_t level) const { + auto pixelWidth = std::max(getPixelWidth() >> level, 1U); + if (getGLType() == GLType::COMPRESSED_TYPE) { + return (pixelWidth + 3) / 4; + } else { + return pixelWidth; + } } -uint32_t Header::evalPixelHeight(uint32_t level) const { - return std::max(getPixelHeight() >> level, 1U); +uint32_t Header::evalPixelOrBlockHeight(uint32_t level) const { + auto pixelWidth = std::max(getPixelHeight() >> level, 1U); + if (getGLType() == GLType::COMPRESSED_TYPE) { + auto format = getGLInternaFormat_Compressed(); + switch (format) { + case GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT: // BC1 + case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // BC1A + case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC3 + case GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1: // BC4 + case GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2: // BC5 + return (pixelWidth + 3) / 4; + default: + throw std::runtime_error("Unknown format"); + } + } else { + return pixelWidth; + } } -uint32_t Header::evalPixelDepth(uint32_t level) const { +uint32_t Header::evalPixelOrBlockDepth(uint32_t level) const { return std::max(getPixelDepth() >> level, 1U); } -size_t Header::evalPixelSize() const { - return 4;//glTypeSize; // Really we should generate the size from the FOrmat etc +size_t Header::evalPixelOrBlockSize() const { + if (getGLType() == GLType::COMPRESSED_TYPE) { + auto format = getGLInternaFormat_Compressed(); + if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT) { + return 8; + } else if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) { + return 8; + } else if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) { + return 16; + } else if (format == GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1) { + return 8; + } else if (format == GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) { + return 16; + } + } else { + auto baseFormat = getGLBaseInternalFormat(); + if (baseFormat == GLBaseInternalFormat::RED) { + return 1; + } else if (baseFormat == GLBaseInternalFormat::RG) { + return 2; + } else if (baseFormat == GLBaseInternalFormat::RGB) { + return 3; + } else if (baseFormat == GLBaseInternalFormat::RGBA) { + return 4; + } + } + throw std::runtime_error("Unknown format"); } size_t Header::evalRowSize(uint32_t level) const { - auto pixWidth = evalPixelWidth(level); - auto pixSize = evalPixelSize(); + auto pixWidth = evalPixelOrBlockWidth(level); + auto pixSize = evalPixelOrBlockSize(); auto netSize = pixWidth * pixSize; auto padding = evalPadding(netSize); return netSize + padding; } size_t Header::evalFaceSize(uint32_t level) const { - auto pixHeight = evalPixelHeight(level); - auto pixDepth = evalPixelDepth(level); + auto pixHeight = evalPixelOrBlockHeight(level); + auto pixDepth = evalPixelOrBlockDepth(level); auto rowSize = evalRowSize(level); return pixDepth * pixHeight * rowSize; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index e8ed7da8e3..7f6f2da939 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -336,11 +336,11 @@ namespace ktx { uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } uint32_t evalMaxDimension() const; - uint32_t evalPixelWidth(uint32_t level) const; - uint32_t evalPixelHeight(uint32_t level) const; - uint32_t evalPixelDepth(uint32_t level) const; + uint32_t evalPixelOrBlockWidth(uint32_t level) const; + uint32_t evalPixelOrBlockHeight(uint32_t level) const; + uint32_t evalPixelOrBlockDepth(uint32_t level) const; - size_t evalPixelSize() const; + size_t evalPixelOrBlockSize() const; size_t evalRowSize(uint32_t level) const; size_t evalFaceSize(uint32_t level) const; size_t evalImageSize(uint32_t level) const; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 4596bf00c0..0d3d4e1d60 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -155,7 +155,7 @@ namespace ktx { ptr++; #ifdef DEBUG for (size_t k = 0; k < descriptors[i]._imageSize/4; k++) { - *(ptr + k) = 0xFFFF00FF; + *(ptr + k) = 0xFFFFFFFF; } #endif currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index abc7ecfc41..3e06142d8e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -245,7 +245,10 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { } NetworkTexture::~NetworkTexture() { - _textureSource->getGPUTexture()->unregisterMipInterestListener(this); + auto texture = _textureSource->getGPUTexture(); + if (texture) { + texture->unregisterMipInterestListener(this); + } } /// Returns a texture version of an image file @@ -412,7 +415,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.fromInclusive = -15000; _ktxMipRequest->setByteRange(range); } else { - // TODO: Discover range for other mips ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[low]._imageOffset + 4; @@ -431,6 +433,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { assert(!_ktxHeaderLoaded); + _headerRequestFinished = true; if (_ktxHeaderRequest->getResult() == ResourceRequest::Success) { _ktxHeaderLoaded = true; _ktxHeaderData = _ktxHeaderRequest->getData(); @@ -447,7 +450,7 @@ void NetworkTexture::ktxMipRequestFinished() { && _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL; if (_ktxMipRequest->getResult() == ResourceRequest::Success) { - if (_initialKtxLoaded) { + if (_highMipRequestFinished) { assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; @@ -456,6 +459,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); //texture->assignStoredMip(level, image._imageSize, ktxData); } else { + _highMipRequestFinished = true; _ktxHighMipData = _ktxMipRequest->getData(); maybeCreateKTX(); } @@ -469,118 +473,144 @@ void NetworkTexture::ktxMipRequestFinished() { // This is called when the header or top mips have been loaded void NetworkTexture::maybeCreateKTX() { - if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { - // create ktx... - auto header = reinterpret_cast<const ktx::Header*>(_ktxHeaderData.data()); + if (_headerRequestFinished && _highMipRequestFinished) { + ResourceCache::requestCompleted(_self); + + if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { + // create ktx... + auto header = reinterpret_cast<const ktx::Header*>(_ktxHeaderData.data()); + + qDebug() << "Creating KTX"; + qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); + qDebug() << "Type:" << header->glType; + qDebug() << "TypeSize:" << header->glTypeSize; + qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; + qDebug() << "numberOfFaces:" << header->numberOfFaces; + qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + 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"; + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + auto imageDescriptors = header->generateImageDescriptors(); + _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; + if (found == keyValues.end()) { + qWarning("Source hash key not found, bailing"); + filename = "test"; + //return; + + } + else { + if (found->_value.size() < 16) { + filename = _activeUrl.fileName().toStdString(); + } + else { + filename = std::string(reinterpret_cast<char*>(found->_value.data()), 32); + } + } + + auto memKtx = ktx::KTX::createBare(*header, keyValues); + + auto d = const_cast<uint8_t*>(memKtx->getStorage()->data()); + ///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); + + auto textureCache = DependencyManager::get<TextureCache>(); + + // Move ktx to file + const char* data = reinterpret_cast<const char*>(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 << "file cache failed"; + return; + } + else { + _file = file; + } + + //_ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); + auto newKtxDescriptor = memKtx->toDescriptor(); + + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); + gpu::TexturePointer texture; + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); + texture->setKtxBacking(file->getFilepath()); + texture->setSource(filename); + texture->registerMipInterestListener(this); + + auto& images = _originalKtxDescriptor->images; + size_t imageSizeRemaining = _ktxHighMipData.size(); + uint8_t* ktxData = reinterpret_cast<uint8_t*>(_ktxHighMipData.data()); + ktxData += _ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + int level; + for (level = images.size() - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; + } + qDebug() << "Transferring " << level; + ktxData -= image._imageSize; + texture->assignStoredMip(level, image._imageSize, ktxData); + ktxData -= 4; + imageSizeRemaining - image._imageSize - 4; + } + + // 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 + if (textureCache) { + texture = textureCache->cacheTextureByHash(filename, texture); + } + + + _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; + for (uint16_t l = 0; l < 200; l++) { + if (texture->isStoredMipFaceAvailable(l)) { + _lowestKnownPopulatedMip = l; + break; + } + } + ResourceCache::requestCompleted(_self); + + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); - qDebug() << "Type:" << header->glType; - qDebug() << "TypeSize:" << header->glTypeSize; - qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; - qDebug() << "numberOfFaces:" << header->numberOfFaces; - qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; - 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"; return; - } - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); - - auto imageDescriptors = header->generateImageDescriptors(); - _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); - - // Create bare ktx in memory - std::string filename = "test"; - auto memKtx = ktx::KTX::createBare(*header, keyValues); - - auto d = const_cast<uint8_t*>(memKtx->getStorage()->data()); - ///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); - - auto textureCache = DependencyManager::get<TextureCache>(); - - // Move ktx to file - const char* data = reinterpret_cast<const char*>(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 << "file cache failed"; - } else { - _file = file; - } - - //_ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); - auto newKtxDescriptor = memKtx->toDescriptor(); - - //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); - gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); - texture->setKtxBacking(file->getFilepath()); - texture->setSource(filename); - texture->registerMipInterestListener(this); - - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = _ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast<uint8_t*>(_ktxHighMipData.data()); - ktxData += _ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - uint16_t level; - for (level = images.size() - 1; level >= 0; --level) { - auto& image = images[level]; - if (image._imageSize > imageSizeRemaining) { - break; + /* + // Force load the next two levels + { + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + //startMipRangeRequest(level, level); + startRequestForNextMipLevel(); + }); + timer->setSingleShot(true); + timer->setInterval(4000); + timer->start(); } - qDebug() << "Transferring " << level; - ktxData -= image._imageSize; - texture->assignStoredMip(level, image._imageSize, ktxData); - ktxData -= 4; - imageSizeRemaining - image._imageSize - 4; - } - _initialKtxLoaded = true; - - // 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 - if (textureCache) { - texture = textureCache->cacheTextureByHash(filename, texture); - } - - - _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; - for (uint16_t l = 0; l < 200; l++) { - if (texture->isStoredMipFaceAvailable(l)) { - _lowestKnownPopulatedMip = l; - break; + { + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + //startMipRangeRequest(level - 1, level - 1); + startRequestForNextMipLevel(); + }); + timer->setSingleShot(true); + timer->setInterval(6000); + timer->start(); } - } - - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - - return; - - // Force load the next two levels - { - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, this, [=]() { - //startMipRangeRequest(level, level); - startRequestForNextMipLevel(); - }); - timer->setSingleShot(true); - timer->setInterval(4000); - timer->start(); - } - - { - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, this, [=]() { - //startMipRangeRequest(level - 1, level - 1); - startRequestForNextMipLevel(); - }); - timer->setSingleShot(true); - timer->setInterval(6000); - timer->start(); + */ } } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 50c8ecf352..30eb9d2d2e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,6 +46,7 @@ class NetworkTexture : public Resource, public Texture, public gpu::Texture::Mip public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); + NetworkTexture::~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } @@ -91,13 +92,6 @@ private: image::TextureUsage::Type _type; - enum KTXLoadState { - LOADING_HEADER, - LOADING_LOWEST_SIX, - DONE_LOADING - }; - - bool _initialKtxLoaded { false }; KTXFilePointer _file; static const uint16_t NULL_MIP_LEVEL; bool _sourceIsKTX { false }; @@ -105,6 +99,8 @@ private: std::pair<uint16_t, uint16_t> _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; + bool _headerRequestFinished{ false }; + bool _highMipRequestFinished{ false }; uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; QByteArray _ktxHeaderData; diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 0f2b696a66..6eb311fa60 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -70,7 +70,7 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u // Represents a memory mapped file FileStorage::FileStorage(const QString& filename) : _file(filename) { if (_file.open(QFile::ReadWrite)) { - qDebug() << ">>> Opening mmapped file: " << filename; + //qDebug() << ">>> Opening mmapped file: " << filename; _mapped = _file.map(0, _file.size()); if (_mapped) { _valid = true; @@ -83,7 +83,7 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } FileStorage::~FileStorage() { - qDebug() << ">>> Closing mmapped file: " << _file.fileName(); + //qDebug() << ">>> Closing mmapped file: " << _file.fileName(); if (_mapped) { if (!_file.unmap(_mapped)) { throw std::runtime_error("Unable to unmap file"); From 11751611e1085e2c3a48b1b8da21b00291a6ad85 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Mon, 17 Apr 2017 22:10:21 -0700 Subject: [PATCH 13/85] Update NetworkTexture to track current KTX download state --- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- libraries/ktx/src/ktx/KTX.cpp | 6 +- .../src/model-networking/TextureCache.cpp | 102 +++++++++--------- .../src/model-networking/TextureCache.h | 21 +++- libraries/networking/src/ResourceCache.cpp | 15 ++- libraries/networking/src/ResourceCache.h | 5 + 6 files changed, 89 insertions(+), 62 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index eb6bb169ce..533797a657 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -252,7 +252,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, cubeFaces)); } - imageOffset += mip->getSize() + 4; + imageOffset += static_cast<uint32_t>(mip->getSize()) + 4; } } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 45820b9351..d9b5ea4a62 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -12,6 +12,7 @@ #include "KTX.h" #include <algorithm> //min max and more +#include <QDebug> using namespace ktx; @@ -90,6 +91,7 @@ size_t Header::evalPixelOrBlockSize() const { return 4; } } + qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; throw std::runtime_error("Unknown format"); } @@ -119,7 +121,7 @@ ImageDescriptors Header::generateImageDescriptors() const { ImageDescriptors descriptors; uint32_t imageOffset = 0; - for (auto level = 0; level < numberOfMipmapLevels; ++level) { + for (uint32_t level = 0; level < numberOfMipmapLevels; ++level) { auto imageSize = static_cast<uint32_t>(evalImageSize(level)); ImageHeader header { numberOfFaces == NUM_CUBEMAPFACES, @@ -131,7 +133,7 @@ ImageDescriptors Header::generateImageDescriptors() const { imageOffset += imageSize + 4; ImageHeader::FaceOffsets offsets; - for (auto i = 0; i < numberOfFaces; ++i) { + for (uint32_t i = 0; i < numberOfFaces; ++i) { offsets.push_back(0); } descriptors.push_back(ImageDescriptor(header, offsets)); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3e06142d8e..3d81dd51e9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -38,6 +38,7 @@ #include <Finally.h> #include <Profile.h> +#include "NetworkLogging.h" #include "ModelNetworkingLogging.h" #include <Trace.h> #include <StatTracker.h> @@ -345,14 +346,12 @@ void NetworkTexture::makeRequest() { // We special-handle ktx requests to run 2 concurrent requests right off the bat PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); - if (!_ktxHeaderLoaded) { + if (!_ktxHeaderLoaded && !_highMipRequestFinished) { qDebug() << ">>> Making request to " << _url << " for header"; _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); if (!_ktxHeaderRequest) { //qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); - ResourceCache::requestCompleted(_self); - finishedLoading(false); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; } @@ -373,9 +372,14 @@ void NetworkTexture::makeRequest() { _bytesReceived = _bytesTotal = _bytes = 0; _ktxHeaderRequest->send(); + + startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); + } else { + if (_lowestKnownPopulatedMip > 0) { + startMipRangeRequest(_lowestKnownPopulatedMip - 1, _lowestKnownPopulatedMip - 1); + } } - startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); } void NetworkTexture::handleMipInterestCallback(uint16_t level) { @@ -384,13 +388,24 @@ void NetworkTexture::handleMipInterestCallback(uint16_t level) { void NetworkTexture::handleMipInterestLevel(int level) { _lowestRequestedMipLevel = std::min(static_cast<uint16_t>(level), _lowestRequestedMipLevel); - if (!_ktxMipRequest) { - startRequestForNextMipLevel(); + if (!_ktxMipRequest && _lowestKnownPopulatedMip > 0) { + //startRequestForNextMipLevel(); + clearLoadPriority(this); + setLoadPriority(this, _lowestKnownPopulatedMip - 1); + ResourceCache::attemptRequest(_self); } } void NetworkTexture::startRequestForNextMipLevel() { - startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1)); + if (_lowestKnownPopulatedMip == 0) { + qWarning(networking) << "Requesting next mip level but all have been fulfilled"; + return; + } + if (_pending) { + return; + } + + //startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1)); } // Load mips in the range [low, high] (inclusive) @@ -406,7 +421,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); - qDebug() << ">>> Making request to " << _url << " for " << low << " to " << high; + qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { @@ -437,7 +452,7 @@ void NetworkTexture::ktxHeaderRequestFinished() { if (_ktxHeaderRequest->getResult() == ResourceRequest::Success) { _ktxHeaderLoaded = true; _ktxHeaderData = _ktxHeaderRequest->getData(); - maybeCreateKTX(); + maybeHandleFinishedInitialLoad(); } else { handleFailedRequest(_ktxHeaderRequest->getResult()); } @@ -455,15 +470,18 @@ void NetworkTexture::ktxMipRequestFinished() { _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - _textureSource->getGPUTexture()->assignStoredMip(_ktxMipLevelRangeInFlight.first, - _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); - //texture->assignStoredMip(level, image._imageSize, ktxData); + auto texture = _textureSource->getGPUTexture(); + if (!texture) { + texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, + _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); + } else { + qWarning(networking) << "Trying to update mips but texture is null"; + } } else { _highMipRequestFinished = true; _ktxHighMipData = _ktxMipRequest->getData(); - maybeCreateKTX(); + maybeHandleFinishedInitialLoad(); } - } else { handleFailedRequest(_ktxMipRequest->getResult()); } @@ -472,11 +490,13 @@ void NetworkTexture::ktxMipRequestFinished() { } // This is called when the header or top mips have been loaded -void NetworkTexture::maybeCreateKTX() { +void NetworkTexture::maybeHandleFinishedInitialLoad() { if (_headerRequestFinished && _highMipRequestFinished) { ResourceCache::requestCompleted(_self); - if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { + if (_ktxHeaderData.size() == 0 || _ktxHighMipData.size() == 0) { + finishedLoading(false); + } else { // create ktx... auto header = reinterpret_cast<const ktx::Header*>(_ktxHeaderData.data()); @@ -490,6 +510,7 @@ void NetworkTexture::maybeCreateKTX() { 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"; + finishedLoading(false); return; } @@ -505,23 +526,23 @@ void NetworkTexture::maybeCreateKTX() { std::string filename; if (found == keyValues.end()) { qWarning("Source hash key not found, bailing"); - filename = "test"; - //return; - + finishedLoading(false); + return; } else { - if (found->_value.size() < 16) { - filename = _activeUrl.fileName().toStdString(); - } - else { + if (found->_value.size() < 32) { + qWarning("Invalid source hash key found, bailing"); + finishedLoading(false); + return; + } else { filename = std::string(reinterpret_cast<char*>(found->_value.data()), 32); } } auto memKtx = ktx::KTX::createBare(*header, keyValues); - auto d = const_cast<uint8_t*>(memKtx->getStorage()->data()); - ///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); + //auto d = const_cast<uint8_t*>(memKtx->getStorage()->data()); + //memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); auto textureCache = DependencyManager::get<TextureCache>(); @@ -531,7 +552,8 @@ void NetworkTexture::maybeCreateKTX() { KTXFilePointer file; auto& ktxCache = textureCache->_ktxCache; if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << "file cache failed"; + qCWarning(modelnetworking) << _url << " failed to write cache file"; + finishedLoading(false); return; } else { @@ -581,36 +603,8 @@ void NetworkTexture::maybeCreateKTX() { break; } } - ResourceCache::requestCompleted(_self); setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - - return; - - /* - // Force load the next two levels - { - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, this, [=]() { - //startMipRangeRequest(level, level); - startRequestForNextMipLevel(); - }); - timer->setSingleShot(true); - timer->setInterval(4000); - timer->start(); - } - - { - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, this, [=]() { - //startMipRangeRequest(level - 1, level - 1); - startRequestForNextMipLevel(); - }); - timer->setSingleShot(true); - timer->setInterval(6000); - timer->start(); - } - */ } } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 30eb9d2d2e..3048f16eb8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -84,7 +84,7 @@ protected: void startRequestForNextMipLevel(); void startMipRangeRequest(uint16_t low, uint16_t high); - void maybeCreateKTX(); + void maybeHandleFinishedInitialLoad(); private: friend class KTXReader; @@ -92,15 +92,28 @@ private: image::TextureUsage::Type _type; - KTXFilePointer _file; static const uint16_t NULL_MIP_LEVEL; + struct KTXResourceState { + NOT_LOADED = 0, + LOADING_INITIAL_DATA, // Loading KTX Header + Low Resolution Mips + WAITING_FOR_MIP_REQUEST, // Waiting for the gpu layer to report that it needs higher resolution mips + PENDING_MIP_REQUEST, // We have added ourselves to the ResourceCache queue + REQUESTING_MIP // We have a mip in flight + }; + + KTXResourceState _ktxResourceState{ NOT_LOADED }; + + KTXFilePointer _file; + bool _sourceIsKTX { false }; bool _ktxHeaderLoaded { false }; + bool _highMipRequestFinished { false }; + std::pair<uint16_t, uint16_t> _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; + ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; - bool _headerRequestFinished{ false }; - bool _highMipRequestFinished{ false }; + bool _headerRequestFinished { false }; uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; QByteArray _ktxHeaderData; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 038ee7fb53..6716b7473f 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -474,8 +474,15 @@ int ResourceCache::getLoadingRequestCount() { bool ResourceCache::attemptRequest(QSharedPointer<Resource> resource) { Q_ASSERT(!resource.isNull()); - auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>(); + if (resource->_pending) { + qWarning(networking) << "Attempted to request " << resource->getURL() << " but it was already pending"; + return false; + } + + resource->_pending = true; + + auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>(); if (_requestsActive >= _requestLimit) { // wait until a slot becomes available sharedItems->appendPendingRequest(resource); @@ -490,6 +497,12 @@ bool ResourceCache::attemptRequest(QSharedPointer<Resource> resource) { void ResourceCache::requestCompleted(QWeakPointer<Resource> resource) { auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>(); + + auto sharedResource = resource.lock(); + if (sharedResource) { + sharedResource->_pending = true; + } + sharedItems->removeRequest(resource); --_requestsActive; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index e699c3d198..13162aac9d 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -424,6 +424,9 @@ protected slots: protected: virtual void init(); + /// Called by ResourceCache to begin loading this Resource. + /// This method can be overriden to provide custom request functionality. If this is done, + /// downloadFinished and ResourceCache::requestCompleted must be called. virtual void makeRequest(); /// Checks whether the resource is cacheable. @@ -460,6 +463,8 @@ protected: int _requestID; ResourceRequest* _request{ nullptr }; + + bool _pending{ false }; public slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); From 70b816827eb56ded04abbc36f9c3ec117e2c8634 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 18 Apr 2017 00:14:04 -0700 Subject: [PATCH 14/85] Improve handling of KTX downloads in NetworkTexture --- .../src/model-networking/TextureCache.cpp | 135 ++++++++++-------- .../src/model-networking/TextureCache.h | 22 +-- libraries/networking/src/ResourceCache.cpp | 17 +-- libraries/networking/src/ResourceCache.h | 5 +- 4 files changed, 95 insertions(+), 84 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3d81dd51e9..25d7018a1a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -281,10 +281,10 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _loaded = true; } - if (_sourceIsKTX) { - _requestByteRange.fromInclusive = 0; - _requestByteRange.toExclusive = 1000; - } + //if (_sourceIsKTX) { + //_requestByteRange.fromInclusive = 0; + //_requestByteRange.toExclusive = 1000; + //} // if we have content, load it after we have our self pointer if (!content.isEmpty()) { @@ -346,7 +346,9 @@ void NetworkTexture::makeRequest() { // We special-handle ktx requests to run 2 concurrent requests right off the bat PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); - if (!_ktxHeaderLoaded && !_highMipRequestFinished) { + if (_ktxResourceState == PENDING_INITIAL_LOAD) { + _ktxResourceState = LOADING_INITIAL_DATA; + qDebug() << ">>> Making request to " << _url << " for header"; _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); @@ -374,10 +376,13 @@ void NetworkTexture::makeRequest() { _ktxHeaderRequest->send(); startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); - } else { + } else if (_ktxResourceState == PENDING_MIP_REQUEST) { + _ktxResourceState = REQUESTING_MIP; if (_lowestKnownPopulatedMip > 0) { startMipRangeRequest(_lowestKnownPopulatedMip - 1, _lowestKnownPopulatedMip - 1); } + } else { + qWarning(networking) << "NetworkTexture::makeRequest() called while not in a valid state: " << _ktxResourceState; } } @@ -387,13 +392,7 @@ void NetworkTexture::handleMipInterestCallback(uint16_t level) { } void NetworkTexture::handleMipInterestLevel(int level) { - _lowestRequestedMipLevel = std::min(static_cast<uint16_t>(level), _lowestRequestedMipLevel); - if (!_ktxMipRequest && _lowestKnownPopulatedMip > 0) { - //startRequestForNextMipLevel(); - clearLoadPriority(this); - setLoadPriority(this, _lowestKnownPopulatedMip - 1); - ResourceCache::attemptRequest(_self); - } + startRequestForNextMipLevel(); } void NetworkTexture::startRequestForNextMipLevel() { @@ -401,11 +400,15 @@ void NetworkTexture::startRequestForNextMipLevel() { qWarning(networking) << "Requesting next mip level but all have been fulfilled"; return; } - if (_pending) { - return; - } - //startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1)); + if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { + _ktxResourceState = PENDING_MIP_REQUEST; + + setLoadPriority(this, _lowestKnownPopulatedMip); + + init(); + ResourceCache::attemptRequest(_self); + } } // Load mips in the range [low, high] (inclusive) @@ -415,10 +418,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; - - if (!isHighMipRequest && !_ktxHeaderLoaded) { - return; - } _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; @@ -446,59 +445,75 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { - assert(!_ktxHeaderLoaded); + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - _headerRequestFinished = true; - if (_ktxHeaderRequest->getResult() == ResourceRequest::Success) { - _ktxHeaderLoaded = true; - _ktxHeaderData = _ktxHeaderRequest->getData(); - maybeHandleFinishedInitialLoad(); - } else { - handleFailedRequest(_ktxHeaderRequest->getResult()); - } - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; + _ktxHeaderRequestFinished = true; + maybeHandleFinishedInitialLoad(); } void NetworkTexture::ktxMipRequestFinished() { - bool isHighMipRequest = _ktxMipLevelRangeInFlight.first == NULL_MIP_LEVEL - && _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL; + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); - if (_ktxMipRequest->getResult() == ResourceRequest::Success) { - if (_highMipRequestFinished) { - assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + if (_ktxResourceState == LOADING_INITIAL_DATA) { + _ktxHighMipRequestFinished = true; + maybeHandleFinishedInitialLoad(); + } else if (_ktxResourceState == REQUESTING_MIP) { + ResourceCache::requestCompleted(_self); + + if (_ktxMipRequest->getResult() == ResourceRequest::Success) { + Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); - _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - auto texture = _textureSource->getGPUTexture(); - if (!texture) { + if (texture) { + _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); } else { qWarning(networking) << "Trying to update mips but texture is null"; } + _ktxResourceState = WAITING_FOR_MIP_REQUEST; } else { - _highMipRequestFinished = true; - _ktxHighMipData = _ktxMipRequest->getData(); - maybeHandleFinishedInitialLoad(); + _ktxResourceState = PENDING_MIP_REQUEST; + handleFailedRequest(_ktxMipRequest->getResult()); } + + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } else { - handleFailedRequest(_ktxMipRequest->getResult()); + qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; } // This is called when the header or top mips have been loaded void NetworkTexture::maybeHandleFinishedInitialLoad() { - if (_headerRequestFinished && _highMipRequestFinished) { + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + + if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { + ResourceCache::requestCompleted(_self); - if (_ktxHeaderData.size() == 0 || _ktxHighMipData.size() == 0) { - finishedLoading(false); + if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { + if (handleFailedRequest(_ktxMipRequest->getResult())) { + _ktxResourceState = PENDING_INITIAL_LOAD; + } else { + _ktxResourceState = FAILED_TO_LOAD; + } + + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } else { // create ktx... - auto header = reinterpret_cast<const ktx::Header*>(_ktxHeaderData.data()); + auto ktxHeaderData = _ktxHeaderRequest->getData(); + auto ktxHighMipData = _ktxMipRequest->getData(); + + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; + + auto header = reinterpret_cast<const ktx::Header*>(ktxHeaderData.data()); qDebug() << "Creating KTX"; qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); @@ -507,14 +522,16 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; qDebug() << "numberOfFaces:" << header->numberOfFaces; qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + auto kvSize = header->bytesOfKeyValueData; - if (kvSize > _ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { + 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); return; } - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); auto imageDescriptors = header->generateImageDescriptors(); _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); @@ -532,6 +549,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { else { if (found->_value.size() < 32) { qWarning("Invalid source hash key found, bailing"); + _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; } else { @@ -553,10 +571,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); return; - } - else { + } else { _file = file; } @@ -571,9 +589,9 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { texture->registerMipInterestListener(this); auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = _ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast<uint8_t*>(_ktxHighMipData.data()); - ktxData += _ktxHighMipData.size(); + size_t imageSizeRemaining = ktxHighMipData.size(); + uint8_t* ktxData = reinterpret_cast<uint8_t*>(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); // TODO Move image offset calculation to ktx ImageDescriptor int level; for (level = images.size() - 1; level >= 0; --level) { @@ -585,7 +603,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { ktxData -= image._imageSize; texture->assignStoredMip(level, image._imageSize, ktxData); ktxData -= 4; - imageSizeRemaining - image._imageSize - 4; + imageSizeRemaining -= (image._imageSize + 4); } // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different @@ -604,6 +622,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } } + _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 3048f16eb8..1ea266a115 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -93,31 +93,31 @@ private: image::TextureUsage::Type _type; static const uint16_t NULL_MIP_LEVEL; - struct KTXResourceState { - NOT_LOADED = 0, + enum KTXResourceState { + PENDING_INITIAL_LOAD = 0, LOADING_INITIAL_DATA, // Loading KTX Header + Low Resolution Mips WAITING_FOR_MIP_REQUEST, // Waiting for the gpu layer to report that it needs higher resolution mips PENDING_MIP_REQUEST, // We have added ourselves to the ResourceCache queue - REQUESTING_MIP // We have a mip in flight + REQUESTING_MIP, // We have a mip in flight + FAILED_TO_LOAD }; - KTXResourceState _ktxResourceState{ NOT_LOADED }; + bool _sourceIsKTX { false }; + KTXResourceState _ktxResourceState { PENDING_INITIAL_LOAD }; + // TODO Can this be removed? KTXFilePointer _file; - bool _sourceIsKTX { false }; - bool _ktxHeaderLoaded { false }; - bool _highMipRequestFinished { false }; - + // The current mips that are currently being requested w/ _ktxMipRequest std::pair<uint16_t, uint16_t> _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; - bool _headerRequestFinished { false }; + bool _ktxHeaderRequestFinished{ false }; + bool _ktxHighMipRequestFinished{ false }; + uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; - QByteArray _ktxHeaderData; - QByteArray _ktxHighMipData; // This is a copy of the original KTX descriptor from the source url. // We need this because the KTX that will be cached will likely include extra data diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 6716b7473f..95bfd0e24d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -475,12 +475,6 @@ int ResourceCache::getLoadingRequestCount() { bool ResourceCache::attemptRequest(QSharedPointer<Resource> resource) { Q_ASSERT(!resource.isNull()); - if (resource->_pending) { - qWarning(networking) << "Attempted to request " << resource->getURL() << " but it was already pending"; - return false; - } - - resource->_pending = true; auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>(); if (_requestsActive >= _requestLimit) { @@ -498,11 +492,6 @@ bool ResourceCache::attemptRequest(QSharedPointer<Resource> resource) { void ResourceCache::requestCompleted(QWeakPointer<Resource> resource) { auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>(); - auto sharedResource = resource.lock(); - if (sharedResource) { - sharedResource->_pending = true; - } - sharedItems->removeRequest(resource); --_requestsActive; @@ -747,7 +736,8 @@ void Resource::handleReplyFinished() { _request = nullptr; } -void Resource::handleFailedRequest(ResourceRequest::Result result) { +bool Resource::handleFailedRequest(ResourceRequest::Result result) { + bool willRetry = false; switch (result) { case ResourceRequest::Result::Timeout: { qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; @@ -763,6 +753,7 @@ void Resource::handleFailedRequest(ResourceRequest::Result result) { << "if resource is still needed"; QTimer::singleShot(waitTime, this, &Resource::attemptRequest); + willRetry = true; break; } // fall through to final failure @@ -772,10 +763,12 @@ void Resource::handleFailedRequest(ResourceRequest::Result result) { auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError : QNetworkReply::UnknownNetworkError; emit failed(error); + willRetry = false; finishedLoading(false); break; } } + return willRetry; } uint qHash(const QPointer<QObject>& value, uint seed) { diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 13162aac9d..3a28c6c313 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -445,7 +445,8 @@ protected: Q_INVOKABLE void allReferencesCleared(); - void handleFailedRequest(ResourceRequest::Result result); + /// Return true if the resource will be retried + bool handleFailedRequest(ResourceRequest::Result result); QUrl _url; QUrl _activeUrl; @@ -464,8 +465,6 @@ protected: int _requestID; ResourceRequest* _request{ nullptr }; - bool _pending{ false }; - public slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); void handleReplyFinished(); From 5594e81fe40779c5c20c1c3646d9e19464904dab Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 18 Apr 2017 17:03:54 -0700 Subject: [PATCH 15/85] Improve gl backend handling of unavailable mips --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 1 + libraries/gpu-gl/src/gpu/gl/GLTexture.h | 1 + libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 1 + .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 14 ++++++++++---- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 + .../gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 10 ++++++++++ .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 10 +++++++++- libraries/gpu/src/gpu/Texture.h | 4 ++++ libraries/gpu/src/gpu/Texture_ktx.cpp | 7 ++++++- libraries/ktx/src/ktx/KTX.cpp | 6 ++++-- .../src/model-networking/TextureCache.cpp | 14 +++++++++++--- 11 files changed, 58 insertions(+), 11 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index b7d2ee0b0f..3853d0a9cc 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -520,6 +520,7 @@ void GLVariableAllocationSupport::processWorkQueues() { _memoryPressureStateStale = true; } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { if (!vartexture->canPromote()) { + vartexture->populateTransferQueue(); continue; } vartexture->promote(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 8b4b545b7d..c8ec4c5fe6 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -116,6 +116,7 @@ protected: bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); + virtual bool canPopulate() const = 0; virtual void populateTransferQueue() = 0; virtual void promote() = 0; virtual void demote() = 0; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 19979a1778..c0b9ea0e45 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -100,6 +100,7 @@ public: GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture); ~GL41VariableAllocationTexture(); + bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } void allocateStorage(uint16 allocatedMip); void syncSampler() const override; void promote() override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index bff5bf3f2c..04c2201c75 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -392,8 +392,10 @@ void GL41VariableAllocationTexture::populateTransferQueue() { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); continue; } @@ -401,6 +403,7 @@ void GL41VariableAllocationTexture::populateTransferQueue() { if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); + didQueueTransfer = true; continue; } @@ -416,14 +419,17 @@ void GL41VariableAllocationTexture::populateTransferQueue() { uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; + didQueueTransfer = true; } } // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); + if (didQueueTransfer) { + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); + } } while (sourceMip != _allocatedMip); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index dbedd81c76..15e98c3af7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -100,6 +100,7 @@ public: GL45VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture); ~GL45VariableAllocationTexture(); Size size() const override { return _size; } + bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } Size _size { 0 }; }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index a539b76b6c..54b2411a14 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -80,6 +80,16 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { default: Q_UNREACHABLE(); } + } else { + + if (texture.getUsageType() == TextureUsageType::RESOURCE) { + auto varTex = static_cast<GL45VariableAllocationTexture*> (object); + + if (varTex->canPromoteAndPopulate()) { + GL45VariableAllocationTexture::_memoryPressureStateStale = true; + } + + } } return object; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 09361689d8..ac2b5f607f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -62,6 +62,7 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t allocatedMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); allocateStorage(allocatedMip); + _memoryPressureStateStale = true; copyMipsFromTexture(); syncSampler(); @@ -105,7 +106,12 @@ void GL45ResourceTexture::syncSampler() const { void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - Q_ASSERT(_allocatedMip > 0); + //Q_ASSERT(_allocatedMip > 0); + uint16_t sourceMip = _populatedMip; + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, 0)) { + const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); + return; + } GLuint oldId = _id; auto oldSize = _size; // create new texture @@ -191,6 +197,7 @@ void GL45ResourceTexture::populateTransferQueue() { for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); + _minRequestedMip = sourceMip; continue; } @@ -218,6 +225,7 @@ void GL45ResourceTexture::populateTransferQueue() { lineOffset += linesToCopy; transferQueued = true; } + _minRequestedMip = std::min(_minRequestedMip, sourceMip); } // queue up the sampler and populated mip change for after the transfer has completed diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index e71cd63fbd..6e13598caa 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -275,6 +275,7 @@ public: virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; + virtual uint16 minAvailableMipLevel() const { return 0; } Texture::Type getType() const { return _type; } Stamp getStamp() const { return _stamp; } @@ -315,6 +316,7 @@ public: bool isMipAvailable(uint16 level, uint8 face = 0) const override; void assignMipData(uint16 level, const storage::StoragePointer& storage) override; void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; + uint16 minAvailableMipLevel() const override; void reset() override { } @@ -332,6 +334,8 @@ public: friend class Texture; }; + uint16 minAvailableMipLevel() const { return _storage->minAvailableMipLevel(); }; + static const uint16 MAX_NUM_MIPS = 0; static const uint16 SINGLE_MIP = 1; static TexturePointer create1D(const Element& texelFormat, uint16 width, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 533797a657..e2b9b8d9ae 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -115,9 +115,14 @@ bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { return avail; } +uint16 KtxStorage::minAvailableMipLevel() const { + return _minMipLevelAvailable; +} + void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { if (level != _minMipLevelAvailable - 1) { qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level; + //throw std::runtime_error("Invalid image size for level"); return; } @@ -128,7 +133,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor if (storage->size() != _ktxDescriptor->images[level]._imageSize) { qDebug() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize << ", filename: " << QString::fromStdString(_filename); - //throw std::runtime_error("Invalid image size for level"); + throw std::runtime_error("Invalid image size for level"); return; } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index d9b5ea4a62..c9dd18d665 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -92,7 +92,8 @@ size_t Header::evalPixelOrBlockSize() const { } } qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; - throw std::runtime_error("Unknown format"); + return 1; + //throw std::runtime_error("Unknown format"); } size_t Header::evalRowSize(uint32_t level) const { @@ -130,9 +131,10 @@ ImageDescriptors Header::generateImageDescriptors() const { 0 }; - imageOffset += imageSize + 4; + imageOffset += (imageSize * numberOfFaces) + 4; ImageHeader::FaceOffsets offsets; + // TODO Add correct face offsets for (uint32_t i = 0; i < numberOfFaces; ++i) { offsets.push_back(0); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 25d7018a1a..11121570c1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -388,6 +388,7 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::handleMipInterestCallback(uint16_t level) { + //qDebug(networking) << "++++ Got request for mip level: " << _url << " " << level; QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(int, level)); } @@ -466,6 +467,7 @@ void NetworkTexture::ktxMipRequestFinished() { auto texture = _textureSource->getGPUTexture(); if (texture) { _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; + qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); } else { @@ -542,9 +544,10 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { }); std::string filename; if (found == keyValues.end()) { - qWarning("Source hash key not found, bailing"); - finishedLoading(false); - return; + //qWarning("Source hash key not found, bailing"); + //finishedLoading(false); + //return; + filename = _url.fileName().toStdString(); } else { if (found->_value.size() < 32) { @@ -558,6 +561,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; + finishedLoading(false); + return; + } //auto d = const_cast<uint8_t*>(memKtx->getStorage()->data()); //memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); From 70eaac8d6c5b509d3e395f2595e41011ee129f71 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 18 Apr 2017 21:16:17 -0700 Subject: [PATCH 16/85] Add persisting of ktx min mips available to ktx cache file --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 6 +++--- libraries/gpu/src/gpu/Texture_ktx.cpp | 4 ++++ libraries/ktx/src/ktx/KTX.cpp | 12 ++++++++++++ libraries/ktx/src/ktx/KTX.h | 1 + .../src/model-networking/TextureCache.cpp | 3 ++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 3853d0a9cc..c82fa4c744 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -458,12 +458,12 @@ void GLVariableAllocationSupport::updateMemoryPressure() { float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; auto newState = MemoryPressureState::Idle; - if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + if (hasTransfers) { + newState = MemoryPressureState::Transfer; + } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { newState = MemoryPressureState::Oversubscribed; } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { newState = MemoryPressureState::Undersubscribed; - } else if (hasTransfers) { - newState = MemoryPressureState::Transfer; } if (newState != _memoryPressureState) { diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index e2b9b8d9ae..c0fe3f5c9c 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -155,6 +155,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; data += 4; + auto offset = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); { std::lock_guard<std::mutex> lock { _cacheFileWriteMutex }; @@ -165,6 +166,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; + if (offset > 0) { + memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + offset, (uint8_t)_minMipLevelAvailable, 1); + } } } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index c9dd18d665..68b83b1682 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -118,6 +118,18 @@ size_t Header::evalImageSize(uint32_t level) const { } } + +size_t KTXDescriptor::getValueOffsetForKey(const std::string& key) const { + size_t offset { 0 }; + for (auto& kv : keyValues) { + if (kv._key == key) { + return offset + kv._key.size() + 1; + } + offset += kv.serializedByteSize(); + } + return 0; +} + ImageDescriptors Header::generateImageDescriptors() const { ImageDescriptors descriptors; diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 7f6f2da939..65ecc430b9 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -471,6 +471,7 @@ namespace ktx { const ImageDescriptors images; size_t getMipFaceTexelsSize(uint16_t mip = 0, uint8_t face = 0) const; size_t getMipFaceTexelsOffset(uint16_t mip = 0, uint8_t face = 0) const; + size_t getValueOffsetForKey(const std::string& key) const; }; class KTX { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 11121570c1..8b6530b5b9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -377,8 +377,8 @@ void NetworkTexture::makeRequest() { startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); } else if (_ktxResourceState == PENDING_MIP_REQUEST) { - _ktxResourceState = REQUESTING_MIP; if (_lowestKnownPopulatedMip > 0) { + _ktxResourceState = REQUESTING_MIP; startMipRangeRequest(_lowestKnownPopulatedMip - 1, _lowestKnownPopulatedMip - 1); } } else { @@ -393,6 +393,7 @@ void NetworkTexture::handleMipInterestCallback(uint16_t level) { } void NetworkTexture::handleMipInterestLevel(int level) { + _lowestRequestedMipLevel = std::min((uint16_t)level, _lowestRequestedMipLevel); startRequestForNextMipLevel(); } From ce12a216c07bc654381eb42e4816f2f8e3812360 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 19 Apr 2017 01:20:03 -0700 Subject: [PATCH 17/85] I think it might be working. --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 3 + libraries/gpu-gl/src/gpu/gl/GLTexture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 5 +- .../src/model-networking/ModelCache.cpp | 1 + .../src/model-networking/TextureCache.cpp | 147 ++++++++++-------- .../networking/src/HTTPResourceRequest.cpp | 3 +- 6 files changed, 90 insertions(+), 71 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index c82fa4c744..652dc3c46f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -459,10 +459,13 @@ void GLVariableAllocationSupport::updateMemoryPressure() { auto newState = MemoryPressureState::Idle; if (hasTransfers) { + qDebug() << "Transferring"; newState = MemoryPressureState::Transfer; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + qDebug() << "Demoting"; newState = MemoryPressureState::Oversubscribed; } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { + qDebug() << "Promoting"; newState = MemoryPressureState::Undersubscribed; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index c8ec4c5fe6..7f3431b7eb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -112,7 +112,7 @@ protected: static void manageMemory(); //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > 0; } + bool canPromote() const { return _allocatedMip > 0 || _populatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index c0fe3f5c9c..a7af329505 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -51,6 +51,9 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { ktx::StoragePointer storage{ new storage::FileStorage(_filename.c_str()) }; auto ktxPointer = ktx::KTX::create(storage); _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); + if (_ktxDescriptor->images.size() < _ktxDescriptor->header.numberOfMipmapLevels) { + qDebug() << "bad images found"; + } auto& keyValues = _ktxDescriptor->keyValues; auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { return val._key.compare(ktx::HIFI_MIN_POPULATED_MIP_KEY) == 0; @@ -167,7 +170,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; if (offset > 0) { - memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + offset, (uint8_t)_minMipLevelAvailable, 1); + memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + offset, (void*)&_minMipLevelAvailable, 1); } } } diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 623832aaa8..defe868abc 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -364,6 +364,7 @@ void Geometry::setTextures(const QVariantMap& textureMap) { bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { + qDebug() << "Textures not loaded for " << _fbxGeometry->originalURL; for (auto& material : _materials) { // Check if material textures are loaded bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 8b6530b5b9..4c40075daa 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -422,7 +422,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); - qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; + //qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { @@ -460,6 +460,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxHighMipRequestFinished = true; maybeHandleFinishedInitialLoad(); } else if (_ktxResourceState == REQUESTING_MIP) { + Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); ResourceCache::requestCompleted(_self); if (_ktxMipRequest->getResult() == ResourceRequest::Success) { @@ -468,20 +469,26 @@ void NetworkTexture::ktxMipRequestFinished() { auto texture = _textureSource->getGPUTexture(); if (texture) { _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - qDebug() << "Writing mip for " << _url; + //qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); } else { qWarning(networking) << "Trying to update mips but texture is null"; } + finishedLoading(true); _ktxResourceState = WAITING_FOR_MIP_REQUEST; } else { _ktxResourceState = PENDING_MIP_REQUEST; + finishedLoading(false); handleFailedRequest(_ktxMipRequest->getResult()); } _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; + + if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { + startRequestForNextMipLevel(); + } } else { qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } @@ -544,85 +551,88 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; }); std::string filename; - if (found == keyValues.end()) { - //qWarning("Source hash key not found, bailing"); - //finishedLoading(false); - //return; - filename = _url.fileName().toStdString(); - } - else { - if (found->_value.size() < 32) { - qWarning("Invalid source hash key found, bailing"); - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } else { - filename = std::string(reinterpret_cast<char*>(found->_value.data()), 32); - } - } - - auto memKtx = ktx::KTX::createBare(*header, keyValues); - if (!memKtx) { - qWarning() << " Ktx could not be created, bailing"; - finishedLoading(false); - return; - } - - //auto d = const_cast<uint8_t*>(memKtx->getStorage()->data()); - //memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); - - auto textureCache = DependencyManager::get<TextureCache>(); - - // Move ktx to file - const char* data = reinterpret_cast<const char*>(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"; + std::string hash; + if (found == keyValues.end() || found->_value.size() != 32) { + qWarning("Invalid source hash key found, bailing"); _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; } else { - _file = file; + hash = filename = std::string(reinterpret_cast<char*>(found->_value.data()), 32); + //hash = filename = _url.path().replace("/", "_").toStdString(); } - //_ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); - auto newKtxDescriptor = memKtx->toDescriptor(); + auto textureCache = DependencyManager::get<TextureCache>(); - //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); - gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); - texture->setKtxBacking(file->getFilepath()); - texture->setSource(filename); - texture->registerMipInterestListener(this); + gpu::TexturePointer texture = textureCache->getTextureByHash(hash); - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast<uint8_t*>(ktxHighMipData.data()); - ktxData += ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - int level; - for (level = 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.reset(gpu::Texture::unserialize(ktxFile->getFilepath())); + if (texture) { + texture = textureCache->cacheTextureByHash(hash, texture); + } } - qDebug() << "Transferring " << level; - ktxData -= image._imageSize; - texture->assignStoredMip(level, image._imageSize, ktxData); - ktxData -= 4; - imageSizeRemaining -= (image._imageSize + 4); } - // 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 - if (textureCache) { + if (!texture) { + + auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; + finishedLoading(false); + return; + } + + // Move ktx to file + const char* data = reinterpret_cast<const char*>(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"; + _ktxResourceState = FAILED_TO_LOAD; + finishedLoading(false); + return; + } + else { + _file = file; + } + + auto newKtxDescriptor = memKtx->toDescriptor(); + + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); + texture.reset(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<uint8_t*>(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + int level; + for (level = images.size() - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; + } + //qDebug() << "Transferring " << level; + ktxData -= image._imageSize; + texture->assignStoredMip(level, image._imageSize, ktxData); + ktxData -= 4; + imageSizeRemaining -= (image._imageSize + 4); + } + + // 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 = _originalKtxDescriptor->header.numberOfMipmapLevels; for (uint16_t l = 0; l < 200; l++) { if (texture->isStoredMipFaceAvailable(l)) { @@ -631,8 +641,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } } + + texture->registerMipInterestListener(this); _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + qDebug() << "Loaded KTX: " << QString::fromStdString(hash) << " : " << _url; } } } diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 8958eeaf3b..0023ecb09c 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -132,7 +132,7 @@ void HTTPResourceRequest::onRequestFinished() { uint64_t size; std::tie(success, size) = parseContentRangeHeader(contentRangeHeader); if (success) { - qWarning(networking) << "Total http resource size is: " << size; + //qWarning(networking) << "Total http resource size is: " << size; _totalSizeOfResource = size; } else { qWarning(networking) << "Error parsing content-range header: " << contentRangeHeader; @@ -187,7 +187,6 @@ void HTTPResourceRequest::onRequestFinished() { } void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - qDebug() << "Progress: " << _url; Q_ASSERT(_state == InProgress); // We've received data, so reset the timer From cc7169b754e4656a87f5a134a06c244d53240117 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 19 Apr 2017 11:08:37 -0700 Subject: [PATCH 18/85] Add debug logging for ktx downloads --- libraries/gpu/src/gpu/Texture_ktx.cpp | 4 ++-- .../model-networking/src/model-networking/ModelCache.cpp | 2 +- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index a7af329505..5d85a9f01e 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -135,8 +135,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor if (storage->size() != _ktxDescriptor->images[level]._imageSize) { qDebug() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize - << ", filename: " << QString::fromStdString(_filename); - throw std::runtime_error("Invalid image size for level"); + << ", level: " << level << ", filename: " << QString::fromStdString(_filename); + //throw std::runtime_error("Invalid image size for level"); return; } diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index defe868abc..fc662e32a3 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -364,7 +364,7 @@ void Geometry::setTextures(const QVariantMap& textureMap) { bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { - qDebug() << "Textures not loaded for " << _fbxGeometry->originalURL; + //qDebug() << "Textures not loaded for " << _fbxGeometry->originalURL; for (auto& material : _materials) { // Check if material textures are loaded bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4c40075daa..1a80280546 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -399,7 +399,7 @@ void NetworkTexture::handleMipInterestLevel(int level) { void NetworkTexture::startRequestForNextMipLevel() { if (_lowestKnownPopulatedMip == 0) { - qWarning(networking) << "Requesting next mip level but all have been fulfilled"; + qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _url; return; } @@ -422,7 +422,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); - //qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { @@ -431,6 +430,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.fromInclusive = -15000; _ktxMipRequest->setByteRange(range); } else { + qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[low]._imageOffset + 4; From 841d301decaf3cc093bc9684c0f4451ef92287e3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 19 Apr 2017 11:25:20 -0700 Subject: [PATCH 19/85] Fix build errors due to TexturePointer change --- libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 6e13598caa..adf6a464f7 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -525,7 +525,7 @@ public: ExternalUpdates getUpdates() const; // Serialize ktx header and keyvalues directly to a file, and return a Texture representing that file - static Texture* serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues); + static TexturePointer serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues); // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 5d85a9f01e..9a1c633be5 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -370,7 +370,7 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe return tex; } -Texture* Texture::serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues) { +TexturePointer Texture::serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues) { // Create a memory-backed KTX object auto ktxBuffer = ktx::KTX::createBare(header, keyValues); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1a80280546..cf94192442 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -569,7 +569,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (!texture) { KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); if (ktxFile) { - texture.reset(gpu::Texture::unserialize(ktxFile->getFilepath())); + texture = gpu::Texture::unserialize(ktxFile->getFilepath()); if (texture) { texture = textureCache->cacheTextureByHash(hash, texture); } @@ -603,7 +603,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto newKtxDescriptor = memKtx->toDescriptor(); //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); + texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); From 472c888529c96d752cc75e91c5ba405349aa2580 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 19 Apr 2017 13:16:53 -0700 Subject: [PATCH 20/85] Fix handling of failed ktx downloads --- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 2 +- libraries/ktx/src/ktx/KTX.cpp | 8 +++- libraries/ktx/src/ktx/KTX.h | 2 + .../src/model-networking/TextureCache.cpp | 44 ++++++++++++++----- .../render-utils/src/MeshPartPayload.cpp | 3 +- tests/gpu-test/CMakeLists.txt | 2 +- 6 files changed, 46 insertions(+), 15 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 7f3431b7eb..bc8467b808 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -114,7 +114,7 @@ protected: //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } bool canPromote() const { return _allocatedMip > 0 || _populatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } + bool hasPendingTransfers() const { return _pendingTransfers.size() > 0; } void executeNextTransfer(const TexturePointer& currentTexture); virtual bool canPopulate() const = 0; virtual void populateTransferQueue() = 0; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 68b83b1682..e00937a67a 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -92,13 +92,16 @@ size_t Header::evalPixelOrBlockSize() const { } } qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; - return 1; + return 0; //throw std::runtime_error("Unknown format"); } size_t Header::evalRowSize(uint32_t level) const { auto pixWidth = evalPixelOrBlockWidth(level); auto pixSize = evalPixelOrBlockSize(); + if (pixSize == 0) { + return 0; + } auto netSize = pixWidth * pixSize; auto padding = evalPadding(netSize); return netSize + padding; @@ -136,6 +139,9 @@ ImageDescriptors Header::generateImageDescriptors() const { uint32_t imageOffset = 0; for (uint32_t level = 0; level < numberOfMipmapLevels; ++level) { auto imageSize = static_cast<uint32_t>(evalImageSize(level)); + if (imageSize == 0) { + return ImageDescriptors(); + } ImageHeader header { numberOfFaces == NUM_CUBEMAPFACES, imageOffset, diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 65ecc430b9..713733b1dd 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -298,6 +298,8 @@ namespace ktx { struct ImageDescriptor; using ImageDescriptors = std::vector<ImageDescriptor>; + bool checkIdentifier(const Byte* identifier); + // Header struct Header { static const size_t IDENTIFIER_LENGTH = 12; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index cf94192442..17f1d62b7b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -399,14 +399,15 @@ void NetworkTexture::handleMipInterestLevel(int level) { void NetworkTexture::startRequestForNextMipLevel() { if (_lowestKnownPopulatedMip == 0) { - qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _url; + qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip + << " " << _textureSource->getGPUTexture()->minAvailableMipLevel() << " " << _url; return; } if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { _ktxResourceState = PENDING_MIP_REQUEST; - setLoadPriority(this, _lowestKnownPopulatedMip); + setLoadPriority(this, -_originalKtxDescriptor->header.numberOfMipmapLevels + _lowestKnownPopulatedMip); init(); ResourceCache::attemptRequest(_self); @@ -449,8 +450,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - _ktxHeaderRequestFinished = true; - maybeHandleFinishedInitialLoad(); +_ktxHeaderRequestFinished = true; +maybeHandleFinishedInitialLoad(); } void NetworkTexture::ktxMipRequestFinished() { @@ -459,7 +460,8 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == LOADING_INITIAL_DATA) { _ktxHighMipRequestFinished = true; maybeHandleFinishedInitialLoad(); - } else if (_ktxResourceState == REQUESTING_MIP) { + } + else if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); ResourceCache::requestCompleted(_self); @@ -472,15 +474,22 @@ void NetworkTexture::ktxMipRequestFinished() { //qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); - } else { + } + else { qWarning(networking) << "Trying to update mips but texture is null"; } finishedLoading(true); _ktxResourceState = WAITING_FOR_MIP_REQUEST; - } else { - _ktxResourceState = PENDING_MIP_REQUEST; + } + else { finishedLoading(false); - handleFailedRequest(_ktxMipRequest->getResult()); + if (handleFailedRequest(_ktxMipRequest->getResult())) { + _ktxResourceState = PENDING_MIP_REQUEST; + } + else { + qWarning() << "Failed to load mip: " << _url; + _ktxResourceState = FAILED_TO_LOAD; + } } _ktxMipRequest->deleteLater(); @@ -489,7 +498,8 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { startRequestForNextMipLevel(); } - } else { + } + else { qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } } @@ -505,7 +515,8 @@ 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; } @@ -533,6 +544,12 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { qDebug() << "numberOfFaces:" << header->numberOfFaces; qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + if (!ktx::checkIdentifier(header->identifier)) { + qWarning() << "Cannot load " << _url << ", invalid header identifier"; + _ktxResourceState = FAILED_TO_LOAD; + finishedLoading(false); + } + 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"; @@ -544,6 +561,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); auto imageDescriptors = header->generateImageDescriptors(); + if (imageDescriptors.size() == 0) { + qWarning(networking) << "Failed to process ktx file " << _url; + _ktxResourceState = FAILED_TO_LOAD; + finishedLoading(false); + } _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); // Create bare ktx in memory diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 51ce0fffa7..e6ae6d21a1 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -544,7 +544,8 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } if (_fadeState == FADE_WAITING_TO_START) { - if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + //if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + if (_model->isLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); _fadeState = FADE_IN_PROGRESS; diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 1712a5a3e1..c37e36b53b 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render-utils) # This is not a testcase -- just set it up as a regular hifi project setup_hifi_project(Quick Gui OpenGL Script Widgets) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") -link_hifi_libraries(networking gl gpu gpu-gl procedural shared fbx model model-networking animation script-engine render render-utils octree image) +link_hifi_libraries(networking gl gpu gpu-gl procedural shared fbx model model-networking animation script-engine render render-utils octree image ktx) package_libraries_for_deployment() target_nsight() From d5f1e6fb373e7c004353fd0d771da8996f3aeb08 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 19 Apr 2017 17:01:36 -0700 Subject: [PATCH 21/85] Adjust handling of requested mips in gl backend --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 2 +- .../gpu/gl45/GL45BackendVariableTexture.cpp | 8 ++--- libraries/gpu/src/gpu/Texture_ktx.cpp | 3 +- libraries/ktx/src/ktx/Reader.cpp | 2 +- .../src/model-networking/TextureCache.cpp | 32 +++++++++++++------ 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 652dc3c46f..1179403bff 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -450,7 +450,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { // Track how much we're actually using totalVariableMemoryAllocation += gltexture->size(); canDemote |= vartexture->canDemote(); - canPromote |= vartexture->canPromote(); + canPromote |= vartexture->canPromote() || (texture->minAvailableMipLevel() < vartexture->_allocatedMip); hasTransfers |= vartexture->hasPendingTransfers(); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index ac2b5f607f..f99209a7c2 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -98,18 +98,14 @@ void GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); qDebug() << "glTextureParameteri " << QString::fromStdString(_source) << _populatedMip << _populatedMip - _allocatedMip; - if (_source == "test" && _populatedMip == 0) { - qDebug() << "here"; - } glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); } void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); //Q_ASSERT(_allocatedMip > 0); - uint16_t sourceMip = _populatedMip; - if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, 0)) { - const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); + if (!_gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0)) { + const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(_populatedMip - 1); return; } GLuint oldId = _id; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 9a1c633be5..620fd56277 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -123,8 +123,9 @@ uint16 KtxStorage::minAvailableMipLevel() const { } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { + qDebug() << "Populating " << level << " " << _filename.c_str(); if (level != _minMipLevelAvailable - 1) { - qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level; + qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level << " " << _filename.c_str(); //throw std::runtime_error("Invalid image size for level"); return; } diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index b22f262e85..27aee8771c 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -50,7 +50,7 @@ namespace ktx { bool checkIdentifier(const Byte* identifier) { if (!(0 == memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH))) { - throw ReaderException("identifier field invalid"); + //throw ReaderException("identifier field invalid"); return false; } return true; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 17f1d62b7b..84e9c82946 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -353,7 +353,8 @@ void NetworkTexture::makeRequest() { _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); if (!_ktxHeaderRequest) { - //qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; } @@ -424,6 +425,13 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); + if (!_ktxMipRequest) { + qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); + return; + } + _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { // This is a special case where we load the high 7 mips @@ -450,8 +458,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); -_ktxHeaderRequestFinished = true; -maybeHandleFinishedInitialLoad(); + _ktxHeaderRequestFinished = true; + maybeHandleFinishedInitialLoad(); } void NetworkTexture::ktxMipRequestFinished() { @@ -470,10 +478,11 @@ void NetworkTexture::ktxMipRequestFinished() { auto texture = _textureSource->getGPUTexture(); if (texture) { - _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; + //_lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; //qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); + _lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel(); } else { qWarning(networking) << "Trying to update mips but texture is null"; @@ -529,11 +538,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto ktxHeaderData = _ktxHeaderRequest->getData(); auto ktxHighMipData = _ktxMipRequest->getData(); - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; - auto header = reinterpret_cast<const ktx::Header*>(ktxHeaderData.data()); qDebug() << "Creating KTX"; @@ -548,10 +552,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { qWarning() << "Cannot load " << _url << ", invalid header identifier"; _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); + return; } auto kvSize = header->bytesOfKeyValueData; - if (kvSize > ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { + 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); @@ -663,11 +668,18 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } } + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + texture->registerMipInterestListener(this); _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); qDebug() << "Loaded KTX: " << QString::fromStdString(hash) << " : " << _url; + + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } } } From 18fb695614c007af22f73414af13b0d12d194230 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 20 Apr 2017 15:26:05 -0700 Subject: [PATCH 22/85] Disable pipelining in HTTPResourceRequest --- libraries/networking/src/HTTPResourceRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 0023ecb09c..7045894633 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -71,7 +71,7 @@ void HTTPResourceRequest::doSend() { qDebug() << "Setting http range to " << byteRange; networkRequest.setRawHeader("Range", byteRange.toLatin1()); } - networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, false); _reply = NetworkAccessManager::getInstance().get(networkRequest); From 4e583416312e4fa6be22158ad63347d4958b1f90 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 20 Apr 2017 16:28:57 -0700 Subject: [PATCH 23/85] Remove http proxy --- interface/src/Application.cpp | 9 --------- libraries/networking/src/NetworkAccessManager.cpp | 9 +-------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 886487603e..f51c3d2b46 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -188,7 +188,6 @@ #include <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h> #include <EntityScriptClient.h> #include <ModelScriptingInterface.h> -#include <QtNetwork/QNetworkProxy> // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -605,7 +604,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo { const QString TEST_SCRIPT = "--testScript"; const QString TRACE_FILE = "--traceFile"; - const QString HTTP_PROXY = "--httpProxy"; const QStringList args = arguments(); for (int i = 0; i < args.size() - 1; ++i) { if (args.at(i) == TEST_SCRIPT) { @@ -617,17 +615,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QString traceFilePath = args.at(i + 1); setProperty(hifi::properties::TRACING, traceFilePath); DependencyManager::get<tracing::Tracer>()->startTracing(); - } else if (args.at(i) == HTTP_PROXY) { } } } - QNetworkProxy proxy; - proxy.setType(QNetworkProxy::HttpProxy); - proxy.setHostName("127.0.0.1"); - proxy.setPort(8888); - QNetworkProxy::setApplicationProxy(proxy); - // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 6895118be5..fd356c3e94 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -19,14 +19,7 @@ QThreadStorage<QNetworkAccessManager*> networkAccessManagers; QNetworkAccessManager& NetworkAccessManager::getInstance() { if (!networkAccessManagers.hasLocalData()) { - auto nm = new QNetworkAccessManager(); - networkAccessManagers.setLocalData(nm); - - QNetworkProxy proxy; - proxy.setType(QNetworkProxy::HttpProxy); - proxy.setHostName("127.0.0.1"); - proxy.setPort(8888); - nm->setProxy(proxy); + networkAccessManagers.setLocalData(new QNetworkAccessManager()); } return *networkAccessManagers.localData(); From 7cab70debac07e44a443ed01ff2ae558e0c0ba9d Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 20 Apr 2017 23:17:42 -0700 Subject: [PATCH 24/85] Remove gl error logging --- libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 008b658205..1d1f92b297 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -259,9 +259,7 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(target, to); - if (CHECK_GL_ERROR()) { - qDebug() << "slot: " << slot << ", target: " << target << ", to: " << to; - } + (void) CHECK_GL_ERROR(); _resource._textures[slot] = resourceTexture; From b2ff0a711e4e9319b937363c7c06c81237312efc Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 20 Apr 2017 23:30:42 -0700 Subject: [PATCH 25/85] Replace canPromoteAndPopulate with multiple function calls --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 54b2411a14..761cd305bb 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -85,7 +85,7 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (texture.getUsageType() == TextureUsageType::RESOURCE) { auto varTex = static_cast<GL45VariableAllocationTexture*> (object); - if (varTex->canPromoteAndPopulate()) { + if (varTex->canPromote() && varTex->canPopulate()) { GL45VariableAllocationTexture::_memoryPressureStateStale = true; } From 790290f40ca7faa829aeb7d9a60cc8b759317b78 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 20 Apr 2017 23:31:10 -0700 Subject: [PATCH 26/85] Fix NetworkTexture build errors on osx/linux --- .../model-networking/src/model-networking/TextureCache.cpp | 6 +++--- .../model-networking/src/model-networking/TextureCache.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 84e9c82946..0bfca1f1fb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -411,7 +411,7 @@ void NetworkTexture::startRequestForNextMipLevel() { setLoadPriority(this, -_originalKtxDescriptor->header.numberOfMipmapLevels + _lowestKnownPopulatedMip); init(); - ResourceCache::attemptRequest(_self); + TextureCache::attemptRequest(_self); } } @@ -471,7 +471,7 @@ void NetworkTexture::ktxMipRequestFinished() { } else if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); if (_ktxMipRequest->getResult() == ResourceRequest::Success) { Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); @@ -519,7 +519,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { if (handleFailedRequest(_ktxMipRequest->getResult())) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 1ea266a115..5fb23c0d2d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,7 +46,7 @@ class NetworkTexture : public Resource, public Texture, public gpu::Texture::Mip public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); - NetworkTexture::~NetworkTexture() override; + ~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } From 5901ab63812eb136c7f58d98a0625064aca86073 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Thu, 20 Apr 2017 23:38:22 -0700 Subject: [PATCH 27/85] Fix initalization order warnings in KTX.h and TextureCache.cpp --- libraries/ktx/src/ktx/KTX.h | 2 +- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 713733b1dd..e84cba085d 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -419,8 +419,8 @@ namespace ktx { // This is the byte offset from the _start_ of the image region. For example, level 0 // will have a byte offset of 0. - const uint32_t _imageOffset; const uint32_t _numFaces; + const uint32_t _imageOffset; const uint32_t _imageSize; const uint32_t _faceSize; const uint32_t _padding; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 0bfca1f1fb..05cfc7c06e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -272,8 +272,8 @@ QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSh NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : Resource(url), _type(type), - _maxNumPixels(maxNumPixels), - _sourceIsKTX(url.path().endsWith(".ktx")) + _sourceIsKTX(url.path().endsWith(".ktx")), + _maxNumPixels(maxNumPixels) { _textureSource = std::make_shared<gpu::TextureSource>(); From bc6d476a4d2c2cb7aab644f815f332974cf18d9b Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Fri, 21 Apr 2017 00:21:25 -0700 Subject: [PATCH 28/85] Cleanup Texture_ktx.cpp --- libraries/gpu/src/gpu/Texture_ktx.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 620fd56277..250b9ea8f3 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -52,7 +52,7 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { auto ktxPointer = ktx::KTX::create(storage); _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); if (_ktxDescriptor->images.size() < _ktxDescriptor->header.numberOfMipmapLevels) { - qDebug() << "bad images found"; + qWarning() << "Bad images found in ktx"; } auto& keyValues = _ktxDescriptor->keyValues; auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { @@ -96,7 +96,6 @@ std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() { } PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { - //qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; storage::StoragePointer result; auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); @@ -112,10 +111,7 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { - auto avail = level >= _minMipLevelAvailable; - //qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels; - //return true; - return avail; + return level >= _minMipLevelAvailable; } uint16 KtxStorage::minAvailableMipLevel() const { @@ -123,10 +119,8 @@ uint16 KtxStorage::minAvailableMipLevel() const { } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { - qDebug() << "Populating " << level << " " << _filename.c_str(); if (level != _minMipLevelAvailable - 1) { qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level << " " << _filename.c_str(); - //throw std::runtime_error("Invalid image size for level"); return; } @@ -135,9 +129,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } if (storage->size() != _ktxDescriptor->images[level]._imageSize) { - qDebug() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize + qWarning() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize << ", level: " << level << ", filename: " << QString::fromStdString(_filename); - //throw std::runtime_error("Invalid image size for level"); return; } From 8d03d50d35843656264842cdb64240eb5b946fc0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Fri, 21 Apr 2017 00:33:05 -0700 Subject: [PATCH 29/85] Cleanup progressive ktx debugging --- .../gpu/gl45/GL45BackendVariableTexture.cpp | 9 +------ libraries/gpu/src/gpu/Texture.cpp | 1 + libraries/gpu/src/gpu/Texture.h | 3 --- libraries/gpu/src/gpu/Texture_ktx.cpp | 22 +-------------- libraries/ktx/src/ktx/KTX.cpp | 3 +-- libraries/ktx/src/ktx/Reader.cpp | 2 +- .../src/model-networking/ModelCache.cpp | 1 - .../src/model-networking/TextureCache.cpp | 27 ++----------------- .../networking/src/HTTPResourceRequest.cpp | 1 - libraries/networking/src/ResourceCache.cpp | 2 -- .../render-utils/src/MeshPartPayload.cpp | 1 - libraries/shared/src/shared/Storage.cpp | 3 --- 12 files changed, 7 insertions(+), 68 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index f99209a7c2..77e42e7fdb 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -41,9 +41,6 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { - if (texture.source().find_first_of("box.ktx") != std::string::npos) { - qDebug() << "In box.ktx ctor"; - } auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; @@ -53,13 +50,11 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend for (uint16_t mip = 0; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) && texture.isStoredMipFaceAvailable(mip)) { - _maxAllocatedMip = _populatedMip = mip; + _lowestRequestedMip = _maxAllocatedMip = _populatedMip = mip; break; } } - //_maxAllocatedMip = _populatedMip = mipLevels; - //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t allocatedMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); allocateStorage(allocatedMip); _memoryPressureStateStale = true; @@ -112,7 +107,6 @@ void GL45ResourceTexture::promote() { auto oldSize = _size; // create new texture const_cast<GLuint&>(_id) = allocate(_gpuObject); - //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min<uint16_t>(_allocatedMip, 2)); @@ -146,7 +140,6 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast<GLuint&>(_id) = allocate(_gpuObject); - //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); uint16_t mips = _gpuObject.getNumMips(); diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 1dbe9db2b4..bed01a805d 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -397,6 +397,7 @@ Size Texture::evalTotalSize(uint16 startingMip) const { Size size = 0; uint16 minMipLevel = std::max(getMinMip(), startingMip); uint16 maxMipLevel = getMaxMip(); + qDebug() << " min: " << minMipLevel << " " << maxMipLevel; for (uint16 level = minMipLevel; level <= maxMipLevel; level++) { size += evalMipSize(level); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index adf6a464f7..146615d631 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -524,9 +524,6 @@ public: ExternalUpdates getUpdates() const; - // Serialize ktx header and keyvalues directly to a file, and return a Texture representing that file - static TexturePointer serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues); - // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 250b9ea8f3..8e30645837 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -134,21 +134,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor return; } - - //auto fileStorage = new storage::FileStorage(_filename.c_str()); - //ktx::StoragePointer file { fileStorage }; auto file = maybeOpenFile(); + auto data = file->mutableData(); - data += file->size(); - - // TODO Cache this data inside Image or ImageDescriptor? - for (int i = _ktxDescriptor->header.numberOfMipmapLevels - 1; i >= level; --i) { - data -= _ktxDescriptor->images[i]._imageSize; - data -= 4; - } - data += 4; - - data = file->mutableData(); data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; data += 4; @@ -173,7 +161,6 @@ void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::Stor throw std::runtime_error("Invalid call"); } - void Texture::setKtxBacking(const std::string& filename) { // Check the KTX file for validity before using it as backing storage { @@ -364,13 +351,6 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe return tex; } -TexturePointer Texture::serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues) { - // Create a memory-backed KTX object - auto ktxBuffer = ktx::KTX::createBare(header, keyValues); - - return unserialize(ktxfile, ktxBuffer->toDescriptor()); -} - bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index e00937a67a..0dbc2e720f 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -91,9 +91,9 @@ size_t Header::evalPixelOrBlockSize() const { return 4; } } + qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; return 0; - //throw std::runtime_error("Unknown format"); } size_t Header::evalRowSize(uint32_t level) const { @@ -284,7 +284,6 @@ Image ImageDescriptor::toImage(const ktx::StoragePointer& storage) const { FaceBytes faces; faces.resize(_faceOffsets.size()); for (size_t face = 0; face < _numFaces; ++face) { - // TODO Should we be storing pointers to unowned data? faces[face] = storage->data() + _faceOffsets[face]; } // Note, implicit cast of *this to const ImageHeader& diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 27aee8771c..b22f262e85 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -50,7 +50,7 @@ namespace ktx { bool checkIdentifier(const Byte* identifier) { if (!(0 == memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH))) { - //throw ReaderException("identifier field invalid"); + throw ReaderException("identifier field invalid"); return false; } return true; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index fc662e32a3..623832aaa8 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -364,7 +364,6 @@ void Geometry::setTextures(const QVariantMap& textureMap) { bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { - //qDebug() << "Textures not loaded for " << _fbxGeometry->originalURL; for (auto& material : _materials) { // Check if material textures are loaded bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 05cfc7c06e..761f55068b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -281,11 +281,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _loaded = true; } - //if (_sourceIsKTX) { - //_requestByteRange.fromInclusive = 0; - //_requestByteRange.toExclusive = 1000; - //} - // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; @@ -364,12 +359,9 @@ void NetworkTexture::makeRequest() { range.toExclusive = 1000; _ktxHeaderRequest->setByteRange(range); - //qCDebug(resourceLog).noquote() << "Starting request for:" << _url.toDisplayString(); emit loading(); connect(_ktxHeaderRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxHeaderRequestProgress); - //connect(this, &Resource::onProgress, this, &NetworkTexture::ktxHeaderRequestFinished); - connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxHeaderRequestFinished); _bytesReceived = _bytesTotal = _bytes = 0; @@ -389,7 +381,6 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::handleMipInterestCallback(uint16_t level) { - //qDebug(networking) << "++++ Got request for mip level: " << _url << " " << level; QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(int, level)); } @@ -426,7 +417,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); if (!_ktxMipRequest) { - qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + qCWarning(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; @@ -439,7 +430,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.fromInclusive = -15000; _ktxMipRequest->setByteRange(range); } else { - qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[low]._imageOffset + 4; @@ -478,8 +468,6 @@ void NetworkTexture::ktxMipRequestFinished() { auto texture = _textureSource->getGPUTexture(); if (texture) { - //_lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - //qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data())); _lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel(); @@ -496,7 +484,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxResourceState = PENDING_MIP_REQUEST; } else { - qWarning() << "Failed to load mip: " << _url; + qWarning(networking) << "Failed to load mip: " << _url; _ktxResourceState = FAILED_TO_LOAD; } } @@ -540,14 +528,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto header = reinterpret_cast<const ktx::Header*>(ktxHeaderData.data()); - qDebug() << "Creating KTX"; - qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); - qDebug() << "Type:" << header->glType; - qDebug() << "TypeSize:" << header->glTypeSize; - qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; - qDebug() << "numberOfFaces:" << header->numberOfFaces; - qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; - if (!ktx::checkIdentifier(header->identifier)) { qWarning() << "Cannot load " << _url << ", invalid header identifier"; _ktxResourceState = FAILED_TO_LOAD; @@ -586,7 +566,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { return; } else { hash = filename = std::string(reinterpret_cast<char*>(found->_value.data()), 32); - //hash = filename = _url.path().replace("/", "_").toStdString(); } auto textureCache = DependencyManager::get<TextureCache>(); @@ -629,7 +608,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto newKtxDescriptor = memKtx->toDescriptor(); - //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); @@ -645,7 +623,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (image._imageSize > imageSizeRemaining) { break; } - //qDebug() << "Transferring " << level; ktxData -= image._imageSize; texture->assignStoredMip(level, image._imageSize, ktxData); ktxData -= 4; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 7045894633..52f5ee43f5 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -132,7 +132,6 @@ void HTTPResourceRequest::onRequestFinished() { uint64_t size; std::tie(success, size) = parseContentRangeHeader(contentRangeHeader); if (success) { - //qWarning(networking) << "Total http resource size is: " << size; _totalSizeOfResource = size; } else { qWarning(networking) << "Error parsing content-range header: " << contentRangeHeader; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 95bfd0e24d..56897ca4cd 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -667,7 +667,6 @@ void Resource::makeRequest() { } PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); - qDebug() << "Making request to " << _url << " for byte range " << _requestByteRange.fromInclusive << "-" << _requestByteRange.toExclusive; _request = ResourceManager::createResourceRequest(this, _activeUrl); @@ -700,7 +699,6 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota } void Resource::handleReplyFinished() { - qDebug() << "Got response for " << _activeUrl; Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index e6ae6d21a1..9232564514 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -544,7 +544,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } if (_fadeState == FADE_WAITING_TO_START) { - //if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { if (_model->isLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 6eb311fa60..aae1f8455f 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -67,10 +67,8 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u return std::make_shared<FileStorage>(filename); } -// Represents a memory mapped file FileStorage::FileStorage(const QString& filename) : _file(filename) { if (_file.open(QFile::ReadWrite)) { - //qDebug() << ">>> Opening mmapped file: " << filename; _mapped = _file.map(0, _file.size()); if (_mapped) { _valid = true; @@ -83,7 +81,6 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } FileStorage::~FileStorage() { - //qDebug() << ">>> Closing mmapped file: " << _file.fileName(); if (_mapped) { if (!_file.unmap(_mapped)) { throw std::runtime_error("Unable to unmap file"); From 970be9d2c567a904f0527c304ae893d2aa5ca053 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Fri, 21 Apr 2017 01:10:54 -0700 Subject: [PATCH 30/85] Add check for canPopulate to gpu backend --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 6 ++++-- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 4 ++-- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 1179403bff..f1bf76b39d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -437,6 +437,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { size_t idealMemoryAllocation = 0; bool canDemote = false; bool canPromote = false; + bool canPopulate = false; bool hasTransfers = false; for (const auto& texture : strongTextures) { // Race conditions can still leave nulls in the list, so we need to check @@ -450,7 +451,8 @@ void GLVariableAllocationSupport::updateMemoryPressure() { // Track how much we're actually using totalVariableMemoryAllocation += gltexture->size(); canDemote |= vartexture->canDemote(); - canPromote |= vartexture->canPromote() || (texture->minAvailableMipLevel() < vartexture->_allocatedMip); + canPromote |= vartexture->canPromote(); + canPopulate |= vartexture->canPopulate(); hasTransfers |= vartexture->hasPendingTransfers(); } @@ -464,7 +466,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { qDebug() << "Demoting"; newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) || canPopulate)) { qDebug() << "Promoting"; newState = MemoryPressureState::Undersubscribed; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index bc8467b808..9aad49546e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -112,11 +112,11 @@ protected: static void manageMemory(); //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > 0 || _populatedMip > 0; } + virtual bool canPopulate() const = 0; + bool canPromote() const { return _allocatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _pendingTransfers.size() > 0; } void executeNextTransfer(const TexturePointer& currentTexture); - virtual bool canPopulate() const = 0; virtual void populateTransferQueue() = 0; virtual void promote() = 0; virtual void demote() = 0; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index c0b9ea0e45..dc6d2b3aa7 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -100,7 +100,7 @@ public: GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture); ~GL41VariableAllocationTexture(); - bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } + bool canPopulate() const override { return _populatedMip > _allocatedMip && _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } void allocateStorage(uint16 allocatedMip); void syncSampler() const override; void promote() override; From 14f8c91e23442b48bd108dfa5aab4f6113cbe1a1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Fri, 21 Apr 2017 01:11:30 -0700 Subject: [PATCH 31/85] Adjust gl45 backend to request interest in mips and keep track of min requested --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 ++ .../src/gpu/gl45/GL45BackendTexture.cpp | 2 ++ .../gpu/gl45/GL45BackendVariableTexture.cpp | 23 +++++++++++-------- libraries/gpu/src/gpu/Texture.cpp | 1 - 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 15e98c3af7..21c429c577 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -117,6 +117,8 @@ public: void allocateStorage(uint16 mip); void copyMipsFromTexture(); + + uint16 _lowestRequestedMip { 0 }; }; #if 0 diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 761cd305bb..299a52eddd 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -119,8 +119,10 @@ GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; glCreateTextures(getGLTextureType(texture), 1, &result); +#ifdef DEBUG auto source = texture.source(); glObjectLabel(GL_TEXTURE, result, source.length(), source.data()); +#endif return result; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 77e42e7fdb..5dfbd6e727 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -75,6 +75,11 @@ void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { _size += _gpuObject.evalMipSize(mip); } + + if (!_gpuObject.isStoredMipFaceAvailable(allocatedMip, 0)) { + const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(allocatedMip); + } + Backend::updateTextureGPUMemoryUsage(0, _size); } @@ -98,9 +103,11 @@ void GL45ResourceTexture::syncSampler() const { void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - //Q_ASSERT(_allocatedMip > 0); - if (!_gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0)) { - const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(_populatedMip - 1); + Q_ASSERT(_allocatedMip > 0); + uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2); + if (!_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { + _lowestRequestedMip = targetAllocatedMip; + const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(targetAllocatedMip); return; } GLuint oldId = _id; @@ -109,7 +116,7 @@ void GL45ResourceTexture::promote() { const_cast<GLuint&>(_id) = allocate(_gpuObject); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level - allocateStorage(_allocatedMip - std::min<uint16_t>(_allocatedMip, 2)); + allocateStorage(targetAllocatedMip); uint16_t mips = _gpuObject.getNumMips(); // copy pre-existing mips for (uint16_t mip = _populatedMip; mip < mips; ++mip) { @@ -176,23 +183,21 @@ void GL45ResourceTexture::populateTransferQueue() { const uint8_t maxFace = GLTexture::getFaceCount(_target); uint16_t sourceMip = _populatedMip; - //qDebug() << "populateTransferQueue info : " << _populatedMip << " " << _maxAllocatedMip << " " << _allocatedMip; do { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); bool transferQueued = false; - //qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); - _minRequestedMip = sourceMip; + _lowestRequestedMip = sourceMip; continue; } + _lowestRequestedMip = sourceMip; // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { - qDebug() << "mip is less than max transfer size"; // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); transferQueued = true; @@ -207,14 +212,12 @@ void GL45ResourceTexture::populateTransferQueue() { Q_ASSERT(0 == (mipSize % lines)); uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); uint32_t lineOffset = 0; - qDebug() << "queing up single line transfers " << linesPerTransfer << " " << lineOffset; while (lineOffset < lines) { uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; transferQueued = true; } - _minRequestedMip = std::min(_minRequestedMip, sourceMip); } // queue up the sampler and populated mip change for after the transfer has completed diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index bed01a805d..1dbe9db2b4 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -397,7 +397,6 @@ Size Texture::evalTotalSize(uint16 startingMip) const { Size size = 0; uint16 minMipLevel = std::max(getMinMip(), startingMip); uint16 maxMipLevel = getMaxMip(); - qDebug() << " min: " << minMipLevel << " " << maxMipLevel; for (uint16 level = minMipLevel; level <= maxMipLevel; level++) { size += evalMipSize(level); } From 6e307dd6ebac8a82fe30d3e05306725d24c8e551 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Fri, 21 Apr 2017 01:17:59 -0700 Subject: [PATCH 32/85] Remove debug logging --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index f1bf76b39d..ef938ecd6d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -461,13 +461,10 @@ void GLVariableAllocationSupport::updateMemoryPressure() { auto newState = MemoryPressureState::Idle; if (hasTransfers) { - qDebug() << "Transferring"; newState = MemoryPressureState::Transfer; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { - qDebug() << "Demoting"; newState = MemoryPressureState::Oversubscribed; } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) || canPopulate)) { - qDebug() << "Promoting"; newState = MemoryPressureState::Undersubscribed; } From 927efc88f0de332e36c39a96d61ab02f2b854f09 Mon Sep 17 00:00:00 2001 From: Atlante45 <clement.brisset@gmail.com> Date: Thu, 20 Apr 2017 16:41:35 -0700 Subject: [PATCH 33/85] Fix GPUKTXPayload conflict across platforms --- libraries/gpu/src/gpu/Texture.h | 6 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 116 +++++++++++++++++++------- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 146615d631..5dc8871e9d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -154,7 +154,7 @@ protected: Desc _desc; }; -enum class TextureUsageType { +enum class TextureUsageType : uint8 { RENDERBUFFER, // Used as attachments to a framebuffer RESOURCE, // Resource textures, like materials... subject to memory manipulation STRICT_RESOURCE, // Resource textures not subject to manipulation, like the normal fitting texture @@ -527,8 +527,8 @@ public: // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); - static TexturePointer unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); - static TexturePointer unserialize(const std::string& ktxFile, const ktx::KTXDescriptor& descriptor, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); + static TexturePointer unserialize(const std::string& ktxFile); + static TexturePointer unserialize(const std::string& ktxFile, const ktx::KTXDescriptor& descriptor); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 8e30645837..89b7b86768 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -20,12 +20,66 @@ using PixelsPointer = Texture::PixelsPointer; using KtxStorage = Texture::KtxStorage; struct GPUKTXPayload { + using Version = uint8; + + static const std::string KEY; + static const Version CURRENT_VERSION { 1 }; + static const size_t PADDING { 2 }; + static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + PADDING }; + static_assert(GPUKTXPayload::SIZE == 36, "Packing size may differ between platforms"); + static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned"); + Sampler::Desc _samplerDesc; Texture::Usage _usage; TextureUsageType _usageType; + Byte* serialize(Byte* data) const { + *(Version*)data = CURRENT_VERSION; + data += sizeof(Version); + + memcpy(data, &_samplerDesc, sizeof(Sampler::Desc)); + data += sizeof(Sampler::Desc); + + // We can't copy the bitset in Texture::Usage in a crossplateform manner + // So serialize it manually + *(uint32*)data = _usage._flags.to_ulong(); + data += sizeof(uint32); + + *(TextureUsageType*)data = _usageType; + data += sizeof(TextureUsageType); + + return data + PADDING; + } + + bool unserialize(const Byte* data, size_t size) { + if (size != SIZE) { + return false; + } + + Version version = *(const Version*)data; + if (version != CURRENT_VERSION) { + glm::vec4 borderColor(1.0f); + if (memcmp(&borderColor, data, sizeof(glm::vec4)) == 0) { + memcpy(this, data, sizeof(GPUKTXPayload)); + return true; + } else { + return false; + } + } + data += sizeof(Version); + + memcpy(&_samplerDesc, data, sizeof(Sampler::Desc)); + data += sizeof(Sampler::Desc); + + // We can't copy the bitset in Texture::Usage in a crossplateform manner + // So unserialize it manually + _usage = Texture::Usage(*(const uint32*)data); + data += sizeof(uint32); + + _usageType = *(const TextureUsageType*)data; + return true; + } - static std::string KEY; static bool isGPUKTX(const ktx::KeyValue& val) { return (val._key.compare(KEY) == 0); } @@ -33,17 +87,14 @@ struct GPUKTXPayload { static bool findInKeyValues(const ktx::KeyValues& keyValues, GPUKTXPayload& payload) { auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); if (found != keyValues.end()) { - if ((*found)._value.size() == sizeof(GPUKTXPayload)) { - memcpy(&payload, (*found)._value.data(), sizeof(GPUKTXPayload)); - return true; - } + auto value = found->_value; + return payload.unserialize(value.data(), value.size()); } return false; } }; - const std::string gpu::SOURCE_HASH_KEY { "hifi.sourceHash" }; -std::string GPUKTXPayload::KEY{ "hifi.gpu" }; +const std::string GPUKTXPayload::KEY { "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { @@ -253,12 +304,15 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { keyval._samplerDesc = texture.getSampler().getDesc(); keyval._usage = texture.getUsage(); keyval._usageType = texture.getUsageType(); + Byte keyvalPayload[GPUKTXPayload::SIZE]; + keyval.serialize(keyvalPayload); + ktx::KeyValues keyValues; - keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval)); + keyValues.emplace_back(GPUKTXPayload::KEY, (uint32)GPUKTXPayload::SIZE, (ktx::Byte*) &keyvalPayload); auto hash = texture.sourceHash(); if (!hash.empty()) { - keyValues.emplace_back(ktx::KeyValue(SOURCE_HASH_KEY, static_cast<uint32>(hash.size()), (ktx::Byte*) hash.c_str())); + keyValues.emplace_back(SOURCE_HASH_KEY, static_cast<uint32>(hash.size()), (ktx::Byte*) hash.c_str()); } auto ktxBuffer = ktx::KTX::create(header, images, keyValues); @@ -291,17 +345,17 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -TexturePointer Texture::unserialize(const std::string& ktxfile, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { - std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(ktx::StoragePointer { new storage::FileStorage(ktxfile.c_str()) }); +TexturePointer Texture::unserialize(const std::string& ktxfile) { + std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(ktxfile.c_str())); if (!ktxPointer) { return nullptr; } ktx::KTXDescriptor descriptor { ktxPointer->toDescriptor() }; - return unserialize(ktxfile, ktxPointer->toDescriptor(), usageType, usage, sampler); + return unserialize(ktxfile, ktxPointer->toDescriptor()); } -TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { +TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor) { const auto& header = descriptor.header; Format mipFormat = Format::COLOR_BGRA_32; @@ -327,28 +381,28 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe type = TEX_3D; } - - // If found, use the GPUKTXPayload gpuktxKeyValue; - bool isGPUKTXPayload = GPUKTXPayload::findInKeyValues(descriptor.keyValues, gpuktxKeyValue); + if (!GPUKTXPayload::findInKeyValues(descriptor.keyValues, gpuktxKeyValue)) { + qCWarning(gpulogging) << "Could not find GPUKTX key values."; + return TexturePointer(); + } - auto tex = Texture::create( (isGPUKTXPayload ? gpuktxKeyValue._usageType : usageType), - type, - texelFormat, - header.getPixelWidth(), - header.getPixelHeight(), - header.getPixelDepth(), - 1, // num Samples - header.getNumberOfSlices(), - header.getNumberOfLevels(), - (isGPUKTXPayload ? gpuktxKeyValue._samplerDesc : sampler)); - - tex->setUsage((isGPUKTXPayload ? gpuktxKeyValue._usage : usage)); + auto texture = create(gpuktxKeyValue._usageType, + type, + texelFormat, + header.getPixelWidth(), + header.getPixelHeight(), + header.getPixelDepth(), + 1, // num Samples + header.getNumberOfSlices(), + header.getNumberOfLevels(), + gpuktxKeyValue._samplerDesc); + texture->setUsage(gpuktxKeyValue._usage); // Assing the mips availables - tex->setStoredMipFormat(mipFormat); - tex->setKtxBacking(ktxfile); - return tex; + texture->setStoredMipFormat(mipFormat); + texture->setKtxBacking(ktxfile); + return texture; } bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { From 3c5754282f99d467d2f7c688f53f3c4d94838462 Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Thu, 20 Apr 2017 16:18:51 -0700 Subject: [PATCH 34/85] write the source hash in ktx header in binary --- libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 5dc8871e9d..3c82f67c86 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -35,6 +35,7 @@ namespace ktx { namespace gpu { extern const std::string SOURCE_HASH_KEY; +const uint8 SOURCE_HASH_BYTES = 16; // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated // with the cube texture diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 89b7b86768..6828af4cc4 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -13,7 +13,10 @@ #include "Texture.h" #include <qdebug.h> +#include <QtCore/QByteArray> + #include <ktx/KTX.h> + using namespace gpu; using PixelsPointer = Texture::PixelsPointer; @@ -312,7 +315,10 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto hash = texture.sourceHash(); if (!hash.empty()) { - keyValues.emplace_back(SOURCE_HASH_KEY, static_cast<uint32>(hash.size()), (ktx::Byte*) hash.c_str()); + // the sourceHash is an std::string in hex + // we use QByteArray to take the hex and turn it into the smaller binary representation (16 bytes) + auto binaryHash = QByteArray::fromHex(QByteArray::fromStdString(hash)); + keyValues.emplace_back(SOURCE_HASH_KEY, static_cast<uint32>(binaryHash.size()), (ktx::Byte*) binaryHash.data()); } auto ktxBuffer = ktx::KTX::create(header, images, keyValues); From e708a71b6482b4baddfa96e3b1ade51f8450e471 Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Thu, 20 Apr 2017 18:19:29 -0700 Subject: [PATCH 35/85] handle 16 byte hash while reading in TextureCache --- .../model-networking/src/model-networking/TextureCache.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 761f55068b..c8765e0a52 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -559,13 +559,16 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { }); std::string filename; std::string hash; - if (found == keyValues.end() || found->_value.size() != 32) { + if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { qWarning("Invalid source hash key found, bailing"); _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; } else { - hash = filename = std::string(reinterpret_cast<char*>(found->_value.data()), 32); + // 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<char*>(found->_value.data()), gpu::SOURCE_HASH_BYTES); + hash = filename = binaryHash.toHex().toStdString(); } auto textureCache = DependencyManager::get<TextureCache>(); From 397a29039effa0768fba2220488144d58d05d524 Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Mon, 17 Apr 2017 19:08:06 -0700 Subject: [PATCH 36/85] add support for byte range requests to ATP --- .../src/assets/SendAssetTask.cpp | 62 ++++++-- libraries/networking/src/AssetClient.cpp | 5 +- libraries/networking/src/AssetClient.h | 3 +- libraries/networking/src/AssetRequest.cpp | 133 +++++++----------- libraries/networking/src/AssetRequest.h | 10 +- libraries/networking/src/ByteRange.h | 24 ++++ libraries/networking/src/ResourceRequest.h | 7 +- 7 files changed, 132 insertions(+), 112 deletions(-) create mode 100644 libraries/networking/src/ByteRange.h diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index ca8733d660..4a49deabde 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -11,6 +11,8 @@ #include "SendAssetTask.h" +#include <cmath> + #include <QFile> #include <DependencyManager.h> @@ -21,6 +23,7 @@ #include <udt/Packet.h> #include "AssetUtils.h" +#include "ByteRange.h" #include "ClientServerUtils.h" SendAssetTask::SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir) : @@ -34,20 +37,21 @@ SendAssetTask::SendAssetTask(QSharedPointer<ReceivedMessage> message, const Shar void SendAssetTask::run() { MessageID messageID; - DataOffset start, end; - + ByteRange byteRange; + _message->readPrimitive(&messageID); QByteArray assetHash = _message->read(SHA256_HASH_LENGTH); // `start` and `end` indicate the range of data to retrieve for the asset identified by `assetHash`. // `start` is inclusive, `end` is exclusive. Requesting `start` = 1, `end` = 10 will retrieve 9 bytes of data, // starting at index 1. - _message->readPrimitive(&start); - _message->readPrimitive(&end); + _message->readPrimitive(&byteRange.fromInclusive); + _message->readPrimitive(&byteRange.toExclusive); QString hexHash = assetHash.toHex(); - qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " << start << " to " << end; + qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " + << byteRange.fromInclusive << " to " << byteRange.toExclusive; qDebug() << "Starting task to send asset: " << hexHash << " for messageID " << messageID; auto replyPacketList = NLPacketList::create(PacketType::AssetGetReply, QByteArray(), true, true); @@ -56,7 +60,7 @@ void SendAssetTask::run() { replyPacketList->writePrimitive(messageID); - if (end <= start) { + if (byteRange.toExclusive < byteRange.fromInclusive) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); } else { QString filePath = _resourcesDir.filePath(QString(hexHash)); @@ -64,15 +68,47 @@ void SendAssetTask::run() { QFile file { filePath }; if (file.open(QIODevice::ReadOnly)) { - if (file.size() < end) { + if (byteRange.isSet()) { + // if the byte range is not set, force it to be from 0 to the end of the file + byteRange.fromInclusive = 0; + byteRange.toExclusive = file.size(); + } + + if (file.size() < std::abs(byteRange.toExclusive)) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); - qCDebug(networking) << "Bad byte range: " << hexHash << " " << start << ":" << end; + qCDebug(networking) << "Bad byte range: " << hexHash << " " + << byteRange.fromInclusive << ":" << byteRange.toExclusive; } else { - auto size = end - start; - file.seek(start); - replyPacketList->writePrimitive(AssetServerError::NoError); - replyPacketList->writePrimitive(size); - replyPacketList->write(file.read(size)); + // we have a valid byte range, handle it and send the asset + auto size = byteRange.size(); + + if (byteRange.fromInclusive > 0) { + // this range is positive, meaning we just need to seek into the file and then read from there + file.seek(byteRange.fromInclusive); + replyPacketList->writePrimitive(AssetServerError::NoError); + replyPacketList->writePrimitive(size); + replyPacketList->write(file.read(size)); + } else { + // this range is negative, at least the first part of the read will be back into the end of the file + + // seek to the part of the file where the negative range begins + file.seek(file.size() + byteRange.fromInclusive); + + replyPacketList->writePrimitive(AssetServerError::NoError); + replyPacketList->writePrimitive(size); + + // first write everything from the negative range to the end of the file + replyPacketList->write(file.read(-byteRange.fromInclusive)); + + if (byteRange.toExclusive != 0) { + // this range has a portion that is at the front of the file + + // seek to the beginning and read what is left over + file.seek(0); + replyPacketList->write(file.read(byteRange.toExclusive)); + } + } + qCDebug(networking) << "Sending asset: " << hexHash; } file.close(); diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 37b1af0996..48f8bb87f9 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -67,7 +67,6 @@ void AssetClient::init() { } } - void AssetClient::cacheInfoRequest(QObject* reciever, QString slot) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "cacheInfoRequest", Qt::QueuedConnection, @@ -182,8 +181,8 @@ RenameMappingRequest* AssetClient::createRenameMappingRequest(const AssetPath& o return request; } -AssetRequest* AssetClient::createRequest(const AssetHash& hash) { - auto request = new AssetRequest(hash); +AssetRequest* AssetClient::createRequest(const AssetHash& hash, ByteRange byteRange) { + auto request = new AssetRequest(hash, byteRange); // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) request->moveToThread(thread()); diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index c0d58cd8e6..b204fab47e 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -21,6 +21,7 @@ #include <DependencyManager.h> #include "AssetUtils.h" +#include "ByteRange.h" #include "ClientServerUtils.h" #include "LimitedNodeList.h" #include "Node.h" @@ -55,7 +56,7 @@ public: Q_INVOKABLE DeleteMappingsRequest* createDeleteMappingsRequest(const AssetPathList& paths); Q_INVOKABLE SetMappingRequest* createSetMappingRequest(const AssetPath& path, const AssetHash& hash); Q_INVOKABLE RenameMappingRequest* createRenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath); - Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash); + Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash, ByteRange byteRange = ByteRange()); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 8d663933ca..e54a058ac2 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -23,10 +23,12 @@ static int requestID = 0; -AssetRequest::AssetRequest(const QString& hash) : +AssetRequest::AssetRequest(const QString& hash, ByteRange byteRange) : _requestID(++requestID), - _hash(hash) + _hash(hash), + _byteRange(byteRange) { + } AssetRequest::~AssetRequest() { @@ -34,9 +36,6 @@ AssetRequest::~AssetRequest() { if (_assetRequestID) { assetClient->cancelGetAssetRequest(_assetRequestID); } - if (_assetInfoRequestID) { - assetClient->cancelGetAssetInfoRequest(_assetInfoRequestID); - } } void AssetRequest::start() { @@ -62,108 +61,74 @@ void AssetRequest::start() { // Try to load from cache _data = loadFromCache(getUrl()); if (!_data.isNull()) { - _info.hash = _hash; - _info.size = _data.size(); _error = NoError; _state = Finished; emit finished(this); return; } - - _state = WaitingForInfo; - + + _state = WaitingForData; + auto assetClient = DependencyManager::get<AssetClient>(); - _assetInfoRequestID = assetClient->getAssetInfo(_hash, - [this](bool responseReceived, AssetServerError serverError, AssetInfo info) { + auto that = QPointer<AssetRequest>(this); // Used to track the request's lifetime + auto hash = _hash; - _assetInfoRequestID = INVALID_MESSAGE_ID; + _assetRequestID = assetClient->getAsset(_hash, _byteRange.fromInclusive, _byteRange.toExclusive, + [this, that, hash](bool responseReceived, AssetServerError serverError, const QByteArray& data) { - _info = info; + if (!that) { + qCWarning(asset_client) << "Got reply for dead asset request " << hash << "- error code" << _error; + // If the request is dead, return + return; + } + _assetRequestID = INVALID_MESSAGE_ID; if (!responseReceived) { _error = NetworkError; } else if (serverError != AssetServerError::NoError) { - switch(serverError) { + switch (serverError) { case AssetServerError::AssetNotFound: _error = NotFound; break; + case AssetServerError::InvalidByteRange: + _error = InvalidByteRange; + break; default: _error = UnknownError; break; } - } + } else { + if (_byteRange.isSet()) { + // we had a byte range, the size of the data does not match what we expect, so we return an error + if (data.size() != _byteRange.size()) { + _error = SizeVerificationFailed; + } + } else if (hashData(data).toHex() != _hash) { + // the hash of the received data does not match what we expect, so we return an error + _error = HashVerificationFailed; + } + if (_error == NoError) { + _data = data; + _totalReceived += data.size(); + emit progress(_totalReceived, data.size()); + + saveToCache(getUrl(), data); + } + } + if (_error != NoError) { - qCWarning(asset_client) << "Got error retrieving asset info for" << _hash; - _state = Finished; - emit finished(this); - + qCWarning(asset_client) << "Got error retrieving asset" << _hash << "- error code" << _error; + } + + _state = Finished; + emit finished(this); + }, [this, that](qint64 totalReceived, qint64 total) { + if (!that) { + // If the request is dead, return return; } - - _state = WaitingForData; - _data.resize(info.size); - - qCDebug(asset_client) << "Got size of " << _hash << " : " << info.size << " bytes"; - - int start = 0, end = _info.size; - - auto assetClient = DependencyManager::get<AssetClient>(); - auto that = QPointer<AssetRequest>(this); // Used to track the request's lifetime - auto hash = _hash; - _assetRequestID = assetClient->getAsset(_hash, start, end, - [this, that, hash, start, end](bool responseReceived, AssetServerError serverError, const QByteArray& data) { - if (!that) { - qCWarning(asset_client) << "Got reply for dead asset request " << hash << "- error code" << _error; - // If the request is dead, return - return; - } - _assetRequestID = INVALID_MESSAGE_ID; - - if (!responseReceived) { - _error = NetworkError; - } else if (serverError != AssetServerError::NoError) { - switch (serverError) { - case AssetServerError::AssetNotFound: - _error = NotFound; - break; - case AssetServerError::InvalidByteRange: - _error = InvalidByteRange; - break; - default: - _error = UnknownError; - break; - } - } else { - Q_ASSERT(data.size() == (end - start)); - - // we need to check the hash of the received data to make sure it matches what we expect - if (hashData(data).toHex() == _hash) { - memcpy(_data.data() + start, data.constData(), data.size()); - _totalReceived += data.size(); - emit progress(_totalReceived, _info.size); - - saveToCache(getUrl(), data); - } else { - // hash doesn't match - we have an error - _error = HashVerificationFailed; - } - - } - - if (_error != NoError) { - qCWarning(asset_client) << "Got error retrieving asset" << _hash << "- error code" << _error; - } - - _state = Finished; - emit finished(this); - }, [this, that](qint64 totalReceived, qint64 total) { - if (!that) { - // If the request is dead, return - return; - } - emit progress(totalReceived, total); - }); + emit progress(totalReceived, total); }); } diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index 1632a55336..5120b8066e 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -17,15 +17,15 @@ #include <QString> #include "AssetClient.h" - #include "AssetUtils.h" +#include "ByteRange.h" + class AssetRequest : public QObject { Q_OBJECT public: enum State { NotStarted = 0, - WaitingForInfo, WaitingForData, Finished }; @@ -36,11 +36,12 @@ public: InvalidByteRange, InvalidHash, HashVerificationFailed, + SizeVerificationFailed, NetworkError, UnknownError }; - AssetRequest(const QString& hash); + AssetRequest(const QString& hash, ByteRange byteRange); virtual ~AssetRequest() override; Q_INVOKABLE void start(); @@ -59,13 +60,12 @@ private: int _requestID; State _state = NotStarted; Error _error = NoError; - AssetInfo _info; uint64_t _totalReceived { 0 }; QString _hash; QByteArray _data; int _numPendingRequests { 0 }; MessageID _assetRequestID { INVALID_MESSAGE_ID }; - MessageID _assetInfoRequestID { INVALID_MESSAGE_ID }; + ByteRange _byteRange; }; #endif diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h new file mode 100644 index 0000000000..2fe5264a49 --- /dev/null +++ b/libraries/networking/src/ByteRange.h @@ -0,0 +1,24 @@ +// +// ByteRange.h +// libraries/networking/src +// +// Created by Stephen Birarda on 4/17/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ByteRange_h +#define hifi_ByteRange_h + +struct ByteRange { + int64_t fromInclusive { 0 }; + int64_t toExclusive { 0 }; + + bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } + int64_t size() { return toExclusive - fromInclusive; } +}; + + +#endif // hifi_ByteRange_h diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 01ca62cf05..77fc8d2591 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -17,12 +17,7 @@ #include <cstdint> -struct ByteRange { - int64_t fromInclusive { 0 }; - int64_t toExclusive { 0 }; - - bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } -}; +#include "ByteRange.h" class ResourceRequest : public QObject { Q_OBJECT From 8145e416f9edcd428374c4da29731f838ebc631a Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Mon, 17 Apr 2017 19:14:24 -0700 Subject: [PATCH 37/85] check both sides of range for invalid byte range --- assignment-client/src/assets/SendAssetTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 4a49deabde..3196c12d97 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -74,7 +74,7 @@ void SendAssetTask::run() { byteRange.toExclusive = file.size(); } - if (file.size() < std::abs(byteRange.toExclusive)) { + if (file.size() < std::abs(byteRange.fromInclusive) || file.size() < byteRange.toExclusive) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); qCDebug(networking) << "Bad byte range: " << hexHash << " " << byteRange.fromInclusive << ":" << byteRange.toExclusive; From 229a481232d85877fd9f8b91e6d30be5b7586893 Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Mon, 17 Apr 2017 19:17:45 -0700 Subject: [PATCH 38/85] add byte range handling to FileResourceRequest --- .../networking/src/FileResourceRequest.cpp | 32 ++++++++++++++++++- libraries/networking/src/ResourceRequest.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index 58a2074103..3895fbc34d 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -11,6 +11,8 @@ #include "FileResourceRequest.h" +#include <cstdlib> + #include <QFile> void FileResourceRequest::doSend() { @@ -25,7 +27,35 @@ void FileResourceRequest::doSend() { QFile file(filename); if (file.exists()) { if (file.open(QFile::ReadOnly)) { - _data = file.readAll(); + + if (!_byteRange.isSet()) { + // no byte range, read the whole file + _data = file.readAll(); + } else { + if (file.size() < std::abs(_byteRange.fromInclusive) || file.size() < _byteRange.toExclusive) { + _result = ResourceRequest::InvalidByteRange; + } else { + // we have a byte range to handle + if (_byteRange.fromInclusive > 0) { + // this is a positive byte range, simply skip to that part of the file and read from there + file.seek(_byteRange.fromInclusive); + _data = file.read(_byteRange.size()); + } else { + // this is a negative byte range, we'll need to grab data from the end of the file first + file.seek(file.size() + _byteRange.fromInclusive); + _data = file.read(-_byteRange.fromInclusive); + + if (_byteRange.toExclusive > 0) { + // there is additional data to read from the front of the file + // handle that now + file.seek(0); + _data.append(file.read(_byteRange.toExclusive)); + } + } + } + + } + _result = ResourceRequest::Success; } else { _result = ResourceRequest::AccessDenied; diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 77fc8d2591..ef40cb3455 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -37,6 +37,7 @@ public: Timeout, ServerUnavailable, AccessDenied, + InvalidByteRange, InvalidURL, NotFound }; From 7ae8c741c5793366e946f001332fdbc65d159d1c Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Mon, 17 Apr 2017 19:30:04 -0700 Subject: [PATCH 39/85] pass the byte range to asset client from AssetResourceRequest --- libraries/networking/src/AssetResourceRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 540fb4767f..092e0ccb3d 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -114,7 +114,7 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { void AssetResourceRequest::requestHash(const AssetHash& hash) { // Make request to atp auto assetClient = DependencyManager::get<AssetClient>(); - _assetRequest = assetClient->createRequest(hash); + _assetRequest = assetClient->createRequest(hash, _byteRange); connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::onDownloadProgress); connect(_assetRequest, &AssetRequest::finished, this, [this](AssetRequest* req) { From fab1bdaeb7a148f79181a41b0b460e4eb8d27d8d Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Mon, 17 Apr 2017 19:44:52 -0700 Subject: [PATCH 40/85] push asset server packet versions for range requests --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 3ad4dbf28d..863f1bfda6 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -64,7 +64,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AssetGetInfo: case PacketType::AssetGet: case PacketType::AssetUpload: - return static_cast<PacketVersion>(AssetServerPacketVersion::VegasCongestionControl); + return static_cast<PacketVersion>(AssetServerPacketVersion::RangeRequestSupport); case PacketType::NodeIgnoreRequest: return 18; // Introduction of node ignore request (which replaced an unused packet tpye) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 074876862f..87af3513b5 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -214,7 +214,8 @@ enum class EntityQueryPacketVersion: PacketVersion { }; enum class AssetServerPacketVersion: PacketVersion { - VegasCongestionControl = 19 + VegasCongestionControl = 19, + RangeRequestSupport }; enum class AvatarMixerPacketVersion : PacketVersion { From 06ce63f421221d06a71e15de623f9b6c9a5d8d6f Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Tue, 18 Apr 2017 10:34:40 -0700 Subject: [PATCH 41/85] fix byte range set check in SendAssetTask --- assignment-client/src/assets/SendAssetTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 3196c12d97..89d59360b9 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -68,7 +68,7 @@ void SendAssetTask::run() { QFile file { filePath }; if (file.open(QIODevice::ReadOnly)) { - if (byteRange.isSet()) { + if (!byteRange.isSet()) { // if the byte range is not set, force it to be from 0 to the end of the file byteRange.fromInclusive = 0; byteRange.toExclusive = file.size(); From 7a3219d8f99f7bf352c3ddcde2f36e0be8d0c1c3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Tue, 18 Apr 2017 10:37:15 -0700 Subject: [PATCH 42/85] force a negative to in ATP byte range to be invalid --- assignment-client/src/assets/SendAssetTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 89d59360b9..0222849915 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -60,7 +60,7 @@ void SendAssetTask::run() { replyPacketList->writePrimitive(messageID); - if (byteRange.toExclusive < byteRange.fromInclusive) { + if (byteRange.toExclusive < byteRange.fromInclusive || byteRange.toExclusive < 0) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); } else { QString filePath = _resourcesDir.filePath(QString(hexHash)); From 814970c4e277c9e577f92c7e75321d2a4a5be901 Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Tue, 18 Apr 2017 12:36:38 -0700 Subject: [PATCH 43/85] cleanup invalid byte range handling --- .../src/assets/SendAssetTask.cpp | 21 ++++--- libraries/networking/src/ByteRange.h | 23 ++++++++ .../networking/src/FileResourceRequest.cpp | 55 ++++++++++--------- 3 files changed, 61 insertions(+), 38 deletions(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 0222849915..cb99636e83 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -60,7 +60,7 @@ void SendAssetTask::run() { replyPacketList->writePrimitive(messageID); - if (byteRange.toExclusive < byteRange.fromInclusive || byteRange.toExclusive < 0) { + if (!byteRange.isValid()) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); } else { QString filePath = _resourcesDir.filePath(QString(hexHash)); @@ -74,15 +74,22 @@ void SendAssetTask::run() { byteRange.toExclusive = file.size(); } - if (file.size() < std::abs(byteRange.fromInclusive) || file.size() < byteRange.toExclusive) { + // check if we're being asked to read data that we just don't have + // because of the file size + if (file.size() < byteRange.fromInclusive || file.size() < byteRange.toExclusive) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); qCDebug(networking) << "Bad byte range: " << hexHash << " " << byteRange.fromInclusive << ":" << byteRange.toExclusive; } else { // we have a valid byte range, handle it and send the asset + + // first fixup the range based on the now known file size + byteRange.fixupRange(file.size()); + auto size = byteRange.size(); if (byteRange.fromInclusive > 0) { + // this range is positive, meaning we just need to seek into the file and then read from there file.seek(byteRange.fromInclusive); replyPacketList->writePrimitive(AssetServerError::NoError); @@ -98,15 +105,7 @@ void SendAssetTask::run() { replyPacketList->writePrimitive(size); // first write everything from the negative range to the end of the file - replyPacketList->write(file.read(-byteRange.fromInclusive)); - - if (byteRange.toExclusive != 0) { - // this range has a portion that is at the front of the file - - // seek to the beginning and read what is left over - file.seek(0); - replyPacketList->write(file.read(byteRange.toExclusive)); - } + replyPacketList->write(file.read(size)); } qCDebug(networking) << "Sending asset: " << hexHash; diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h index 2fe5264a49..3993bac324 100644 --- a/libraries/networking/src/ByteRange.h +++ b/libraries/networking/src/ByteRange.h @@ -18,6 +18,29 @@ struct ByteRange { bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } int64_t size() { return toExclusive - fromInclusive; } + + // byte ranges are invalid if: + // (1) the toExclusive of the range is negative + // (2) the toExclusive of the range is less than the fromInclusive, and isn't zero + // (3) the fromExclusive of the range is negative, and the toExclusive isn't zero + bool isValid() { + return toExclusive < 0 + || (toExclusive < fromInclusive && toExclusive != 0) + || (fromInclusive < 0 && toExclusive != 0); + } + + void fixupRange(int64_t fileSize) { + if (fromInclusive > 0 && toExclusive == 0) { + // we have a left side of the range that is non-zero + // if the RHS of the range is zero, set it to the end of the file now + toExclusive = fileSize; + } else if (-fromInclusive >= fileSize) { + // we have a negative range that is equal or greater than the full size of the file + // so we just set this to be a range across the entire file, from 0 + fromInclusive = 0; + toExclusive = fileSize; + } + } }; diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index 3895fbc34d..9e2b8f4ab1 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -23,45 +23,46 @@ void FileResourceRequest::doSend() { if (filename.isEmpty()) { filename = _url.toString(); } - - QFile file(filename); - if (file.exists()) { - if (file.open(QFile::ReadOnly)) { - if (!_byteRange.isSet()) { - // no byte range, read the whole file - _data = file.readAll(); - } else { - if (file.size() < std::abs(_byteRange.fromInclusive) || file.size() < _byteRange.toExclusive) { + if (!_byteRange.isValid()) { + _result = ResourceRequest::InvalidByteRange; + } else { + QFile file(filename); + if (file.exists()) { + if (file.open(QFile::ReadOnly)) { + + if (file.size() < _byteRange.fromInclusive || file.size() < _byteRange.toExclusive) { _result = ResourceRequest::InvalidByteRange; } else { - // we have a byte range to handle - if (_byteRange.fromInclusive > 0) { - // this is a positive byte range, simply skip to that part of the file and read from there - file.seek(_byteRange.fromInclusive); - _data = file.read(_byteRange.size()); + if (!_byteRange.isSet()) { + // no byte range, read the whole file + _data = file.readAll(); } else { - // this is a negative byte range, we'll need to grab data from the end of the file first - file.seek(file.size() + _byteRange.fromInclusive); - _data = file.read(-_byteRange.fromInclusive); + // we have a byte range to handle - if (_byteRange.toExclusive > 0) { - // there is additional data to read from the front of the file - // handle that now - file.seek(0); - _data.append(file.read(_byteRange.toExclusive)); + // fix it up based on the known size of the file + _byteRange.fixupRange(file.size()); + + if (_byteRange.fromInclusive > 0) { + // this is a positive byte range, simply skip to that part of the file and read from there + file.seek(_byteRange.fromInclusive); + _data = file.read(_byteRange.size()); + } else { + // this is a negative byte range, we'll need to grab data from the end of the file first + file.seek(file.size() + _byteRange.fromInclusive); + _data = file.read(_byteRange.size()); } } + + _result = ResourceRequest::Success; } + } else { + _result = ResourceRequest::AccessDenied; } - - _result = ResourceRequest::Success; } else { - _result = ResourceRequest::AccessDenied; + _result = ResourceRequest::NotFound; } - } else { - _result = ResourceRequest::NotFound; } _state = Finished; From d9c5997b638ba80b27cfa67f5453664e07d9d2ed Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Tue, 18 Apr 2017 13:10:45 -0700 Subject: [PATCH 44/85] fix references to TextureCache in NetworkTexture --- .../model-networking/src/model-networking/TextureCache.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c8765e0a52..0eda44019b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -458,8 +458,7 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == LOADING_INITIAL_DATA) { _ktxHighMipRequestFinished = true; maybeHandleFinishedInitialLoad(); - } - else if (_ktxResourceState == REQUESTING_MIP) { + } else if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); TextureCache::requestCompleted(_self); From 3928e116117606fac9d677d0ad8f54ec5038f3ec Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Tue, 18 Apr 2017 13:51:08 -0700 Subject: [PATCH 45/85] fix valid byte range check and send asset from 0 --- assignment-client/src/assets/SendAssetTask.cpp | 2 +- libraries/networking/src/ByteRange.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index cb99636e83..5a394eaa07 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -88,7 +88,7 @@ void SendAssetTask::run() { auto size = byteRange.size(); - if (byteRange.fromInclusive > 0) { + if (byteRange.fromInclusive >= 0) { // this range is positive, meaning we just need to seek into the file and then read from there file.seek(byteRange.fromInclusive); diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h index 3993bac324..b5e214605d 100644 --- a/libraries/networking/src/ByteRange.h +++ b/libraries/networking/src/ByteRange.h @@ -24,9 +24,9 @@ struct ByteRange { // (2) the toExclusive of the range is less than the fromInclusive, and isn't zero // (3) the fromExclusive of the range is negative, and the toExclusive isn't zero bool isValid() { - return toExclusive < 0 - || (toExclusive < fromInclusive && toExclusive != 0) - || (fromInclusive < 0 && toExclusive != 0); + return toExclusive >= 0 + && (toExclusive >= fromInclusive || toExclusive == 0) + && (fromInclusive >= 0 || toExclusive == 0); } void fixupRange(int64_t fileSize) { From 6fb074715e575a530548ed106341989542be9525 Mon Sep 17 00:00:00 2001 From: Atlante45 <clement.brisset@gmail.com> Date: Fri, 21 Apr 2017 15:28:29 -0700 Subject: [PATCH 46/85] Fix alpha rendering bug --- libraries/gpu/src/gpu/Texture_ktx.cpp | 3 ++- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 6828af4cc4..b2b08ca170 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -11,12 +11,13 @@ #include "Texture.h" -#include <qdebug.h> #include <QtCore/QByteArray> #include <ktx/KTX.h> +#include "GPULogging.h" + using namespace gpu; using PixelsPointer = Texture::PixelsPointer; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9232564514..51ce0fffa7 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -544,7 +544,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } if (_fadeState == FADE_WAITING_TO_START) { - if (_model->isLoaded()) { + if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); _fadeState = FADE_IN_PROGRESS; From 8269e01474da975b48a35db8afaca3a261644b5d Mon Sep 17 00:00:00 2001 From: Atlante45 <clement.brisset@gmail.com> Date: Fri, 21 Apr 2017 17:08:50 -0700 Subject: [PATCH 47/85] Fix atp-get compile error --- libraries/networking/src/AssetRequest.h | 2 +- tests/render-texture-load/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index 5120b8066e..e617d75157 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -41,7 +41,7 @@ public: UnknownError }; - AssetRequest(const QString& hash, ByteRange byteRange); + AssetRequest(const QString& hash, ByteRange byteRange = ByteRange()); virtual ~AssetRequest() override; Q_INVOKABLE void start(); diff --git a/tests/render-texture-load/CMakeLists.txt b/tests/render-texture-load/CMakeLists.txt index b73b67f56c..1f0c0a069a 100644 --- a/tests/render-texture-load/CMakeLists.txt +++ b/tests/render-texture-load/CMakeLists.txt @@ -10,7 +10,7 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image) +link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics ktx image) package_libraries_for_deployment() From 194541b2d0758e419e1b2408689925f708219be1 Mon Sep 17 00:00:00 2001 From: Atlante45 <clement.brisset@gmail.com> Date: Fri, 21 Apr 2017 17:30:59 -0700 Subject: [PATCH 48/85] remove temp code --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 20e4f4bc18..0d286c46eb 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -31,7 +31,7 @@ const float METERS_TO_INCHES = 39.3701f; static uint32_t _currentWebCount { 0 }; // Don't allow more than 100 concurrent web views -static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 0; +static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20; // If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; @@ -71,7 +71,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { bool RenderableWebEntityItem::buildWebSurface(QSharedPointer<EntityTreeRenderer> renderer) { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { - //qWarning() << "Too many concurrent web views to create new view"; + qWarning() << "Too many concurrent web views to create new view"; return false; } QString javaScriptToInject; From 043c587395cf0672d7a66fc97984c64cd6d049a1 Mon Sep 17 00:00:00 2001 From: Atlante45 <clement.brisset@gmail.com> Date: Fri, 21 Apr 2017 19:16:56 -0700 Subject: [PATCH 49/85] Fix byte range for file resource requests --- .../src/assets/SendAssetTask.cpp | 12 +++------ libraries/networking/src/ByteRange.h | 6 +++++ .../networking/src/FileResourceRequest.cpp | 27 +++++++------------ 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 5a394eaa07..eab88e0d46 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -68,11 +68,9 @@ void SendAssetTask::run() { QFile file { filePath }; if (file.open(QIODevice::ReadOnly)) { - if (!byteRange.isSet()) { - // if the byte range is not set, force it to be from 0 to the end of the file - byteRange.fromInclusive = 0; - byteRange.toExclusive = file.size(); - } + + // first fixup the range based on the now known file size + byteRange.fixupRange(file.size()); // check if we're being asked to read data that we just don't have // because of the file size @@ -82,10 +80,6 @@ void SendAssetTask::run() { << byteRange.fromInclusive << ":" << byteRange.toExclusive; } else { // we have a valid byte range, handle it and send the asset - - // first fixup the range based on the now known file size - byteRange.fixupRange(file.size()); - auto size = byteRange.size(); if (byteRange.fromInclusive >= 0) { diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h index b5e214605d..77932a8c28 100644 --- a/libraries/networking/src/ByteRange.h +++ b/libraries/networking/src/ByteRange.h @@ -30,6 +30,12 @@ struct ByteRange { } void fixupRange(int64_t fileSize) { + if (!isSet()) { + // if the byte range is not set, force it to be from 0 to the end of the file + fromInclusive = 0; + toExclusive = fileSize; + } + if (fromInclusive > 0 && toExclusive == 0) { // we have a left side of the range that is non-zero // if the RHS of the range is zero, set it to the end of the file now diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index 9e2b8f4ab1..1e549e5fa3 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -34,24 +34,17 @@ void FileResourceRequest::doSend() { if (file.size() < _byteRange.fromInclusive || file.size() < _byteRange.toExclusive) { _result = ResourceRequest::InvalidByteRange; } else { - if (!_byteRange.isSet()) { - // no byte range, read the whole file - _data = file.readAll(); + // fix it up based on the known size of the file + _byteRange.fixupRange(file.size()); + + if (_byteRange.fromInclusive >= 0) { + // this is a positive byte range, simply skip to that part of the file and read from there + file.seek(_byteRange.fromInclusive); + _data = file.read(_byteRange.size()); } else { - // we have a byte range to handle - - // fix it up based on the known size of the file - _byteRange.fixupRange(file.size()); - - if (_byteRange.fromInclusive > 0) { - // this is a positive byte range, simply skip to that part of the file and read from there - file.seek(_byteRange.fromInclusive); - _data = file.read(_byteRange.size()); - } else { - // this is a negative byte range, we'll need to grab data from the end of the file first - file.seek(file.size() + _byteRange.fromInclusive); - _data = file.read(_byteRange.size()); - } + // this is a negative byte range, we'll need to grab data from the end of the file first + file.seek(file.size() + _byteRange.fromInclusive); + _data = file.read(_byteRange.size()); } _result = ResourceRequest::Success; From e768b720ea0c407919296304635fdb76d20765bd Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Mon, 24 Apr 2017 10:51:54 -0700 Subject: [PATCH 50/85] Fix load priority for ktx loading --- .../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 0eda44019b..02e1427ddb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -399,7 +399,7 @@ void NetworkTexture::startRequestForNextMipLevel() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { _ktxResourceState = PENDING_MIP_REQUEST; - setLoadPriority(this, -_originalKtxDescriptor->header.numberOfMipmapLevels + _lowestKnownPopulatedMip); + setLoadPriority(this, -static_cast<int>(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); init(); TextureCache::attemptRequest(_self); From 430edb1560dcaefb3d3505dadbe999f6286aa596 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Mon, 24 Apr 2017 10:52:16 -0700 Subject: [PATCH 51/85] Fix loss-of-precision warning in TextureCache --- .../model-networking/src/model-networking/TextureCache.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 02e1427ddb..d42cb51b6e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -619,14 +619,13 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { uint8_t* ktxData = reinterpret_cast<uint8_t*>(ktxHighMipData.data()); ktxData += ktxHighMipData.size(); // TODO Move image offset calculation to ktx ImageDescriptor - int level; - for (level = images.size() - 1; level >= 0; --level) { + for (auto level = images.size() - 1; level >= 0; --level) { auto& image = images[level]; if (image._imageSize > imageSizeRemaining) { break; } ktxData -= image._imageSize; - texture->assignStoredMip(level, image._imageSize, ktxData); + texture->assignStoredMip(static_cast<gpu::uint16>(level), image._imageSize, ktxData); ktxData -= 4; imageSizeRemaining -= (image._imageSize + 4); } From c10e394ad7311dd0c60cbd1542a695c952cfc824 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Mon, 24 Apr 2017 12:07:40 -0700 Subject: [PATCH 52/85] Fix unfailable condition in for loop --- .../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 d42cb51b6e..f9ee9efe3f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -619,7 +619,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { uint8_t* ktxData = reinterpret_cast<uint8_t*>(ktxHighMipData.data()); ktxData += ktxHighMipData.size(); // TODO Move image offset calculation to ktx ImageDescriptor - for (auto level = images.size() - 1; level >= 0; --level) { + for (int level = static_cast<int>(images.size()) - 1; level >= 0; --level) { auto& image = images[level]; if (image._imageSize > imageSizeRemaining) { break; From e4c21627f92533ef9de141db1cc26e089400abbb Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Mon, 24 Apr 2017 12:05:34 -0700 Subject: [PATCH 53/85] Remove unused newHeader from Writer.cpp --- libraries/ktx/src/ktx/Writer.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 0d3d4e1d60..4226b8fa84 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -43,12 +43,9 @@ namespace ktx { std::unique_ptr<KTX> KTX::createBare(const Header& header, const KeyValues& keyValues) { auto descriptors = header.generateImageDescriptors(); - auto newHeader = header; - Byte minMip = header.numberOfMipmapLevels; auto newKeyValues = keyValues; newKeyValues.emplace_back(KeyValue(HIFI_MIN_POPULATED_MIP_KEY, sizeof(Byte), &minMip)); - newHeader.bytesOfKeyValueData = KeyValue::serializedKeyValuesByteSize(newKeyValues); StoragePointer storagePointer; { From a2f7a8843392f8ccbf2b3bf6a99326285b914cf1 Mon Sep 17 00:00:00 2001 From: Atlante45 <clement.brisset@gmail.com> Date: Tue, 25 Apr 2017 16:29:36 -0700 Subject: [PATCH 54/85] Start drawing models before we get the textures --- libraries/render-utils/src/MeshPartPayload.cpp | 18 ++++++++++++------ libraries/render-utils/src/MeshPartPayload.h | 17 +++++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 51ce0fffa7..9485790463 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -118,7 +118,7 @@ void MeshPartPayload::drawCall(gpu::Batch& batch) const { batch.drawIndexed(gpu::TRIANGLES, _drawPart._numIndices, _drawPart._startIndex); } -void MeshPartPayload::bindMesh(gpu::Batch& batch) const { +void MeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); batch.setInputFormat((_drawMesh->getVertexFormat())); @@ -255,7 +255,7 @@ void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::Loca } -void MeshPartPayload::render(RenderArgs* args) const { +void MeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("MeshPartPayload::render"); gpu::Batch& batch = *(args->_batch); @@ -485,7 +485,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { return builder.build(); } -void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { +void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { if (!_isBlendShaped) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); @@ -517,7 +517,7 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: batch.setModelTransform(_transform); } -float ModelMeshPartPayload::computeFadeAlpha() const { +float ModelMeshPartPayload::computeFadeAlpha() { if (_fadeState == FADE_WAITING_TO_START) { return 0.0f; } @@ -536,7 +536,7 @@ float ModelMeshPartPayload::computeFadeAlpha() const { return Interpolate::simpleNonLinearBlend(fadeAlpha); } -void ModelMeshPartPayload::render(RenderArgs* args) const { +void ModelMeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); if (!_model->addedToScene() || !_model->isVisible()) { @@ -544,7 +544,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } if (_fadeState == FADE_WAITING_TO_START) { - if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + if (_model->isLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); _fadeState = FADE_IN_PROGRESS; @@ -557,6 +557,12 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } } + if (_materialNeedsUpdate && _model->getGeometry()->areTexturesLoaded()) { + qDebug() << "Updating for textures"; + _model->setRenderItemsNeedUpdate(); + _materialNeedsUpdate = false; + } + if (!args) { return; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index ef74011c40..11d1bbf6a7 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -46,11 +46,11 @@ public: virtual render::ItemKey getKey() const; virtual render::Item::Bound getBound() const; virtual render::ShapeKey getShapeKey() const; // shape interface - virtual void render(RenderArgs* args) const; + virtual void render(RenderArgs* args); // ModelMeshPartPayload functions to perform render void drawCall(gpu::Batch& batch) const; - virtual void bindMesh(gpu::Batch& batch) const; + virtual void bindMesh(gpu::Batch& batch); virtual void bindMaterial(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool enableTextures) const; virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const; @@ -93,16 +93,16 @@ public: const Transform& boundTransform, const gpu::BufferPointer& buffer); - float computeFadeAlpha() const; + float computeFadeAlpha(); // Render Item interface render::ItemKey getKey() const override; int getLayer() const; render::ShapeKey getShapeKey() const override; // shape interface - void render(RenderArgs* args) const override; + void render(RenderArgs* args) override; // ModelMeshPartPayload functions to perform render - void bindMesh(gpu::Batch& batch) const override; + void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; void initCache(); @@ -116,11 +116,12 @@ public: int _shapeID; bool _isSkinned{ false }; - bool _isBlendShaped{ false }; + bool _isBlendShaped { false }; + bool _materialNeedsUpdate { true }; private: - mutable quint64 _fadeStartTime { 0 }; - mutable uint8_t _fadeState { FADE_WAITING_TO_START }; + quint64 _fadeStartTime { 0 }; + uint8_t _fadeState { FADE_WAITING_TO_START }; }; namespace render { From d338ccac26afcc7ccc924fc82b55add4ca879078 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 25 Apr 2017 16:32:06 -0700 Subject: [PATCH 55/85] Update NetworkTexture to automatically download all mips --- libraries/model-networking/src/model-networking/TextureCache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f9ee9efe3f..e04e002784 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -276,6 +276,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _maxNumPixels(maxNumPixels) { _textureSource = std::make_shared<gpu::TextureSource>(); + _lowestRequestedMipLevel = 0; if (!url.isValid()) { _loaded = true; From 382fe5d38d9719bbe4c694922a097692c044f023 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 25 Apr 2017 16:31:42 -0700 Subject: [PATCH 56/85] Update gpu backend to only transfer if it can populate --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index ef938ecd6d..e0c6d86502 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -464,7 +464,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { newState = MemoryPressureState::Transfer; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) || canPopulate)) { + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) && canPopulate)) { newState = MemoryPressureState::Undersubscribed; } From 51ee058c0b7b8d3ff2b37ec9c9278eab8e40a6e2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 25 Apr 2017 16:43:03 -0700 Subject: [PATCH 57/85] Update GLTexture to not promote if we can't populate --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index e0c6d86502..fceed7c2eb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -521,8 +521,7 @@ void GLVariableAllocationSupport::processWorkQueues() { vartexture->demote(); _memoryPressureStateStale = true; } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { - if (!vartexture->canPromote()) { - vartexture->populateTransferQueue(); + if (!vartexture->canPromote() || !vartexture->canPopulate()) { continue; } vartexture->promote(); From 65d30d1d0bd5793e5205de297db7cb1b5a2e83f9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 25 Apr 2017 16:47:38 -0700 Subject: [PATCH 58/85] Remove lowestRequestMip from gltexture --- .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 1 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 -- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 15 +++++---------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 04c2201c75..7c43a585b7 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -395,7 +395,6 @@ void GL41VariableAllocationTexture::populateTransferQueue() { bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { - const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); continue; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 21c429c577..15e98c3af7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -117,8 +117,6 @@ public: void allocateStorage(uint16 mip); void copyMipsFromTexture(); - - uint16 _lowestRequestedMip { 0 }; }; #if 0 diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 5dfbd6e727..39c8c4a7f5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -50,7 +50,7 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend for (uint16_t mip = 0; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) && texture.isStoredMipFaceAvailable(mip)) { - _lowestRequestedMip = _maxAllocatedMip = _populatedMip = mip; + _maxAllocatedMip = _populatedMip = mip; break; } } @@ -106,8 +106,6 @@ void GL45ResourceTexture::promote() { Q_ASSERT(_allocatedMip > 0); uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2); if (!_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { - _lowestRequestedMip = targetAllocatedMip; - const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(targetAllocatedMip); return; } GLuint oldId = _id; @@ -187,20 +185,17 @@ void GL45ResourceTexture::populateTransferQueue() { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); - bool transferQueued = false; + bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { - const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip); - _lowestRequestedMip = sourceMip; continue; } - _lowestRequestedMip = sourceMip; // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); - transferQueued = true; + didQueueTransfer = true; continue; } @@ -216,12 +211,12 @@ void GL45ResourceTexture::populateTransferQueue() { uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; - transferQueued = true; + didQueueTransfer = true; } } // queue up the sampler and populated mip change for after the transfer has completed - if (transferQueued) { + if (didQueueTransfer) { _pendingTransfers.emplace(new TransferJob(*this, [=] { _populatedMip = sourceMip; syncSampler(); From f5bb42b19fa7ccd2c4594aa6a7374aa622f2063d Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 25 Apr 2017 16:52:21 -0700 Subject: [PATCH 59/85] Remove request of mip from GL45BackendVariableTexture --- libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 39c8c4a7f5..2a02f9b87b 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -76,10 +76,6 @@ void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { _size += _gpuObject.evalMipSize(mip); } - if (!_gpuObject.isStoredMipFaceAvailable(allocatedMip, 0)) { - const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(allocatedMip); - } - Backend::updateTextureGPUMemoryUsage(0, _size); } From 4d48cb2e801ee93b3328719bb139c81dfc17fdd5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 25 Apr 2017 16:52:34 -0700 Subject: [PATCH 60/85] Remove extraneous glTextureParameteri logging --- libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 2a02f9b87b..e3f694f551 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -93,7 +93,6 @@ void GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); - qDebug() << "glTextureParameteri " << QString::fromStdString(_source) << _populatedMip << _populatedMip - _allocatedMip; glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); } From 9505bf746cbd2a17b3d3c8f4d12e444b99d504d9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Tue, 25 Apr 2017 21:12:14 -0700 Subject: [PATCH 61/85] Fix lower mips not being downloaded after initial mips --- .../model-networking/src/model-networking/TextureCache.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e04e002784..0e9ce46912 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -635,8 +635,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { // 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 = _originalKtxDescriptor->header.numberOfMipmapLevels; @@ -660,6 +658,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } + startRequestForNextMipLevel(); } } From 674e767513a1086c052d77acee36cab14eba1cb2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 08:42:21 -0700 Subject: [PATCH 62/85] Remove MipInterestListener --- libraries/gpu/src/gpu/Texture.cpp | 22 ------------------- libraries/gpu/src/gpu/Texture.h | 11 ---------- .../src/model-networking/TextureCache.cpp | 18 --------------- .../src/model-networking/TextureCache.h | 5 +---- 4 files changed, 1 insertion(+), 55 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 1dbe9db2b4..0ede0406c3 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -476,32 +476,10 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin } } -void Texture::requestInterestInMip(uint16 level) { - if (!_storage->isMipAvailable(level, 0)) { - std::lock_guard<std::mutex> lock(_mipInterestListenersMutex); - for (auto& callback : _mipInterestListeners) { - callback->handleMipInterestCallback(level); - } - } -} - bool Texture::isStoredMipFaceAvailable(uint16 level, uint8 face) const { return _storage->isMipAvailable(level, face); } -void Texture::registerMipInterestListener(MipInterestListener* listener) { - std::lock_guard<std::mutex> lock(_mipInterestListenersMutex); - _mipInterestListeners.push_back(listener); -} - -void Texture::unregisterMipInterestListener(MipInterestListener* listener) { - std::lock_guard<std::mutex> lock(_mipInterestListenersMutex); - auto it = find(_mipInterestListeners.begin(), _mipInterestListeners.end(), listener); - if (it != _mipInterestListeners.end()) { - _mipInterestListeners.erase(it); - } -} - void Texture::setAutoGenerateMips(bool enable) { bool changed = false; if (!_autoGenerateMips) { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 3c82f67c86..a79e679f15 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -488,17 +488,6 @@ public: void setStorage(std::unique_ptr<Storage>& newStorage); void setKtxBacking(const std::string& filename); - class MipInterestListener { - public: - virtual void handleMipInterestCallback(uint16 level) = 0; - }; - void registerMipInterestListener(MipInterestListener* listener); - void unregisterMipInterestListener(MipInterestListener* listener); - std::vector<MipInterestListener*> _mipInterestListeners; - std::mutex _mipInterestListenersMutex; - - void requestInterestInMip(uint16 level); - // Usage is a a set of flags providing Semantic about the usage of the Texture. void setUsage(const Usage& usage) { _usage = usage; } Usage getUsage() const { return _usage; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 0e9ce46912..1d37a26f37 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -245,13 +245,6 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { return result; } -NetworkTexture::~NetworkTexture() { - auto texture = _textureSource->getGPUTexture(); - if (texture) { - texture->unregisterMipInterestListener(this); - } -} - /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) { QImage image = QImage(path); @@ -381,15 +374,6 @@ void NetworkTexture::makeRequest() { } -void NetworkTexture::handleMipInterestCallback(uint16_t level) { - QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(int, level)); -} - -void NetworkTexture::handleMipInterestLevel(int level) { - _lowestRequestedMipLevel = std::min((uint16_t)level, _lowestRequestedMipLevel); - startRequestForNextMipLevel(); -} - void NetworkTexture::startRequestForNextMipLevel() { if (_lowestKnownPopulatedMip == 0) { qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip @@ -647,8 +631,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - - texture->registerMipInterestListener(this); _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); qDebug() << "Loaded KTX: " << QString::fromStdString(hash) << " : " << _url; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 5fb23c0d2d..c7a7799216 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -41,7 +41,7 @@ public: }; /// A texture loaded from the network. -class NetworkTexture : public Resource, public Texture, public gpu::Texture::MipInterestListener { +class NetworkTexture : public Resource, public Texture { Q_OBJECT public: @@ -58,9 +58,6 @@ public: gpu::TexturePointer getFallbackTexture() const; - void handleMipInterestCallback(uint16_t level) override; - Q_INVOKABLE void handleMipInterestLevel(int level); - signals: void networkTextureCreated(const QWeakPointer<NetworkTexture>& self); From b9ec573c8b2765e5bec4a393499c1565f86a0e48 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 10:04:52 -0700 Subject: [PATCH 63/85] Update gl41 an gl45 texture backends to take into account min avail mip --- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 2 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 19 ++++++++++++++++--- .../gpu/gl45/GL45BackendVariableTexture.cpp | 13 ++++++++++--- .../src/model-networking/TextureCache.cpp | 3 +-- .../src/model-networking/TextureCache.h | 1 - 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index dc6d2b3aa7..c0b9ea0e45 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -100,7 +100,7 @@ public: GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture); ~GL41VariableAllocationTexture(); - bool canPopulate() const override { return _populatedMip > _allocatedMip && _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } + bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } void allocateStorage(uint16 allocatedMip); void syncSampler() const override; void promote() override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 7c43a585b7..51307511c2 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -231,15 +231,20 @@ using GL41VariableAllocationTexture = GL41Backend::GL41VariableAllocationTexture GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL41Texture(backend, texture) { auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; + _maxAllocatedMip = _populatedMip = mipLevels; + uint16_t minAvailableMip = texture.minAvailableMipLevel(); uvec3 mipDimensions; for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) + && mip >= minAvailableMip) { _maxAllocatedMip = _populatedMip = mip; break; } } - uint16_t allocatedMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); + auto targetMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); + uint16_t allocatedMip = std::max<uint16_t>(minAvailableMip, targetMip); + allocateStorage(allocatedMip); _memoryPressureStateStale = true; size_t maxFace = GLTexture::getFaceCount(_target); @@ -292,6 +297,14 @@ void GL41VariableAllocationTexture::syncSampler() const { void GL41VariableAllocationTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); + + uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2); + targetAllocatedMip = std::max<uint16_t>(_gpuObject.minAvailableMipLevel(), targetAllocatedMip); + + if (targetAllocatedMip >= _allocatedMip || !_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { + return; + } + GLuint oldId = _id; auto oldSize = _size; // create new texture @@ -299,7 +312,7 @@ void GL41VariableAllocationTexture::promote() { uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level - allocateStorage(_allocatedMip - std::min<uint16_t>(_allocatedMip, 2)); + allocateStorage(targetAllocatedMip); withPreservedTexture([&] { GLuint fbo { 0 }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index e3f694f551..9b35f35934 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -46,16 +46,20 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend _maxAllocatedMip = _populatedMip = mipLevels; + uint16_t minAvailableMip = texture.minAvailableMipLevel(); + uvec3 mipDimensions; for (uint16_t mip = 0; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) - && texture.isStoredMipFaceAvailable(mip)) { + && mip >= minAvailableMip) { _maxAllocatedMip = _populatedMip = mip; break; } } - uint16_t allocatedMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); + auto targetMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); + uint16_t allocatedMip = std::max<uint16_t>(minAvailableMip, targetMip); + allocateStorage(allocatedMip); _memoryPressureStateStale = true; copyMipsFromTexture(); @@ -99,8 +103,11 @@ void GL45ResourceTexture::syncSampler() const { void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); + uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2); - if (!_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { + targetAllocatedMip = std::max<uint16_t>(_gpuObject.minAvailableMipLevel(), targetAllocatedMip); + + if (targetAllocatedMip >= _allocatedMip || !_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { return; } GLuint oldId = _id; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1d37a26f37..62b94ed455 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -588,8 +588,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; - } - else { + } else { _file = file; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index c7a7799216..1e61b9ecee 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,7 +46,6 @@ class NetworkTexture : public Resource, public Texture { public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); - ~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } From 4f16eb9bccc7f846257828483593d8bf41bc873d Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 10:21:27 -0700 Subject: [PATCH 64/85] Cleanup KTX logging and add fragment to resource url to indicate mip level --- .../src/model-networking/TextureCache.cpp | 12 +++++++++--- libraries/networking/src/HTTPResourceRequest.cpp | 5 ----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 62b94ed455..48999c21f3 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -338,7 +338,9 @@ void NetworkTexture::makeRequest() { if (_ktxResourceState == PENDING_INITIAL_LOAD) { _ktxResourceState = LOADING_INITIAL_DATA; - qDebug() << ">>> Making request to " << _url << " for header"; + // Add a fragment to the base url so we can identify the section of the ktx being requested when debugging + // The actual requested url is _activeUrl and will not contain the fragment + _url.setFragment("head"); _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); if (!_ktxHeaderRequest) { @@ -366,7 +368,12 @@ void NetworkTexture::makeRequest() { } else if (_ktxResourceState == PENDING_MIP_REQUEST) { if (_lowestKnownPopulatedMip > 0) { _ktxResourceState = REQUESTING_MIP; - startMipRangeRequest(_lowestKnownPopulatedMip - 1, _lowestKnownPopulatedMip - 1); + + // Add a fragment to the base url so we can identify the section of the ktx being requested when debugging + // The actual requested url is _activeUrl and will not contain the fragment + uint16_t nextMip = _lowestKnownPopulatedMip - 1; + _url.setFragment(QString::number(nextMip)); + startMipRangeRequest(nextMip, nextMip); } } else { qWarning(networking) << "NetworkTexture::makeRequest() called while not in a valid state: " << _ktxResourceState; @@ -632,7 +639,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - qDebug() << "Loaded KTX: " << QString::fromStdString(hash) << " : " << _url; _ktxHeaderRequest->deleteLater(); _ktxHeaderRequest = nullptr; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 52f5ee43f5..c6a4b93e51 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -22,7 +22,6 @@ #include "NetworkLogging.h" HTTPResourceRequest::~HTTPResourceRequest() { - qDebug() << "Cleaning up:" << _url << " " << _byteRange.fromInclusive << "-" << _byteRange.toExclusive; if (_reply) { _reply->disconnect(this); _reply->deleteLater(); @@ -68,7 +67,6 @@ void HTTPResourceRequest::doSend() { // HTTP byte ranges are inclusive on the `to` end: [from, to] byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive - 1); } - qDebug() << "Setting http range to " << byteRange; networkRequest.setRawHeader("Range", byteRange.toLatin1()); } networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, false); @@ -79,12 +77,9 @@ void HTTPResourceRequest::doSend() { connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress); setupTimer(); - qDebug() << "Sent: " << _url; } void HTTPResourceRequest::onRequestFinished() { - qDebug() << "On request finished: " << _url; - Q_ASSERT(_state == InProgress); Q_ASSERT(_reply); From 29641ba69a2454e01d736eb3fbd4bcc02c319bbc Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 10:55:55 -0700 Subject: [PATCH 65/85] Fix min mip not being written to ktx correctly --- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index b2b08ca170..6fe07d4241 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -207,7 +207,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; if (offset > 0) { - memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + offset, (void*)&_minMipLevelAvailable, 1); + memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + 4 + offset, (void*)&_minMipLevelAvailable, 1); } } } From 3184ddb29f61d0f3f4edd89c95b789bb0ec083fc Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 11:00:16 -0700 Subject: [PATCH 66/85] Fix duplicate low mip calculation in NetworkTexture --- .../src/model-networking/TextureCache.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 48999c21f3..011b3f4ed9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -627,14 +627,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { texture = textureCache->cacheTextureByHash(filename, texture); } - _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; - for (uint16_t l = 0; l < 200; l++) { - if (texture->isStoredMipFaceAvailable(l)) { - _lowestKnownPopulatedMip = l; - break; - } - } - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); _ktxResourceState = WAITING_FOR_MIP_REQUEST; From 0f461a2188f4e471dbc83c4ea0ef896f74a6e72d Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 11:34:27 -0700 Subject: [PATCH 67/85] Fix priority being reset when requesting low mips --- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 011b3f4ed9..c367ad4289 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -391,9 +391,9 @@ void NetworkTexture::startRequestForNextMipLevel() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { _ktxResourceState = PENDING_MIP_REQUEST; - setLoadPriority(this, -static_cast<int>(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); - init(); + setLoadPriority(this, -static_cast<int>(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); + _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); TextureCache::attemptRequest(_self); } } From 01724c9c907b89503933cf1af4671eba8495cf13 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 11:34:42 -0700 Subject: [PATCH 68/85] Update default load priority to be 0 --- libraries/networking/src/ResourceCache.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 56897ca4cd..0a24cb4d91 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -555,6 +555,10 @@ void Resource::clearLoadPriority(const QPointer<QObject>& owner) { } float Resource::getLoadPriority() { + if (_loadPriorities.size() == 0) { + return 0; + } + float highestPriority = -FLT_MAX; for (QHash<QPointer<QObject>, float>::iterator it = _loadPriorities.begin(); it != _loadPriorities.end(); ) { if (it.key().isNull()) { From 1555fc308861645e1f9746feec4b661a4b7a0a35 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 11:36:33 -0700 Subject: [PATCH 69/85] Update Resource to only clear load priorities if successful --- libraries/networking/src/ResourceCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 0a24cb4d91..7ae75b9538 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -643,12 +643,12 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { qCDebug(networking).noquote() << "Finished loading:" << _url.toDisplayString(); + _loadPriorities.clear(); _loaded = true; } else { qCDebug(networking).noquote() << "Failed to load:" << _url.toDisplayString(); _failedToLoad = true; } - _loadPriorities.clear(); emit finished(success); } From 70ece9f0fdfcf0b6f20a2b2f9068f6f312b038c8 Mon Sep 17 00:00:00 2001 From: Atlante45 <clement.brisset@gmail.com> Date: Wed, 26 Apr 2017 13:56:26 -0700 Subject: [PATCH 70/85] Load skybox first and sounds later --- libraries/audio/src/SoundCache.cpp | 6 +++++- .../model-networking/src/model-networking/TextureCache.cpp | 5 +++++ libraries/render-utils/src/MeshPartPayload.cpp | 1 - 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index 6b34c68959..1646540da6 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -14,6 +14,8 @@ #include "AudioLogging.h" #include "SoundCache.h" +static const int SOUNDS_LOADING_PRIORITY { -7 }; // Make sure sounds load after the low rez texture mips + int soundPointerMetaTypeId = qRegisterMetaType<SharedSoundPointer>(); SoundCache::SoundCache(QObject* parent) : @@ -37,5 +39,7 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) { QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, const void* extra) { qCDebug(audio) << "Requesting sound at" << url.toString(); - return QSharedPointer<Resource>(new Sound(url), &Resource::deleter); + auto resource = QSharedPointer<Resource>(new Sound(url), &Resource::deleter); + resource->setLoadPriority(this, SOUNDS_LOADING_PRIORITY); + return resource; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c367ad4289..4597304550 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -50,6 +50,8 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.k const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; +static const int SKYBOX_LOAD_PRIORITY { 10 }; // Make sure skybox loads first + TextureCache::TextureCache() : _ktxCache(KTX_DIRNAME, KTX_EXT) { setUnusedResourceCacheSize(0); @@ -259,6 +261,9 @@ QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSh auto content = textureExtra ? textureExtra->content : QByteArray(); auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels); + if (type == image::TextureUsage::CUBE_TEXTURE) { + texture->setLoadPriority(this, SKYBOX_LOAD_PRIORITY); + } return QSharedPointer<Resource>(texture, &Resource::deleter); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9485790463..2e08420073 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -558,7 +558,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) { } if (_materialNeedsUpdate && _model->getGeometry()->areTexturesLoaded()) { - qDebug() << "Updating for textures"; _model->setRenderItemsNeedUpdate(); _materialNeedsUpdate = false; } From a71d246e754bb93a358d2ee3bde55c7808f003a0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 12:50:42 -0700 Subject: [PATCH 71/85] Replace canPopulate with _minAllocatedMip --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 6 ++---- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 6 ++++-- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 1 - libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 12 ++++++++++++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 - libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 9 ++++++--- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index fceed7c2eb..dc4b828fbb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -437,7 +437,6 @@ void GLVariableAllocationSupport::updateMemoryPressure() { size_t idealMemoryAllocation = 0; bool canDemote = false; bool canPromote = false; - bool canPopulate = false; bool hasTransfers = false; for (const auto& texture : strongTextures) { // Race conditions can still leave nulls in the list, so we need to check @@ -452,7 +451,6 @@ void GLVariableAllocationSupport::updateMemoryPressure() { totalVariableMemoryAllocation += gltexture->size(); canDemote |= vartexture->canDemote(); canPromote |= vartexture->canPromote(); - canPopulate |= vartexture->canPopulate(); hasTransfers |= vartexture->hasPendingTransfers(); } @@ -464,7 +462,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { newState = MemoryPressureState::Transfer; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) && canPopulate)) { + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { newState = MemoryPressureState::Undersubscribed; } @@ -521,7 +519,7 @@ void GLVariableAllocationSupport::processWorkQueues() { vartexture->demote(); _memoryPressureStateStale = true; } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { - if (!vartexture->canPromote() || !vartexture->canPopulate()) { + if (!vartexture->canPromote()) { continue; } vartexture->promote(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 9aad49546e..3e944ed70d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -112,8 +112,7 @@ protected: static void manageMemory(); //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - virtual bool canPopulate() const = 0; - bool canPromote() const { return _allocatedMip > 0; } + bool canPromote() const { return _allocatedMip > _minAllocatedMip; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _pendingTransfers.size() > 0; } void executeNextTransfer(const TexturePointer& currentTexture); @@ -131,6 +130,9 @@ protected: // The highest (lowest resolution) mip that we will support, relative to the number // of mips in the gpu::Texture object uint16 _maxAllocatedMip { 0 }; + // The lowest (highest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object + uint16 _minAllocatedMip { 0 }; // Contains a series of lambdas that when executed will transfer data to the GPU, modify // the _populatedMip and update the sampler in order to fully populate the allocated texture // until _populatedMip == _allocatedMip diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index c0b9ea0e45..19979a1778 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -100,7 +100,6 @@ public: GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture); ~GL41VariableAllocationTexture(); - bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } void allocateStorage(uint16 allocatedMip); void syncSampler() const override; void promote() override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 51307511c2..9fa873f6e2 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -55,6 +55,18 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { default: Q_UNREACHABLE(); } + } else { + if (texture.getUsageType() == TextureUsageType::RESOURCE) { + auto varTex = static_cast<GL41VariableAllocationTexture*> (object); + + if (varTex->_minAllocatedMip > 0) { + auto minAvailableMip = texture.minAvailableMipLevel(); + if (minAvailableMip < varTex->_minAllocatedMip) { + varTex->_minAllocatedMip = minAvailableMip; + GL41VariableAllocationTexture::_memoryPressureStateStale = true; + } + } + } } return object; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 15e98c3af7..dbedd81c76 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -100,7 +100,6 @@ public: GL45VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture); ~GL45VariableAllocationTexture(); Size size() const override { return _size; } - bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } Size _size { 0 }; }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 299a52eddd..120be923f5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -85,10 +85,13 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (texture.getUsageType() == TextureUsageType::RESOURCE) { auto varTex = static_cast<GL45VariableAllocationTexture*> (object); - if (varTex->canPromote() && varTex->canPopulate()) { - GL45VariableAllocationTexture::_memoryPressureStateStale = true; + if (varTex->_minAllocatedMip > 0) { + auto minAvailableMip = texture.minAvailableMipLevel(); + if (minAvailableMip < varTex->_minAllocatedMip) { + varTex->_minAllocatedMip = minAvailableMip; + GL45VariableAllocationTexture::_memoryPressureStateStale = true; + } } - } } From 6a0474934c286db44415ba0bed7344c728da435e Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 13:36:42 -0700 Subject: [PATCH 72/85] Fixup variabletexture ctors --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 9 ++++----- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 9fa873f6e2..14c7148b3b 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -244,18 +244,17 @@ GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; _maxAllocatedMip = _populatedMip = mipLevels; - uint16_t minAvailableMip = texture.minAvailableMipLevel(); + _minAllocatedMip = texture.minAvailableMipLevel(); uvec3 mipDimensions; - for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) - && mip >= minAvailableMip) { + for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { _maxAllocatedMip = _populatedMip = mip; break; } } auto targetMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); - uint16_t allocatedMip = std::max<uint16_t>(minAvailableMip, targetMip); + uint16_t allocatedMip = std::max<uint16_t>(_minAllocatedMip, targetMip); allocateStorage(allocatedMip); _memoryPressureStateStale = true; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 9b35f35934..c25d011073 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -46,19 +46,18 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend _maxAllocatedMip = _populatedMip = mipLevels; - uint16_t minAvailableMip = texture.minAvailableMipLevel(); + _minAllocatedMip = texture.minAvailableMipLevel(); uvec3 mipDimensions; - for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) - && mip >= minAvailableMip) { + for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { _maxAllocatedMip = _populatedMip = mip; break; } } auto targetMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2); - uint16_t allocatedMip = std::max<uint16_t>(minAvailableMip, targetMip); + uint16_t allocatedMip = std::max<uint16_t>(_minAllocatedMip, targetMip); allocateStorage(allocatedMip); _memoryPressureStateStale = true; From 1bd95ee19fdb79a28e34f6a3ca3815df45e68e6b Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 14:26:09 -0700 Subject: [PATCH 73/85] Remove didQueueTransfer check from populateTransferQueue --- .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 13 ++++--------- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 13 ++++--------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 14c7148b3b..948381f288 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -416,7 +416,6 @@ void GL41VariableAllocationTexture::populateTransferQueue() { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); - bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { continue; @@ -426,7 +425,6 @@ void GL41VariableAllocationTexture::populateTransferQueue() { if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); - didQueueTransfer = true; continue; } @@ -442,17 +440,14 @@ void GL41VariableAllocationTexture::populateTransferQueue() { uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; - didQueueTransfer = true; } } // queue up the sampler and populated mip change for after the transfer has completed - if (didQueueTransfer) { - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); - } + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); } while (sourceMip != _allocatedMip); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index c25d011073..6e00159a2e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -186,7 +186,6 @@ void GL45ResourceTexture::populateTransferQueue() { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); - bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { continue; @@ -196,7 +195,6 @@ void GL45ResourceTexture::populateTransferQueue() { if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); - didQueueTransfer = true; continue; } @@ -212,17 +210,14 @@ void GL45ResourceTexture::populateTransferQueue() { uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; - didQueueTransfer = true; } } // queue up the sampler and populated mip change for after the transfer has completed - if (didQueueTransfer) { - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); - } + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); } while (sourceMip != _allocatedMip); } From 7bb6010149700be77c9e1337fca131f183bc87be Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 14:37:40 -0700 Subject: [PATCH 74/85] Fixup targetAllocatedMip inside gl textures --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 7 ++----- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 7 +------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 948381f288..5db924dd5c 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -245,6 +245,7 @@ GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr _allocatedMip = mipLevels; _maxAllocatedMip = _populatedMip = mipLevels; _minAllocatedMip = texture.minAvailableMipLevel(); + uvec3 mipDimensions; for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { @@ -310,11 +311,7 @@ void GL41VariableAllocationTexture::promote() { Q_ASSERT(_allocatedMip > 0); uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2); - targetAllocatedMip = std::max<uint16_t>(_gpuObject.minAvailableMipLevel(), targetAllocatedMip); - - if (targetAllocatedMip >= _allocatedMip || !_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { - return; - } + targetAllocatedMip = std::max<uint16_t>(_minAllocatedMip, targetAllocatedMip); GLuint oldId = _id; auto oldSize = _size; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 6e00159a2e..92d820e5f0 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -43,9 +43,7 @@ using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; - _maxAllocatedMip = _populatedMip = mipLevels; - _minAllocatedMip = texture.minAvailableMipLevel(); uvec3 mipDimensions; @@ -104,11 +102,8 @@ void GL45ResourceTexture::promote() { Q_ASSERT(_allocatedMip > 0); uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2); - targetAllocatedMip = std::max<uint16_t>(_gpuObject.minAvailableMipLevel(), targetAllocatedMip); + targetAllocatedMip = std::max<uint16_t>(_minAllocatedMip, targetAllocatedMip); - if (targetAllocatedMip >= _allocatedMip || !_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { - return; - } GLuint oldId = _id; auto oldSize = _size; // create new texture From e4e554aaeaf7c3f79ebdd5394ea6011010dc2021 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 14:38:13 -0700 Subject: [PATCH 75/85] Remove dead code from Texture.h --- libraries/gpu/src/gpu/Texture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index a79e679f15..825e9237f5 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -480,7 +480,7 @@ public: // Access the stored mips and faces const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } - bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const;// { return _storage->isMipAvailable(level, face); } + bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const; Size getStoredMipFaceSize(uint16 level, uint8 face = 0) const { return _storage->getMipFaceSize(level, face); } Size getStoredMipSize(uint16 level) const; Size getStoredSize() const; From c7ac82b4e2b0eb167a88af1aeac6d4595020bdc3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 14:43:25 -0700 Subject: [PATCH 76/85] Remove magic number for kv and image sizes --- libraries/gpu/src/gpu/Texture_ktx.cpp | 6 +++--- libraries/ktx/src/ktx/KTX.h | 2 ++ .../model-networking/src/model-networking/TextureCache.cpp | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 6fe07d4241..207de61b35 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -193,7 +193,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor auto data = file->mutableData(); data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; - data += 4; + data += ktx::IMAGE_SIZE_WIDTH; auto offset = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); { @@ -207,7 +207,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; if (offset > 0) { - memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + 4 + offset, (void*)&_minMipLevelAvailable, 1); + memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + ktx::KV_SIZE_WIDTH + offset, (void*)&_minMipLevelAvailable, 1); } } } @@ -300,7 +300,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, cubeFaces)); } - imageOffset += static_cast<uint32_t>(mip->getSize()) + 4; + imageOffset += static_cast<uint32_t>(mip->getSize()) + ktx::IMAGE_SIZE_WIDTH; } } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index e84cba085d..5a3167cf53 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -390,6 +390,8 @@ namespace ktx { }; static const size_t KTX_HEADER_SIZE = 64; static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static and should not change from the spec"); + static const size_t KV_SIZE_WIDTH = 4; // Number of bytes for keyAndValueByteSize + static const size_t IMAGE_SIZE_WIDTH = 4; // Number of bytes for imageSize // Key Values struct KeyValue { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4597304550..74ddf1ae1a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -429,7 +429,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } else { ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData - + _originalKtxDescriptor->images[low]._imageOffset + 4; + + _originalKtxDescriptor->images[low]._imageOffset + ktx::IMAGE_SIZE_WIDTH; range.toExclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[high + 1]._imageOffset; _ktxMipRequest->setByteRange(range); @@ -622,8 +622,8 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } ktxData -= image._imageSize; texture->assignStoredMip(static_cast<gpu::uint16>(level), image._imageSize, ktxData); - ktxData -= 4; - imageSizeRemaining -= (image._imageSize + 4); + 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 From 19d5414c30be0ba212a568c49aa74ae8f7231f4c Mon Sep 17 00:00:00 2001 From: Atlante45 <clement.brisset@gmail.com> Date: Wed, 26 Apr 2017 16:23:09 -0700 Subject: [PATCH 77/85] Reduce High Mips request size --- .../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 74ddf1ae1a..55704236e3 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -422,9 +422,10 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { + static const int HIGH_MIP_MAX_SIZE = 5516; // This is a special case where we load the high 7 mips ByteRange range; - range.fromInclusive = -15000; + range.fromInclusive = -HIGH_MIP_MAX_SIZE; _ktxMipRequest->setByteRange(range); } else { ByteRange range; From f15a34e145fd1ea64e391d5c4dde3b7d92868147 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 15:41:49 -0700 Subject: [PATCH 78/85] Cleanup data pointer calculations in assignMipData --- libraries/gpu/src/gpu/Texture_ktx.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 207de61b35..e3c4b325fb 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -191,9 +191,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor auto file = maybeOpenFile(); - auto data = file->mutableData(); - data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; - data += ktx::IMAGE_SIZE_WIDTH; + auto imageData = file->mutableData(); + imageData += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; + imageData += ktx::IMAGE_SIZE_WIDTH; auto offset = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); { @@ -204,10 +204,11 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor return; } - memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); + memcpy(imageData, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; if (offset > 0) { - memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + ktx::KV_SIZE_WIDTH + offset, (void*)&_minMipLevelAvailable, 1); + auto minMipKeyData = file->mutableData() + ktx::KTX_HEADER_SIZE + ktx::KV_SIZE_WIDTH + offset; + memcpy(minMipKeyData, (void*)&_minMipLevelAvailable, 1); } } } From f509403b25c3169966918fe2b93ae2dec3a08c96 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 16:15:19 -0700 Subject: [PATCH 79/85] Update prioritization of memory pressure states --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index dc4b828fbb..5bd5ac8db1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -458,12 +458,12 @@ void GLVariableAllocationSupport::updateMemoryPressure() { float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; auto newState = MemoryPressureState::Idle; - if (hasTransfers) { - newState = MemoryPressureState::Transfer; + if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { + newState = MemoryPressureState::Undersubscribed; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { - newState = MemoryPressureState::Undersubscribed; + } else if (hasTransfers) { + newState = MemoryPressureState::Transfer; } if (newState != _memoryPressureState) { @@ -539,6 +539,7 @@ void GLVariableAllocationSupport::processWorkQueues() { } if (workQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; _memoryPressureStateStale = true; } } From 4395cd7ee18f42d1bbc106f5fd925a24f5fa9ad5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 16:30:17 -0700 Subject: [PATCH 80/85] Update hasPendingTransfers to not touch _pendingTransfers --- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 3e944ed70d..cd7b30b961 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -114,7 +114,7 @@ protected: //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } bool canPromote() const { return _allocatedMip > _minAllocatedMip; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _pendingTransfers.size() > 0; } + bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); virtual void populateTransferQueue() = 0; virtual void promote() = 0; From 47cf44dc60819842bd86816d989891490cf0e4a7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 17:11:19 -0700 Subject: [PATCH 81/85] Store offset to min mip kv in KtxStorage --- libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 17 ++++++++--------- libraries/ktx/src/ktx/KTX.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 825e9237f5..502ad33143 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -330,6 +330,7 @@ public: std::string _filename; std::atomic<uint8_t> _minMipLevelAvailable; + size_t _offsetToMinMipKV; ktx::KTXDescriptorPointer _ktxDescriptor; friend class Texture; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index e3c4b325fb..8181bb21ae 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -109,18 +109,18 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { if (_ktxDescriptor->images.size() < _ktxDescriptor->header.numberOfMipmapLevels) { qWarning() << "Bad images found in ktx"; } - auto& keyValues = _ktxDescriptor->keyValues; - auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { - return val._key.compare(ktx::HIFI_MIN_POPULATED_MIP_KEY) == 0; - }); - if (found != keyValues.end()) { - _minMipLevelAvailable = found->_value[0]; + + _offsetToMinMipKV = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); + if (_offsetToMinMipKV) { + auto data = storage->data() + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV; + _minMipLevelAvailable = *data; } else { // Assume all mip levels are available _minMipLevelAvailable = 0; } } + // now that we know the ktx, let's get the header info to configure this Texture::Storage: Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; @@ -195,7 +195,6 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor imageData += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; imageData += ktx::IMAGE_SIZE_WIDTH; - auto offset = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); { std::lock_guard<std::mutex> lock { _cacheFileWriteMutex }; @@ -206,8 +205,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(imageData, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; - if (offset > 0) { - auto minMipKeyData = file->mutableData() + ktx::KTX_HEADER_SIZE + ktx::KV_SIZE_WIDTH + offset; + if (_offsetToMinMipKV > 0) { + auto minMipKeyData = file->mutableData() + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV; memcpy(minMipKeyData, (void*)&_minMipLevelAvailable, 1); } } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 0dbc2e720f..cea397927a 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -126,7 +126,7 @@ size_t KTXDescriptor::getValueOffsetForKey(const std::string& key) const { size_t offset { 0 }; for (auto& kv : keyValues) { if (kv._key == key) { - return offset + kv._key.size() + 1; + return offset + ktx::KV_SIZE_WIDTH + kv._key.size() + 1; } offset += kv.serializedByteSize(); } From cb299695f461a3ca29dee4858bea330f2607531e Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 17:52:53 -0700 Subject: [PATCH 82/85] Update imageOffset to size_t --- libraries/gpu/src/gpu/Texture_ktx.cpp | 1 - libraries/ktx/src/ktx/KTX.cpp | 4 ++-- libraries/ktx/src/ktx/KTX.h | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 8181bb21ae..efff6c7afe 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -97,7 +97,6 @@ struct GPUKTXPayload { return false; } }; -const std::string gpu::SOURCE_HASH_KEY { "hifi.sourceHash" }; const std::string GPUKTXPayload::KEY { "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index cea397927a..38bb91e5c2 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -136,7 +136,7 @@ size_t KTXDescriptor::getValueOffsetForKey(const std::string& key) const { ImageDescriptors Header::generateImageDescriptors() const { ImageDescriptors descriptors; - uint32_t imageOffset = 0; + size_t imageOffset = 0; for (uint32_t level = 0; level < numberOfMipmapLevels; ++level) { auto imageSize = static_cast<uint32_t>(evalImageSize(level)); if (imageSize == 0) { @@ -149,7 +149,7 @@ ImageDescriptors Header::generateImageDescriptors() const { 0 }; - imageOffset += (imageSize * numberOfFaces) + 4; + imageOffset += (imageSize * numberOfFaces) + ktx::IMAGE_SIZE_WIDTH; ImageHeader::FaceOffsets offsets; // TODO Add correct face offsets diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 5a3167cf53..42ff4644ca 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -422,11 +422,11 @@ namespace ktx { // This is the byte offset from the _start_ of the image region. For example, level 0 // will have a byte offset of 0. const uint32_t _numFaces; - const uint32_t _imageOffset; + const size_t _imageOffset; const uint32_t _imageSize; const uint32_t _faceSize; const uint32_t _padding; - ImageHeader(bool cube, uint32_t imageOffset, uint32_t imageSize, uint32_t padding) : + ImageHeader(bool cube, size_t imageOffset, uint32_t imageSize, uint32_t padding) : _numFaces(cube ? NUM_CUBEMAPFACES : 1), _imageOffset(imageOffset), _imageSize(imageSize * _numFaces), @@ -448,10 +448,10 @@ namespace ktx { struct Image : public ImageHeader { FaceBytes _faceBytes; Image(const ImageHeader& header, const FaceBytes& faces) : ImageHeader(header), _faceBytes(faces) {} - Image(uint32_t imageOffset, uint32_t imageSize, uint32_t padding, const Byte* bytes) : + Image(size_t imageOffset, uint32_t imageSize, uint32_t padding, const Byte* bytes) : ImageHeader(false, imageOffset, imageSize, padding), _faceBytes(1, bytes) {} - Image(uint32_t imageOffset, uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : + Image(size_t imageOffset, uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : ImageHeader(true, imageOffset, pageSize, padding) { if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { From fee36453a3d699fd29b70182ba7873eb98ccfd8b Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 17:53:13 -0700 Subject: [PATCH 83/85] Move hifi.minMip --- libraries/gpu/src/gpu/Texture.h | 4 +++- libraries/ktx/src/ktx/KTX.h | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 502ad33143..9b23b4e695 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -34,7 +34,9 @@ namespace ktx { namespace gpu { -extern const std::string SOURCE_HASH_KEY; + +const std::string SOURCE_HASH_KEY { "hifi.sourceHash" }; + const uint8 SOURCE_HASH_BYTES = 16; // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 42ff4644ca..e8fa019a07 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -70,10 +70,9 @@ end namespace ktx { - const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifi.minMip"; - - const uint32_t PACKING_SIZE { sizeof(uint32_t) }; + const std::string HIFI_MIN_POPULATED_MIP_KEY{ "hifi.minMip" }; + using Byte = uint8_t; enum class GLType : uint32_t { From f5b7feccde699e0986ac3dfc0577b601e1683dcc Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 18:01:56 -0700 Subject: [PATCH 84/85] Update AssetClient/Request to use const byteRange --- libraries/networking/src/AssetClient.cpp | 2 +- libraries/networking/src/AssetClient.h | 2 +- libraries/networking/src/AssetRequest.cpp | 2 +- libraries/networking/src/AssetRequest.h | 4 ++-- libraries/networking/src/ByteRange.h | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 48f8bb87f9..15e0b8c9b5 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -181,7 +181,7 @@ RenameMappingRequest* AssetClient::createRenameMappingRequest(const AssetPath& o return request; } -AssetRequest* AssetClient::createRequest(const AssetHash& hash, ByteRange byteRange) { +AssetRequest* AssetClient::createRequest(const AssetHash& hash, const ByteRange& byteRange) { auto request = new AssetRequest(hash, byteRange); // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index b204fab47e..6f9cc3cd31 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -56,7 +56,7 @@ public: Q_INVOKABLE DeleteMappingsRequest* createDeleteMappingsRequest(const AssetPathList& paths); Q_INVOKABLE SetMappingRequest* createSetMappingRequest(const AssetPath& path, const AssetHash& hash); Q_INVOKABLE RenameMappingRequest* createRenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath); - Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash, ByteRange byteRange = ByteRange()); + Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash, const ByteRange& byteRange = ByteRange()); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index e54a058ac2..341c3b45da 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -23,7 +23,7 @@ static int requestID = 0; -AssetRequest::AssetRequest(const QString& hash, ByteRange byteRange) : +AssetRequest::AssetRequest(const QString& hash, const ByteRange& byteRange) : _requestID(++requestID), _hash(hash), _byteRange(byteRange) diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index e617d75157..b808ae0ca6 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -41,7 +41,7 @@ public: UnknownError }; - AssetRequest(const QString& hash, ByteRange byteRange = ByteRange()); + AssetRequest(const QString& hash, const ByteRange& byteRange = ByteRange()); virtual ~AssetRequest() override; Q_INVOKABLE void start(); @@ -65,7 +65,7 @@ private: QByteArray _data; int _numPendingRequests { 0 }; MessageID _assetRequestID { INVALID_MESSAGE_ID }; - ByteRange _byteRange; + const ByteRange _byteRange; }; #endif diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h index 77932a8c28..6fd3559154 100644 --- a/libraries/networking/src/ByteRange.h +++ b/libraries/networking/src/ByteRange.h @@ -16,8 +16,8 @@ struct ByteRange { int64_t fromInclusive { 0 }; int64_t toExclusive { 0 }; - bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } - int64_t size() { return toExclusive - fromInclusive; } + bool isSet() const { return fromInclusive < 0 || fromInclusive < toExclusive; } + int64_t size() const { return toExclusive - fromInclusive; } // byte ranges are invalid if: // (1) the toExclusive of the range is negative From 60ba874e2e423301d0116019842282d6322ac54a Mon Sep 17 00:00:00 2001 From: Ryan Huffman <ryanhuffman@gmail.com> Date: Wed, 26 Apr 2017 18:02:23 -0700 Subject: [PATCH 85/85] Fix implementation of mutableData in non-File Storage classes --- libraries/shared/src/shared/Storage.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 3983387c15..da5b773d52 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -43,7 +43,7 @@ namespace storage { MemoryStorage(size_t size, const uint8_t* data = nullptr); const uint8_t* data() const override { return _data.data(); } uint8_t* data() { return _data.data(); } - uint8_t* mutableData() override { return 0; } + uint8_t* mutableData() override { return _data.data(); } size_t size() const override { return _data.size(); } operator bool() const override { return true; } private: @@ -73,7 +73,7 @@ namespace storage { public: ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); const uint8_t* data() const override { return _data; } - uint8_t* mutableData() override { return 0; } + uint8_t* mutableData() override { throw std::runtime_error("Cannot modify ViewStorage"); } size_t size() const override { return _size; } operator bool() const override { return *_owner; } private: