From cf3dc12542452310b48bc48b43917cce1032f8a7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 13 Apr 2017 15:54:25 -0700 Subject: [PATCH] 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& 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& 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(_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(_id) = allocate(_gpuObject); + //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); @@ -130,6 +144,7 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast(_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(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::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(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(_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);