From 1b036b7225665ee7e052d33c7b84396bea2c87b0 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 7 Mar 2017 00:58:22 -0800 Subject: [PATCH] Implement the ktx keyvalue feature for read and write and use it with gpu::Texture to store meat data required for full serialization --- interface/src/avatar/MyAvatar.cpp | 4 +- libraries/gpu/src/gpu/Texture.h | 3 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 46 ++++++++++-- libraries/ktx/src/ktx/KTX.cpp | 4 +- libraries/ktx/src/ktx/KTX.h | 4 +- libraries/ktx/src/ktx/Reader.cpp | 93 +++++++++++------------- libraries/ktx/src/ktx/Writer.cpp | 39 +++++++++- libraries/model/src/model/TextureMap.cpp | 4 +- 8 files changed, 131 insertions(+), 66 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 969268c549..3a34e4f434 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1617,8 +1617,8 @@ void MyAvatar::postUpdate(float deltaTime) { } } - DebugDraw::getInstance().updateMyAvatarPos(getPosition()); - DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); +// DebugDraw::getInstance().updateMyAvatarPos(getPosition()); + // DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); AnimPose postUpdateRoomPose(_sensorToWorldMatrix); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9996026254..c7fb496fc0 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -143,6 +143,7 @@ public: uint8 getMinMip() const { return _desc._minMip; } uint8 getMaxMip() const { return _desc._maxMip; } + const Desc& getDesc() const { return _desc; } protected: Desc _desc; }; @@ -516,7 +517,7 @@ public: // Textures can be serialized directly to ktx data file, here is how static ktx::KTXUniquePointer serialize(const Texture& texture); - static Texture* unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler = Sampler()); + static Texture* unserialize(const ktx::KTXUniquePointer& srcData, 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 bd34246f51..5fed4d82bc 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -18,6 +18,30 @@ using namespace gpu; using PixelsPointer = Texture::PixelsPointer; using KtxStorage = Texture::KtxStorage; +struct GPUKTXPayload { + Sampler::Desc _samplerDesc; + Texture::Usage _usage; + TextureUsageType _usageType; + + static std::string KEY; + static bool isGPUKTX(const ktx::KeyValue& val) { + return (val._key.compare(KEY) == 0); + } + + 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; + } + } + return false; + } +}; + +std::string GPUKTXPayload::KEY { "hifi.gpu" }; + KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) { // if the source ktx is valid let's config this KtxStorage correctly @@ -116,7 +140,14 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } } - auto ktxBuffer = ktx::KTX::create(header, images); + GPUKTXPayload keyval; + keyval._samplerDesc = texture.getSampler().getDesc(); + keyval._usage = texture.getUsage(); + keyval._usageType = texture.getUsageType(); + ktx::KeyValues keyValues; + keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval)); + + auto ktxBuffer = ktx::KTX::create(header, images, keyValues); #if 0 auto expectedMipCount = texture.evalNumMips(); assert(expectedMipCount == ktxBuffer->_images.size()); @@ -146,7 +177,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler) { +Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { if (!srcData) { return nullptr; } @@ -175,7 +206,12 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx type = TEX_3D; } - auto tex = Texture::create( usageType, + + // If found, use the + GPUKTXPayload gpuktxKeyValue; + bool isGPUKTXPayload = GPUKTXPayload::findInKeyValues(srcData->_keyValues, gpuktxKeyValue); + + auto tex = Texture::create( (isGPUKTXPayload ? gpuktxKeyValue._usageType : usageType), type, texelFormat, header.getPixelWidth(), @@ -183,9 +219,9 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx header.getPixelDepth(), 1, // num Samples header.getNumberOfSlices(), - sampler); + (isGPUKTXPayload ? gpuktxKeyValue._samplerDesc : sampler)); - tex->setUsage(usage); + tex->setUsage((isGPUKTXPayload ? gpuktxKeyValue._usage : usage)); // Assing the mips availables tex->setStoredMipFormat(mipFormat); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 7035d8fc54..f9bfb377f5 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -72,7 +72,7 @@ size_t Header::evalImageSize(uint32_t level) const { KeyValue::KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value) : - _byteSize((uint32_t) key.size() + valueByteSize), + _byteSize((uint32_t) key.size() + 1 + valueByteSize), // keyString size + '\0' ending char + the value size _key(key), _value(valueByteSize) { @@ -92,7 +92,7 @@ uint32_t KeyValue::serializedByteSize() const { } uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { - size_t keyValuesSize = 0; + uint32_t keyValuesSize = 0; for (auto& keyval : keyValues) { keyValuesSize += keyval.serializedByteSize(); } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 8fdbe25ac9..8e901b1105 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -398,8 +398,8 @@ namespace ktx { uint32_t serializedByteSize() const; - static KeyValue parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes); - static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes); + static KeyValue parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes); + static uint32_t writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval); using KeyValues = std::list; static uint32_t serializedKeyValuesByteSize(const KeyValues& keyValues); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 10a75ddfe7..81f523cb69 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -56,55 +56,6 @@ namespace ktx { return true; } - - KeyValue KeyValue::parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes) { - // find the first null character \0 - uint32_t keyLength = 0; - while (reinterpret_cast(bytes[++keyLength]) != '\0') { - if (keyLength == keyAndValueByteSize) { - // key must be null-terminated, and there must be space for the value - throw ReaderException("invalid key-value " + std::string(reinterpret_cast(bytes), keyLength)); - } - } - - return KeyValue(std::string(reinterpret_cast(bytes), keyLength), keyAndValueByteSize - keyLength, bytes + keyLength); - } - - static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes) { - uint32_t keyValueByteSize; - memcpy(&keyValueByteSize, bytes, sizeof(uint32_t)); - if (keyValueByteSize > byteSizeAhead) { - throw ReaderException("invalid key-value size"); - } - - auto keyValueBytes = bytes + sizeof(uint32_t); - - // parse the key-value - return KeyValue::parseKeyAndValue(keyValueByteSize, keyValueBytes); - } - - static KeyValues parseKeyValues(size_t srcSize, const Byte* srcBytes); - - KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* src) { - KeyValues keyValues; - try { - uint32_t length = (uint32_t) srcSize; - uint32_t offset = 0; - while (offset < length) { - auto keyValue = parseSerializedKeyAndValue(length - offset, src); - keyValues.emplace_back(keyValue); - - // advance offset/src - offset += keyValue.serializedByteSize(); - src += keyValue.serializedByteSize(); - } - } - catch (const ReaderException& e) { - qWarning() << e.what(); - } - return keyValues; - } - bool KTX::checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes) { try { // validation @@ -140,6 +91,50 @@ namespace ktx { } } + KeyValue KeyValue::parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes) { + uint32_t keyAndValueByteSize; + memcpy(&keyAndValueByteSize, srcBytes, sizeof(uint32_t)); + if (keyAndValueByteSize + sizeof(uint32_t) > srcSize) { + throw ReaderException("invalid key-value size"); + } + auto keyValueBytes = srcBytes + sizeof(uint32_t); + + // find the first null character \0 and extract the key + uint32_t keyLength = 0; + while (reinterpret_cast(keyValueBytes[++keyLength]) != '\0') { + if (keyLength == keyAndValueByteSize) { + // key must be null-terminated, and there must be space for the value + throw ReaderException("invalid key-value " + std::string(reinterpret_cast(keyValueBytes), keyLength)); + } + } + uint32_t valueStartOffset = keyLength + 1; + + // parse the key-value + return KeyValue(std::string(reinterpret_cast(keyValueBytes), keyLength), + keyAndValueByteSize - valueStartOffset, keyValueBytes + valueStartOffset); + } + + KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* srcBytes) { + KeyValues keyValues; + try { + auto src = srcBytes; + uint32_t length = (uint32_t) srcSize; + uint32_t offset = 0; + while (offset < length) { + auto keyValue = KeyValue::parseSerializedKeyAndValue(length - offset, src); + keyValues.emplace_back(keyValue); + + // advance offset/src + offset += keyValue.serializedByteSize(); + src += keyValue.serializedByteSize(); + } + } + catch (const ReaderException& e) { + qWarning() << e.what(); + } + return keyValues; + } + Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { Images images; auto currentPtr = srcBytes; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 1a1c39e2c6..06fba326d5 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -10,6 +10,9 @@ // #include "KTX.h" + +#include +#include #ifndef _MSC_VER #define NOEXCEPT noexcept #else @@ -70,7 +73,7 @@ namespace ktx { // KeyValues if (!keyValues.empty()) { - destHeader->bytesOfKeyValueData = writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); + destHeader->bytesOfKeyValueData = (uint32_t) writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); } else { // Make sure the header contains the right bytesOfKeyValueData size destHeader->bytesOfKeyValueData = 0; @@ -84,9 +87,39 @@ namespace ktx { return destByteSize; } - static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { - + uint32_t KeyValue::writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval) { + uint32_t keyvalSize = keyval.serializedByteSize(); + if (keyvalSize > destByteSize) { + throw WriterException("invalid key-value size"); + } + *((uint32_t*) destBytes) = keyval._byteSize; + + auto dest = destBytes + sizeof(uint32_t); + + auto keySize = keyval._key.size() + 1; // Add 1 for the '\0' character at the end of the string + memcpy(dest, keyval._key.data(), keySize); + dest += keySize; + + memcpy(dest, keyval._value.data(), keyval._value.size()); + + return keyvalSize; + } + + size_t KTX::writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { + size_t writtenByteSize = 0; + try { + auto dest = destBytes; + for (auto& keyval : keyValues) { + size_t keyvalSize = KeyValue::writeSerializedKeyAndValue(dest, (uint32_t) (destByteSize - writtenByteSize), keyval); + writtenByteSize += keyvalSize; + dest += keyvalSize; + } + } + catch (const WriterException& e) { + qWarning() << e.what(); + } + return writtenByteSize; } Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 4f9d57b21b..56b86f22d6 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -145,8 +145,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo fclose (file); //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), - ktx::KTX::create(std::static_pointer_cast(storage)), srcTexture->getSampler()); + auto theNewTexure = Texture::unserialize(ktx::KTX::create(std::static_pointer_cast(storage)), + srcTexture->getUsageType(), srcTexture->getUsage(), srcTexture->getSampler().getDesc()); if (theNewTexure) { returnedTexture = theNewTexure;