From 7d5d6823cdefa085cab105bde85ca7ec7c1df5df Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 15 Feb 2017 00:54:16 -0800 Subject: [PATCH 01/10] Adding the ktx library --- libraries/gpu/CMakeLists.txt | 2 +- libraries/ktx/CMakeLists.txt | 3 + libraries/ktx/src/ktx/KTX.cpp | 92 +++++++ libraries/ktx/src/ktx/KTX.h | 397 +++++++++++++++++++++++++++++++ libraries/ktx/src/ktx/Reader.cpp | 159 +++++++++++++ libraries/ktx/src/ktx/Writer.cpp | 21 ++ 6 files changed, 673 insertions(+), 1 deletion(-) create mode 100644 libraries/ktx/CMakeLists.txt create mode 100644 libraries/ktx/src/ktx/KTX.cpp create mode 100644 libraries/ktx/src/ktx/KTX.h create mode 100644 libraries/ktx/src/ktx/Reader.cpp create mode 100644 libraries/ktx/src/ktx/Writer.cpp diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 384c5709ee..207431d8c7 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu) autoscribe_shader_lib(gpu) setup_hifi_library() -link_hifi_libraries(shared) +link_hifi_libraries(shared ktx) target_nsight() diff --git a/libraries/ktx/CMakeLists.txt b/libraries/ktx/CMakeLists.txt new file mode 100644 index 0000000000..404660b247 --- /dev/null +++ b/libraries/ktx/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME ktx) +setup_hifi_library() +link_hifi_libraries() \ No newline at end of file diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp new file mode 100644 index 0000000000..2d5e0bc812 --- /dev/null +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -0,0 +1,92 @@ +// +// KTX.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// 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 +// + +#include "KTX.h" + +#include //min max and more + +using namespace ktx; + +const Header::Identifier ktx::Header::IDENTIFIER {{ + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A +}}; + +Header::Header() { + memcpy(identifier, IDENTIFIER.data(), IDENTIFIER_LENGTH); +} + +uint32_t Header::evalMaxDimension() const { + return std::max(pixelWidth, std::max(pixelHeight, pixelDepth)); +} + +uint32_t Header::evalMaxLevel() const { + return 1 + log2(evalMaxDimension()); +} + +uint32_t Header::evalPixelWidth(uint32_t level) const { + return std::max(pixelWidth >> level, 1U); +} +uint32_t Header::evalPixelHeight(uint32_t level) const { + return std::max(pixelHeight >> level, 1U); +} +uint32_t Header::evalPixelDepth(uint32_t level) const { + return std::max(pixelDepth >> level, 1U); +} + +size_t Header::evalPixelSize() const { + return glTypeSize; // Really we should generate the size from the FOrmat etc +} + +size_t Header::evalRowSize(uint32_t level) const { + auto pixelWidth = evalPixelWidth(level); + auto pixSize = evalPixelSize(); + auto netSize = pixelWidth * pixSize; + auto packing = netSize % 4; + return netSize + (packing ? 4 - packing : 0); +} +size_t Header::evalFaceSize(uint32_t level) const { + auto pixelHeight = evalPixelHeight(level); + auto pixelDepth = evalPixelDepth(level); + auto rowSize = evalRowSize(level); + return pixelDepth * pixelHeight * rowSize; +} +size_t Header::evalImageSize(uint32_t level) const { + auto faceSize = evalFaceSize(level); + if (numberOfFaces == 6 && numberOfArrayElements == 0) { + return faceSize; + } else { + return (numberOfArrayElements * numberOfFaces * faceSize); + } +} + + +KTX::KTX() { +} + +void KTX::resetStorage(Storage* storage) { + _storage.reset(storage); +} + +const Header* KTX::getHeader() const { + if (_storage) { + return reinterpret_cast (_storage->_bytes); + } else { + return nullptr; + } +} + +const Byte* KTX::getKeyValueData() const { + if (_storage) { + return (_storage->_bytes + sizeof(Header)); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h new file mode 100644 index 0000000000..003d3f0d1b --- /dev/null +++ b/libraries/ktx/src/ktx/KTX.h @@ -0,0 +1,397 @@ +// +// KTX.h +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// 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 +// +#pragma once +#ifndef hifi_ktx_KTX_h +#define hifi_ktx_KTX_h + +#include +#include +#include +#include +#include + +/* KTX Spec: + +Byte[12] identifier +UInt32 endianness +UInt32 glType +UInt32 glTypeSize +UInt32 glFormat +Uint32 glInternalFormat +Uint32 glBaseInternalFormat +UInt32 pixelWidth +UInt32 pixelHeight +UInt32 pixelDepth +UInt32 numberOfArrayElements +UInt32 numberOfFaces +UInt32 numberOfMipmapLevels +UInt32 bytesOfKeyValueData + +for each keyValuePair that fits in bytesOfKeyValueData + UInt32 keyAndValueByteSize + Byte keyAndValue[keyAndValueByteSize] + Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)] +end + +for each mipmap_level in numberOfMipmapLevels* + UInt32 imageSize; + for each array_element in numberOfArrayElements* + for each face in numberOfFaces + for each z_slice in pixelDepth* + for each row or row_of_blocks in pixelHeight* + for each pixel or block_of_pixels in pixelWidth + Byte data[format-specific-number-of-bytes]** + end + end + end + Byte cubePadding[0-3] + end + end + Byte mipPadding[3 - ((imageSize + 3) % 4)] +end + +* Replace with 1 if this field is 0. + +** Uncompressed texture data matches a GL_UNPACK_ALIGNMENT of 4. +*/ + + + +namespace ktx { + + enum GLType : uint32_t { + COMPRESSED_TYPE = 0, + + // GL 4.4 Table 8.2 + UNSIGNED_BYTE = 0x1401, + BYTE = 0x1400, + UNSIGNED_SHORT = 0x1403, + SHORT = 0x1402, + UNSIGNED_INT = 0x1405, + INT = 0x1404, + HALF_FLOAT = 0x140B, + FLOAT = 0x1406, + UNSIGNED_BYTE_3_3_2 = 0x8032, + UNSIGNED_BYTE_2_3_3_REV = 0x8362, + UNSIGNED_SHORT_5_6_5 = 0x8363, + UNSIGNED_SHORT_5_6_5_REV = 0x8364, + UNSIGNED_SHORT_4_4_4_4 = 0x8033, + UNSIGNED_SHORT_4_4_4_4_REV = 0x8365, + UNSIGNED_SHORT_5_5_5_1 = 0x8034, + UNSIGNED_SHORT_1_5_5_5_REV = 0x8366, + UNSIGNED_INT_8_8_8_8 = 0x8035, + UNSIGNED_INT_8_8_8_8_REV = 0x8367, + UNSIGNED_INT_10_10_10_2 = 0x8036, + UNSIGNED_INT_2_10_10_10_REV = 0x8368, + UNSIGNED_INT_24_8 = 0x84FA, + UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B, + UNSIGNED_INT_5_9_9_9_REV = 0x8C3E, + FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD, + + NUM_GLTYPES = 25, + }; + + enum GLFormat : uint32_t { + COMPRESSED_FORMAT = 0, + + // GL 4.4 Table 8.3 + STENCIL_INDEX = 0x1901, + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, + + RED = 0x1903, + GREEN = 0x1904, + BLUE = 0x1905, + RG = 0x8227, + RGB = 0x1907, + RGBA = 0x1908, + BGR = 0x80E0, + BGRA = 0x80E1, + + RG_INTEGER = 0x8228, + RED_INTEGER = 0x8D94, + GREEN_INTEGER = 0x8D95, + BLUE_INTEGER = 0x8D96, + RGB_INTEGER = 0x8D98, + RGBA_INTEGER = 0x8D99, + BGR_INTEGER = 0x8D9A, + BGRA_INTEGER = 0x8D9B, + + NUM_GLFORMATS = 20, + }; + + enum GLInternalFormat_Uncompressed : uint32_t { + // GL 4.4 Table 8.12 + R8 = 0x8229, + R8_SNORM = 0x8F94, + + R16 = 0x822A, + R16_SNORM = 0x8F98, + + RG8 = 0x822B, + RG8_SNORM = 0x8F95, + + RG16 = 0x822C, + RG16_SNORM = 0x8F99, + + R3_G3_B2 = 0x2A10, + RGB4 = 0x804F, + RGB5 = 0x8050, + RGB565 = 0x8D62, + + RGB8 = 0x8051, + RGB8_SNORM = 0x8F96, + RGB10 = 0x8052, + RGB12 = 0x8053, + + RGB16 = 0x8054, + RGB16_SNORM = 0x8F9A, + + RGBA2 = 0x8055, + RGBA4 = 0x8056, + RGB5_A1 = 0x8057, + RGBA8 = 0x8058, + RGBA8_SNORM = 0x8F97, + + RGB10_A2 = 0x8059, + RGB10_A2UI = 0x906F, + + RGBA12 = 0x805A, + RGBA16 = 0x805B, + RGBA16_SNORM = 0x8F9B, + + SRGB8 = 0x8C41, + SRGB8_ALPHA8 = 0x8C43, + + R16F = 0x822D, + RG16F = 0x822F, + RGB16F = 0x881B, + RGBA16F = 0x881A, + + R32F = 0x822E, + RG32F = 0x8230, + RGB32F = 0x8815, + RGBA32F = 0x8814, + + R11F_G11F_B10F = 0x8C3A, + RGB9_E5 = 0x8C3D, + + + R8I = 0x8231, + R8UI = 0x8232, + R16I = 0x8233, + R16UI = 0x8234, + R32I = 0x8235, + R32UI = 0x8236, + RG8I = 0x8237, + RG8UI = 0x8238, + RG16I = 0x8239, + RG16UI = 0x823A, + RG32I = 0x823B, + RG32UI = 0x823C, + + RGB8I = 0x8D8F, + RGB8UI = 0x8D7D, + RGB16I = 0x8D89, + RGB16UI = 0x8D77, + + RGB32I = 0x8D83, + RGB32UI = 0x8D71, + RGBA8I = 0x8D8E, + RGBA8UI = 0x8D7C, + RGBA16I = 0x8D88, + RGBA16UI = 0x8D76, + RGBA32I = 0x8D82, + + RGBA32UI = 0x8D70, + + // GL 4.4 Table 8.13 + DEPTH_COMPONENT16 = 0x81A5, + DEPTH_COMPONENT24 = 0x81A6, + DEPTH_COMPONENT32 = 0x81A7, + + DEPTH_COMPONENT32F = 0x8CAC, + DEPTH24_STENCIL8 = 0x88F0, + DEPTH32F_STENCIL8 = 0x8CAD, + + STENCIL_INDEX1 = 0x8D46, + STENCIL_INDEX4 = 0x8D47, + STENCIL_INDEX8 = 0x8D48, + STENCIL_INDEX16 = 0x8D49, + + NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74, + }; + + enum GLInternalFormat_Compressed : uint32_t { + // GL 4.4 Table 8.14 + COMPRESSED_RED = 0x8225, + COMPRESSED_RG = 0x8226, + COMPRESSED_RGB = 0x84ED, + COMPRESSED_RGBA = 0x84EE, + + COMPRESSED_SRGB = 0x8C48, + COMPRESSED_SRGB_ALPHA = 0x8C49, + + COMPRESSED_RED_RGTC1 = 0x8DBB, + COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC, + COMPRESSED_RG_RGTC2 = 0x8DBD, + COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE, + + COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C, + COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D, + COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, + COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, + + COMPRESSED_RGB8_ETC2 = 0x9274, + COMPRESSED_SRGB8_ETC2 = 0x9275, + COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276, + COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277, + COMPRESSED_RGBA8_ETC2_EAC = 0x9278, + COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279, + + COMPRESSED_R11_EAC = 0x9270, + COMPRESSED_SIGNED_R11_EAC = 0x9271, + COMPRESSED_RG11_EAC = 0x9272, + COMPRESSED_SIGNED_RG11_EAC = 0x9273, + + NUM_COMPRESSED_GLINTERNALFORMATS = 24, + }; + + enum GLBaseInternalFormat : uint32_t { + // GL 4.4 Table 8.11 + BIF_DEPTH_COMPONENT = 0x1902, + BIF_DEPTH_STENCIL = 0x84F9, + BIF_RED = 0x1903, + BIF_RG = 0x8227, + BIF_RGB = 0x1907, + BIF_RGBA = 0x1908, + BIF_STENCIL_INDEX = 0x1901, + + NUM_GLBASEINTERNALFORMATS = 7, + }; + + enum CubeMapFace { + POS_X = 0, + NEG_X = 1, + POS_Y = 2, + NEG_Y = 3, + POS_Z = 4, + NEG_Z = 5, + NUM_CUBEMAPFACES = 6, + }; + + using Byte = uint8_t; + + // Chunk of data + struct Storage { + size_t _size {0}; + Byte* _bytes {nullptr}; + + Byte* data() { + return _bytes; + } + const Byte* data() const { + return _bytes; + } + size_t size() const { return _size; } + + ~Storage() { if (_bytes) { delete _bytes; } } + + Storage() {} + Storage(size_t size) : + _size(size) + { + if (_size) { _bytes = new Byte[_size]; } + } + + Storage(size_t size, Byte* bytes) : + _size(size) + { + if (_size && _bytes) { _bytes = bytes; } + } + }; + + // Header + struct Header { + static const size_t IDENTIFIER_LENGTH = 12; + using Identifier = std::array; + static const Identifier IDENTIFIER; + + static const uint32_t ENDIAN_TEST = 0x04030201; + static const uint32_t REVERSE_ENDIAN_TEST = 0x01020304; + + Header(); + + Byte identifier[IDENTIFIER_LENGTH]; + uint32_t endianness { ENDIAN_TEST }; + uint32_t glType; + uint32_t glTypeSize; + uint32_t glFormat; + uint32_t glInternalFormat; + uint32_t glBaseInternalFormat; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; + uint32_t numberOfArrayElements; + uint32_t numberOfFaces; + uint32_t numberOfMipmapLevels; + uint32_t bytesOfKeyValueData; + + uint32_t evalMaxDimension() const; + uint32_t evalMaxLevel() const; + uint32_t evalPixelWidth(uint32_t level) const; + uint32_t evalPixelHeight(uint32_t level) const; + uint32_t evalPixelDepth(uint32_t level) const; + + size_t evalPixelSize() const; + size_t evalRowSize(uint32_t level) const; + size_t evalFaceSize(uint32_t level) const; + size_t evalImageSize(uint32_t level) const; + + }; + + // Key Values + using KeyValue = std::pair; + using KeyValues = std::list; + + + struct Mip { + uint32_t imageSize; + const Byte* _bytes; + }; + using Mips = std::vector; + + class KTX { + void resetStorage(Storage* src); + + public: + + KTX(); + + bool read(const Storage* src); + bool read(Storage* src); + + std::unique_ptr _storage; + + const Header* getHeader() const; + const Byte* getKeyValueData() const; + + KeyValues _keyValues; + + Mips _mips; + + static bool checkStorageHeader(const Storage& storage); + static KTX* create(const Storage* src); + }; + +} + +#endif // hifi_ktx_KTX_h diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp new file mode 100644 index 0000000000..8ec4de7686 --- /dev/null +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -0,0 +1,159 @@ +// +// Reader.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// 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 +// +#include "KTX.h" + +#include +#include + +namespace ktx { + class Exception: public std::exception { + public: + Exception(std::string explanation) : _explanation(explanation) {} + const char* what() const override { + return ("KTX deserialization error: " + _explanation).c_str(); + } + private: + std::string _explanation; + }; + + bool checkEndianness(uint32_t endianness, bool& matching) { + switch (endianness) { + case Header::ENDIAN_TEST: { + matching = true; + return true; + } + break; + case Header::REVERSE_ENDIAN_TEST: + { + matching = false; + return true; + } + break; + default: + return false; + } + } + + bool checkIdentifier(const Byte* identifier) { + return memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH); + } + + KeyValues getKeyValues(size_t length, const Byte* src) { + KeyValues keyValues; + size_t offset = 0; + + while (offset < length) { + // determine byte size + uint32_t keyValueByteSize; + memcpy(&keyValueByteSize, src, sizeof(uint32_t)); + if (keyValueByteSize > length - offset) { + throw Exception("invalid key-value size"); + } + + // find the first null character \0 + int keyLength = 0; + while (reinterpret_cast(src[++keyLength]) != '\0') { + if (keyLength == keyValueByteSize) { + // key must be null-terminated, and there must be space for the value + throw Exception("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); + } + } + + // populate the key-value + keyValues.emplace_back( + std::move(std::string(reinterpret_cast(src), keyLength)), + std::move(std::string(reinterpret_cast(src + keyLength), keyValueByteSize - keyLength))); + + // advance offset/src + uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % 4); + offset += keyValueByteSize + keyValuePadding; + src += keyValueByteSize + keyValuePadding; + } + + return keyValues; + } + + bool KTX::read(Storage* src) { + resetStorage(src); + + return true; + } + + bool KTX::checkStorageHeader(const Storage& src) { + try { + size_t srcSize = src.size(); + const uint8_t* srcBytes = src.data(); + + // validation + if (srcSize < sizeof(Header)) { + throw Exception("length is too short for header"); + } + const Header* header = reinterpret_cast(srcBytes); + + if (!checkIdentifier(header->identifier)) { + throw Exception("identifier field invalid"); + } + + bool endianMatch { true }; + if (!checkEndianness(header->endianness, endianMatch)) { + throw Exception("endianness field has invalid value"); + } + + // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary + + + // TODO: calculated bytesOfTexData + if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData)) { + throw Exception("length is too short for metadata"); + } + + size_t bytesOfTexData = 0; + if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData + bytesOfTexData)) { + + throw Exception("length is too short for data"); + } + + + // read metadata + KeyValues keyValues = getKeyValues(header->bytesOfKeyValueData, srcBytes + sizeof(Header)); + + // prepare gpu::Texture using header & key-values + // TODO + + // read data + // TODO + + return true; + } catch (Exception& e) { + qWarning(e.what()); + return false; + } + } + + KTX* KTX::create(const Storage* data) { + try { + if (!checkStorageHeader(*data)) { + + } + + auto result = new KTX(); + result->resetStorage(const_cast(data)); + + // read metadata + KeyValues keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + + return nullptr; + } catch (Exception& e) { + qWarning(e.what()); + return nullptr; + } + } +} \ No newline at end of file diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp new file mode 100644 index 0000000000..d502a4a29a --- /dev/null +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -0,0 +1,21 @@ +// +// Writer.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// 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 +// +#include "KTX.h" + + +namespace ktx { + /* size_t serialize(const gpu::Texture& texture, uint8_t* data) { + return 0; + }*/ + /* KTX serialize(const gpu::Texture& texture) { + return KTX(0, nullptr); + }*/ +} From 6888b0f09913f52e54b0765ad78da4a55e607a6a Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Feb 2017 16:24:06 -0800 Subject: [PATCH 02/10] DOne with the parsing side of things --- libraries/ktx/src/ktx/KTX.cpp | 29 +++++++++++- libraries/ktx/src/ktx/KTX.h | 49 +++++++++++++++------ libraries/ktx/src/ktx/Reader.cpp | 75 ++++++++++++++++++++++---------- 3 files changed, 114 insertions(+), 39 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 2d5e0bc812..4ce4c94c69 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -49,8 +49,8 @@ size_t Header::evalRowSize(uint32_t level) const { auto pixelWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); auto netSize = pixelWidth * pixSize; - auto packing = netSize % 4; - return netSize + (packing ? 4 - packing : 0); + auto packing = netSize % PACKING_SIZE; + return netSize + (packing ? PACKING_SIZE - packing : 0); } size_t Header::evalFaceSize(uint32_t level) const { auto pixelHeight = evalPixelHeight(level); @@ -83,6 +83,23 @@ const Header* KTX::getHeader() const { } } + +size_t KTX::getKeyValueDataSize() const { + if (_storage) { + return getHeader()->bytesOfKeyValueData; + } else { + return 0; + } +} + +size_t KTX::getTexelsDataSize() const { + if (_storage) { + return _storage->size() - sizeof(Header) + getKeyValueDataSize(); + } else { + return 0; + } +} + const Byte* KTX::getKeyValueData() const { if (_storage) { return (_storage->_bytes + sizeof(Header)); @@ -90,3 +107,11 @@ const Byte* KTX::getKeyValueData() const { return nullptr; } } + +const Byte* KTX::getTexelsData() const { + if (_storage) { + return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 003d3f0d1b..b6a9240972 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -66,6 +66,7 @@ end namespace ktx { + const uint32_t PACKING_SIZE { sizeof(uint32_t) }; enum GLType : uint32_t { COMPRESSED_TYPE = 0, @@ -317,6 +318,17 @@ namespace ktx { { if (_size && _bytes) { _bytes = bytes; } } + Storage(size_t size, const Byte* bytes) : + Storage(size) + { + if (_size && _bytes && bytes) { + memcpy(_bytes, bytes, size); + } + } + Storage(const Storage& src) : + Storage(src.size(), src.data()) + {} + }; // Header @@ -364,8 +376,14 @@ namespace ktx { struct Mip { - uint32_t imageSize; + uint32_t _imageSize; + uint32_t _padding; const Byte* _bytes; + + Mip(uint32_t imageSize, uint32_t padding, const Byte* bytes) : + _imageSize(imageSize), + _padding(padding), + _bytes(bytes) {} }; using Mips = std::vector; @@ -375,21 +393,26 @@ namespace ktx { public: KTX(); + ~KTX(); - bool read(const Storage* src); - bool read(Storage* src); - - std::unique_ptr _storage; - - const Header* getHeader() const; - const Byte* getKeyValueData() const; - - KeyValues _keyValues; - - Mips _mips; + // parse a block of memory and create a KTX object from it + static std::unique_ptr create(const Storage& src); + static std::unique_ptr create(std::unique_ptr& src); static bool checkStorageHeader(const Storage& storage); - static KTX* create(const Storage* src); + + // Access raw pointers to the main sections of the KTX + const Header* getHeader() const; + const Byte* getKeyValueData() const; + const Byte* getTexelsData() const; + + size_t getKeyValueDataSize() const; + size_t getTexelsDataSize() const; + + + std::unique_ptr _storage; + KeyValues _keyValues; + Mips _mips; }; } diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 8ec4de7686..ff682d5bdb 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -73,7 +73,7 @@ namespace ktx { std::move(std::string(reinterpret_cast(src + keyLength), keyValueByteSize - keyLength))); // advance offset/src - uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % 4); + uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % PACKING_SIZE); offset += keyValueByteSize + keyValuePadding; src += keyValueByteSize + keyValuePadding; } @@ -81,16 +81,39 @@ namespace ktx { return keyValues; } - bool KTX::read(Storage* src) { - resetStorage(src); + Mips getMipsTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { + Mips mips; + auto currentPtr = mipsData; + auto numMips = header.numberOfMipmapLevels + 1; - return true; + // Keep identifying new mip as long as we can at list query the next imageSize + while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { + + // Grab the imageSize coming up + auto imageSize = *reinterpret_cast(currentPtr); + currentPtr += sizeof(uint32_t); + + // If enough data ahead then capture the pointer + if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { + auto padding = imageSize % PACKING_SIZE; + padding = (padding ? 4 - padding : 0); + + mips.emplace_back(Mip(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + } else { + break; + } + } + + return mips; } + bool KTX::checkStorageHeader(const Storage& src) { try { - size_t srcSize = src.size(); - const uint8_t* srcBytes = src.data(); + auto srcSize = src.size(); + auto srcBytes = src.data(); // validation if (srcSize < sizeof(Header)) { @@ -121,16 +144,6 @@ namespace ktx { throw Exception("length is too short for data"); } - - // read metadata - KeyValues keyValues = getKeyValues(header->bytesOfKeyValueData, srcBytes + sizeof(Header)); - - // prepare gpu::Texture using header & key-values - // TODO - - // read data - // TODO - return true; } catch (Exception& e) { qWarning(e.what()); @@ -138,20 +151,34 @@ namespace ktx { } } - KTX* KTX::create(const Storage* data) { + std::unique_ptr KTX::create(const Storage& src) { + auto srcCopy = std::make_unique(src); + + return create(srcCopy); + } + + std::unique_ptr KTX::create(std::unique_ptr& src) { + if (!src) { + return nullptr; + } + try { - if (!checkStorageHeader(*data)) { - + if (!checkStorageHeader(*src)) { + } - auto result = new KTX(); - result->resetStorage(const_cast(data)); + std::unique_ptr result(new KTX()); + result->resetStorage(src.release()); // read metadata - KeyValues keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - return nullptr; - } catch (Exception& e) { + // populate mip table + result->_mips = getMipsTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + + return result; + } + catch (Exception& e) { qWarning(e.what()); return nullptr; } From 7343c2f90b97516109c3c72464761cd2bfec60ea Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Feb 2017 17:04:40 -0800 Subject: [PATCH 03/10] Starting to work on the writer side --- libraries/ktx/src/ktx/KTX.cpp | 10 +++++-- libraries/ktx/src/ktx/KTX.h | 23 ++++++++++------ libraries/ktx/src/ktx/Reader.cpp | 42 ++++++++++++++--------------- libraries/ktx/src/ktx/Writer.cpp | 46 +++++++++++++++++++++++++++----- 4 files changed, 84 insertions(+), 37 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 4ce4c94c69..d2872e76b5 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -15,6 +15,11 @@ using namespace ktx; +uint32_t evalPadding(size_t byteSize) { + auto padding = byteSize % PACKING_SIZE; + return (padding ? PACKING_SIZE - padding : 0); +} + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; @@ -49,8 +54,8 @@ size_t Header::evalRowSize(uint32_t level) const { auto pixelWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); auto netSize = pixelWidth * pixSize; - auto packing = netSize % PACKING_SIZE; - return netSize + (packing ? PACKING_SIZE - packing : 0); + auto padding = evalPadding(netSize); + return netSize + padding; } size_t Header::evalFaceSize(uint32_t level) const { auto pixelHeight = evalPixelHeight(level); @@ -115,3 +120,4 @@ const Byte* KTX::getTexelsData() const { return nullptr; } } + diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index b6a9240972..5b72c4c1ce 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -67,6 +67,8 @@ end namespace ktx { const uint32_t PACKING_SIZE { sizeof(uint32_t) }; + using Byte = uint8_t; + uint32_t evalPadding(size_t byteSize); enum GLType : uint32_t { COMPRESSED_TYPE = 0, @@ -289,7 +291,6 @@ namespace ktx { NUM_CUBEMAPFACES = 6, }; - using Byte = uint8_t; // Chunk of data struct Storage { @@ -375,31 +376,37 @@ namespace ktx { using KeyValues = std::list; - struct Mip { + struct Image { uint32_t _imageSize; uint32_t _padding; const Byte* _bytes; - Mip(uint32_t imageSize, uint32_t padding, const Byte* bytes) : + Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : _imageSize(imageSize), _padding(padding), _bytes(bytes) {} }; - using Mips = std::vector; + using Images = std::vector; + class KTX { void resetStorage(Storage* src); + KTX(); public: - KTX(); ~KTX(); - // parse a block of memory and create a KTX object from it + // Define a KTX object manually to write it somewhere (in a file on disk?) + // This path allocate the Storage where to store header, keyvalues and copy mips + // Then COPY all the data + static std::unique_ptr create(const Header& header, const KeyValues& keyValues, const Images& images); + + // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const Storage& src); static std::unique_ptr create(std::unique_ptr& src); - static bool checkStorageHeader(const Storage& storage); + static bool checkHeaderFromStorage(const Storage& storage); // Access raw pointers to the main sections of the KTX const Header* getHeader() const; @@ -412,7 +419,7 @@ namespace ktx { std::unique_ptr _storage; KeyValues _keyValues; - Mips _mips; + Images _images; }; } diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index ff682d5bdb..2b45d30786 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -14,9 +14,9 @@ #include namespace ktx { - class Exception: public std::exception { + class ReaderException: public std::exception { public: - Exception(std::string explanation) : _explanation(explanation) {} + ReaderException(std::string explanation) : _explanation(explanation) {} const char* what() const override { return ("KTX deserialization error: " + _explanation).c_str(); } @@ -55,7 +55,7 @@ namespace ktx { uint32_t keyValueByteSize; memcpy(&keyValueByteSize, src, sizeof(uint32_t)); if (keyValueByteSize > length - offset) { - throw Exception("invalid key-value size"); + throw ReaderException("invalid key-value size"); } // find the first null character \0 @@ -63,7 +63,7 @@ namespace ktx { while (reinterpret_cast(src[++keyLength]) != '\0') { if (keyLength == keyValueByteSize) { // key must be null-terminated, and there must be space for the value - throw Exception("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); + throw ReaderException("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); } } @@ -81,8 +81,8 @@ namespace ktx { return keyValues; } - Mips getMipsTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { - Mips mips; + Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { + Images images; auto currentPtr = mipsData; auto numMips = header.numberOfMipmapLevels + 1; @@ -95,10 +95,9 @@ namespace ktx { // If enough data ahead then capture the pointer if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = imageSize % PACKING_SIZE; - padding = (padding ? 4 - padding : 0); + auto padding = evalPadding(imageSize); - mips.emplace_back(Mip(imageSize, padding, currentPtr)); + images.emplace_back(Image(imageSize, padding, currentPtr)); currentPtr += imageSize + padding; } else { @@ -106,28 +105,28 @@ namespace ktx { } } - return mips; + return images; } - bool KTX::checkStorageHeader(const Storage& src) { + bool KTX::checkHeaderFromStorage(const Storage& src) { try { auto srcSize = src.size(); auto srcBytes = src.data(); // validation if (srcSize < sizeof(Header)) { - throw Exception("length is too short for header"); + throw ReaderException("length is too short for header"); } const Header* header = reinterpret_cast(srcBytes); if (!checkIdentifier(header->identifier)) { - throw Exception("identifier field invalid"); + throw ReaderException("identifier field invalid"); } bool endianMatch { true }; if (!checkEndianness(header->endianness, endianMatch)) { - throw Exception("endianness field has invalid value"); + throw ReaderException("endianness field has invalid value"); } // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary @@ -135,17 +134,18 @@ namespace ktx { // TODO: calculated bytesOfTexData if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData)) { - throw Exception("length is too short for metadata"); + throw ReaderException("length is too short for metadata"); } size_t bytesOfTexData = 0; if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData + bytesOfTexData)) { - throw Exception("length is too short for data"); + throw ReaderException("length is too short for data"); } return true; - } catch (Exception& e) { + } + catch (ReaderException& e) { qWarning(e.what()); return false; } @@ -163,7 +163,7 @@ namespace ktx { } try { - if (!checkStorageHeader(*src)) { + if (!checkHeaderFromStorage(*src)) { } @@ -173,12 +173,12 @@ namespace ktx { // read metadata result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - // populate mip table - result->_mips = getMipsTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + // populate image table + result->_images = getImagesTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); return result; } - catch (Exception& e) { + catch (ReaderException& e) { qWarning(e.what()); return nullptr; } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index d502a4a29a..a2b3d55178 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -12,10 +12,44 @@ namespace ktx { - /* size_t serialize(const gpu::Texture& texture, uint8_t* data) { - return 0; - }*/ - /* KTX serialize(const gpu::Texture& texture) { - return KTX(0, nullptr); - }*/ + + class WriterException : public std::exception { + public: + WriterException(std::string explanation) : _explanation(explanation) {} + const char* what() const override { + return ("KTX serialization error: " + _explanation).c_str(); + } + private: + std::string _explanation; + }; + + std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { + size_t storageSize = sizeof(Header); + auto numMips = header.numberOfMipmapLevels + 1; + + for (uint32_t l = 0; l < numMips; l++) { + if (images.size() > l) { + + storageSize += images[l]._imageSize; + storageSize += images[l]._imageSize; + } + + } + + } + + std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { + + std::unique_ptr result(new KTX()); + result->resetStorage(generateStorage(header, keyValues, images).release()); + + // read metadata + result->_keyValues = keyValues; + + // populate image table + result->_images = images; + + return result; + } + } From 0d28d17e40f7ef1e6c72d432e0ce1f5b66280e1e Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 02:45:53 -0800 Subject: [PATCH 04/10] Maybe saving the first ktx textures, testing the save pipeline --- interface/CMakeLists.txt | 2 +- libraries/gpu/src/gpu/Texture.cpp | 25 +++++++++ libraries/gpu/src/gpu/Texture.h | 8 +++ libraries/ktx/src/ktx/KTX.cpp | 38 ++++++++----- libraries/ktx/src/ktx/KTX.h | 71 ++++++++++++++++-------- libraries/ktx/src/ktx/Reader.cpp | 6 +- libraries/ktx/src/ktx/Writer.cpp | 55 ++++++++++++++++-- libraries/model/CMakeLists.txt | 2 +- libraries/model/src/model/TextureMap.cpp | 17 ++++++ libraries/render-utils/CMakeLists.txt | 2 +- libraries/render/CMakeLists.txt | 2 +- 11 files changed, 181 insertions(+), 47 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 868a2cf933..03b7a6b7e9 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -178,7 +178,7 @@ endif() # link required hifi libraries link_hifi_libraries( - shared octree gpu gl gpu-gl procedural model render + shared octree ktx gpu gl gpu-gl procedural model render recording fbx networking model-networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer ui auto-updater diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 5b0c4c876a..80d636eec2 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -1001,3 +1001,28 @@ Texture::ExternalUpdates Texture::getUpdates() const { } return result; } + +#include + +ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { + + ktx::Header header; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + header.pixelWidth = texture.getWidth(); + header.pixelHeight = texture.getHeight(); + header.numberOfMipmapLevels = texture.mipLevels(); + + ktx::Images images; + for (int level = 0; level < header.numberOfMipmapLevels; level++) { + auto mip = texture.accessStoredMipFace(level); + if (mip) { + images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); + } + } + + auto ktxBuffer = ktx::KTX::create(header, ktx::KeyValues(), images); + return ktxBuffer; +} +TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + return nullptr; +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 856bd4983d..62966b58ae 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -20,6 +20,11 @@ #include "Forward.h" #include "Resource.h" +namespace ktx { + class KTX; + using KTXUniquePointer = std::unique_ptr; +} + namespace gpu { // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated @@ -475,6 +480,9 @@ public: ExternalUpdates getUpdates() const; + static ktx::KTXUniquePointer serialize(const Texture& texture); + static TexturePointer unserialize(const ktx::KTXUniquePointer& srcData); + protected: // Should only be accessed internally or by the backend sync function mutable Mutex _externalMutex; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index d2872e76b5..f8f315d784 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -15,11 +15,12 @@ using namespace ktx; -uint32_t evalPadding(size_t byteSize) { +uint32_t Header::evalPadding(size_t byteSize) { auto padding = byteSize % PACKING_SIZE; - return (padding ? PACKING_SIZE - padding : 0); + return (uint32_t) (padding ? PACKING_SIZE - padding : 0); } + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; @@ -29,7 +30,7 @@ Header::Header() { } uint32_t Header::evalMaxDimension() const { - return std::max(pixelWidth, std::max(pixelHeight, pixelDepth)); + return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); } uint32_t Header::evalMaxLevel() const { @@ -37,13 +38,13 @@ uint32_t Header::evalMaxLevel() const { } uint32_t Header::evalPixelWidth(uint32_t level) const { - return std::max(pixelWidth >> level, 1U); + return std::max(getPixelWidth() >> level, 1U); } uint32_t Header::evalPixelHeight(uint32_t level) const { - return std::max(pixelHeight >> level, 1U); + return std::max(getPixelHeight() >> level, 1U); } uint32_t Header::evalPixelDepth(uint32_t level) const { - return std::max(pixelDepth >> level, 1U); + return std::max(getPixelDepth() >> level, 1U); } size_t Header::evalPixelSize() const { @@ -51,24 +52,24 @@ size_t Header::evalPixelSize() const { } size_t Header::evalRowSize(uint32_t level) const { - auto pixelWidth = evalPixelWidth(level); + auto pixWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); - auto netSize = pixelWidth * pixSize; + auto netSize = pixWidth * pixSize; auto padding = evalPadding(netSize); return netSize + padding; } size_t Header::evalFaceSize(uint32_t level) const { - auto pixelHeight = evalPixelHeight(level); - auto pixelDepth = evalPixelDepth(level); + auto pixHeight = evalPixelHeight(level); + auto pixDepth = evalPixelDepth(level); auto rowSize = evalRowSize(level); - return pixelDepth * pixelHeight * rowSize; + return pixDepth * pixHeight * rowSize; } size_t Header::evalImageSize(uint32_t level) const { auto faceSize = evalFaceSize(level); if (numberOfFaces == 6 && numberOfArrayElements == 0) { return faceSize; } else { - return (numberOfArrayElements * numberOfFaces * faceSize); + return (getNumberOfSlices() * numberOfFaces * faceSize); } } @@ -76,6 +77,9 @@ size_t Header::evalImageSize(uint32_t level) const { KTX::KTX() { } +KTX::~KTX() { +} + void KTX::resetStorage(Storage* storage) { _storage.reset(storage); } @@ -99,7 +103,8 @@ size_t KTX::getKeyValueDataSize() const { size_t KTX::getTexelsDataSize() const { if (_storage) { - return _storage->size() - sizeof(Header) + getKeyValueDataSize(); + //return _storage->size() - (sizeof(Header) + getKeyValueDataSize()); + return (_storage->_bytes + _storage->_size) - getTexelsData(); } else { return 0; } @@ -121,3 +126,10 @@ const Byte* KTX::getTexelsData() const { } } +Byte* KTX::getTexelsData() { + if (_storage) { + return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 5b72c4c1ce..1006124693 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -68,9 +68,8 @@ end namespace ktx { const uint32_t PACKING_SIZE { sizeof(uint32_t) }; using Byte = uint8_t; - uint32_t evalPadding(size_t byteSize); - enum GLType : uint32_t { + enum class GLType : uint32_t { COMPRESSED_TYPE = 0, // GL 4.4 Table 8.2 @@ -102,7 +101,7 @@ namespace ktx { NUM_GLTYPES = 25, }; - enum GLFormat : uint32_t { + enum class GLFormat : uint32_t { COMPRESSED_FORMAT = 0, // GL 4.4 Table 8.3 @@ -131,7 +130,7 @@ namespace ktx { NUM_GLFORMATS = 20, }; - enum GLInternalFormat_Uncompressed : uint32_t { + enum class GLInternalFormat_Uncompressed : uint32_t { // GL 4.4 Table 8.12 R8 = 0x8229, R8_SNORM = 0x8F94, @@ -233,7 +232,7 @@ namespace ktx { NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74, }; - enum GLInternalFormat_Compressed : uint32_t { + enum class GLInternalFormat_Compressed : uint32_t { // GL 4.4 Table 8.14 COMPRESSED_RED = 0x8225, COMPRESSED_RG = 0x8226, @@ -268,15 +267,15 @@ namespace ktx { NUM_COMPRESSED_GLINTERNALFORMATS = 24, }; - enum GLBaseInternalFormat : uint32_t { + enum class GLBaseInternalFormat : uint32_t { // GL 4.4 Table 8.11 - BIF_DEPTH_COMPONENT = 0x1902, - BIF_DEPTH_STENCIL = 0x84F9, - BIF_RED = 0x1903, - BIF_RG = 0x8227, - BIF_RGB = 0x1907, - BIF_RGBA = 0x1908, - BIF_STENCIL_INDEX = 0x1901, + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, + RED = 0x1903, + RG = 0x8227, + RGB = 0x1907, + RGBA = 0x1908, + STENCIL_INDEX = 0x1901, NUM_GLBASEINTERNALFORMATS = 7, }; @@ -341,22 +340,33 @@ namespace ktx { static const uint32_t ENDIAN_TEST = 0x04030201; static const uint32_t REVERSE_ENDIAN_TEST = 0x01020304; + static uint32_t evalPadding(size_t byteSize); + Header(); Byte identifier[IDENTIFIER_LENGTH]; uint32_t endianness { ENDIAN_TEST }; + uint32_t glType; - uint32_t glTypeSize; + uint32_t glTypeSize { 0 }; uint32_t glFormat; uint32_t glInternalFormat; uint32_t glBaseInternalFormat; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t numberOfArrayElements; - uint32_t numberOfFaces; - uint32_t numberOfMipmapLevels; - uint32_t bytesOfKeyValueData; + + uint32_t pixelWidth { 0 }; + uint32_t pixelHeight { 0 }; + uint32_t pixelDepth { 0 }; + uint32_t numberOfArrayElements { 0 }; + uint32_t numberOfFaces { 1 }; + uint32_t numberOfMipmapLevels { 1 }; + + uint32_t bytesOfKeyValueData { 0 }; + + uint32_t getPixelWidth() const { return (pixelWidth ? pixelWidth : 1); } + uint32_t getPixelHeight() const { return (pixelHeight ? pixelHeight : 1); } + uint32_t getPixelDepth() const { return (pixelDepth ? pixelDepth : 1); } + uint32_t getNumberOfSlices() const { return (numberOfArrayElements ? numberOfArrayElements : 1); } + uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } uint32_t evalMaxDimension() const; uint32_t evalMaxLevel() const; @@ -369,6 +379,20 @@ namespace ktx { size_t evalFaceSize(uint32_t level) const; size_t evalImageSize(uint32_t level) const; + void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat_Uncompressed internalFormat, GLBaseInternalFormat baseInternalFormat) { + glType = (uint32_t) type; + glTypeSize = 0; + glFormat = (uint32_t) format; + glInternalFormat = (uint32_t) internalFormat; + glBaseInternalFormat = (uint32_t) baseInternalFormat; + } + void setCompressed(GLInternalFormat_Compressed internalFormat, GLBaseInternalFormat baseInternalFormat) { + glType = (uint32_t) GLType::COMPRESSED_TYPE; + glTypeSize = 1; + glFormat = (uint32_t) GLFormat::COMPRESSED_FORMAT; + glInternalFormat = (uint32_t) internalFormat; + glBaseInternalFormat = (uint32_t) baseInternalFormat; + } }; // Key Values @@ -392,6 +416,9 @@ namespace ktx { class KTX { void resetStorage(Storage* src); + void resetHeader(const Header& header); + void resetImages(const Images& images); + KTX(); public: @@ -412,11 +439,11 @@ namespace ktx { const Header* getHeader() const; const Byte* getKeyValueData() const; const Byte* getTexelsData() const; + Byte* getTexelsData(); size_t getKeyValueDataSize() const; size_t getTexelsDataSize() const; - std::unique_ptr _storage; KeyValues _keyValues; Images _images; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 2b45d30786..9586a9c033 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -84,18 +84,18 @@ namespace ktx { Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { Images images; auto currentPtr = mipsData; - auto numMips = header.numberOfMipmapLevels + 1; + auto numMips = header.getNumberOfLevels(); // Keep identifying new mip as long as we can at list query the next imageSize while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { // Grab the imageSize coming up - auto imageSize = *reinterpret_cast(currentPtr); + size_t imageSize = *reinterpret_cast(currentPtr); currentPtr += sizeof(uint32_t); // If enough data ahead then capture the pointer if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = evalPadding(imageSize); + auto padding = Header::evalPadding(imageSize); images.emplace_back(Image(imageSize, padding, currentPtr)); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index a2b3d55178..ae2dfc2d6d 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -25,17 +25,60 @@ namespace ktx { std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { size_t storageSize = sizeof(Header); - auto numMips = header.numberOfMipmapLevels + 1; + auto numMips = header.getNumberOfLevels(); for (uint32_t l = 0; l < numMips; l++) { if (images.size() > l) { - - storageSize += images[l]._imageSize; + storageSize += sizeof(uint32_t); storageSize += images[l]._imageSize; + storageSize += Header::evalPadding(images[l]._imageSize); } - } + std::unique_ptr storage(new Storage(storageSize)); + return storage; + } + + + void KTX::resetHeader(const Header& header) { + if (!_storage) { + return; + } + memcpy(_storage->_bytes, &header, sizeof(Header)); + } + void KTX::resetImages(const Images& srcImages) { + auto imagesDataPtr = getTexelsData(); + if (!imagesDataPtr) { + return; + } + auto allocatedImagesDataSize = getTexelsDataSize(); + size_t currentDataSize = 0; + auto currentPtr = imagesDataPtr; + + _images.clear(); + + + for (uint32_t l = 0; l < srcImages.size(); l++) { + if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { + size_t imageSize = srcImages[l]._imageSize; + *(reinterpret_cast (currentPtr)) = imageSize; + currentPtr += sizeof(uint32_t); + currentDataSize += sizeof(uint32_t); + + // If enough data ahead then capture the copy source pointer + if (currentDataSize + imageSize <= (allocatedImagesDataSize)) { + + auto copied = memcpy(currentPtr, srcImages[l]._bytes, imageSize); + + auto padding = Header::evalPadding(imageSize); + + _images.emplace_back(Image(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + currentDataSize += imageSize + padding; + } + } + } } std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { @@ -43,11 +86,13 @@ namespace ktx { std::unique_ptr result(new KTX()); result->resetStorage(generateStorage(header, keyValues, images).release()); + result->resetHeader(header); + // read metadata result->_keyValues = keyValues; // populate image table - result->_images = images; + result->resetImages(images); return result; } diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 63f632e484..021aa3d027 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME model) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared gpu) +link_hifi_libraries(shared ktx gpu) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d1fbaf767a..42f908c754 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -10,6 +10,8 @@ // #include "TextureMap.h" +#include + #include #include #include @@ -264,8 +266,23 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { ::generateMips(theTexture, image, formatMip, false); } + auto theKTX = Texture::serialize(*theTexture); + if (theKTX) { + // save that! + std::string filename("C://temp//ktxCache//texmex"); + filename += std::to_string((size_t) theTexture); + filename += ".ktx"; + + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } + + } } + return theTexture; } diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index ecafb8f565..3bf389973a 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared gpu model model-networking render animation fbx entities) +link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities) if (NOT ANDROID) target_nsight() diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 735bb7f086..8fd05bd320 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -3,6 +3,6 @@ AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() # render needs octree only for getAccuracyAngle(float, int) -link_hifi_libraries(shared gpu model octree) +link_hifi_libraries(shared ktx gpu model octree) target_nsight() From 2f7181fb322407d6b9fe0f7cb84c684a0036cf97 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 16 Feb 2017 13:27:16 -0800 Subject: [PATCH 05/10] Address review comments --- libraries/gpu/src/gpu/Texture.cpp | 24 ----- libraries/gpu/src/gpu/Texture_ktx.cpp | 42 ++++++++ libraries/ktx/src/ktx/KTX.h | 17 +++- libraries/ktx/src/ktx/Reader.cpp | 6 +- libraries/ktx/src/ktx/Writer.cpp | 118 ++++++++++++++++------- libraries/model/src/model/TextureMap.cpp | 15 ++- 6 files changed, 156 insertions(+), 66 deletions(-) create mode 100644 libraries/gpu/src/gpu/Texture_ktx.cpp diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 80d636eec2..d7ae202e7d 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -1002,27 +1002,3 @@ Texture::ExternalUpdates Texture::getUpdates() const { return result; } -#include - -ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { - - ktx::Header header; - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - header.pixelWidth = texture.getWidth(); - header.pixelHeight = texture.getHeight(); - header.numberOfMipmapLevels = texture.mipLevels(); - - ktx::Images images; - for (int level = 0; level < header.numberOfMipmapLevels; level++) { - auto mip = texture.accessStoredMipFace(level); - if (mip) { - images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); - } - } - - auto ktxBuffer = ktx::KTX::create(header, ktx::KeyValues(), images); - return ktxBuffer; -} -TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { - return nullptr; -} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp new file mode 100644 index 0000000000..d06454b933 --- /dev/null +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -0,0 +1,42 @@ +// +// Texture_ktx.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 2/16/2017. +// Copyright 2014 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 +// + + +#include "Texture.h" + +#include +using namespace gpu; + +ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { + + ktx::Header header; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + header.pixelWidth = texture.getWidth(); + header.pixelHeight = texture.getHeight(); + header.numberOfMipmapLevels = texture.mipLevels(); + + ktx::Images images; + for (int level = 0; level < header.numberOfMipmapLevels; level++) { + auto mip = texture.accessStoredMipFace(level); + if (mip) { + images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); + } + } + + auto ktxBuffer = ktx::KTX::create(header, images); + return ktxBuffer; +} +TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + + const auto& header = *srcData->getHeader(); + + return nullptr; +} \ No newline at end of file diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 1006124693..41032c8222 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -427,7 +427,22 @@ namespace ktx { // Define a KTX object manually to write it somewhere (in a file on disk?) // This path allocate the Storage where to store header, keyvalues and copy mips // Then COPY all the data - static std::unique_ptr create(const Header& header, const KeyValues& keyValues, const Images& images); + static std::unique_ptr create(const Header& header, const Images& images, 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 + // size_t sizeNeeded = KTX::evalStorageSize(header, images); + // + // //allocate a buffer of size "sizeNeeded" or map a file with enough capacity + // Byte* destBytes = new Byte[sizeNeeded]; + // + // // THen perform the writing of the src data to the destinnation buffer + // write(destBytes, sizeNeeded, header, images); + // + // 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 write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const Storage& src); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 9586a9c033..4099aa6ef8 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -16,12 +16,12 @@ namespace ktx { class ReaderException: public std::exception { public: - ReaderException(std::string explanation) : _explanation(explanation) {} + ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} const char* what() const override { - return ("KTX deserialization error: " + _explanation).c_str(); + return _explanation.c_str(); } private: - std::string _explanation; + const std::string _explanation; }; bool checkEndianness(uint32_t endianness, bool& matching) { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index ae2dfc2d6d..75836ad5b5 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -15,30 +15,14 @@ namespace ktx { class WriterException : public std::exception { public: - WriterException(std::string explanation) : _explanation(explanation) {} + WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} const char* what() const override { - return ("KTX serialization error: " + _explanation).c_str(); + return _explanation.c_str(); } private: - std::string _explanation; + const std::string _explanation; }; - std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { - size_t storageSize = sizeof(Header); - auto numMips = header.getNumberOfLevels(); - - for (uint32_t l = 0; l < numMips; l++) { - if (images.size() > l) { - storageSize += sizeof(uint32_t); - storageSize += images[l]._imageSize; - storageSize += Header::evalPadding(images[l]._imageSize); - } - } - - std::unique_ptr storage(new Storage(storageSize)); - return storage; - } - void KTX::resetHeader(const Header& header) { if (!_storage) { @@ -52,10 +36,84 @@ namespace ktx { return; } auto allocatedImagesDataSize = getTexelsDataSize(); + + // Just copy in our storage + _images = writeImages(imagesDataPtr, allocatedImagesDataSize, srcImages); + } + + std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { + auto storageSize = evalStorageSize(header, images, keyValues); + + std::unique_ptr result(new KTX()); + + result->resetStorage(new Storage(storageSize)); + + result->resetHeader(header); + + // read metadata + result->_keyValues = keyValues; + + // populate image table + result->resetImages(images); + + return result; + } + + size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { + size_t storageSize = sizeof(Header); + + if (header.bytesOfKeyValueData && !keyValues.empty()) { + + } + + auto numMips = header.getNumberOfLevels(); + for (uint32_t l = 0; l < numMips; l++) { + if (images.size() > l) { + storageSize += sizeof(uint32_t); + storageSize += images[l]._imageSize; + storageSize += Header::evalPadding(images[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))) { + return 0; + } + + auto currentDestPtr = destBytes; + // Header + auto destHeader = reinterpret_cast(currentDestPtr); + memcpy(currentDestPtr, &header, sizeof(Header)); + currentDestPtr += sizeof(Header); + + // KeyValues + // Skip for now + if (header.bytesOfKeyValueData && !keyValues.empty()) { + + } + destHeader->bytesOfKeyValueData = 0; + currentDestPtr += destHeader->bytesOfKeyValueData; + + // Images + auto destImages = writeImages(currentDestPtr, destByteSize - sizeof(Header) - destHeader->bytesOfKeyValueData, srcImages); + // We chould check here that the amoutn of dest IMages generated is the same as the source + + return destByteSize; + } + + Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { + Images destImages; + auto imagesDataPtr = destBytes; + if (!imagesDataPtr) { + return destImages; + } + auto allocatedImagesDataSize = destByteSize; size_t currentDataSize = 0; auto currentPtr = imagesDataPtr; - _images.clear(); for (uint32_t l = 0; l < srcImages.size(); l++) { @@ -72,29 +130,15 @@ namespace ktx { auto padding = Header::evalPadding(imageSize); - _images.emplace_back(Image(imageSize, padding, currentPtr)); + destImages.emplace_back(Image(imageSize, padding, currentPtr)); currentPtr += imageSize + padding; currentDataSize += imageSize + padding; } } } - } - - std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { - - std::unique_ptr result(new KTX()); - result->resetStorage(generateStorage(header, keyValues, images).release()); - - result->resetHeader(header); - - // read metadata - result->_keyValues = keyValues; - - // populate image table - result->resetImages(images); - - return result; + + return destImages; } } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 42f908c754..37c35ef250 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include @@ -269,7 +272,17 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag auto theKTX = Texture::serialize(*theTexture); if (theKTX) { // save that! - std::string filename("C://temp//ktxCache//texmex"); + QString path("hifi_ktx/"); + QFileInfo originalFileInfo(path); + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + path = docsLocation + "/" + path; + QFileInfo info(path); + if (!info.absoluteDir().exists()) { + QString originalRelativePath = originalFileInfo.path(); + QDir(docsLocation).mkpath(originalRelativePath); + } + + std::string filename(path.toStdString()); filename += std::to_string((size_t) theTexture); filename += ".ktx"; From 8ee5defc6086a2cd2165b8e96672f489f965f40e Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 16:03:55 -0800 Subject: [PATCH 06/10] Adding the reading path --- libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 16 ++++++++++-- libraries/model/src/model/TextureMap.cpp | 32 +++++++++++++++++++++--- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 62966b58ae..b99e9da371 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -481,7 +481,7 @@ public: ExternalUpdates getUpdates() const; static ktx::KTXUniquePointer serialize(const Texture& texture); - static TexturePointer unserialize(const ktx::KTXUniquePointer& srcData); + static Texture* unserialize(const ktx::KTXUniquePointer& srcData); protected: // Should only be accessed internally or by the backend sync function diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d06454b933..6380b23903 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -34,9 +34,21 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto ktxBuffer = ktx::KTX::create(header, images); return ktxBuffer; } -TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { +Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + if (!srcData) { + return nullptr; + } const auto& header = *srcData->getHeader(); - return nullptr; + Format pixelFormat = Format::COLOR_RGBA_32; + + auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); + uint16_t level = 0; + for (auto& image : srcData->_images) { + tex->assignStoredMip(level, pixelFormat, image._imageSize, image._bytes); + level++; + } + + return tex; } \ No newline at end of file diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 37c35ef250..0416c22d5e 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -286,12 +286,36 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag filename += std::to_string((size_t) theTexture); filename += ".ktx"; - FILE* file = fopen (filename.c_str(),"wb"); - if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); + { + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } } + { + FILE* file = fopen (filename.c_str(),"rb"); + if (file != nullptr) { + // obtain file size: + fseek (file , 0 , SEEK_END); + auto size = ftell(file); + rewind(file); + + std::unique_ptr storage(new ktx::Storage(size)); + fread(storage->_bytes, 1, storage->_size, file); + fclose (file); + + //then create a new texture out of the ktx + auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); + + if (theNewTexure) { + auto srcTexture = theTexture; + theTexture = theNewTexure; + delete srcTexture; + } + } + } } } From 2ee3f05713722d134b820d7cd69854c35df872b2 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 17:28:02 -0800 Subject: [PATCH 07/10] CLeaning the read case --- libraries/gpu/src/gpu/Format.cpp | 4 + libraries/gpu/src/gpu/Format.h | 2 + libraries/gpu/src/gpu/Texture_ktx.cpp | 7 +- libraries/ktx/src/ktx/KTX.h | 4 +- libraries/ktx/src/ktx/Reader.cpp | 105 +++++++++++--------------- 5 files changed, 57 insertions(+), 65 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 2a8185bf94..0f5e85c907 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -12,6 +12,10 @@ using namespace gpu; const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; + +const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA }; +const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA }; + const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 13809a41e6..c7dc2eed8d 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -229,6 +229,8 @@ public: static const Element COLOR_RGBA_32; static const Element COLOR_SRGBA_32; + static const Element COLOR_BGRA_32; + static const Element COLOR_SBGRA_32; static const Element COLOR_R11G11B10; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 6380b23903..623a3493da 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -24,7 +24,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { header.numberOfMipmapLevels = texture.mipLevels(); ktx::Images images; - for (int level = 0; level < header.numberOfMipmapLevels; level++) { + for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { auto mip = texture.accessStoredMipFace(level); if (mip) { images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); @@ -41,12 +41,13 @@ Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { const auto& header = *srcData->getHeader(); - Format pixelFormat = Format::COLOR_RGBA_32; + Format mipFormat = Format::COLOR_SBGRA_32; + Format pixelFormat = Format::COLOR_SRGBA_32; auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); uint16_t level = 0; for (auto& image : srcData->_images) { - tex->assignStoredMip(level, pixelFormat, image._imageSize, image._bytes); + tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); level++; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 41032c8222..3a4c19971a 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -445,10 +445,10 @@ namespace ktx { static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it - static std::unique_ptr create(const Storage& src); static std::unique_ptr create(std::unique_ptr& src); - static bool checkHeaderFromStorage(const Storage& storage); + static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); + static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); // Access raw pointers to the main sections of the KTX const Header* getHeader() const; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 4099aa6ef8..00c0c4e19a 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -38,12 +38,17 @@ namespace ktx { } break; default: + throw ReaderException("endianness field has invalid value"); return false; } } bool checkIdentifier(const Byte* identifier) { - return memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH); + if (!(0 == memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH))) { + throw ReaderException("identifier field invalid"); + return false; + } + return true; } KeyValues getKeyValues(size_t length, const Byte* src) { @@ -81,53 +86,18 @@ namespace ktx { return keyValues; } - Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { - Images images; - auto currentPtr = mipsData; - auto numMips = header.getNumberOfLevels(); - - // Keep identifying new mip as long as we can at list query the next imageSize - while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { - - // Grab the imageSize coming up - size_t imageSize = *reinterpret_cast(currentPtr); - currentPtr += sizeof(uint32_t); - - // If enough data ahead then capture the pointer - if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = Header::evalPadding(imageSize); - - images.emplace_back(Image(imageSize, padding, currentPtr)); - - currentPtr += imageSize + padding; - } else { - break; - } - } - - return images; - } - - - bool KTX::checkHeaderFromStorage(const Storage& src) { + bool KTX::checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes) { try { - auto srcSize = src.size(); - auto srcBytes = src.data(); - // validation if (srcSize < sizeof(Header)) { throw ReaderException("length is too short for header"); } const Header* header = reinterpret_cast(srcBytes); - if (!checkIdentifier(header->identifier)) { - throw ReaderException("identifier field invalid"); - } + checkIdentifier(header->identifier); bool endianMatch { true }; - if (!checkEndianness(header->endianness, endianMatch)) { - throw ReaderException("endianness field has invalid value"); - } + checkEndianness(header->endianness, endianMatch); // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary @@ -151,10 +121,31 @@ namespace ktx { } } - std::unique_ptr KTX::create(const Storage& src) { - auto srcCopy = std::make_unique(src); + Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { + Images images; + auto currentPtr = srcBytes; + auto numMips = header.getNumberOfLevels(); - return create(srcCopy); + // Keep identifying new mip as long as we can at list query the next imageSize + while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) { + + // Grab the imageSize coming up + size_t imageSize = *reinterpret_cast(currentPtr); + currentPtr += sizeof(uint32_t); + + // If enough data ahead then capture the pointer + if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { + auto padding = Header::evalPadding(imageSize); + + images.emplace_back(Image(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + } else { + break; + } + } + + return images; } std::unique_ptr KTX::create(std::unique_ptr& src) { @@ -162,25 +153,19 @@ namespace ktx { return nullptr; } - try { - if (!checkHeaderFromStorage(*src)) { - - } - - std::unique_ptr result(new KTX()); - result->resetStorage(src.release()); - - // read metadata - result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - - // populate image table - result->_images = getImagesTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); - - return result; - } - catch (ReaderException& e) { - qWarning(e.what()); + if (!checkHeaderFromStorage(src->size(), src->data())) { return nullptr; } + + std::unique_ptr result(new KTX()); + result->resetStorage(src.release()); + + // read metadata + // result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + + // populate image table + result->_images = parseImages(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + + return result; } } \ No newline at end of file From 56d11206541c93fef18eecd9fd4ce4d9e1fc28d3 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 17 Feb 2017 01:15:27 -0800 Subject: [PATCH 08/10] progressing on io with ktx --- libraries/gpu/src/gpu/Texture.cpp | 20 ++-- libraries/gpu/src/gpu/Texture.h | 17 ++-- libraries/gpu/src/gpu/Texture_ktx.cpp | 84 ++++++++++++++-- libraries/ktx/src/ktx/KTX.h | 19 +++- libraries/model/src/model/TextureMap.cpp | 120 +++++++++++++---------- 5 files changed, 183 insertions(+), 77 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index d7ae202e7d..ba7723001a 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -264,19 +264,19 @@ Texture* Texture::createExternal2D(const ExternalRecycler& recycler, const Sampl } Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler); + return create(TEX_1D, texelFormat, width, 1, 1, 1, 0, sampler); } Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); } Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler) { - return create(TEX_3D, texelFormat, width, height, depth, 1, 1, sampler); + return create(TEX_3D, texelFormat, width, height, depth, 1, 0, sampler); } Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler); + return create(TEX_CUBE, texelFormat, width, width, 1, 1, 0, sampler); } Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) @@ -321,7 +321,7 @@ Texture::~Texture() } Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) { - if (width && height && depth && numSamples && numSlices) { + if (width && height && depth && numSamples) { bool changed = false; if ( _type != type) { @@ -382,20 +382,20 @@ Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 widt } Texture::Size Texture::resize1D(uint16 width, uint16 numSamples) { - return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 1); + return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 0); } Texture::Size Texture::resize2D(uint16 width, uint16 height, uint16 numSamples) { - return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 1); + return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 0); } Texture::Size Texture::resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples) { - return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 1); + return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 0); } Texture::Size Texture::resizeCube(uint16 width, uint16 numSamples) { - return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 1); + return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 0); } Texture::Size Texture::reformat(const Element& texelFormat) { - return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), getNumSlices()); + return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), _numSlices); } bool Texture::isColorRenderTarget() const { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index b99e9da371..fb8d94150d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -352,7 +352,12 @@ public: uint32 getNumTexels() const { return _width * _height * _depth * getNumFaces(); } - uint16 getNumSlices() const { return _numSlices; } + // The texture is an array if the _numSlices is not 0. + // otherwise, if _numSLices is 0, then the texture is NOT an array + // The number of slices returned is 1 at the minimum (if not an array) or the actual _numSlices. + bool isArray() const { return _numSlices > 0; } + uint16 getNumSlices() const { return (isArray() ? _numSlices : 1); } + uint16 getNumSamples() const { return _numSamples; } @@ -502,12 +507,12 @@ protected: uint32 _size = 0; Element _texelFormat; - uint16 _width = 1; - uint16 _height = 1; - uint16 _depth = 1; + uint16 _width { 1 }; + uint16 _height { 1 }; + uint16 _depth { 1 }; - uint16 _numSamples = 1; - uint16 _numSlices = 1; + uint16 _numSamples { 1 }; + uint16 _numSlices { 0 }; // if _numSlices is 0, the texture is not an "Array", the getNumSlices reported is 1 uint16 _maxMip { 0 }; uint16 _minMip { 0 }; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 623a3493da..c36c0f14d8 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -16,12 +16,57 @@ using namespace gpu; ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { - ktx::Header header; + + // From texture format to ktx format description + auto texelFormat = texture.getTexelFormat(); + if ( !( (texelFormat == Format::COLOR_RGBA_32) + || (texelFormat == Format::COLOR_SRGBA_32) + )) + return nullptr; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - header.pixelWidth = texture.getWidth(); - header.pixelHeight = texture.getHeight(); - header.numberOfMipmapLevels = texture.mipLevels(); + + // Set Dimensions + switch (texture.getType()) { + case TEX_1D: { + if (texture.isArray()) { + header.set1DArray(texture.getWidth(), texture.getNumSlices()); + } else { + header.set1D(texture.getWidth()); + } + break; + } + case TEX_2D: { + if (texture.isArray()) { + header.set2DArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); + } else { + header.set2D(texture.getWidth(), texture.getHeight()); + } + break; + } + case TEX_3D: { + if (texture.isArray()) { + header.set3DArray(texture.getWidth(), texture.getHeight(), texture.getDepth(), texture.getNumSlices()); + } else { + header.set3D(texture.getWidth(), texture.getHeight(), texture.getDepth()); + } + break; + } + case TEX_CUBE: { + if (texture.isArray()) { + header.setCubeArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); + } else { + header.setCube(texture.getWidth(), texture.getHeight()); + } + break; + } + default: + return nullptr; + } + + // Number level of mips coming + header.numberOfMipmapLevels = texture.maxMip(); ktx::Images images; for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { @@ -34,17 +79,42 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto ktxBuffer = ktx::KTX::create(header, images); return ktxBuffer; } + Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { if (!srcData) { return nullptr; } - const auto& header = *srcData->getHeader(); Format mipFormat = Format::COLOR_SBGRA_32; - Format pixelFormat = Format::COLOR_SRGBA_32; + Format texelFormat = Format::COLOR_SRGBA_32; - auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); + // Find Texture Type based on dimensions + Type type = TEX_1D; + if (header.pixelWidth == 0) { + return nullptr; + } else if (header.pixelHeight == 0) { + type = TEX_1D; + } else if (header.pixelDepth == 0) { + if (header.numberOfFaces == ktx::NUM_CUBEMAPFACES) { + type = TEX_CUBE; + } else { + type = TEX_2D; + } + } else { + type = TEX_3D; + } + + auto tex = Texture::create( type, + texelFormat, + header.getPixelWidth(), + header.getPixelHeight(), + header.getPixelDepth(), + 1, // num Samples + header.getNumberOfSlices(), + Sampler()); + + // Assing the mips availables uint16_t level = 0; for (auto& image : srcData->_images) { tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 3a4c19971a..8e79d90dc0 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -353,7 +353,7 @@ namespace ktx { uint32_t glInternalFormat; uint32_t glBaseInternalFormat; - uint32_t pixelWidth { 0 }; + uint32_t pixelWidth { 1 }; uint32_t pixelHeight { 0 }; uint32_t pixelDepth { 0 }; uint32_t numberOfArrayElements { 0 }; @@ -393,6 +393,23 @@ namespace ktx { glInternalFormat = (uint32_t) internalFormat; glBaseInternalFormat = (uint32_t) baseInternalFormat; } + + void setDimensions(uint32_t width, uint32_t height = 0, uint32_t depth = 0, uint32_t numSlices = 0, uint32_t numFaces = 1) { + pixelWidth = (width > 0 ? width : 1); + pixelHeight = height; + pixelDepth = depth; + numberOfArrayElements = numSlices; + numberOfFaces = ((numFaces == 1) || (numFaces == NUM_CUBEMAPFACES) ? numFaces : 1); + } + void set1D(uint32_t width) { setDimensions(width); } + void set1DArray(uint32_t width, uint32_t numSlices) { setDimensions(width, 0, 0, (numSlices > 0 ? numSlices : 1)); } + void set2D(uint32_t width, uint32_t height) { setDimensions(width, height); } + void set2DArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1)); } + void set3D(uint32_t width, uint32_t height, uint32_t depth) { setDimensions(width, height, depth); } + void set3DArray(uint32_t width, uint32_t height, uint32_t depth, uint32_t numSlices) { setDimensions(width, height, depth, (numSlices > 0 ? numSlices : 1)); } + 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); } + }; // Key Values diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 0416c22d5e..8e27c4f4f0 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,6 +85,61 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { + if (!srcTexture) { + return nullptr; + } + gpu::Texture* returnedTexture = srcTexture; + + auto theKTX = Texture::serialize(*srcTexture); + if (theKTX) { + // Prepare cache directory + QString path("hifi_ktx/"); + QFileInfo originalFileInfo(path); + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + path = docsLocation + "/" + path; + QFileInfo info(path); + if (!info.absoluteDir().exists()) { + QString originalRelativePath = originalFileInfo.path(); + QDir(docsLocation).mkpath(originalRelativePath); + } + std::string filename(path.toStdString()); + filename += name; + filename += ".ktx"; + + if (write) { + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } + } + + if (read) { + FILE* file = fopen (filename.c_str(),"rb"); + if (file != nullptr) { + // obtain file size: + fseek (file , 0 , SEEK_END); + auto size = ftell(file); + rewind(file); + + std::unique_ptr storage(new ktx::Storage(size)); + fread(storage->_bytes, 1, storage->_size, file); + fclose (file); + + //then create a new texture out of the ktx + auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); + + if (theNewTexure) { + returnedTexture = theNewTexure; + delete srcTexture; + } + } + } + } + return returnedTexture; +} + void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { _textureSource = textureSource; } @@ -269,57 +324,10 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { ::generateMips(theTexture, image, formatMip, false); } - auto theKTX = Texture::serialize(*theTexture); - if (theKTX) { - // save that! - QString path("hifi_ktx/"); - QFileInfo originalFileInfo(path); - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - path = docsLocation + "/" + path; - QFileInfo info(path); - if (!info.absoluteDir().exists()) { - QString originalRelativePath = originalFileInfo.path(); - QDir(docsLocation).mkpath(originalRelativePath); - } - std::string filename(path.toStdString()); - filename += std::to_string((size_t) theTexture); - filename += ".ktx"; - - { - FILE* file = fopen (filename.c_str(),"wb"); - if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); - } - } - - { - FILE* file = fopen (filename.c_str(),"rb"); - if (file != nullptr) { - // obtain file size: - fseek (file , 0 , SEEK_END); - auto size = ftell(file); - rewind(file); - - std::unique_ptr storage(new ktx::Storage(size)); - fread(storage->_bytes, 1, storage->_size, file); - fclose (file); - - //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); - - if (theNewTexure) { - auto srcTexture = theTexture; - theTexture = theNewTexure; - delete srcTexture; - } - } - } - } + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } - return theTexture; } @@ -360,6 +368,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -446,6 +456,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -479,8 +491,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - - // FIXME queue for transfer to GPU and block on completion + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -518,8 +530,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - - // FIXME queue for transfer to GPU and block on completion + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -555,7 +567,7 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - // FIXME queue for transfer to GPU and block on completion + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -886,6 +898,8 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm PROFILE_RANGE(resource_parse, "generateIrradiance"); theTexture->generateIrradiance(); } + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } } From b502b1e200f49a8dede4005504dd7b6e13aec9e7 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 20 Feb 2017 19:40:39 -0800 Subject: [PATCH 09/10] assigning the names of source to the texture --- libraries/model/src/model/TextureMap.cpp | 31 ++++++++++++++++-------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3f10f66256..5eb1b3982a 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,7 +85,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = false) { if (!srcTexture) { return nullptr; } @@ -103,8 +103,13 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo QString originalRelativePath = originalFileInfo.path(); QDir(docsLocation).mkpath(originalRelativePath); } + + std::string cleanedName = name; + + cleanedName = cleanedName.substr(cleanedName.find_last_of('//') + 1); + std::string filename(path.toStdString()); - filename += name; + filename += cleanedName; filename += ".ktx"; if (write) { @@ -328,8 +333,8 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { ::generateMips(theTexture, image, formatMip, false); } - - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -376,7 +381,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -464,7 +470,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -499,7 +506,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -538,7 +546,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -574,7 +583,8 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -906,7 +916,8 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm theTexture->generateIrradiance(); } - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } } From e5c2f6f651f17f1797ef82b4be2613431bf2b39d Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 21 Feb 2017 01:28:13 -0800 Subject: [PATCH 10/10] enabled the usage and cube map with KTX correctly --- libraries/gpu/src/gpu/Texture.cpp | 2 ++ libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 23 +++++++++++++++++++---- libraries/ktx/src/ktx/KTX.cpp | 5 +++-- libraries/ktx/src/ktx/KTX.h | 22 ++++++++++++++++++++-- libraries/ktx/src/ktx/Reader.cpp | 17 ++++++++++++++--- libraries/ktx/src/ktx/Writer.cpp | 21 ++++++++++++++++----- libraries/model/src/model/TextureMap.cpp | 15 ++++++++++----- 8 files changed, 85 insertions(+), 22 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index f65242c578..ba44bd6341 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -450,6 +450,7 @@ bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size Size expectedSize = evalStoredMipFaceSize(level, format); if (size == expectedSize) { _storage->assignMipFaceData(level, format, size, bytes, face); + _maxMip = std::max(_maxMip, level); _stamp++; return true; } else if (size > expectedSize) { @@ -458,6 +459,7 @@ bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... _storage->assignMipFaceData(level, format, size, bytes, face); + _maxMip = std::max(_maxMip, level); _stamp++; return true; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 0a5afe78c3..b8537b07c2 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -488,7 +488,7 @@ public: ExternalUpdates getUpdates() const; static ktx::KTXUniquePointer serialize(const Texture& texture); - static Texture* unserialize(TextureUsageType usageType, const ktx::KTXUniquePointer& srcData); + static Texture* unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler = Sampler()); protected: const TextureUsageType _usageType; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 404aca77a4..508467e79f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -28,6 +28,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); // Set Dimensions + uint32_t numFaces = 1; switch (texture.getType()) { case TEX_1D: { if (texture.isArray()) { @@ -59,6 +60,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } else { header.setCube(texture.getWidth(), texture.getHeight()); } + numFaces = 6; break; } default: @@ -72,7 +74,16 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { auto mip = texture.accessStoredMipFace(level); if (mip) { - images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); + if (numFaces == 1) { + images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); + } else { + ktx::Image::FaceBytes cubeFaces(6); + cubeFaces[0] = mip->readData(); + for (int face = 1; face < 6; face++) { + cubeFaces[face] = texture.accessStoredMipFace(level, face)->readData(); + } + images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, cubeFaces)); + } } } @@ -80,7 +91,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -Texture* Texture::unserialize(TextureUsageType usageType, const ktx::KTXUniquePointer& srcData) { +Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler) { if (!srcData) { return nullptr; } @@ -113,12 +124,16 @@ Texture* Texture::unserialize(TextureUsageType usageType, const ktx::KTXUniquePo header.getPixelDepth(), 1, // num Samples header.getNumberOfSlices(), - Sampler()); + sampler); + + tex->setUsage(usage); // Assing the mips availables uint16_t level = 0; for (auto& image : srcData->_images) { - tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); + for (uint32_t face = 0; face < image._numFaces; face++) { + tex->assignStoredMipFace(level, mipFormat, image._faceSize, image._faceBytes[face], face); + } level++; } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index f8f315d784..8635fbb684 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -16,8 +16,9 @@ using namespace ktx; uint32_t Header::evalPadding(size_t byteSize) { - auto padding = byteSize % PACKING_SIZE; - return (uint32_t) (padding ? PACKING_SIZE - padding : 0); + //auto padding = byteSize % PACKING_SIZE; + // return (uint32_t) (padding ? PACKING_SIZE - padding : 0); + return (uint32_t) (3 - (byteSize + 3) % PACKING_SIZE);// padding ? PACKING_SIZE - padding : 0); } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 8e79d90dc0..52c65c9cd4 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -418,14 +418,32 @@ namespace ktx { struct Image { + using FaceBytes = std::vector; + + uint32_t _numFaces{ 1 }; uint32_t _imageSize; + uint32_t _faceSize; uint32_t _padding; - const Byte* _bytes; + FaceBytes _faceBytes; + Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : + _numFaces(1), _imageSize(imageSize), _padding(padding), - _bytes(bytes) {} + _faceSize(imageSize), + _faceBytes(1, bytes) {} + + Image(uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : + _numFaces(NUM_CUBEMAPFACES), + _imageSize(pageSize * NUM_CUBEMAPFACES), + _padding(padding), + _faceSize(pageSize) + { + if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { + _faceBytes = cubeFaceBytes; + } + } }; using Images = std::vector; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 00c0c4e19a..dffe6cf828 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -125,6 +125,7 @@ namespace ktx { Images images; auto currentPtr = srcBytes; auto numMips = header.getNumberOfLevels(); + auto numFaces = header.numberOfFaces; // Keep identifying new mip as long as we can at list query the next imageSize while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) { @@ -137,9 +138,19 @@ namespace ktx { if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { auto padding = Header::evalPadding(imageSize); - images.emplace_back(Image(imageSize, padding, currentPtr)); - - currentPtr += imageSize + padding; + if (numFaces == 6) { + size_t faceSize = imageSize / 6; + Image::FaceBytes faces(6); + for (uint32_t face = 0; face < 6; face++) { + faces[face] = currentPtr; + currentPtr += faceSize; + } + images.emplace_back(Image(faceSize, padding, faces)); + currentPtr += padding; + } else { + images.emplace_back(Image(imageSize, padding, currentPtr)); + currentPtr += imageSize + padding; + } } else { break; } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 75836ad5b5..edd9a9ec97 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -125,14 +125,25 @@ namespace ktx { // If enough data ahead then capture the copy source pointer if (currentDataSize + imageSize <= (allocatedImagesDataSize)) { - - auto copied = memcpy(currentPtr, srcImages[l]._bytes, imageSize); - auto padding = Header::evalPadding(imageSize); - destImages.emplace_back(Image(imageSize, padding, currentPtr)); + // Single face vs cubes + if (srcImages[l]._numFaces == 1) { + auto copied = memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); + destImages.emplace_back(Image(imageSize, padding, currentPtr)); + currentPtr += imageSize; + } else { + Image::FaceBytes faceBytes(6); + auto faceSize = srcImages[l]._faceSize; + for (int face = 0; face < 6; face++) { + auto copied = memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); + faceBytes[face] = currentPtr; + currentPtr += faceSize; + } + destImages.emplace_back(Image(faceSize, padding, faceBytes)); + } - currentPtr += imageSize + padding; + currentPtr += padding; currentDataSize += imageSize + padding; } } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 5eb1b3982a..a95ed78be3 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,7 +85,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = false) { +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { if (!srcTexture) { return nullptr; } @@ -113,10 +113,15 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo filename += ".ktx"; if (write) { - FILE* file = fopen (filename.c_str(),"wb"); + /* FILE *file = fopen(name.c_str(), "r"); if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); + fclose(file); + } else*/ { + FILE *file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } } } @@ -133,7 +138,7 @@ 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->getUsageType(), ktx::KTX::create(storage)); + auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), ktx::KTX::create(storage), srcTexture->getSampler()); if (theNewTexure) { returnedTexture = theNewTexure;