From 7d5d6823cdefa085cab105bde85ca7ec7c1df5df Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 15 Feb 2017 00:54:16 -0800 Subject: [PATCH 001/106] 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 002/106] 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 003/106] 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 004/106] 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 005/106] 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 006/106] 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 007/106] 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 008/106] 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 7fb7aa87eb8c64b11a99eb864541743f7c797630 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 24 Jan 2017 15:37:18 -0800 Subject: [PATCH 009/106] Working on new texture management strategy --- interface/src/ui/ApplicationOverlay.cpp | 6 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 3 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 32 +- .../src/RenderableWebEntityItem.cpp | 2 +- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 7 +- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 13 +- .../gpu-gl/src/gpu/gl/GLBackendTexture.cpp | 54 +- libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp | 9 +- libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h | 2 +- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 233 +---- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 207 +--- .../gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 208 ---- .../gpu-gl/src/gpu/gl/GLTextureTransfer.h | 78 -- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 33 +- .../gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp | 10 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 187 ++-- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 5 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 177 +++- .../gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp | 10 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 545 +++-------- .../gpu/gl45/GL45BackendVariableTexture.cpp | 888 ++++++++++++++++++ libraries/gpu/src/gpu/Batch.cpp | 7 - libraries/gpu/src/gpu/Framebuffer.cpp | 12 +- libraries/gpu/src/gpu/Texture.cpp | 35 +- libraries/gpu/src/gpu/Texture.h | 23 +- .../src/model-networking/TextureCache.cpp | 15 +- .../src/model-networking/TextureCache.h | 1 + libraries/model/src/model/TextureMap.cpp | 13 +- libraries/model/src/model/TextureMap.h | 3 +- .../render-utils/src/AntialiasingEffect.cpp | 2 +- .../render-utils/src/DeferredFramebuffer.cpp | 10 +- .../src/DeferredLightingEffect.cpp | 4 +- .../render-utils/src/RenderDeferredTask.cpp | 2 +- .../render-utils/src/SubsurfaceScattering.cpp | 6 +- .../render-utils/src/SurfaceGeometryPass.cpp | 12 +- .../src/OculusLegacyDisplayPlugin.cpp | 2 +- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 8 +- tests/render-perf/src/main.cpp | 1 - tests/render-texture-load/src/main.cpp | 1 + 40 files changed, 1508 insertions(+), 1360 deletions(-) delete mode 100644 libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp delete mode 100644 libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h create mode 100644 libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 364dff52a3..730e21139f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -99,7 +99,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { PROFILE_RANGE(app, __FUNCTION__); if (!_uiTexture) { - _uiTexture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); + _uiTexture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); _uiTexture->setSource(__FUNCTION__); } // Once we move UI rendering and screen rendering to different @@ -272,13 +272,13 @@ void ApplicationOverlay::buildFramebufferObject() { auto width = uiSize.x; auto height = uiSize.y; if (!_overlayFramebuffer->getDepthStencilBuffer()) { - auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER)); + auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER)); _overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT); } if (!_overlayFramebuffer->getRenderBuffer(0)) { const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP); - auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); + auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); _overlayFramebuffer->setRenderBuffer(0, colorBuffer); } } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index cb649e8766..f67acb0304 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -251,7 +251,7 @@ void Web3DOverlay::render(RenderArgs* args) { if (!_texture) { auto webSurface = _webSurface; - _texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); + _texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); _texture->setSource(__FUNCTION__); } OffscreenQmlSurface::TextureAndFence newTextureAndFence; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index cf6b39812a..1bfe4f3dcc 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -355,7 +355,7 @@ void OpenGLDisplayPlugin::customizeContext() { if ((image.width() > 0) && (image.height() > 0)) { cursorData.texture.reset( - gpu::Texture::create2D( + gpu::Texture::createStrict( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -363,6 +363,7 @@ void OpenGLDisplayPlugin::customizeContext() { auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); cursorData.texture->setUsage(usage.build()); cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + cursorData.texture->autoGenerateMips(-1); } } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index a8b8ba3618..24f4e429ef 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -296,7 +296,7 @@ void HmdDisplayPlugin::internalPresent() { image = image.convertToFormat(QImage::Format_RGBA8888); if (!_previewTexture) { _previewTexture.reset( - gpu::Texture::create2D( + gpu::Texture::createStrict( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -306,23 +306,21 @@ void HmdDisplayPlugin::internalPresent() { _previewTexture->autoGenerateMips(-1); } - if (getGLBackend()->isTextureReady(_previewTexture)) { - auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); + auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); - render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - batch.setStateScissorRect(viewport); - batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _previewTexture); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); - _clearPreviewFlag = false; - swapBuffers(); - } + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + batch.setStateScissorRect(viewport); + batch.setViewportTransform(viewport); + batch.setResourceTexture(0, _previewTexture); + batch.setPipeline(_presentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + _clearPreviewFlag = false; + swapBuffers(); } postPreview(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 972c23d534..b3978b9356 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -214,7 +214,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { if (!_texture) { auto webSurface = _webSurface; - _texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); + _texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); _texture->setSource(__FUNCTION__); } OffscreenQmlSurface::TextureAndFence newTextureAndFence; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index c51f468908..76cc64f3e3 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -62,8 +62,6 @@ BackendPointer GLBackend::createBackend() { INSTANCE = result.get(); void* voidInstance = &(*result); qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); - - gl::GLTexture::initTextureTransferHelper(); return result; } @@ -623,6 +621,7 @@ void GLBackend::queueLambda(const std::function lambda) const { } void GLBackend::recycle() const { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__) { std::list> lamdbasTrash; { @@ -745,10 +744,6 @@ void GLBackend::recycle() const { glDeleteQueries((GLsizei)ids.size(), ids.data()); } } - -#ifndef THREADED_TEXTURE_TRANSFER - gl::GLTexture::_textureTransferHelper->process(); -#endif } void GLBackend::setCameraCorrection(const Mat4& correction) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 950ac65a3f..76c950ec2b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -187,10 +187,15 @@ public: virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; - virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0; + virtual GLuint getTextureID(const TexturePointer& texture) final; virtual GLuint getBufferID(const Buffer& buffer) = 0; virtual GLuint getQueryID(const QueryPointer& query) = 0; - virtual bool isTextureReady(const TexturePointer& texture); + + virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; + virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; + virtual GLTexture* syncGPUObject(const TexturePointer& texture); + virtual GLQuery* syncGPUObject(const Query& query) = 0; + //virtual bool isTextureReady(const TexturePointer& texture); virtual void releaseBuffer(GLuint id, Size size) const; virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; @@ -206,10 +211,6 @@ public: protected: void recycle() const override; - virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; - virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; - virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0; - virtual GLQuery* syncGPUObject(const Query& query) = 0; static const size_t INVALID_OFFSET = (size_t)-1; bool _inRenderTransferPass { false }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp index f51eac0e33..ca4e328612 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp @@ -14,12 +14,56 @@ using namespace gpu; using namespace gpu::gl; -bool GLBackend::isTextureReady(const TexturePointer& texture) { - // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(texture, true); - return object && object->isReady(); + +GLuint GLBackend::getTextureID(const TexturePointer& texture) { + GLTexture* object = syncGPUObject(texture); + + if (!object) { + return 0; + } + + return object->_id; } +GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { + const Texture& texture = *texturePointer; + // Special case external textures + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + Texture::ExternalUpdates updates = texture.getUpdates(); + if (!updates.empty()) { + Texture::ExternalRecycler recycler = texture.getExternalRecycler(); + Q_ASSERT(recycler); + // Discard any superfluous updates + while (updates.size() > 1) { + const auto& update = updates.front(); + // Superfluous updates will never have been read, but we want to ensure the previous + // writes to them are complete before they're written again, so return them with the + // same fences they arrived with. This can happen on any thread because no GL context + // work is involved + recycler(update.first, update.second); + updates.pop_front(); + } + + // The last texture remaining is the one we'll use to create the GLTexture + const auto& update = updates.front(); + // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence + if (update.second) { + GLsync fence = static_cast(update.second); + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); + } + + // Create the new texture object (replaces any previous texture object) + new GLExternalTexture(shared_from_this(), texture, update.first); + } + + // Return the texture object (if any) associated with the texture, without extensive logic + // (external textures are + return Backend::getGPUObject(texture); + } + + return nullptr; +} void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); @@ -28,7 +72,7 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { } // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture, false); + GLTexture* object = syncGPUObject(resourceTexture); if (!object) { return; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp index 85cf069062..2ac7e9d060 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp @@ -21,13 +21,12 @@ GLFramebuffer::~GLFramebuffer() { } } -bool GLFramebuffer::checkStatus(GLenum target) const { - bool result = false; +bool GLFramebuffer::checkStatus() const { switch (_status) { case GL_FRAMEBUFFER_COMPLETE: // Success ! - result = true; - break; + return true; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; break; @@ -44,5 +43,5 @@ bool GLFramebuffer::checkStatus(GLenum target) const { qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; break; } - return result; + return false; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h index 9b4f9703fc..c0633cfdef 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h @@ -64,7 +64,7 @@ public: protected: GLenum _status { GL_FRAMEBUFFER_COMPLETE }; virtual void update() = 0; - bool checkStatus(GLenum target) const; + bool checkStatus() const; GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} ~GLFramebuffer(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 1e0dd08ae1..1de820e1df 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -10,15 +10,13 @@ #include -#include "GLTextureTransfer.h" #include "GLBackend.h" using namespace gpu; using namespace gpu::gl; -std::shared_ptr GLTexture::_textureTransferHelper; -const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = { +const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z @@ -67,6 +65,17 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) { } +uint8_t GLTexture::getFaceCount(GLenum target) { + switch (target) { + case GL_TEXTURE_2D: + return TEXTURE_2D_NUM_FACES; + case GL_TEXTURE_CUBE_MAP: + return TEXTURE_CUBE_NUM_FACES; + default: + Q_UNREACHABLE(); + break; + } +} const std::vector& GLTexture::getFaceTargets(GLenum target) { static std::vector cubeFaceTargets { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, @@ -89,216 +98,34 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { return faceTargets; } -// Default texture memory = GPU total memory - 2GB -#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048) -// Minimum texture memory = 1GB -#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024) - - -float GLTexture::getMemoryPressure() { - // Check for an explicit memory limit - auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); - - - // If no memory limit has been set, use a percentage of the total dedicated memory - if (!availableTextureMemory) { -#if 0 - auto totalMemory = getDedicatedMemory(); - if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) { - availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; - } else { - availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES; - } -#else - // Hardcode texture limit for sparse textures at 1 GB for now - availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; -#endif - } - - // Return the consumed texture memory divided by the available texture memory. - auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage(); - float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory; - static Context::Size lastConsumedGpuMemory = 0; - if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) { - lastConsumedGpuMemory = consumedGpuMemory; - qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory; - } - return memoryPressure; -} - - -// Create the texture and allocate storage -GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : - GLObject(backend, texture, id), - _external(false), - _source(texture.source()), - _storageStamp(texture.getStamp()), - _target(getGLTextureType(texture)), - _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), - _maxMip(texture.maxMip()), - _minMip(texture.minMip()), - _virtualSize(texture.evalTotalSize()), - _transferrable(transferrable) -{ - auto strongBackend = _backend.lock(); - strongBackend->recycle(); - Backend::incrementTextureGPUCount(); - Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); - Backend::setGPUObject(texture, this); -} - GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : GLObject(backend, texture, id), - _external(true), _source(texture.source()), - _storageStamp(0), - _target(getGLTextureType(texture)), - _internalFormat(GL_RGBA8), - // FIXME force mips to 0? - _maxMip(texture.maxMip()), - _minMip(texture.minMip()), - _virtualSize(0), - _transferrable(false) + _target(getGLTextureType(texture)) { Backend::setGPUObject(texture, this); - - // FIXME Is this necessary? - //withPreservedTexture([this] { - // syncSampler(); - // if (_gpuObject.isAutogenerateMips()) { - // generateMips(); - // } - //}); } GLTexture::~GLTexture() { + auto backend = _backend.lock(); + if (backend && _id) { + backend->releaseTexture(_id, 0); + } +} + + +GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) + : Parent(backend, texture, id) { } + +GLExternalTexture::~GLExternalTexture() { auto backend = _backend.lock(); if (backend) { - if (_external) { - auto recycler = _gpuObject.getExternalRecycler(); - if (recycler) { - backend->releaseExternalTexture(_id, recycler); - } else { - qWarning() << "No recycler available for texture " << _id << " possible leak"; - } - } else if (_id) { - // WARNING! Sparse textures do not use this code path. See GL45BackendTexture for - // the GL45Texture destructor for doing any required work tracking GPU stats - backend->releaseTexture(_id, _size); + auto recycler = _gpuObject.getExternalRecycler(); + if (recycler) { + backend->releaseExternalTexture(_id, recycler); + } else { + qWarning() << "No recycler available for texture " << _id << " possible leak"; } - - if (!_external && !_transferrable) { - Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); - } - } - Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); -} - -void GLTexture::createTexture() { - withPreservedTexture([&] { - allocateStorage(); - (void)CHECK_GL_ERROR(); - syncSampler(); - (void)CHECK_GL_ERROR(); - }); -} - -void GLTexture::withPreservedTexture(std::function f) const { - GLint boundTex = -1; - switch (_target) { - case GL_TEXTURE_2D: - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - break; - - case GL_TEXTURE_CUBE_MAP: - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - break; - - default: - qFatal("Unsupported texture type"); - } - (void)CHECK_GL_ERROR(); - - glBindTexture(_target, _texture); - f(); - glBindTexture(_target, boundTex); - (void)CHECK_GL_ERROR(); -} - -void GLTexture::setSize(GLuint size) const { - if (!_external && !_transferrable) { - Backend::updateTextureGPUFramebufferMemoryUsage(_size, size); - } - Backend::updateTextureGPUMemoryUsage(_size, size); - const_cast(_size) = size; -} - -bool GLTexture::isInvalid() const { - return _storageStamp < _gpuObject.getStamp(); -} - -bool GLTexture::isOutdated() const { - return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp(); -} - -bool GLTexture::isReady() const { - // If we have an invalid texture, we're never ready - if (isInvalid()) { - return false; - } - - auto syncState = _syncState.load(); - if (isOutdated() || Idle != syncState) { - return false; - } - - return true; -} - - -// Do any post-transfer operations that might be required on the main context / rendering thread -void GLTexture::postTransfer() { - setSyncState(GLSyncState::Idle); - ++_transferCount; - - // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory - switch (_gpuObject.getType()) { - case Texture::TEX_2D: - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i)) { - _gpuObject.notifyMipFaceGPULoaded(i); - } - } - break; - - case Texture::TEX_CUBE: - // transfer pixels from each faces - for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - _gpuObject.notifyMipFaceGPULoaded(i, f); - } - } - } - break; - - default: - qCWarning(gpugllogging) << __FUNCTION__ << " case for Texture Type " << _gpuObject.getType() << " not supported"; - break; + const_cast(_id) = 0; } } - -void GLTexture::initTextureTransferHelper() { - _textureTransferHelper = std::make_shared(); -} - -void GLTexture::startTransfer() { - createTexture(); -} - -void GLTexture::finishTransfer() { - if (_gpuObject.isAutogenerateMips()) { - generateMips(); - } -} - diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 0f75a6fe51..1f91e17157 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -9,7 +9,6 @@ #define hifi_gpu_gl_GLTexture_h #include "GLShared.h" -#include "GLTextureTransfer.h" #include "GLBackend.h" #include "GLTexelFormat.h" @@ -20,210 +19,48 @@ struct GLFilterMode { GLint magFilter; }; - class GLTexture : public GLObject { + using Parent = GLObject; + friend class GLBackend; public: static const uint16_t INVALID_MIP { (uint16_t)-1 }; static const uint8_t INVALID_FACE { (uint8_t)-1 }; - static void initTextureTransferHelper(); - static std::shared_ptr _textureTransferHelper; - - template - static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) { - const Texture& texture = *texturePointer; - - // Special case external textures - if (texture.getUsage().isExternal()) { - Texture::ExternalUpdates updates = texture.getUpdates(); - if (!updates.empty()) { - Texture::ExternalRecycler recycler = texture.getExternalRecycler(); - Q_ASSERT(recycler); - // Discard any superfluous updates - while (updates.size() > 1) { - const auto& update = updates.front(); - // Superfluous updates will never have been read, but we want to ensure the previous - // writes to them are complete before they're written again, so return them with the - // same fences they arrived with. This can happen on any thread because no GL context - // work is involved - recycler(update.first, update.second); - updates.pop_front(); - } - - // The last texture remaining is the one we'll use to create the GLTexture - const auto& update = updates.front(); - // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence - if (update.second) { - GLsync fence = static_cast(update.second); - glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(fence); - } - - // Create the new texture object (replaces any previous texture object) - new GLTextureType(backend.shared_from_this(), texture, update.first); - } - - // Return the texture object (if any) associated with the texture, without extensive logic - // (external textures are - return Backend::getGPUObject(texture); - } - - if (!texture.isDefined()) { - // NO texture definition yet so let's avoid thinking - return nullptr; - } - - // If the object hasn't been created, or the object definition is out of date, drop and re-create - GLTexture* object = Backend::getGPUObject(texture); - - // Create the texture if need be (force re-creation if the storage stamp changes - // for easier use of immutable storage) - if (!object || object->isInvalid()) { - // This automatically any previous texture - object = new GLTextureType(backend.shared_from_this(), texture, needTransfer); - if (!object->_transferrable) { - object->createTexture(); - object->_contentStamp = texture.getDataStamp(); - object->updateSize(); - object->postTransfer(); - } - } - - // Object maybe doens't neet to be tranasferred after creation - if (!object->_transferrable) { - return object; - } - - // If we just did a transfer, return the object after doing post-transfer work - if (GLSyncState::Transferred == object->getSyncState()) { - object->postTransfer(); - } - - if (object->isOutdated()) { - // Object might be outdated, if so, start the transfer - // (outdated objects that are already in transfer will have reported 'true' for ready() - _textureTransferHelper->transferTexture(texturePointer); - return nullptr; - } - - if (!object->isReady()) { - return nullptr; - } - - ((GLTexture*)object)->updateMips(); - - return object; - } - - template - static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) { - if (!texture) { - return 0; - } - GLTexture* object { nullptr }; - if (shouldSync) { - object = sync(backend, texture, shouldSync); - } else { - object = Backend::getGPUObject(*texture); - } - - if (!object) { - return 0; - } - - if (!shouldSync) { - return object->_id; - } - - // Don't return textures that are in transfer state - if ((object->getSyncState() != GLSyncState::Idle) || - // Don't return transferrable textures that have never completed transfer - (!object->_transferrable || 0 != object->_transferCount)) { - return 0; - } - - return object->_id; - } - ~GLTexture(); - // Is this texture generated outside the GPU library? - const bool _external; const GLuint& _texture { _id }; const std::string _source; - const Stamp _storageStamp; const GLenum _target; - const GLenum _internalFormat; - const uint16 _maxMip; - uint16 _minMip; - const GLuint _virtualSize; // theoretical size as expected - Stamp _contentStamp { 0 }; - const bool _transferrable; - Size _transferCount { 0 }; - GLuint size() const { return _size; } - GLSyncState getSyncState() const { return _syncState; } - // Is the storage out of date relative to the gpu texture? - bool isInvalid() const; + static const std::vector& getFaceTargets(GLenum textureType); + static uint8_t getFaceCount(GLenum textureType); + static GLenum getGLTextureType(const Texture& texture); - // Is the content out of date relative to the gpu texture? - bool isOutdated() const; - - // Is the texture in a state where it can be rendered with no work? - bool isReady() const; - - // Execute any post-move operations that must occur only on the main thread - virtual void postTransfer(); - - uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } - - static const size_t CUBE_NUM_FACES = 6; - static const GLenum CUBE_FACE_LAYOUT[6]; + static const uint8_t TEXTURE_2D_NUM_FACES = 1; + static const uint8_t TEXTURE_CUBE_NUM_FACES = 6; + static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES]; static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; - // Return a floating point value indicating how much of the allowed - // texture memory we are currently consuming. A value of 0 indicates - // no texture memory usage, while a value of 1 indicates all available / allowed memory - // is consumed. A value above 1 indicates that there is a problem. - static float getMemoryPressure(); protected: - - static const std::vector& getFaceTargets(GLenum textureType); - - static GLenum getGLTextureType(const Texture& texture); - - - const GLuint _size { 0 }; // true size as reported by the gl api - std::atomic _syncState { GLSyncState::Idle }; - - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); - - void setSyncState(GLSyncState syncState) { _syncState = syncState; } - - void createTexture(); - - virtual void updateMips() {} - virtual void allocateStorage() const = 0; - virtual void updateSize() const = 0; - virtual void syncSampler() const = 0; + virtual uint32 size() const = 0; virtual void generateMips() const = 0; - virtual void withPreservedTexture(std::function f) const; -protected: - void setSize(GLuint size) const; - - virtual void startTransfer(); - // Returns true if this is the last block required to complete transfer - virtual bool continueTransfer() { return false; } - virtual void finishTransfer(); - -private: - friend class GLTextureTransferHelper; - friend class GLBackend; + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); }; +class GLExternalTexture : public GLTexture { + using Parent = GLTexture; + friend class GLBackend; +public: + ~GLExternalTexture(); +protected: + GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); + void generateMips() const override {} + uint32 size() const override { return 0; } +}; + + } } #endif diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp deleted file mode 100644 index 9dac2986e3..0000000000 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ /dev/null @@ -1,208 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/03 -// Copyright 2013-2016 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 "GLTextureTransfer.h" - -#include -#include - -#include - -#include "GLShared.h" -#include "GLTexture.h" - -#ifdef HAVE_NSIGHT -#include "nvToolsExt.h" -std::unordered_map _map; -#endif - - -#ifdef TEXTURE_TRANSFER_PBOS -#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) -#define TEXTURE_TRANSFER_PBO_COUNT 128 -#endif - -using namespace gpu; -using namespace gpu::gl; - -GLTextureTransferHelper::GLTextureTransferHelper() { -#ifdef THREADED_TEXTURE_TRANSFER - setObjectName("TextureTransferThread"); - _context.create(); - initialize(true, QThread::LowPriority); - // Clean shutdown on UNIX, otherwise _canvas is freed early - connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); }); -#else - initialize(false, QThread::LowPriority); -#endif -} - -GLTextureTransferHelper::~GLTextureTransferHelper() { -#ifdef THREADED_TEXTURE_TRANSFER - if (isStillRunning()) { - terminate(); - } -#else - terminate(); -#endif -} - -void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { - GLTexture* object = Backend::getGPUObject(*texturePointer); - - Backend::incrementTextureGPUTransferCount(); - object->setSyncState(GLSyncState::Pending); - Lock lock(_mutex); - _pendingTextures.push_back(texturePointer); -} - -void GLTextureTransferHelper::setup() { -#ifdef THREADED_TEXTURE_TRANSFER - _context.makeCurrent(); - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // FIXME don't use opengl 4.5 DSA functionality without verifying it's present - glCreateRenderbuffers(1, &_drawRenderbuffer); - glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); - glCreateFramebuffers(1, &_drawFramebuffer); - glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); - glCreateFramebuffers(1, &_readFramebuffer); -#endif - -#ifdef TEXTURE_TRANSFER_PBOS - std::array pbos; - glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); - for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) { - TextureTransferBlock newBlock; - newBlock._pbo = pbos[i]; - glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); - newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); - _readyQueue.push(newBlock); - } -#endif -#endif -} - -void GLTextureTransferHelper::shutdown() { -#ifdef THREADED_TEXTURE_TRANSFER - _context.makeCurrent(); -#endif - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); - glDeleteFramebuffers(1, &_drawFramebuffer); - _drawFramebuffer = 0; - glDeleteFramebuffers(1, &_readFramebuffer); - _readFramebuffer = 0; - - glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0); - glDeleteRenderbuffers(1, &_drawRenderbuffer); - _drawRenderbuffer = 0; -#endif -} - -void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { - Lock lock(_mutex); - _pendingCommands.push_back(lambda); -} - -#define MAX_TRANSFERS_PER_PASS 2 - -bool GLTextureTransferHelper::process() { - // Take any new textures or commands off the queue - VoidLambdaList pendingCommands; - TextureList newTransferTextures; - { - Lock lock(_mutex); - newTransferTextures.swap(_pendingTextures); - pendingCommands.swap(_pendingCommands); - } - - if (!pendingCommands.empty()) { - for (auto command : pendingCommands) { - command(); - } - glFlush(); - } - - if (!newTransferTextures.empty()) { - for (auto& texturePointer : newTransferTextures) { -#ifdef HAVE_NSIGHT - _map[texturePointer] = nvtxRangeStart("TextureTansfer"); -#endif - GLTexture* object = Backend::getGPUObject(*texturePointer); - object->startTransfer(); - _transferringTextures.push_back(texturePointer); - _textureIterator = _transferringTextures.begin(); - } - _transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool { - return a->getSize() < b->getSize(); - }); - } - - // No transfers in progress, sleep - if (_transferringTextures.empty()) { -#ifdef THREADED_TEXTURE_TRANSFER - QThread::usleep(1); -#endif - return true; - } - PROFILE_COUNTER_IF_CHANGED(render_gpu_gl, "transferringTextures", int, (int) _transferringTextures.size()) - - static auto lastReport = usecTimestampNow(); - auto now = usecTimestampNow(); - auto lastReportInterval = now - lastReport; - if (lastReportInterval > USECS_PER_SECOND * 4) { - lastReport = now; - qCDebug(gpulogging) << "Texture list " << _transferringTextures.size(); - } - - size_t transferCount = 0; - for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { - if (++transferCount > MAX_TRANSFERS_PER_PASS) { - break; - } - auto texture = *_textureIterator; - GLTexture* gltexture = Backend::getGPUObject(*texture); - if (gltexture->continueTransfer()) { - ++_textureIterator; - continue; - } - - gltexture->finishTransfer(); - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use -#endif - -#ifdef THREADED_TEXTURE_TRANSFER - clientWait(); -#endif - gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); - gltexture->updateSize(); - gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); - Backend::decrementTextureGPUTransferCount(); -#ifdef HAVE_NSIGHT - // Mark the texture as transferred - nvtxRangeEnd(_map[texture]); - _map.erase(texture); -#endif - _textureIterator = _transferringTextures.erase(_textureIterator); - } - -#ifdef THREADED_TEXTURE_TRANSFER - if (!_transferringTextures.empty()) { - // Don't saturate the GPU - clientWait(); - } else { - // Don't saturate the CPU - QThread::msleep(1); - } -#endif - - return true; -} diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h deleted file mode 100644 index a23c282fd4..0000000000 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/03 -// Copyright 2013-2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#ifndef hifi_gpu_gl_GLTextureTransfer_h -#define hifi_gpu_gl_GLTextureTransfer_h - -#include -#include - -#include - -#include - -#include "GLShared.h" - -#ifdef Q_OS_WIN -#define THREADED_TEXTURE_TRANSFER -#endif - -#ifdef THREADED_TEXTURE_TRANSFER -// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread -// also, the current draw code is implicitly using OpenGL 4.5 functionality -//#define TEXTURE_TRANSFER_FORCE_DRAW -// FIXME PBO's increase the complexity and don't seem to work reliably -//#define TEXTURE_TRANSFER_PBOS -#endif - -namespace gpu { namespace gl { - -using TextureList = std::list; -using TextureListIterator = TextureList::iterator; - -class GLTextureTransferHelper : public GenericThread { -public: - using VoidLambda = std::function; - using VoidLambdaList = std::list; - using Pointer = std::shared_ptr; - GLTextureTransferHelper(); - ~GLTextureTransferHelper(); - void transferTexture(const gpu::TexturePointer& texturePointer); - void queueExecution(VoidLambda lambda); - - void setup() override; - void shutdown() override; - bool process() override; - -private: -#ifdef THREADED_TEXTURE_TRANSFER - ::gl::OffscreenContext _context; -#endif - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread - GLuint _drawRenderbuffer { 0 }; - GLuint _drawFramebuffer { 0 }; - GLuint _readFramebuffer { 0 }; -#endif - - // A mutex for protecting items access on the render and transfer threads - Mutex _mutex; - // Commands that have been submitted for execution on the texture transfer thread - VoidLambdaList _pendingCommands; - // Textures that have been submitted for transfer - TextureList _pendingTextures; - // Textures currently in the transfer process - // Only used on the transfer thread - TextureList _transferringTextures; - TextureListIterator _textureIterator; - -}; - -} } - -#endif \ No newline at end of file diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 72e2f5a804..6d2f91c436 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -40,18 +40,28 @@ public: class GL41Texture : public GLTexture { using Parent = GLTexture; - GLuint allocate(); - public: - GL41Texture(const std::weak_ptr& backend, const Texture& buffer, GLuint externalId); - GL41Texture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); + static GLuint allocate(); + + public: + ~GL41Texture(); + + private: + GL41Texture(const std::weak_ptr& backend, const Texture& buffer); - protected: - void transferMip(uint16_t mipLevel, uint8_t face) const; - void startTransfer() override; - void allocateStorage() const override; - void updateSize() const override; - void syncSampler() const override; void generateMips() const override; + uint32 size() const override; + + friend class GL41Backend; + const Stamp _storageStamp; + mutable Stamp _contentStamp { 0 }; + mutable Stamp _samplerStamp { 0 }; + const uint32 _size; + + + bool isOutdated() const; + void withPreservedTexture(std::function f) const; + void syncContent() const; + void syncSampler() const; }; @@ -62,8 +72,7 @@ protected: GLuint getBufferID(const Buffer& buffer) override; GLBuffer* syncGPUObject(const Buffer& buffer) override; - GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; - GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture) override; GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp index 6d11a52035..195b155bf3 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp @@ -53,10 +53,12 @@ public: GL_COLOR_ATTACHMENT15 }; int unit = 0; + auto backend = _backend.lock(); for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } else { gltexture = nullptr; } @@ -81,9 +83,11 @@ public: } if (_gpuObject.getDepthStamp() != _depthStamp) { + auto backend = _backend.lock(); auto surface = _gpuObject.getDepthStencilBuffer(); if (_gpuObject.hasDepthStencil() && surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } if (gltexture) { @@ -110,7 +114,7 @@ public: glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); } - checkStatus(GL_DRAW_FRAMEBUFFER); + checkStatus(); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 65c45111db..65c4dda202 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -29,20 +29,97 @@ GLuint GL41Texture::allocate() { return result; } -GLuint GL41Backend::getTextureID(const TexturePointer& texture, bool transfer) { - return GL41Texture::getId(*this, texture, transfer); +GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { + if (!texturePointer) { + return nullptr; + } + const Texture& texture = *texturePointer; + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + return Parent::syncGPUObject(texturePointer); + } + + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GL41Texture* object = Backend::getGPUObject(texture); + if (!object || object->_storageStamp < texture.getStamp()) { + // This automatically any previous texture + object = new GL41Texture(shared_from_this(), texture); + } + + // FIXME internalize to GL41Texture 'sync' function + if (object->isOutdated()) { + object->withPreservedTexture([&] { + if (object->_contentStamp < texture.getDataStamp()) { + // FIXME implement synchronous texture transfer here + object->syncContent(); + } + + if (object->_samplerStamp < texture.getSamplerStamp()) { + object->syncSampler(); + } + }); + } + + return object; } -GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { - return GL41Texture::sync(*this, texture, transfer); +GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) { + + withPreservedTexture([&] { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const Sampler& sampler = _gpuObject.getSampler(); + auto minMip = sampler.getMinMip(); + auto maxMip = sampler.getMaxMip(); + for (uint16_t l = minMip; l <= maxMip; l++) { + // Get the mip level dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(l); + for (GLenum target : getFaceTargets(_target)) { + glTexImage2D(target, l - minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); + (void)CHECK_GL_ERROR(); + } + } + }); } -GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) - : GLTexture(backend, texture, externalId) { +GL41Texture::~GL41Texture() { + } -GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) - : GLTexture(backend, texture, allocate(), transferrable) { +bool GL41Texture::isOutdated() const { + if (_samplerStamp <= _gpuObject.getSamplerStamp()) { + return true; + } + if (TextureUsageType::RESOURCE == _gpuObject.getUsageType() && _contentStamp <= _gpuObject.getDataStamp()) { + return true; + } + return false; +} + +void GL41Texture::withPreservedTexture(std::function f) const { + GLint boundTex = -1; + switch (_target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + glBindTexture(_target, _texture); + f(); + glBindTexture(_target, boundTex); + (void)CHECK_GL_ERROR(); } void GL41Texture::generateMips() const { @@ -52,94 +129,12 @@ void GL41Texture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GL41Texture::allocateStorage() const { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); - (void)CHECK_GL_ERROR(); - glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - (void)CHECK_GL_ERROR(); - if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) { - // Get the dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); - glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y); - (void)CHECK_GL_ERROR(); - } else { - for (uint16_t l = _minMip; l <= _maxMip; l++) { - // Get the mip level dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(l); - for (GLenum target : getFaceTargets(_target)) { - glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); - (void)CHECK_GL_ERROR(); - } - } - } +void GL41Texture::syncContent() const { + // FIXME actually copy the texture data + _contentStamp = _gpuObject.getDataStamp() + 1; } -void GL41Texture::updateSize() const { - setSize(_virtualSize); - if (!_id) { - return; - } - - if (_gpuObject.getTexelFormat().isCompressed()) { - GLenum proxyType = GL_TEXTURE_2D; - GLuint numFaces = 1; - if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) { - proxyType = CUBE_FACE_LAYOUT[0]; - numFaces = (GLuint)CUBE_NUM_FACES; - } - GLint gpuSize{ 0 }; - glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize); - (void)CHECK_GL_ERROR(); - - if (gpuSize) { - for (GLuint level = _minMip; level < _maxMip; level++) { - GLint levelSize{ 0 }; - glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize); - levelSize *= numFaces; - - if (levelSize <= 0) { - break; - } - gpuSize += levelSize; - } - (void)CHECK_GL_ERROR(); - setSize(gpuSize); - return; - } - } -} - -// Move content bits from the CPU to the GPU for a given mip / face -void GL41Texture::transferMip(uint16_t mipLevel, uint8_t face) const { - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); - //GLenum target = getFaceTargets()[face]; - GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face]; - auto size = _gpuObject.evalMipDimensions(mipLevel); - glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - (void)CHECK_GL_ERROR(); -} - -void GL41Texture::startTransfer() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - Parent::startTransfer(); - - glBindTexture(_target, _id); - (void)CHECK_GL_ERROR(); - - // transfer pixels from each faces - uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; - for (uint8_t f = 0; f < numFaces; f++) { - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - transferMip(i, f); - } - } - } -} - -void GL41Backend::GL41Texture::syncSampler() const { +void GL41Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); const auto& fm = FILTER_MODES[sampler.getFilter()]; glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); @@ -161,5 +156,9 @@ void GL41Backend::GL41Texture::syncSampler() const { glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); + _samplerStamp = _gpuObject.getSamplerStamp() + 1; } +uint32 GL41Texture::size() const { + return _size; +} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index d7dde8b7d6..f0ef2ac7a8 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -163,8 +163,3 @@ void GL45Backend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOf _stats._DSNumAPIDrawcalls++; (void)CHECK_GL_ERROR(); } - -void GL45Backend::recycle() const { - Parent::recycle(); - derezTextures(); -} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 2242bba5d9..e44f71e3e5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -8,6 +8,7 @@ // 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_gpu_45_GL45Backend_h #define hifi_gpu_45_GL45Backend_h @@ -19,6 +20,7 @@ namespace gpu { namespace gl45 { using namespace gpu::gl; +using TextureWeakPointer = std::weak_ptr; class GL45Backend : public GLBackend { using Parent = GLBackend; @@ -31,60 +33,152 @@ public: class GL45Texture : public GLTexture { using Parent = GLTexture; + friend class GL45Backend; static GLuint allocate(const Texture& texture); - static const uint32_t DEFAULT_PAGE_DIMENSION = 128; - static const uint32_t DEFAULT_MAX_SPARSE_LEVEL = 0xFFFF; + protected: + GL45Texture(const std::weak_ptr& backend, const Texture& texture); + void generateMips() const override; + void copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const; + virtual void syncSampler() const; + }; + + // + // Textures that have fixed allocation sizes and cannot be managed at runtime + // + + class GL45FixedAllocationTexture : public GL45Texture { + using Parent = GL45Texture; + friend class GL45Backend; public: - GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId); - GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable); - ~GL45Texture(); + GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL45FixedAllocationTexture(); - void postTransfer() override; + protected: + uint32 size() const override { return _size; } + void allocateStorage() const; + void syncSampler() const override; + const uint32 _size { 0 }; + }; - struct SparseInfo { - SparseInfo(GL45Texture& texture); - void maybeMakeSparse(); - void update(); - uvec3 getPageCounts(const uvec3& dimensions) const; - uint32_t getPageCount(const uvec3& dimensions) const; - uint32_t getSize() const; + class GL45AttachmentTexture : public GL45FixedAllocationTexture { + using Parent = GL45FixedAllocationTexture; + friend class GL45Backend; + protected: + GL45AttachmentTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL45AttachmentTexture(); + }; - GL45Texture& texture; - bool sparse { false }; - uvec3 pageDimensions { DEFAULT_PAGE_DIMENSION }; - GLuint maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; - uint32_t allocatedPages { 0 }; - uint32_t maxPages { 0 }; - uint32_t pageBytes { 0 }; - GLint pageDimensionsIndex { 0 }; + class GL45StrictResourceTexture : public GL45FixedAllocationTexture { + using Parent = GL45FixedAllocationTexture; + friend class GL45Backend; + protected: + GL45StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); + }; + + // + // Textures that can be managed at runtime to increase or decrease their memory load + // + + class GL45VariableAllocationTexture : public GL45Texture { + using Parent = GL45Texture; + friend class GL45Backend; + using PromoteLambda = std::function; + + public: + enum class MemoryPressureState { + Idle, + Transfer, + Oversubscribed, + Undersubscribed, }; protected: - void updateMips() override; - void stripToMip(uint16_t newMinMip); - void startTransfer() override; - bool continueTransfer() override; - void finishTransfer() override; - void incrementalTransfer(const uvec3& size, const gpu::Texture::PixelsPointer& mip, std::function f) const; - void transferMip(uint16_t mipLevel, uint8_t face = 0) const; - void allocateMip(uint16_t mipLevel, uint8_t face = 0) const; - void allocateStorage() const override; - void updateSize() const override; - void syncSampler() const override; - void generateMips() const override; - void withPreservedTexture(std::function f) const override; - void derez(); + static std::atomic _memoryPressureStateStale; + static MemoryPressureState _memoryPressureState; + static std::list _memoryManagedTextures; + static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; - SparseInfo _sparseInfo; - uint16_t _mipOffset { 0 }; - friend class GL45Backend; + static void updateMemoryPressure(); + static void processWorkQueues(); + static void addMemoryManagedTexture(const TexturePointer& texturePointer); + + static void manageMemory(); + + protected: + GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL45VariableAllocationTexture(); + //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } + bool canPromote() const { return _allocatedMip > 0; } + bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } + bool hasPendingTransfers() const { return !_pendingTransfers.empty(); } + void executeNextTransfer(); + uint32 size() const override { return _size; } + virtual void populateTransferQueue() = 0; + virtual void promote() = 0; + virtual void demote() = 0; + + uint16 _populatedMip { 0 }; + uint16 _allocatedMip { 0 }; + uint16 _maxAllocatedMip { 0 }; + uint32 _size { 0 }; + std::queue _pendingTransfers; }; + class GL45ResourceTexture : public GL45VariableAllocationTexture { + using Parent = GL45VariableAllocationTexture; + friend class GL45Backend; + protected: + GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture); + + void syncSampler() const override; + void promote() override; + void demote() override; + void populateTransferQueue() override; + + void allocateStorage(uint16 mip); + void copyMipsFromTexture(); + private: + }; + +#if 0 + class GL45SparseResourceTexture : public GL45VariableAllocationTexture { + using Parent = GL45VariableAllocationTexture; + friend class GL45Backend; + using TextureTypeFormat = std::pair; + using PageDimensions = std::vector; + using PageDimensionsMap = std::map; + static PageDimensionsMap pageDimensionsByFormat; + static Mutex pageDimensionsMutex; + + static bool isSparseEligible(const Texture& texture); + static PageDimensions getPageDimensionsForFormat(const TextureTypeFormat& typeFormat); + static PageDimensions getPageDimensionsForFormat(GLenum type, GLenum format); + static const uint32_t DEFAULT_PAGE_DIMENSION = 128; + static const uint32_t DEFAULT_MAX_SPARSE_LEVEL = 0xFFFF; + + protected: + GL45SparseResourceTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL45SparseResourceTexture(); + uint32 size() const override { return _allocatedPages * _pageBytes; } + void promote() override; + void demote() override; + + private: + uvec3 getPageCounts(const uvec3& dimensions) const; + uint32_t getPageCount(const uvec3& dimensions) const; + + uint32_t _allocatedPages { 0 }; + uint32_t _pageBytes { 0 }; + uvec3 _pageDimensions { DEFAULT_PAGE_DIMENSION }; + GLuint _maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; + }; +#endif + protected: + void recycle() const override; - void derezTextures() const; GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; @@ -92,8 +186,7 @@ protected: GLuint getBufferID(const Buffer& buffer) override; GLBuffer* syncGPUObject(const Buffer& buffer) override; - GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; - GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture) override; GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; @@ -126,5 +219,5 @@ protected: Q_DECLARE_LOGGING_CATEGORY(gpugl45logging) - #endif + diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp index c5b84b7deb..9648af9b21 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp @@ -49,10 +49,12 @@ public: GL_COLOR_ATTACHMENT15 }; int unit = 0; + auto backend = _backend.lock(); for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } else { gltexture = nullptr; } @@ -78,8 +80,10 @@ public: if (_gpuObject.getDepthStamp() != _depthStamp) { auto surface = _gpuObject.getDepthStencilBuffer(); + auto backend = _backend.lock(); if (_gpuObject.hasDepthStencil() && surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } if (gltexture) { @@ -102,7 +106,7 @@ public: _status = glCheckNamedFramebufferStatus(_id, GL_DRAW_FRAMEBUFFER); // restore the current framebuffer - checkStatus(GL_DRAW_FRAMEBUFFER); + checkStatus(); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 6948a045a2..c46c07ee37 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -8,9 +8,10 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GL45Backend.h" +#include "GL45Backend.h" #include +#include #include #include #include @@ -19,142 +20,73 @@ #include #include +#include #include "../gl/GLTexelFormat.h" using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -// Allocate 1 MB of buffer space for paged transfers -#define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) -#define DEFAULT_GL_PIXEL_ALIGNMENT 4 - -using GL45Texture = GL45Backend::GL45Texture; - -static std::map> texturesByMipCounts; -static Mutex texturesByMipCountsMutex; -using TextureTypeFormat = std::pair; -std::map> sparsePageDimensionsByFormat; -Mutex sparsePageDimensionsByFormatMutex; - -static std::vector getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { - { - Lock lock(sparsePageDimensionsByFormatMutex); - if (sparsePageDimensionsByFormat.count(typeFormat)) { - return sparsePageDimensionsByFormat[typeFormat]; - } - } - GLint count = 0; - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count); - - std::vector result; - if (count > 0) { - std::vector x, y, z; - x.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]); - y.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]); - z.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]); - - result.resize(count); - for (GLint i = 0; i < count; ++i) { - result[i] = uvec3(x[i], y[i], z[i]); - } - } - - { - Lock lock(sparsePageDimensionsByFormatMutex); - if (0 == sparsePageDimensionsByFormat.count(typeFormat)) { - sparsePageDimensionsByFormat[typeFormat] = result; - } - } - - return result; -} - -static std::vector getPageDimensionsForFormat(GLenum target, GLenum format) { - return getPageDimensionsForFormat({ target, format }); -} - -GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { - return GL45Texture::sync(*this, texture, transfer); -} - -using SparseInfo = GL45Backend::GL45Texture::SparseInfo; - -SparseInfo::SparseInfo(GL45Texture& texture) - : texture(texture) { -} - -void SparseInfo::maybeMakeSparse() { - // Don't enable sparse for objects with explicitly managed mip levels - if (!texture._gpuObject.isAutogenerateMips()) { - return; - } - return; - - const uvec3 dimensions = texture._gpuObject.getDimensions(); - auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat); - // In order to enable sparse the texture size must be an integer multiple of the page size - for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { - pageDimensionsIndex = (uint32_t) i; - pageDimensions = allowedPageDimensions[i]; - // Is this texture an integer multiple of page dimensions? - if (uvec3(0) == (dimensions % pageDimensions)) { - qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str(); - sparse = true; - break; - } - } - - if (sparse) { - glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); - glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); - } else { - qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << - " is not supported by any sparse page size for texture" << texture._source.c_str(); - } -} - #define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f -// This can only be called after we've established our storage size -void SparseInfo::update() { - if (!sparse) { - return; +GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { + if (!texturePointer) { + return nullptr; } - glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel); - pageBytes = texture._gpuObject.getTexelFormat().getSize(); - pageBytes *= pageDimensions.x * pageDimensions.y * pageDimensions.z; - // Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for - // sparse textures as compared to non-sparse, so we acount for that here. - pageBytes = (uint32_t)(pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE); - for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) { - auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel); - auto mipPageCount = getPageCount(mipDimensions); - maxPages += mipPageCount; + const Texture& texture = *texturePointer; + if (std::string("cursor texture") == texture.source()) { + qDebug() << "Loading cursor texture"; } - if (texture._target == GL_TEXTURE_CUBE_MAP) { - maxPages *= GLTexture::CUBE_NUM_FACES; + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + return Parent::syncGPUObject(texturePointer); } + + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + GL45Texture* object = Backend::getGPUObject(texture); + if (!object) { + switch (texture.getUsageType()) { + case TextureUsageType::RENDERBUFFER: + object = new GL45AttachmentTexture(shared_from_this(), texture); + break; + + case TextureUsageType::STRICT_RESOURCE: + qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); + object = new GL45StrictResourceTexture(shared_from_this(), texture); + break; + + case TextureUsageType::RESOURCE: { + + GL45VariableAllocationTexture* varObject { nullptr }; +#if 0 + if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { + varObject = new GL45SparseResourceTexture(shared_from_this(), texture); + } else { + varObject = new GL45ResourceTexture(shared_from_this(), texture); + } +#else + varObject = new GL45ResourceTexture(shared_from_this(), texture); +#endif + GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); + object = varObject; + break; + } + + default: + Q_UNREACHABLE(); + } + } + + return object; } -uvec3 SparseInfo::getPageCounts(const uvec3& dimensions) const { - auto result = (dimensions / pageDimensions) + - glm::clamp(dimensions % pageDimensions, glm::uvec3(0), glm::uvec3(1)); - return result; -} - -uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const { - auto pageCounts = getPageCounts(dimensions); - return pageCounts.x * pageCounts.y * pageCounts.z; -} - - -uint32_t SparseInfo::getSize() const { - return allocatedPages * pageBytes; +void GL45Backend::recycle() const { + Parent::recycle(); + GL45VariableAllocationTexture::manageMemory(); } void GL45Backend::initTextureManagementStage() { @@ -171,6 +103,11 @@ void GL45Backend::initTextureManagementStage() { } } +using GL45Texture = GL45Backend::GL45Texture; + +GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate(texture)) { +} GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; @@ -178,162 +115,37 @@ GLuint GL45Texture::allocate(const Texture& texture) { return result; } -GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { - return GL45Texture::getId(*this, texture, transfer); -} - -GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) - : GLTexture(backend, texture, externalId), _sparseInfo(*this) -{ -} - -GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) - : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this) - { - - auto theBackend = _backend.lock(); - if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) { - _sparseInfo.maybeMakeSparse(); - if (_sparseInfo.sparse) { - Backend::incrementTextureGPUSparseCount(); - } - } -} - -GL45Texture::~GL45Texture() { - // Remove this texture from the candidate list of derezzable textures - if (_transferrable) { - auto mipLevels = usedMipLevels(); - Lock lock(texturesByMipCountsMutex); - if (texturesByMipCounts.count(mipLevels)) { - auto& textures = texturesByMipCounts[mipLevels]; - textures.erase(this); - if (textures.empty()) { - texturesByMipCounts.erase(mipLevels); - } - } - } - - if (_sparseInfo.sparse) { - Backend::decrementTextureGPUSparseCount(); - - // Experimenation suggests that allocating sparse textures on one context/thread and deallocating - // them on another is buggy. So for sparse textures we need to queue a lambda with the deallocation - // callls to the transfer thread - auto id = _id; - // Set the class _id to 0 so we don't try to double delete - const_cast(_id) = 0; - std::list> destructionFunctions; - - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - auto maxSparseMip = std::min(_maxMip, _sparseInfo.maxSparseLevel); - for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { - auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); - destructionFunctions.push_back([id, maxFace, mipLevel, mipDimensions] { - glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); - }); - - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; - assert(deallocatedPages <= _sparseInfo.allocatedPages); - _sparseInfo.allocatedPages -= deallocatedPages; - } - - if (0 != _sparseInfo.allocatedPages) { - qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages; - } - - auto size = _size; - const_cast(_size) = 0; - _textureTransferHelper->queueExecution([id, size, destructionFunctions] { - for (auto function : destructionFunctions) { - function(); - } - glDeleteTextures(1, &id); - Backend::decrementTextureGPUCount(); - Backend::updateTextureGPUMemoryUsage(size, 0); - Backend::updateTextureGPUSparseMemoryUsage(size, 0); - }); - } -} - -void GL45Texture::withPreservedTexture(std::function f) const { - f(); -} - void GL45Texture::generateMips() const { glGenerateTextureMipmap(_id); (void)CHECK_GL_ERROR(); } -void GL45Texture::allocateStorage() const { - if (_gpuObject.getTexelFormat().isCompressed()) { - qFatal("Compressed textures not yet supported"); +void GL45Texture::copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const { + const auto& texture = _gpuObject; + if (!texture.isStoredMipFaceAvailable(sourceMip)) { + return; } - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - // Get the dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip + _mipOffset); - glTextureStorage2D(_id, usedMipLevels(), _internalFormat, dimensions.x, dimensions.y); - (void)CHECK_GL_ERROR(); -} - -void GL45Texture::updateSize() const { - if (_gpuObject.getTexelFormat().isCompressed()) { - qFatal("Compressed textures not yet supported"); - } - - if (_transferrable && _sparseInfo.sparse) { - auto size = _sparseInfo.getSize(); - Backend::updateTextureGPUSparseMemoryUsage(_size, size); - setSize(size); - } else { - setSize(_gpuObject.evalTotalSize(_mipOffset)); - } -} - -void GL45Texture::startTransfer() { - Parent::startTransfer(); - _sparseInfo.update(); -} - -bool GL45Texture::continueTransfer() { - PROFILE_RANGE(render_gpu_gl, "continueTransfer") - size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; + size_t maxFace = GLTexture::getFaceCount(_target); for (uint8_t face = 0; face < maxFace; ++face) { - for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) { - auto size = _gpuObject.evalMipDimensions(mipLevel); - if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) { - glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE); - _sparseInfo.allocatedPages += _sparseInfo.getPageCount(size); - } - if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { - PROFILE_RANGE_EX(render_gpu_gl, "texSubImage", 0x0000ffff, (size.x * size.y * maxFace / 1024)); - - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); - if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - } else if (GL_TEXTURE_CUBE_MAP == _target) { - // DSA ARB does not work on AMD, so use EXT - // unless EXT is not available on the driver - if (glTextureSubImage2DEXT) { - auto target = CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - } else { - glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); - } - } else { - Q_ASSERT(false); - } - (void)CHECK_GL_ERROR(); + auto size = texture.evalMipDimensions(sourceMip); + auto mipData = texture.accessStoredMipFace(sourceMip, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat()); + if (GL_TEXTURE_2D == _target) { + glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData()); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + // DSA ARB does not work on AMD, so use EXT + // unless EXT is not available on the driver + if (glTextureSubImage2DEXT) { + auto target = GLTexture::CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData()); + } else { + glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mipData->readData()); } + } else { + Q_ASSERT(false); } + (void)CHECK_GL_ERROR(); } - return false; -} - -void GL45Texture::finishTransfer() { - Parent::finishTransfer(); } void GL45Texture::syncSampler() const { @@ -353,163 +165,66 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); + glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + +#if 0 // FIXME account for mip offsets here auto baseMip = std::max(sampler.getMipOffset(), _minMip); glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip() - _mipOffset)); - glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); +#endif } -void GL45Texture::postTransfer() { - Parent::postTransfer(); - auto mipLevels = usedMipLevels(); - if (_transferrable && mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) { - Lock lock(texturesByMipCountsMutex); - texturesByMipCounts[mipLevels].insert(this); - } -} +using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; -void GL45Texture::stripToMip(uint16_t newMinMip) { - if (newMinMip < _minMip) { - qCWarning(gpugl45logging) << "Cannot decrease the min mip"; - return; - } - - if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) { - qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; - return; - } - - PROFILE_RANGE(render_gpu_gl, "GL45Texture::stripToMip"); - - auto mipLevels = usedMipLevels(); - { - Lock lock(texturesByMipCountsMutex); - assert(0 != texturesByMipCounts.count(mipLevels)); - assert(0 != texturesByMipCounts[mipLevels].count(this)); - texturesByMipCounts[mipLevels].erase(this); - if (texturesByMipCounts[mipLevels].empty()) { - texturesByMipCounts.erase(mipLevels); - } - } - - // If we weren't generating mips before, we need to now that we're stripping down mip levels. - if (!_gpuObject.isAutogenerateMips()) { - qCDebug(gpugl45logging) << "Force mip generation for texture"; - glGenerateTextureMipmap(_id); - } - - - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - if (_sparseInfo.sparse) { - for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { - auto id = _id; - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - _textureTransferHelper->queueExecution([id, mip, mipDimensions, maxFace] { - glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); - }); - - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; - assert(deallocatedPages < _sparseInfo.allocatedPages); - _sparseInfo.allocatedPages -= deallocatedPages; - } - _minMip = newMinMip; - } else { - GLuint oldId = _id; - // Find the distance between the old min mip and the new one - uint16 mipDelta = newMinMip - _minMip; - _mipOffset += mipDelta; - const_cast(_maxMip) -= mipDelta; - auto newLevels = usedMipLevels(); - - // Create and setup the new texture (allocate) - { - Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset); - PROFILE_RANGE_EX(render_gpu_gl, "Re-Allocate", 0xff0000ff, (newDimensions.x * newDimensions.y)); - - glCreateTextures(_target, 1, &const_cast(_id)); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y); - } - - // Copy the contents of the old texture to the new - { - PROFILE_RANGE(render_gpu_gl, "Blit"); - // Preferred path only available in 4.3 - for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) { - uint16 sourceMip = targetMip + mipDelta; - Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset); - for (GLenum target : getFaceTargets(_target)) { - glCopyImageSubData( - oldId, target, sourceMip, 0, 0, 0, - _id, target, targetMip, 0, 0, 0, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } - - glDeleteTextures(1, &oldId); - } - } - - // Re-sync the sampler to force access to the new mip level +GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) { + allocateStorage(); syncSampler(); - updateSize(); +} - // Re-insert into the texture-by-mips map if appropriate - mipLevels = usedMipLevels(); - if (mipLevels > 1 && (!_sparseInfo.sparse || _minMip < _sparseInfo.maxSparseLevel)) { - Lock lock(texturesByMipCountsMutex); - texturesByMipCounts[mipLevels].insert(this); +GL45FixedAllocationTexture::~GL45FixedAllocationTexture() { +} + +void GL45FixedAllocationTexture::allocateStorage() const { + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto dimensions = _gpuObject.getDimensions(); + const auto mips = _gpuObject.evalNumMips(); + glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); +} + +void GL45FixedAllocationTexture::syncSampler() const { + Parent::syncSampler(); + const Sampler& sampler = _gpuObject.getSampler(); + auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); + glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); +} + +// Renderbuffer attachment textures +using GL45AttachmentTexture = GL45Backend::GL45AttachmentTexture; + +GL45AttachmentTexture::GL45AttachmentTexture(const std::weak_ptr& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) { + Backend::updateTextureGPUFramebufferMemoryUsage(0, size()); +} + +GL45AttachmentTexture::~GL45AttachmentTexture() { + Backend::updateTextureGPUFramebufferMemoryUsage(size(), 0); +} + +// Strict resource textures +using GL45StrictResourceTexture = GL45Backend::GL45StrictResourceTexture; + +GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) { + auto mipLevels = _gpuObject.evalNumMips(); + for (uint16_t sourceMip = 0; sourceMip < mipLevels; ++sourceMip) { + uint16_t targetMip = sourceMip; + copyMipFromTexture(sourceMip, targetMip); + } + if (texture.isAutogenerateMips()) { + generateMips(); } } -void GL45Texture::updateMips() { - if (!_sparseInfo.sparse) { - return; - } - auto newMinMip = std::min(_gpuObject.minMip(), _sparseInfo.maxSparseLevel); - if (_minMip < newMinMip) { - stripToMip(newMinMip); - } -} - -void GL45Texture::derez() { - if (_sparseInfo.sparse) { - assert(_minMip < _sparseInfo.maxSparseLevel); - } - assert(_minMip < _maxMip); - assert(_transferrable); - stripToMip(_minMip + 1); -} - -void GL45Backend::derezTextures() const { - if (GLTexture::getMemoryPressure() < 1.0f) { - return; - } - - Lock lock(texturesByMipCountsMutex); - if (texturesByMipCounts.empty()) { - // No available textures to derez - return; - } - - auto mipLevel = texturesByMipCounts.rbegin()->first; - if (mipLevel <= 1) { - // No mips available to remove - return; - } - - GL45Texture* targetTexture = nullptr; - { - auto& textures = texturesByMipCounts[mipLevel]; - assert(!textures.empty()); - targetTexture = *textures.begin(); - } - lock.unlock(); - targetTexture->derez(); -} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp new file mode 100644 index 0000000000..c5db65058d --- /dev/null +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -0,0 +1,888 @@ +// +// GL45BackendTexture.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 1/19/2015. +// 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 "GL45Backend.h" +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "../gl/GLTexelFormat.h" + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gl45; + +// Variable sized textures +using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; +using MemoryPressureState = GL45VariableAllocationTexture::MemoryPressureState; + +std::list GL45VariableAllocationTexture::_memoryManagedTextures; +MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; +std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; +const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; + +#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f +#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f +#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) + +static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); + +using QueuePair = std::pair; +class QueuePairLess { +public: + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second < b.second; + } +}; +class QueuePairGreater { +public: + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second > b.second; + } +}; +using DemoteQueue = std::priority_queue, QueuePairLess>; +using PromoteQueue = std::priority_queue, QueuePairGreater>; +using TransferQueue = std::queue; +static DemoteQueue demoteQueue; +static PromoteQueue promoteQueue; +static TransferQueue transferQueue; + +void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { + _memoryManagedTextures.push_back(texturePointer); + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texturePointer); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + if (object->canDemote()) { + demoteQueue.push({ texturePointer, object->size() }); + } + break; + + case MemoryPressureState::Undersubscribed: + if (object->canPromote()) { + promoteQueue.push({ texturePointer, object->size() }); + } + break; + + case MemoryPressureState::Transfer: + if (object->hasPendingTransfers()) { + transferQueue.push( texturePointer ); + } + break; + + case MemoryPressureState::Idle: + break; + + default: + Q_UNREACHABLE(); + } +} + +void GL45VariableAllocationTexture::updateMemoryPressure() { + static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { + _memoryPressureStateStale = true; + lastAllowedMemoryAllocation = allowedMemoryAllocation; + } + + if (!_memoryPressureStateStale) { + return; + } + _memoryPressureStateStale = false; + // Clear any defunct textures + _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { + return weakPointer.expired(); + }); + + // Convert weak pointers to strong + std::list strongTextures; { + std::transform( + _memoryManagedTextures.begin(), _memoryManagedTextures.end(), + std::back_inserter(strongTextures), + [](const TextureWeakPointer& p) { return p.lock(); }); + } + + size_t totalVariableMemoryAllocation = 0; + size_t idealMemoryAllocation = 0; + bool canDemote = false; + bool canPromote = false; + bool hasTransfers = false; + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + idealMemoryAllocation += texture->evalTotalSize(); + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + totalVariableMemoryAllocation += object->size(); + canDemote |= object->canDemote(); + canPromote |= object->canPromote(); + hasTransfers |= object->hasPendingTransfers(); + } + + size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; + if (0 == allowedMemoryAllocation) { + allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; + } + + + float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; + + auto newState = MemoryPressureState::Idle; + if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + newState = MemoryPressureState::Oversubscribed; + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { + newState = MemoryPressureState::Undersubscribed; + } else if (hasTransfers) { + newState = MemoryPressureState::Transfer; + } + + if (newState != _memoryPressureState) { + _memoryPressureState = newState; + + demoteQueue = DemoteQueue(); + promoteQueue = PromoteQueue(); + transferQueue = TransferQueue(); + + switch (_memoryPressureState) { + case MemoryPressureState::Idle: + break; + + case MemoryPressureState::Oversubscribed: + for (const auto& texture : strongTextures) { + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (object->canDemote()) { + demoteQueue.push({ texture, object->size() }); + } + } + break; + + case MemoryPressureState::Undersubscribed: + for (const auto& texture : strongTextures) { + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (object->canPromote()) { + promoteQueue.push({ texture, object->size() }); + } + } + break; + + case MemoryPressureState::Transfer: + for (const auto& texture : strongTextures) { + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (object->hasPendingTransfers()) { + transferQueue.push(texture); + } + } + break; + + default: + Q_UNREACHABLE(); + break; + } + } +} + +void GL45VariableAllocationTexture::processWorkQueues() { + switch (_memoryPressureState) { + case MemoryPressureState::Idle: + break; + + case MemoryPressureState::Oversubscribed: + // Grab the first item off the demote queue + while (!demoteQueue.empty()) { + auto demoteTarget = demoteQueue.top(); + demoteQueue.pop(); + auto texture = demoteTarget.first.lock(); + if (!texture) { + continue; + } + + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (!object->canDemote()) { + continue; + } + + //qDebug() << "QQQ executing demote for " << texture->source().c_str(); + object->demote(); + // if the object can be further demoted, reinsert into the queue + if (object->canDemote()) { + demoteQueue.push({ demoteTarget.first, object->size() }); + } + break; + } + if (demoteQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; + } + break; + + case MemoryPressureState::Undersubscribed: + while (!promoteQueue.empty()) { + auto promoteTarget = promoteQueue.top(); + promoteQueue.pop(); + auto texture = promoteTarget.first.lock(); + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (!object->canPromote()) { + continue; + } + //qDebug() << "QQQ executing promote for " << texture->source().c_str(); + object->promote(); + if (object->canPromote()) { + promoteQueue.push({ promoteTarget.first, object->size() }); + } + break; + } + if (promoteQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; + } + break; + + case MemoryPressureState::Transfer: + while (!transferQueue.empty()) { + auto weakTexture = transferQueue.front(); + transferQueue.pop(); + auto texture = weakTexture.lock(); + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (!object->hasPendingTransfers()) { + continue; + } + //qDebug() << "QQQ executing transfer for " << texture->source().c_str(); + object->executeNextTransfer(); + if (object->hasPendingTransfers()) { + transferQueue.push(weakTexture); + } + break; + } + if (transferQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; + } + break; + + default: + Q_UNREACHABLE(); + break; + } +} + +void GL45VariableAllocationTexture::manageMemory() { + static auto lastProcessTime = usecTimestampNow(); + auto now = usecTimestampNow(); + auto interval = now - lastProcessTime; + if (interval > (USECS_PER_MSEC * 20)) { + lastProcessTime = now; + updateMemoryPressure(); + processWorkQueues(); + } +} + +GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { +} + +GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { + _memoryPressureStateStale = true; + Backend::updateTextureGPUMemoryUsage(_size, 0); +} + +void GL45VariableAllocationTexture::executeNextTransfer() { + if (!_pendingTransfers.empty()) { + _pendingTransfers.front()(); + _pendingTransfers.pop(); + } +} + +// Managed size resource textures +using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; + +GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { + auto mipLevels = texture.evalNumMips(); + _allocatedMip = mipLevels; + uvec3 mipDimensions; + for (uint16_t mip = 0; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + _maxAllocatedMip = _populatedMip = mip; + break; + } + } + + uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); + allocateStorage(allocatedMip); + _memoryPressureStateStale = true; + copyMipsFromTexture(); + syncSampler(); + +} + +void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { + _allocatedMip = allocatedMip; + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip); + const auto totalMips = _gpuObject.evalNumMips(); + const auto mips = totalMips - _allocatedMip; + glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); + auto mipLevels = _gpuObject.evalNumMips(); + _size = 0; + for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { + _size += _gpuObject.evalMipSize(mip); + } + Backend::updateTextureGPUMemoryUsage(0, _size); + +} + +void GL45ResourceTexture::copyMipsFromTexture() { + auto mipLevels = _gpuObject.evalNumMips(); + for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) { + uint16_t targetMip = sourceMip - _allocatedMip; + copyMipFromTexture(sourceMip, targetMip); + } +} + +void GL45ResourceTexture::syncSampler() const { + Parent::syncSampler(); + const Sampler& sampler = _gpuObject.getSampler(); + uint16_t maxMip = _gpuObject.evalNumMips() - _allocatedMip; + auto minMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); + minMip = std::min(minMip, maxMip); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)minMip); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (float)maxMip); +} + +void GL45ResourceTexture::promote() { + Q_ASSERT(_allocatedMip > 0); + GLuint oldId = _id; + uint32_t oldSize = _size; + // create new texture + const_cast(_id) = allocate(_gpuObject); + uint16_t oldAllocatedMip = _allocatedMip; + // allocate storage for new level + allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); + uint16_t mips = _gpuObject.evalNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } + // destroy the old texture + glDeleteTextures(1, &oldId); + // update the memory usage + Backend::updateTextureGPUMemoryUsage(oldSize, 0); + _memoryPressureStateStale = true; + syncSampler(); + populateTransferQueue(); +} + +void GL45ResourceTexture::demote() { + Q_ASSERT(_allocatedMip < _maxAllocatedMip); + auto oldId = _id; + auto oldSize = _size; + const_cast(_id) = allocate(_gpuObject); + allocateStorage(_allocatedMip + 1); + _populatedMip = std::max(_populatedMip, _allocatedMip); + uint16_t mips = _gpuObject.evalNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = targetMip + 1; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } + // destroy the old texture + glDeleteTextures(1, &oldId); + // update the memory usage + Backend::updateTextureGPUMemoryUsage(oldSize, 0); + _memoryPressureStateStale = true; + syncSampler(); + populateTransferQueue(); +} + +void GL45ResourceTexture::populateTransferQueue() { + _pendingTransfers = std::queue(); + if (_populatedMip <= _allocatedMip) { + return; + } + + for (int16_t mip = _populatedMip - 1; mip >= _allocatedMip; --mip) { + // FIXME break down the transfers into chunks so that no single transfer is + // consuming more than X bandwidth + _pendingTransfers.push([mip, this] { + Q_ASSERT(mip >= _allocatedMip); + // FIXME modify the copy mechanism to be incremental + copyMipFromTexture(mip, mip - _allocatedMip); + _populatedMip = mip; + syncSampler(); + }); + } +} + +// Sparsely allocated, managed size resource textures +#if 0 +#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f + +using GL45SparseResourceTexture = GL45Backend::GL45SparseResourceTexture; + +GL45Texture::PageDimensionsMap GL45Texture::pageDimensionsByFormat; +Mutex GL45Texture::pageDimensionsMutex; + +GL45Texture::PageDimensions GL45Texture::getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { + { + Lock lock(pageDimensionsMutex); + if (pageDimensionsByFormat.count(typeFormat)) { + return pageDimensionsByFormat[typeFormat]; + } + } + + GLint count = 0; + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count); + + std::vector result; + if (count > 0) { + std::vector x, y, z; + x.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]); + y.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]); + z.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]); + + result.resize(count); + for (GLint i = 0; i < count; ++i) { + result[i] = uvec3(x[i], y[i], z[i]); + } + } + + { + Lock lock(pageDimensionsMutex); + if (0 == pageDimensionsByFormat.count(typeFormat)) { + pageDimensionsByFormat[typeFormat] = result; + } + } + + return result; +} + +GL45Texture::PageDimensions GL45Texture::getPageDimensionsForFormat(GLenum target, GLenum format) { + return getPageDimensionsForFormat({ target, format }); +} +bool GL45Texture::isSparseEligible(const Texture& texture) { + Q_ASSERT(TextureUsageType::RESOURCE == texture.getUsageType()); + + // Disabling sparse for the momemnt + return false; + + const auto allowedPageDimensions = getPageDimensionsForFormat(getGLTextureType(texture), + gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())); + const auto textureDimensions = texture.getDimensions(); + for (const auto& pageDimensions : allowedPageDimensions) { + if (uvec3(0) == (textureDimensions % pageDimensions)) { + return true; + } + } + + return false; +} + + +GL45SparseResourceTexture::GL45SparseResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const uvec3 dimensions = _gpuObject.getDimensions(); + auto allowedPageDimensions = getPageDimensionsForFormat(_target, texelFormat.internalFormat); + uint32_t pageDimensionsIndex = 0; + // In order to enable sparse the texture size must be an integer multiple of the page size + for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { + pageDimensionsIndex = (uint32_t)i; + _pageDimensions = allowedPageDimensions[i]; + // Is this texture an integer multiple of page dimensions? + if (uvec3(0) == (dimensions % _pageDimensions)) { + qCDebug(gpugl45logging) << "Enabling sparse for texture " << _gpuObject.source().c_str(); + break; + } + } + glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + glTextureParameteri(_id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); + glGetTextureParameterIuiv(_id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); + + _pageBytes = _gpuObject.getTexelFormat().getSize(); + _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; + // Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for + // sparse textures as compared to non-sparse, so we acount for that here. + _pageBytes = (uint32_t)(_pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE); + + //allocateStorage(); + syncSampler(); +} + +GL45SparseResourceTexture::~GL45SparseResourceTexture() { + Backend::updateTextureGPUVirtualMemoryUsage(size(), 0); +} + +uvec3 GL45SparseResourceTexture::getPageCounts(const uvec3& dimensions) const { + auto result = (dimensions / _pageDimensions) + + glm::clamp(dimensions % _pageDimensions, glm::uvec3(0), glm::uvec3(1)); + return result; +} + +uint32_t GL45SparseResourceTexture::getPageCount(const uvec3& dimensions) const { + auto pageCounts = getPageCounts(dimensions); + return pageCounts.x * pageCounts.y * pageCounts.z; +} + +void GL45SparseResourceTexture::promote() { +} + +void GL45SparseResourceTexture::demote() { +} + +SparseInfo::SparseInfo(GL45Texture& texture) + : texture(texture) { +} + +void SparseInfo::maybeMakeSparse() { + // Don't enable sparse for objects with explicitly managed mip levels + if (!texture._gpuObject.isAutogenerateMips()) { + return; + } + + const uvec3 dimensions = texture._gpuObject.getDimensions(); + auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat); + // In order to enable sparse the texture size must be an integer multiple of the page size + for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { + pageDimensionsIndex = (uint32_t)i; + pageDimensions = allowedPageDimensions[i]; + // Is this texture an integer multiple of page dimensions? + if (uvec3(0) == (dimensions % pageDimensions)) { + qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str(); + sparse = true; + break; + } + } + + if (sparse) { + glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); + } else { + qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << + " is not supported by any sparse page size for texture" << texture._source.c_str(); + } +} + + +// This can only be called after we've established our storage size +void SparseInfo::update() { + if (!sparse) { + return; + } + glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel); + + for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) { + auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel); + auto mipPageCount = getPageCount(mipDimensions); + maxPages += mipPageCount; + } + if (texture._target == GL_TEXTURE_CUBE_MAP) { + maxPages *= GLTexture::CUBE_NUM_FACES; + } +} + + +void SparseInfo::allocateToMip(uint16_t targetMip) { + // Not sparse, do nothing + if (!sparse) { + return; + } + + if (allocatedMip == INVALID_MIP) { + allocatedMip = maxSparseLevel + 1; + } + + // Don't try to allocate below the maximum sparse level + if (targetMip > maxSparseLevel) { + targetMip = maxSparseLevel; + } + + // Already allocated this level + if (allocatedMip <= targetMip) { + return; + } + + uint32_t maxFace = (uint32_t)(GL_TEXTURE_CUBE_MAP == texture._target ? CUBE_NUM_FACES : 1); + for (uint16_t mip = targetMip; mip < allocatedMip; ++mip) { + auto size = texture._gpuObject.evalMipDimensions(mip); + glTexturePageCommitmentEXT(texture._id, mip, 0, 0, 0, size.x, size.y, maxFace, GL_TRUE); + allocatedPages += getPageCount(size); + } + allocatedMip = targetMip; +} + +uint32_t SparseInfo::getSize() const { + return allocatedPages * pageBytes; +} +using SparseInfo = GL45Backend::GL45Texture::SparseInfo; + +void GL45Texture::updateSize() const { + if (_gpuObject.getTexelFormat().isCompressed()) { + qFatal("Compressed textures not yet supported"); + } + + if (_transferrable && _sparseInfo.sparse) { + auto size = _sparseInfo.getSize(); + Backend::updateTextureGPUSparseMemoryUsage(_size, size); + setSize(size); + } else { + setSize(_gpuObject.evalTotalSize(_mipOffset)); + } +} + +void GL45Texture::startTransfer() { + Parent::startTransfer(); + _sparseInfo.update(); + _populatedMip = _maxMip + 1; +} + +bool GL45Texture::continueTransfer() { + size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; + if (_populatedMip == _minMip) { + return false; + } + + uint16_t targetMip = _populatedMip - 1; + while (targetMip > 0 && !_gpuObject.isStoredMipFaceAvailable(targetMip)) { + --targetMip; + } + + _sparseInfo.allocateToMip(targetMip); + for (uint8_t face = 0; face < maxFace; ++face) { + auto size = _gpuObject.evalMipDimensions(targetMip); + if (_gpuObject.isStoredMipFaceAvailable(targetMip, face)) { + auto mip = _gpuObject.accessStoredMipFace(targetMip, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); + if (GL_TEXTURE_2D == _target) { + glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + // DSA ARB does not work on AMD, so use EXT + // unless EXT is not available on the driver + if (glTextureSubImage2DEXT) { + auto target = CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else { + glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); + } + } else { + Q_ASSERT(false); + } + (void)CHECK_GL_ERROR(); + break; + } + } + _populatedMip = targetMip; + return _populatedMip != _minMip; +} + +void GL45Texture::finishTransfer() { + Parent::finishTransfer(); +} + +void GL45Texture::postTransfer() { + Parent::postTransfer(); +} + +void GL45Texture::stripToMip(uint16_t newMinMip) { + if (newMinMip < _minMip) { + qCWarning(gpugl45logging) << "Cannot decrease the min mip"; + return; + } + + if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) { + qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; + return; + } + + // If we weren't generating mips before, we need to now that we're stripping down mip levels. + if (!_gpuObject.isAutogenerateMips()) { + qCDebug(gpugl45logging) << "Force mip generation for texture"; + glGenerateTextureMipmap(_id); + } + + + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + if (_sparseInfo.sparse) { + for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { + auto id = _id; + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages < _sparseInfo.allocatedPages); + _sparseInfo.allocatedPages -= deallocatedPages; + } + _minMip = newMinMip; + } else { + GLuint oldId = _id; + // Find the distance between the old min mip and the new one + uint16 mipDelta = newMinMip - _minMip; + _mipOffset += mipDelta; + const_cast(_maxMip) -= mipDelta; + auto newLevels = usedMipLevels(); + + // Create and setup the new texture (allocate) + glCreateTextures(_target, 1, &const_cast(_id)); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); + glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); + Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset); + glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y); + + // Copy the contents of the old texture to the new + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) { + uint16 sourceMip = targetMip + mipDelta; + Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset); + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTextureSubImage2D(_id, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + glDeleteTextures(1, &oldId); + } + + // Re-sync the sampler to force access to the new mip level + syncSampler(); + updateSize(); +} + +bool GL45Texture::derezable() const { + if (_external) { + return false; + } + auto maxMinMip = _sparseInfo.sparse ? _sparseInfo.maxSparseLevel : _maxMip; + return _transferrable && (_targetMinMip < maxMinMip); +} + +size_t GL45Texture::getMipByteCount(uint16_t mip) const { + if (!_sparseInfo.sparse) { + return Parent::getMipByteCount(mip); + } + + auto dimensions = _gpuObject.evalMipDimensions(_targetMinMip); + return _sparseInfo.getPageCount(dimensions) * _sparseInfo.pageBytes; +} + +std::pair GL45Texture::preDerez() { + assert(!_sparseInfo.sparse || _targetMinMip < _sparseInfo.maxSparseLevel); + size_t freedMemory = getMipByteCount(_targetMinMip); + bool liveMip = _populatedMip != INVALID_MIP && _populatedMip <= _targetMinMip; + ++_targetMinMip; + return { freedMemory, liveMip }; +} + +void GL45Texture::derez() { + if (_sparseInfo.sparse) { + assert(_minMip < _sparseInfo.maxSparseLevel); + } + assert(_minMip < _maxMip); + assert(_transferrable); + stripToMip(_minMip + 1); +} + +size_t GL45Texture::getCurrentGpuSize() const { + if (!_sparseInfo.sparse) { + return Parent::getCurrentGpuSize(); + } + + return _sparseInfo.getSize(); +} + +size_t GL45Texture::getTargetGpuSize() const { + if (!_sparseInfo.sparse) { + return Parent::getTargetGpuSize(); + } + + size_t result = 0; + for (auto mip = _targetMinMip; mip <= _sparseInfo.maxSparseLevel; ++mip) { + result += (_sparseInfo.pageBytes * _sparseInfo.getPageCount(_gpuObject.evalMipDimensions(mip))); + } + + return result; +} + +GL45Texture::~GL45Texture() { + if (_sparseInfo.sparse) { + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + auto maxSparseMip = std::min(_maxMip, _sparseInfo.maxSparseLevel); + for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { + auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); + glTexturePageCommitmentEXT(_texture, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages <= _sparseInfo.allocatedPages); + _sparseInfo.allocatedPages -= deallocatedPages; + } + + if (0 != _sparseInfo.allocatedPages) { + qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages; + } + Backend::decrementTextureGPUSparseCount(); + } +} +GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate(texture)), _sparseInfo(*this), _targetMinMip(_minMip) +{ + + auto theBackend = _backend.lock(); + if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) { + _sparseInfo.maybeMakeSparse(); + if (_sparseInfo.sparse) { + Backend::incrementTextureGPUSparseCount(); + } + } +} +#endif diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index c15da61800..f822da129b 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -292,15 +292,8 @@ void Batch::setUniformBuffer(uint32 slot, const BufferView& view) { setUniformBuffer(slot, view._buffer, view._offset, view._size); } - void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) { - if (texture && texture->getUsage().isExternal()) { - auto recycler = texture->getExternalRecycler(); - Q_ASSERT(recycler); - } - ADD_COMMAND(setResourceTexture); - _params.emplace_back(_textures.cache(texture)); _params.emplace_back(slot); } diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index e8ccfce3b2..0d3291a74d 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -32,7 +32,7 @@ Framebuffer* Framebuffer::create(const std::string& name) { Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, uint16 width, uint16 height) { auto framebuffer = Framebuffer::create(name); - auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); colorTexture->setSource("Framebuffer::colorTexture"); framebuffer->setRenderBuffer(0, colorTexture); @@ -43,8 +43,8 @@ Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBuf Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height) { auto framebuffer = Framebuffer::create(name); - auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); - auto depthTexture = TexturePointer(Texture::create2D(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); framebuffer->setRenderBuffer(0, colorTexture); framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat); @@ -55,7 +55,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) { auto framebuffer = Framebuffer::create("Shadowmap"); auto depthFormat = Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); // Depth32 texel format - auto depthTexture = TexturePointer(Texture::create2D(depthFormat, width, width)); + auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthFormat, width, width)); Sampler::Desc samplerDesc; samplerDesc._borderColor = glm::vec4(1.0f); samplerDesc._wrapModeU = Sampler::WRAP_BORDER; @@ -143,6 +143,8 @@ int Framebuffer::setRenderBuffer(uint32 slot, const TexturePointer& texture, uin return -1; } + Q_ASSERT(!texture || TextureUsageType::RENDERBUFFER == texture->getUsageType()); + // Check for the slot if (slot >= getMaxNumRenderBuffers()) { return -1; @@ -222,6 +224,8 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For return false; } + Q_ASSERT(!texture || TextureUsageType::RENDERBUFFER == texture->getUsageType()); + // Check for the compatibility of size if (texture) { if (!validateTargetCompatibility(*texture)) { diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 5b0c4c876a..9db3fb60c3 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -253,35 +253,42 @@ bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Si return allocated == size; } -Texture* Texture::createExternal2D(const ExternalRecycler& recycler, const Sampler& sampler) { - Texture* tex = new Texture(); +Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { + Texture* tex = new Texture(TextureUsageType::EXTERNAL); tex->_type = TEX_2D; tex->_maxMip = 0; tex->_sampler = sampler; - tex->setUsage(Usage::Builder().withExternal().withColor()); tex->setExternalRecycler(recycler); return tex; } +Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { + return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); +} + Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 1, 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(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); +} + +Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { + return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, 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(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 1, 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(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler); } -Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) +Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) { - Texture* tex = new Texture(); + Texture* tex = new Texture(usageType); tex->_storage.reset(new Storage()); tex->_type = type; tex->_storage->assignTexture(tex); @@ -293,16 +300,14 @@ Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, ui return tex; } -Texture::Texture(): - Resource() -{ +Texture::Texture(TextureUsageType usageType) : + Resource(), _usageType(usageType) { _textureCPUCount++; } -Texture::~Texture() -{ +Texture::~Texture() { _textureCPUCount--; - if (getUsage().isExternal()) { + if (_usageType == TextureUsageType::EXTERNAL) { Texture::ExternalUpdates externalUpdates; { Lock lock(_externalMutex); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 856bd4983d..3c6c34c68d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -139,6 +139,13 @@ protected: Desc _desc; }; +enum class TextureUsageType { + RENDERBUFFER, // Used as attachments to a framebuffer + RESOURCE, // Resource textures, like materials... subject to memory manipulation + STRICT_RESOURCE, // Resource textures not subject to manipulation, like the normal fitting texture + EXTERNAL, +}; + class Texture : public Resource { static std::atomic _textureCPUCount; static std::atomic _textureCPUMemoryUsage; @@ -173,9 +180,9 @@ public: NORMAL, // Texture is a normal map ALPHA, // Texture has an alpha channel ALPHA_MASK, // Texture alpha channel is a Mask 0/1 - EXTERNAL, NUM_FLAGS, }; + typedef std::bitset Flags; // The key is the Flags @@ -199,7 +206,6 @@ public: Builder& withNormal() { _flags.set(NORMAL); return (*this); } Builder& withAlpha() { _flags.set(ALPHA); return (*this); } Builder& withAlphaMask() { _flags.set(ALPHA_MASK); return (*this); } - Builder& withExternal() { _flags.set(EXTERNAL); return (*this); } }; Usage(const Builder& builder) : Usage(builder._flags) {} @@ -208,8 +214,6 @@ public: bool isAlpha() const { return _flags[ALPHA]; } bool isAlphaMask() const { return _flags[ALPHA_MASK]; } - bool isExternal() const { return _flags[EXTERNAL]; } - bool operator==(const Usage& usage) { return (_flags == usage._flags); } bool operator!=(const Usage& usage) { return (_flags != usage._flags); } @@ -298,9 +302,11 @@ public: static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler()); static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); - static Texture* createExternal2D(const ExternalRecycler& recycler, const Sampler& sampler = Sampler()); + static Texture* createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); + static Texture* createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); + static Texture* createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler()); - Texture(); + Texture(TextureUsageType usageType); Texture(const Texture& buf); // deep copy of the sysmem texture Texture& operator=(const Texture& buf); // deep copy of the sysmem texture ~Texture(); @@ -325,6 +331,7 @@ public: // Size and format Type getType() const { return _type; } + TextureUsageType getUsageType() const { return _usageType; } bool isColorRenderTarget() const; bool isDepthStencilRenderTarget() const; @@ -476,6 +483,8 @@ public: ExternalUpdates getUpdates() const; protected: + const TextureUsageType _usageType; + // Should only be accessed internally or by the backend sync function mutable Mutex _externalMutex; mutable std::list _externalUpdates; @@ -513,7 +522,7 @@ protected: bool _isIrradianceValid = false; bool _defined = false; - static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); + static Texture* create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); }; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f371207981..6a84fc960f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -120,7 +120,7 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF }; const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (!_whiteTexture) { - _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _whiteTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _whiteTexture->setSource("TextureCache::_whiteTexture"); _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } @@ -129,7 +129,7 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() { const gpu::TexturePointer& TextureCache::getGrayTexture() { if (!_grayTexture) { - _grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _grayTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _grayTexture->setSource("TextureCache::_grayTexture"); _grayTexture->assignStoredMip(0, _grayTexture->getTexelFormat(), sizeof(OPAQUE_GRAY), OPAQUE_GRAY); } @@ -138,7 +138,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() { const gpu::TexturePointer& TextureCache::getBlueTexture() { if (!_blueTexture) { - _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _blueTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _blueTexture->setSource("TextureCache::_blueTexture"); _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } @@ -147,7 +147,7 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() { const gpu::TexturePointer& TextureCache::getBlackTexture() { if (!_blackTexture) { - _blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _blackTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _blackTexture->setSource("TextureCache::_blackTexture"); _blackTexture->assignStoredMip(0, _blackTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK); } @@ -157,7 +157,7 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() { const gpu::TexturePointer& TextureCache::getNormalFittingTexture() { if (!_normalFittingTexture) { - _normalFittingTexture = getImageTexture(PathUtils::resourcesPath() + "images/normalFittingScale.dds"); + _normalFittingTexture = getImageTexture(PathUtils::resourcesPath() + "images/normalFittingScale.dds", NetworkTexture::STRICT_TEXTURE); } return _normalFittingTexture; } @@ -227,11 +227,16 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return model::TextureUsage::createMetallicTextureFromImage; break; } + case Type::STRICT_TEXTURE: { + return model::TextureUsage::createStrict2DTextureFromImage; + break; + } case Type::CUSTOM_TEXTURE: { Q_ASSERT(false); return NetworkTexture::TextureLoaderFunc(); break; } + case Type::DEFAULT_TEXTURE: default: { return model::TextureUsage::create2DTextureFromImage; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index cb509490c6..749b5a2ebb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -43,6 +43,7 @@ class NetworkTexture : public Resource, public Texture { public: enum Type { DEFAULT_TEXTURE, + STRICT_TEXTURE, ALBEDO_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d1fbaf767a..6a9446f2ef 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -235,7 +235,7 @@ void generateFaceMips(gpu::Texture* texture, QImage& image, gpu::Element formatM #endif } -gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips) { +gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict) { PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage"); bool validAlpha = false; bool alphaAsMask = true; @@ -248,7 +248,11 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag gpu::Element formatMip; defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + if (isStrict) { + theTexture = (gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + } else { + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + } theTexture->setSource(srcImageName); auto usage = gpu::Texture::Usage::Builder().withColor(); if (validAlpha) { @@ -269,11 +273,14 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag return theTexture; } +gpu::Texture* TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true, true); +} + gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true); } - gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true); } diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index 220ee57a97..a4bb861502 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -32,6 +32,7 @@ public: int _environmentUsage = 0; static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName); @@ -47,7 +48,7 @@ public: static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask); static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip, const QImage& srcImage, bool isLinear, bool doCompress); - static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips); + static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict = false); static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance); }; diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 2941197e6d..f95d45de04 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -52,7 +52,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing")); auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); + _antialiasingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height, defaultSampler)); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); } diff --git a/libraries/render-utils/src/DeferredFramebuffer.cpp b/libraries/render-utils/src/DeferredFramebuffer.cpp index e8783e0e0d..40c22beba4 100644 --- a/libraries/render-utils/src/DeferredFramebuffer.cpp +++ b/libraries/render-utils/src/DeferredFramebuffer.cpp @@ -53,9 +53,9 @@ void DeferredFramebuffer::allocate() { auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(linearFormat, width, height, defaultSampler)); - _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + _deferredColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler)); + _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(linearFormat, width, height, defaultSampler)); + _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler)); _deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture); _deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture); @@ -65,7 +65,7 @@ void DeferredFramebuffer::allocate() { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format if (!_primaryDepthTexture) { - _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler)); + _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height, defaultSampler)); } _deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); @@ -75,7 +75,7 @@ void DeferredFramebuffer::allocate() { auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - _lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler)); + _lightingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler)); _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting")); _lightingFramebuffer->setRenderBuffer(0, _lightingTexture); _lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 6f1152ac16..ce340583ee 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -496,14 +496,14 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con auto colorFormat = gpu::Element::COLOR_SRGBA_32; auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, defaultSampler)); + auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, defaultSampler)); _primaryFramebuffer->setRenderBuffer(0, primaryColorTexture); auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, defaultSampler)); + auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, defaultSampler)); _primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 55a9c8b9e4..d1a7080eca 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -190,7 +190,7 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { { // Grab a texture map representing the different status icons and assign that to the drawStatsuJob auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; - auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath, NetworkTexture::STRICT_TEXTURE); addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); } } diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 188381b822..25a01bff1b 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -414,7 +414,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringProfile(Rend const int PROFILE_RESOLUTION = 512; // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; - auto profileMap = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto profileMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); profileMap->setSource("Generated Scattering Profile"); diffuseProfileGPU(profileMap, args); return profileMap; @@ -425,7 +425,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin const int TABLE_RESOLUTION = 512; // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; - auto scatteringLUT = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto scatteringLUT = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); //diffuseScatter(scatteringLUT); scatteringLUT->setSource("Generated pre-integrated scattering"); diffuseScatterGPU(profile, scatteringLUT, args); @@ -434,7 +434,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringSpecularBeckmann(RenderArgs* args) { const int SPECULAR_RESOLUTION = 256; - auto beckmannMap = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto beckmannMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); beckmannMap->setSource("Generated beckmannMap"); computeSpecularBeckmannGPU(beckmannMap, args); return beckmannMap; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index f0ac56ac26..3a23e70664 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -72,18 +72,18 @@ void LinearDepthFramebuffer::allocate() { auto height = _frameSize.y; // For Linear Depth: - _linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, + _linearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); // For Downsampling: - _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, + _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _halfLinearDepthTexture->autoGenerateMips(5); - _halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, + _halfNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _downsampleFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("halfLinearDepth")); @@ -304,15 +304,15 @@ void SurfaceGeometryFramebuffer::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; - _curvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _curvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _curvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::curvature")); _curvatureFramebuffer->setRenderBuffer(0, _curvatureTexture); - _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _lowCurvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::lowCurvature")); _lowCurvatureFramebuffer->setRenderBuffer(0, _lowCurvatureTexture); - _blurringTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _blurringTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _blurringFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::blurring")); _blurringFramebuffer->setRenderBuffer(0, _blurringTexture); } diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 09f3e6dc8c..b759a06aee 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -255,7 +255,7 @@ void OculusLegacyDisplayPlugin::hmdPresent() { memset(eyePoses, 0, sizeof(ovrPosef) * 2); eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation; - GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); + GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); if (_hmdWindow->makeCurrent()) { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 42f3ece9cd..f9c69da4d6 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -494,9 +494,9 @@ void OpenVrDisplayPlugin::customizeContext() { _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { if (0 != i) { - _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); + _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); } - _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture, false); + _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture); } _submitThread->_canvas = _submitCanvas; _submitThread->start(QThread::HighPriority); @@ -624,7 +624,7 @@ void OpenVrDisplayPlugin::compositeLayers() { glFlush(); if (!newComposite.textureID) { - newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture, false); + newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture); } withPresentThreadLock([&] { _submitThread->update(newComposite); @@ -638,7 +638,7 @@ void OpenVrDisplayPlugin::hmdPresent() { if (_threadedSubmit) { _submitThread->waitForPresent(); } else { - GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); + GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); vr::Texture_t vrTexture { (void*)glTexId, vr::API_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 7e9d2c426f..522fe79b10 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -642,7 +642,6 @@ protected: gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(64)); return; - default: break; } diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index 09a420f018..d924f76232 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -48,6 +48,7 @@ #include #include +#include #include #include #include From 1238edd0d7b8dcf8bfd34f02f3adf494f0578ced Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Feb 2017 10:20:24 -0800 Subject: [PATCH 010/106] Add incremental transfers for large mips --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 3 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 47 ++++++++------ .../gpu/gl45/GL45BackendVariableTexture.cpp | 63 ++++++++++++++++--- 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index e44f71e3e5..f811b26d94 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -38,7 +38,8 @@ public: protected: GL45Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; - void copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const; + void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const; + void copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const; virtual void syncSampler() const; }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index c46c07ee37..21c211ca20 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -120,32 +120,36 @@ void GL45Texture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GL45Texture::copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const { +void GL45Texture::copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const { const auto& texture = _gpuObject; if (!texture.isStoredMipFaceAvailable(sourceMip)) { return; } - size_t maxFace = GLTexture::getFaceCount(_target); - for (uint8_t face = 0; face < maxFace; ++face) { - auto size = texture.evalMipDimensions(sourceMip); - auto mipData = texture.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat()); - if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData()); - } else if (GL_TEXTURE_CUBE_MAP == _target) { - // DSA ARB does not work on AMD, so use EXT - // unless EXT is not available on the driver - if (glTextureSubImage2DEXT) { - auto target = GLTexture::CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData()); - } else { - glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mipData->readData()); - } + auto mipDimensions = texture.evalMipDimensions(sourceMip); + glm::uvec3 size = { mipDimensions.x, lines, mipDimensions.z }; + auto mipData = texture.accessStoredMipFace(sourceMip, face); + auto sourcePointer = mipData->readData() + dataOffset; + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat()); + if (GL_TEXTURE_2D == _target) { + glTextureSubImage2D(_id, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + // DSA ARB does not work on AMD, so use EXT + // unless EXT is not available on the driver + if (glTextureSubImage2DEXT) { + auto target = GLTexture::CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer); } else { - Q_ASSERT(false); + glTextureSubImage3D(_id, targetMip, 0, lineOffset, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, sourcePointer); } - (void)CHECK_GL_ERROR(); + } else { + Q_ASSERT(false); } + (void)CHECK_GL_ERROR(); +} + +void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { + auto size = _gpuObject.evalMipDimensions(sourceMip); + copyMipFaceLinesFromTexture(sourceMip, targetMip, face, 0, size.y, 0); } void GL45Texture::syncSampler() const { @@ -221,7 +225,10 @@ GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr= _allocatedMip; --mip) { - // FIXME break down the transfers into chunks so that no single transfer is - // consuming more than X bandwidth - _pendingTransfers.push([mip, this] { - Q_ASSERT(mip >= _allocatedMip); - // FIXME modify the copy mechanism to be incremental - copyMipFromTexture(mip, mip - _allocatedMip); - _populatedMip = mip; + static const uvec3 MAX_TRANSFER_DIMENSIONS { 512, 512, 1 }; + static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; + const uint8_t maxFace = GLTexture::getFaceCount(_target); + + uint16_t sourceMip = _populatedMip; + do { + --sourceMip; + auto targetMip = sourceMip - _allocatedMip; + auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + for (uint8_t face = 0; face < maxFace; ++face) { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + continue; + } + + // If the mip is less than the max transfer size, then just do it in one transfer + if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { + // Can the mip be transferred in one go + _pendingTransfers.push([=] { + Q_ASSERT(sourceMip >= _allocatedMip); + // FIXME modify the copy mechanism to be incremental + copyMipFaceFromTexture(sourceMip, targetMip, face); + }); + continue; + } + + // break down the transfers into chunks so that no single transfer is + // consuming more than X bandwidth + auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); + const auto lines = mipDimensions.y; + auto bytesPerLine = (uint32_t)mipData->getSize() / lines; + Q_ASSERT(0 == (mipData->getSize() % lines)); + auto linesPerTransfer = MAX_TRANSFER_SIZE / bytesPerLine; + size_t offset = 0; + uint32_t lineOffset = 0; + while (lineOffset < lines) { + uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); + uvec3 size { mipDimensions.x, linesToCopy, 1 }; + _pendingTransfers.push([=] { + copyMipFaceLinesFromTexture(sourceMip, targetMip, face, lineOffset, linesToCopy, offset); + }); + lineOffset += linesToCopy; + offset += (linesToCopy * bytesPerLine); + } + } + + // queue up the sampler and populated mip change for after the transfer has completed + _pendingTransfers.push([=] { + _populatedMip = targetMip; syncSampler(); }); - } + } while (sourceMip != _allocatedMip); } // Sparsely allocated, managed size resource textures From fe5c511eeb310f5ef7181a0cf61353f27cff1b59 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Feb 2017 12:09:59 -0800 Subject: [PATCH 011/106] Fix texture count --- libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index a6ab13c258..67ab1b5bb5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -385,6 +385,7 @@ void GL45ResourceTexture::promote() { uint32_t oldSize = _size; // create new texture const_cast(_id) = allocate(_gpuObject); + incrementTextureGPUCount(); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); @@ -418,6 +419,7 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast(_id) = allocate(_gpuObject); + incrementTextureGPUCount(); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); uint16_t mips = _gpuObject.evalNumMips(); @@ -482,7 +484,7 @@ void GL45ResourceTexture::populateTransferQueue() { const auto lines = mipDimensions.y; auto bytesPerLine = (uint32_t)mipData->getSize() / lines; Q_ASSERT(0 == (mipData->getSize() % lines)); - auto linesPerTransfer = MAX_TRANSFER_SIZE / bytesPerLine; + uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); size_t offset = 0; uint32_t lineOffset = 0; while (lineOffset < lines) { From 0d89b3a922c86c9f3598729a4526a1a508020b19 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Feb 2017 15:29:59 -0800 Subject: [PATCH 012/106] Better sampler handling --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 10 ++-------- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 6 ------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 21c211ca20..23ab9b203c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -171,14 +171,8 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); - -#if 0 - // FIXME account for mip offsets here - auto baseMip = std::max(sampler.getMipOffset(), _minMip); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); - glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); - glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip() - _mipOffset)); -#endif + glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); } using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 67ab1b5bb5..b6c72aab77 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -370,13 +370,7 @@ void GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); - const Sampler& sampler = _gpuObject.getSampler(); - uint16_t maxMip = _gpuObject.evalNumMips() - _allocatedMip; - auto minMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); - minMip = std::min(minMip, maxMip); glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); - glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)minMip); - glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (float)maxMip); } void GL45ResourceTexture::promote() { From 283ff01038e60e61f3a03b03c05cb6840403da5c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Feb 2017 18:29:20 -0800 Subject: [PATCH 013/106] Remove duplicate code, polish --- .../src/gpu/gl41/GL41BackendTexture.cpp | 2 +- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 12 + .../src/gpu/gl45/GL45BackendTexture.cpp | 1 + .../gpu/gl45/GL45BackendVariableTexture.cpp | 229 ++++++------------ 4 files changed, 82 insertions(+), 162 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 65c4dda202..cf389c83f3 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -69,7 +69,7 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture) : GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) { - + incrementTextureGPUCount(); withPreservedTexture([&] { GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); const Sampler& sampler = _gpuObject.getSampler(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index f811b26d94..e6a61e9498 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -94,15 +94,27 @@ public: Undersubscribed, }; + using QueuePair = std::pair; + class QueuePairLess { + public: + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second < b.second; + } + }; + using WorkQueue = std::priority_queue, QueuePairLess>; + protected: static std::atomic _memoryPressureStateStale; static MemoryPressureState _memoryPressureState; static std::list _memoryManagedTextures; + static WorkQueue _workQueue; static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; + static void updateMemoryPressure(); static void processWorkQueues(); static void addMemoryManagedTexture(const TexturePointer& texturePointer); + static void addToWorkQueue(const TexturePointer& texture); static void manageMemory(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 23ab9b203c..87eb5228a4 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -107,6 +107,7 @@ using GL45Texture = GL45Backend::GL45Texture; GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) : GLTexture(backend, texture, allocate(texture)) { + incrementTextureGPUCount(); } GLuint GL45Texture::allocate(const Texture& texture) { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index b6c72aab77..0f14c9cc43 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -30,11 +30,13 @@ using namespace gpu::gl45; // Variable sized textures using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; using MemoryPressureState = GL45VariableAllocationTexture::MemoryPressureState; +using WorkQueue = GL45VariableAllocationTexture::WorkQueue; std::list GL45VariableAllocationTexture::_memoryManagedTextures; MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; +WorkQueue GL45VariableAllocationTexture::_workQueue; #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f #define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f @@ -42,45 +44,29 @@ const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); -using QueuePair = std::pair; -class QueuePairLess { -public: - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second < b.second; - } -}; -class QueuePairGreater { -public: - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second > b.second; - } -}; -using DemoteQueue = std::priority_queue, QueuePairLess>; -using PromoteQueue = std::priority_queue, QueuePairGreater>; -using TransferQueue = std::queue; -static DemoteQueue demoteQueue; -static PromoteQueue promoteQueue; -static TransferQueue transferQueue; - void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { _memoryManagedTextures.push_back(texturePointer); + addToWorkQueue(texturePointer); +} + +void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texturePointer) { GL45VariableAllocationTexture* object = Backend::getGPUObject(*texturePointer); switch (_memoryPressureState) { case MemoryPressureState::Oversubscribed: if (object->canDemote()) { - demoteQueue.push({ texturePointer, object->size() }); + _workQueue.push({ texturePointer, (float) object->size() }); } break; case MemoryPressureState::Undersubscribed: if (object->canPromote()) { - promoteQueue.push({ texturePointer, object->size() }); + _workQueue.push({ texturePointer, 1.0f / (float)object->size() }); } break; case MemoryPressureState::Transfer: if (object->hasPendingTransfers()) { - transferQueue.push( texturePointer ); + _workQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); } break; @@ -94,23 +80,32 @@ void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer void GL45VariableAllocationTexture::updateMemoryPressure() { static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + if (0 == allowedMemoryAllocation) { + allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; + } + + // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale + // so that we react if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { _memoryPressureStateStale = true; lastAllowedMemoryAllocation = allowedMemoryAllocation; } - if (!_memoryPressureStateStale) { + if (!_memoryPressureStateStale.exchange(false)) { return; } - _memoryPressureStateStale = false; - // Clear any defunct textures + + // Clear any defunct textures (weak pointers that no longer have a valid texture) _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { return weakPointer.expired(); }); - // Convert weak pointers to strong - std::list strongTextures; { + // Convert weak pointers to strong. This new list may still contain nulls if a texture was + // deleted on another thread between the previous line and this one + std::vector strongTextures; { + strongTextures.reserve(_memoryManagedTextures.size()); std::transform( _memoryManagedTextures.begin(), _memoryManagedTextures.end(), std::back_inserter(strongTextures), @@ -127,8 +122,10 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { if (!texture) { continue; } - idealMemoryAllocation += texture->evalTotalSize(); GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + // Track how much the texture thinks it should be using + idealMemoryAllocation += texture->evalTotalSize(); + // Track how much we're actually using totalVariableMemoryAllocation += object->size(); canDemote |= object->canDemote(); canPromote |= object->canPromote(); @@ -136,11 +133,6 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { } size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; - if (0 == allowedMemoryAllocation) { - allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; - } - - float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; auto newState = MemoryPressureState::Idle; @@ -154,142 +146,59 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { if (newState != _memoryPressureState) { _memoryPressureState = newState; - - demoteQueue = DemoteQueue(); - promoteQueue = PromoteQueue(); - transferQueue = TransferQueue(); - - switch (_memoryPressureState) { - case MemoryPressureState::Idle: - break; - - case MemoryPressureState::Oversubscribed: - for (const auto& texture : strongTextures) { - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (object->canDemote()) { - demoteQueue.push({ texture, object->size() }); - } - } - break; - - case MemoryPressureState::Undersubscribed: - for (const auto& texture : strongTextures) { - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (object->canPromote()) { - promoteQueue.push({ texture, object->size() }); - } - } - break; - - case MemoryPressureState::Transfer: - for (const auto& texture : strongTextures) { - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (object->hasPendingTransfers()) { - transferQueue.push(texture); - } - } - break; - - default: - Q_UNREACHABLE(); - break; + // Clear the existing queue + _workQueue = WorkQueue(); + // Populate the existing textures into the queue + for (const auto& texture : strongTextures) { + addToWorkQueue(texture); } } } void GL45VariableAllocationTexture::processWorkQueues() { - switch (_memoryPressureState) { - case MemoryPressureState::Idle: - break; + if (MemoryPressureState::Idle == _memoryPressureState) { + return; + } - case MemoryPressureState::Oversubscribed: - // Grab the first item off the demote queue - while (!demoteQueue.empty()) { - auto demoteTarget = demoteQueue.top(); - demoteQueue.pop(); - auto texture = demoteTarget.first.lock(); - if (!texture) { - continue; - } + while (!_workQueue.empty()) { + auto workTarget = _workQueue.top(); + _workQueue.pop(); + auto texture = workTarget.first.lock(); + if (!texture) { + continue; + } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (!object->canDemote()) { - continue; - } - - //qDebug() << "QQQ executing demote for " << texture->source().c_str(); - object->demote(); - // if the object can be further demoted, reinsert into the queue - if (object->canDemote()) { - demoteQueue.push({ demoteTarget.first, object->size() }); - } - break; + // Grab the first item off the demote queue + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (MemoryPressureState::Oversubscribed == _memoryPressureState) { + if (!object->canDemote()) { + continue; } - if (demoteQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; + //qDebug() << "QQQ executing demote for " << texture->source().c_str(); + object->demote(); + } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { + if (!object->canPromote()) { + continue; } - break; - - case MemoryPressureState::Undersubscribed: - while (!promoteQueue.empty()) { - auto promoteTarget = promoteQueue.top(); - promoteQueue.pop(); - auto texture = promoteTarget.first.lock(); - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (!object->canPromote()) { - continue; - } - //qDebug() << "QQQ executing promote for " << texture->source().c_str(); - object->promote(); - if (object->canPromote()) { - promoteQueue.push({ promoteTarget.first, object->size() }); - } - break; + //qDebug() << "QQQ executing promote for " << texture->source().c_str(); + object->promote(); + } else if (MemoryPressureState::Transfer == _memoryPressureState) { + if (!object->hasPendingTransfers()) { + continue; } - if (promoteQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; - } - break; - - case MemoryPressureState::Transfer: - while (!transferQueue.empty()) { - auto weakTexture = transferQueue.front(); - transferQueue.pop(); - auto texture = weakTexture.lock(); - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (!object->hasPendingTransfers()) { - continue; - } - //qDebug() << "QQQ executing transfer for " << texture->source().c_str(); - object->executeNextTransfer(); - if (object->hasPendingTransfers()) { - transferQueue.push(weakTexture); - } - break; - } - if (transferQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; - } - break; - - default: + //qDebug() << "QQQ executing transfer for " << texture->source().c_str(); + object->executeNextTransfer(); + } else { Q_UNREACHABLE(); - break; + } + + // Reinject into the queue if more work to be done + addToWorkQueue(texture); + break; + } + + if (_workQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; } } @@ -379,7 +288,6 @@ void GL45ResourceTexture::promote() { uint32_t oldSize = _size; // create new texture const_cast(_id) = allocate(_gpuObject); - incrementTextureGPUCount(); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); @@ -413,7 +321,6 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast(_id) = allocate(_gpuObject); - incrementTextureGPUCount(); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); uint16_t mips = _gpuObject.evalNumMips(); From 35a6359d5976b61fab4f21f17188872a6e6122b5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Feb 2017 10:56:01 -0800 Subject: [PATCH 014/106] Fixing transfer logic, adding stats display & profile ranges --- interface/resources/qml/Stats.qml | 11 +-- interface/src/ui/Stats.cpp | 3 + interface/src/ui/Stats.h | 2 + libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 7 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 3 - .../gpu/gl45/GL45BackendVariableTexture.cpp | 70 ++++++++++++++++--- 6 files changed, 72 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index faf37d5366..4cba41f6cc 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -241,7 +241,7 @@ Item { text: "GPU Textures: "; } StatText { - text: " Sparse Enabled: " + (0 == root.gpuSparseTextureEnabled ? "false" : "true"); + text: " Pressure State: " + root.gpuTextureMemoryPressureState; } StatText { text: " Count: " + root.gpuTextures; @@ -253,14 +253,7 @@ Item { text: " Decimated: " + root.decimatedTextureCount; } StatText { - text: " Sparse Count: " + root.gpuTexturesSparse; - visible: 0 != root.gpuSparseTextureEnabled; - } - StatText { - text: " Virtual Memory: " + root.gpuTextureVirtualMemory + " MB"; - } - StatText { - text: " Commited Memory: " + root.gpuTextureMemory + " MB"; + text: " Resource Memory: " + root.gpuTextureMemory + " MB"; } StatText { text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB"; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index e82f99bed2..7af454b702 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -38,6 +38,8 @@ using namespace std; static Stats* INSTANCE{ nullptr }; +QString getTextureMemoryPressureModeString(); + Stats* Stats::getInstance() { if (!INSTANCE) { Stats::registerType(); @@ -323,6 +325,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(gpuTextureVirtualMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUVirtualMemoryUsage())); STAT_UPDATE(gpuTextureFramebufferMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUFramebufferMemoryUsage())); STAT_UPDATE(gpuTextureSparseMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUSparseMemoryUsage())); + STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString()); STAT_UPDATE(gpuSparseTextureEnabled, gpuContext->getBackend()->isTextureManagementSparseEnabled() ? 1 : 0); STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemory())); STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index f501f4b09a..069429d639 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -110,6 +110,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, gpuTextureFramebufferMemory, 0) STATS_PROPERTY(int, gpuTextureSparseMemory, 0) STATS_PROPERTY(int, gpuSparseTextureEnabled, 0) + STATS_PROPERTY(QString, gpuTextureMemoryPressureState, QString()) STATS_PROPERTY(int, gpuFreeMemory, 0) STATS_PROPERTY(float, gpuFrameTime, 0) STATS_PROPERTY(float, batchFrameTime, 0) @@ -217,6 +218,7 @@ signals: void gpuTextureVirtualMemoryChanged(); void gpuTextureFramebufferMemoryChanged(); void gpuTextureSparseMemoryChanged(); + void gpuTextureMemoryPressureStateChanged(); void gpuSparseTextureEnabledChanged(); void gpuFreeMemoryChanged(); void gpuFrameTimeChanged(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index e6a61e9498..1983088ca5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -103,11 +103,13 @@ public: }; using WorkQueue = std::priority_queue, QueuePairLess>; + static MemoryPressureState _memoryPressureState; protected: static std::atomic _memoryPressureStateStale; - static MemoryPressureState _memoryPressureState; static std::list _memoryManagedTextures; - static WorkQueue _workQueue; + static WorkQueue _transferQueue; + static WorkQueue _promoteQueue; + static WorkQueue _demoteQueue; static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; @@ -115,6 +117,7 @@ public: static void processWorkQueues(); static void addMemoryManagedTexture(const TexturePointer& texturePointer); static void addToWorkQueue(const TexturePointer& texture); + static WorkQueue& getActiveWorkQueue(); static void manageMemory(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 87eb5228a4..c344b453a9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -35,9 +35,6 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { } const Texture& texture = *texturePointer; - if (std::string("cursor texture") == texture.source()) { - qDebug() << "Loading cursor texture"; - } if (TextureUsageType::EXTERNAL == texture.getUsageType()) { return Parent::syncGPUObject(texturePointer); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 0f14c9cc43..282ec2161c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -36,7 +36,9 @@ std::list GL45VariableAllocationTexture::_memoryManagedTextu MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; -WorkQueue GL45VariableAllocationTexture::_workQueue; +WorkQueue GL45VariableAllocationTexture::_transferQueue; +WorkQueue GL45VariableAllocationTexture::_promoteQueue; +WorkQueue GL45VariableAllocationTexture::_demoteQueue; #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f #define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f @@ -54,19 +56,19 @@ void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texture switch (_memoryPressureState) { case MemoryPressureState::Oversubscribed: if (object->canDemote()) { - _workQueue.push({ texturePointer, (float) object->size() }); + _demoteQueue.push({ texturePointer, (float)object->size() }); } break; case MemoryPressureState::Undersubscribed: if (object->canPromote()) { - _workQueue.push({ texturePointer, 1.0f / (float)object->size() }); + _promoteQueue.push({ texturePointer, 1.0f / (float)object->size() }); } break; case MemoryPressureState::Transfer: if (object->hasPendingTransfers()) { - _workQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); + _transferQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); } break; @@ -78,6 +80,44 @@ void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texture } } +WorkQueue& GL45VariableAllocationTexture::getActiveWorkQueue() { + static WorkQueue empty; + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return _demoteQueue; + + case MemoryPressureState::Undersubscribed: + return _promoteQueue; + + case MemoryPressureState::Transfer: + return _transferQueue; + + default: + break; + } + Q_UNREACHABLE(); + return empty; +} + +// FIXME hack for stats display +QString getTextureMemoryPressureModeString() { + switch (GL45VariableAllocationTexture::_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return "Oversubscribed"; + + case MemoryPressureState::Undersubscribed: + return "Undersubscribed"; + + case MemoryPressureState::Transfer: + return "Transfer"; + + case MemoryPressureState::Idle: + return "Idle"; + } + Q_UNREACHABLE(); + return "Unknown"; +} + void GL45VariableAllocationTexture::updateMemoryPressure() { static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); @@ -97,6 +137,8 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { return; } + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + // Clear any defunct textures (weak pointers that no longer have a valid texture) _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { return weakPointer.expired(); @@ -147,7 +189,9 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { if (newState != _memoryPressureState) { _memoryPressureState = newState; // Clear the existing queue - _workQueue = WorkQueue(); + _transferQueue = WorkQueue(); + _promoteQueue = WorkQueue(); + _demoteQueue = WorkQueue(); // Populate the existing textures into the queue for (const auto& texture : strongTextures) { addToWorkQueue(texture); @@ -160,9 +204,11 @@ void GL45VariableAllocationTexture::processWorkQueues() { return; } - while (!_workQueue.empty()) { - auto workTarget = _workQueue.top(); - _workQueue.pop(); + auto& workQueue = getActiveWorkQueue(); + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + while (!workQueue.empty()) { + auto workTarget = workQueue.top(); + workQueue.pop(); auto texture = workTarget.first.lock(); if (!texture) { continue; @@ -197,7 +243,7 @@ void GL45VariableAllocationTexture::processWorkQueues() { break; } - if (_workQueue.empty()) { + if (workQueue.empty()) { _memoryPressureState = MemoryPressureState::Idle; } } @@ -208,6 +254,7 @@ void GL45VariableAllocationTexture::manageMemory() { auto interval = now - lastProcessTime; if (interval > (USECS_PER_MSEC * 20)) { lastProcessTime = now; + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); updateMemoryPressure(); processWorkQueues(); } @@ -283,6 +330,7 @@ void GL45ResourceTexture::syncSampler() const { } void GL45ResourceTexture::promote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); GLuint oldId = _id; uint32_t oldSize = _size; @@ -317,6 +365,7 @@ void GL45ResourceTexture::promote() { } void GL45ResourceTexture::demote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip < _maxAllocatedMip); auto oldId = _id; auto oldSize = _size; @@ -349,6 +398,7 @@ void GL45ResourceTexture::demote() { } void GL45ResourceTexture::populateTransferQueue() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); _pendingTransfers = std::queue(); if (_populatedMip <= _allocatedMip) { return; @@ -401,7 +451,7 @@ void GL45ResourceTexture::populateTransferQueue() { // queue up the sampler and populated mip change for after the transfer has completed _pendingTransfers.push([=] { - _populatedMip = targetMip; + _populatedMip = sourceMip; syncSampler(); }); } while (sourceMip != _allocatedMip); From 439cb388f2205ca9733217822301b4b73d00841c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Feb 2017 11:15:22 -0800 Subject: [PATCH 015/106] Increase the rate of work queue processing --- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 282ec2161c..22bb26cc10 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -249,15 +249,9 @@ void GL45VariableAllocationTexture::processWorkQueues() { } void GL45VariableAllocationTexture::manageMemory() { - static auto lastProcessTime = usecTimestampNow(); - auto now = usecTimestampNow(); - auto interval = now - lastProcessTime; - if (interval > (USECS_PER_MSEC * 20)) { - lastProcessTime = now; - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - updateMemoryPressure(); - processWorkQueues(); - } + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + updateMemoryPressure(); + processWorkQueues(); } GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { @@ -404,7 +398,7 @@ void GL45ResourceTexture::populateTransferQueue() { return; } - static const uvec3 MAX_TRANSFER_DIMENSIONS { 512, 512, 1 }; + static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; const uint8_t maxFace = GLTexture::getFaceCount(_target); From 066a6483a0badf9b533d2e77b4b3b7a6e8768971 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Feb 2017 11:23:37 -0800 Subject: [PATCH 016/106] Code comments --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 13 +++++++++++-- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 1983088ca5..d2d17160ba 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -134,10 +134,20 @@ public: virtual void promote() = 0; virtual void demote() = 0; - uint16 _populatedMip { 0 }; + // The allocated mip level, relative to the number of mips in the gpu::Texture object + // The relationship between a given glMip to the original gpu::Texture mip is always + // glMip + _allocatedMip uint16 _allocatedMip { 0 }; + // The populated mip level, relative to the number of mips in the gpu::Texture object + // This must always be >= the allocated mip + uint16 _populatedMip { 0 }; + // The highest (lowest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object uint16 _maxAllocatedMip { 0 }; uint32 _size { 0 }; + // Contains a series of lambdas that when executed will transfer data to the GPU, modify + // the _populatedMip and update the sampler in order to fully populate the allocated texture + // until _populatedMip == _allocatedMip std::queue _pendingTransfers; }; @@ -154,7 +164,6 @@ public: void allocateStorage(uint16 mip); void copyMipsFromTexture(); - private: }; #if 0 diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 22bb26cc10..597e35750a 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -56,18 +56,21 @@ void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texture switch (_memoryPressureState) { case MemoryPressureState::Oversubscribed: if (object->canDemote()) { + // Demote largest first _demoteQueue.push({ texturePointer, (float)object->size() }); } break; case MemoryPressureState::Undersubscribed: if (object->canPromote()) { + // Promote smallest first _promoteQueue.push({ texturePointer, 1.0f / (float)object->size() }); } break; case MemoryPressureState::Transfer: if (object->hasPendingTransfers()) { + // Transfer priority given to smaller mips first _transferQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); } break; From 75c17e89a27d5ce287b2a1af5191bb5d514326d0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 13 Feb 2017 12:37:42 -0800 Subject: [PATCH 017/106] Fix OpenGL 4.1 texture loading --- .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index cf389c83f3..efbc6903b1 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -73,14 +73,21 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& withPreservedTexture([&] { GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); const Sampler& sampler = _gpuObject.getSampler(); - auto minMip = sampler.getMinMip(); - auto maxMip = sampler.getMaxMip(); - for (uint16_t l = minMip; l <= maxMip; l++) { + auto numMips = _gpuObject.evalNumMips(); + for (uint16_t mipLevel = 0; mipLevel < numMips; ++mipLevel) { // Get the mip level dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(l); + Vec3u dimensions = _gpuObject.evalMipDimensions(mipLevel); + uint8_t face = 0; for (GLenum target : getFaceTargets(_target)) { - glTexImage2D(target, l - minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); + const Byte* mipData = nullptr; + if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { + auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); + mipData = mip->readData(); + texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); + } + glTexImage2D(target, mipLevel, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, mipData); (void)CHECK_GL_ERROR(); + ++face; } } }); From 1f058f069e16976a35f39ed7d587fd0c92289e39 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 14 Feb 2017 17:58:41 -0800 Subject: [PATCH 018/106] First pass at new texture transfer logic --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 48 +++++- .../src/gpu/gl45/GL45BackendTexture.cpp | 24 ++- .../gpu/gl45/GL45BackendVariableTexture.cpp | 163 +++++++++++++++--- 3 files changed, 192 insertions(+), 43 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index d2d17160ba..4f299d417f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -14,6 +14,7 @@ #include "../gl/GLBackend.h" #include "../gl/GLTexture.h" +#include #define INCREMENTAL_TRANSFER 0 @@ -39,7 +40,7 @@ public: GL45Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const; - void copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const; virtual void syncSampler() const; }; @@ -95,14 +96,50 @@ public: }; using QueuePair = std::pair; - class QueuePairLess { - public: + struct QueuePairLess { bool operator()(const QueuePair& a, const QueuePair& b) { return a.second < b.second; } }; using WorkQueue = std::priority_queue, QueuePairLess>; + class TransferJob { + using VoidLambda = std::function; + using VoidLambdaQueue = std::queue; + using ThreadPointer = std::shared_ptr; + const GL45VariableAllocationTexture& _parent; + const uint16_t _sourceMip; + const uint16_t _targetMip; + const uint8_t _face; + const uint32_t _lines; + const uint32_t _lineOffset; + // Holds the contents to transfer to the GPU in CPU memory + std::vector _buffer; + // Indicates if a transfer from backing storage to interal storage has started + bool _bufferingStarted { false }; + bool _transferOnly { false }; + bool _bufferingCompleted { false }; + VoidLambda _transferLambda; + VoidLambda _bufferingLambda; + static ThreadPointer _bufferThread; + static Mutex _mutex; + static VoidLambdaQueue _bufferLambdaQueue; + static std::atomic _shutdownBufferingThread; + static void bufferLoop(); + + public: + TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); + TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); + bool tryTransfer(); + static void startTransferLoop(); + static void stopTransferLoop(); + + private: + void startBuffering(); + void transfer(); + }; + + using TransferQueue = std::queue; static MemoryPressureState _memoryPressureState; protected: static std::atomic _memoryPressureStateStale; @@ -110,6 +147,7 @@ public: static WorkQueue _transferQueue; static WorkQueue _promoteQueue; static WorkQueue _demoteQueue; + static TexturePointer _currentTransferTexture; static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; @@ -128,7 +166,7 @@ public: bool canPromote() const { return _allocatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return !_pendingTransfers.empty(); } - void executeNextTransfer(); + void executeNextTransfer(const TexturePointer& currentTexture); uint32 size() const override { return _size; } virtual void populateTransferQueue() = 0; virtual void promote() = 0; @@ -148,7 +186,7 @@ public: // Contains a series of lambdas that when executed will transfer data to the GPU, modify // the _populatedMip and update the sampler in order to fully populate the allocated texture // until _populatedMip == _allocatedMip - std::queue _pendingTransfers; + TransferQueue _pendingTransfers; }; class GL45ResourceTexture : public GL45VariableAllocationTexture { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index c344b453a9..6dd1d6aea3 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -118,26 +118,17 @@ void GL45Texture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GL45Texture::copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const { - const auto& texture = _gpuObject; - if (!texture.isStoredMipFaceAvailable(sourceMip)) { - return; - } - auto mipDimensions = texture.evalMipDimensions(sourceMip); - glm::uvec3 size = { mipDimensions.x, lines, mipDimensions.z }; - auto mipData = texture.accessStoredMipFace(sourceMip, face); - auto sourcePointer = mipData->readData() + dataOffset; - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat()); +void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const { if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer); + glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); } else if (GL_TEXTURE_CUBE_MAP == _target) { // DSA ARB does not work on AMD, so use EXT // unless EXT is not available on the driver if (glTextureSubImage2DEXT) { auto target = GLTexture::CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer); + glTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); } else { - glTextureSubImage3D(_id, targetMip, 0, lineOffset, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, sourcePointer); + glTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, format, type, sourcePointer); } } else { Q_ASSERT(false); @@ -146,8 +137,13 @@ void GL45Texture::copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targe } void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { + return; + } auto size = _gpuObject.evalMipDimensions(sourceMip); - copyMipFaceLinesFromTexture(sourceMip, targetMip, face, 0, size.y, 0); + auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mipData->getFormat()); + copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); } void GL45Texture::syncSampler() const { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 597e35750a..e26a5c262f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -39,6 +39,7 @@ const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, WorkQueue GL45VariableAllocationTexture::_transferQueue; WorkQueue GL45VariableAllocationTexture::_promoteQueue; WorkQueue GL45VariableAllocationTexture::_demoteQueue; +TexturePointer GL45VariableAllocationTexture::_currentTransferTexture; #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f #define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f @@ -46,6 +47,123 @@ WorkQueue GL45VariableAllocationTexture::_demoteQueue; static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); +using TransferJob = GL45VariableAllocationTexture::TransferJob; + +static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; +static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; + +std::shared_ptr TransferJob::_bufferThread { nullptr }; +std::atomic TransferJob::_shutdownBufferingThread { false }; +Mutex TransferJob::_mutex; +TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue; + +void TransferJob::startTransferLoop() { + if (_bufferThread) { + return; + } + _shutdownBufferingThread = false; + _bufferThread = std::make_shared([] { + TransferJob::bufferLoop(); + }); +} + +void TransferJob::stopTransferLoop() { + if (!_bufferThread) { + return; + } + _shutdownBufferingThread = true; + _bufferThread->join(); + _bufferThread.reset(); + _shutdownBufferingThread = false; +} + +TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) + : _parent(parent), _sourceMip(sourceMip), _targetMip(targetMip), _face(face), _lines(lines), _lineOffset(lineOffset) { + + if (0 == lines) { + _bufferingLambda = [this] { + auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); + auto size = mipData->getSize(); + _buffer.resize(size); + memcpy(&_buffer[0], mipData->readData(), size); + _bufferingCompleted = true; + }; + + } else { + _bufferingLambda = [this] { + auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); + auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip); + auto mipSize = mipData->getSize(); + auto bytesPerLine = (uint32_t)mipSize / dimensions.y; + auto transferSize = bytesPerLine * _lines; + auto sourceOffset = bytesPerLine * _lineOffset; + _buffer.resize(transferSize); + memcpy(&_buffer[0], mipData->readData() + sourceOffset, transferSize); + _bufferingCompleted = true; + }; + } + + _transferLambda = [this] { + auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); + auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat()); + _parent.copyMipFaceLinesFromTexture(_targetMip, _face, dimensions, _lineOffset, texelFormat.format, texelFormat.type, &_buffer[0]); + _buffer.swap(std::vector()); + }; +} + +TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda) + : _parent(parent), _sourceMip(0), _targetMip(0), _face(0), _lines(0), _lineOffset(0), _bufferingCompleted(true), _transferLambda(transferLambda) { + if (!_bufferThread) { + _bufferThread = std::make_shared([] { + TransferJob::bufferLoop(); + }); + } +} + +bool TransferJob::tryTransfer() { + // Are we ready to transfer + if (_bufferingCompleted) { + _transferLambda(); + return true; + } + + startBuffering(); + return false; +} + +void TransferJob::startBuffering() { + if (_bufferingStarted) { + return; + } + _bufferingStarted = true; + { + Lock lock(_mutex); + _bufferLambdaQueue.push(_bufferingLambda); + } +} + +void TransferJob::bufferLoop() { + while (!_shutdownBufferingThread) { + VoidLambdaQueue workingQueue; + { + Lock lock(_mutex); + _bufferLambdaQueue.swap(workingQueue); + } + + if (workingQueue.empty()) { + QThread::msleep(5); + continue; + } + + while (!workingQueue.empty()) { + workingQueue.front()(); + workingQueue.pop(); + } + } +} + + void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { _memoryManagedTextures.push_back(texturePointer); addToWorkQueue(texturePointer); @@ -190,7 +308,14 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { } if (newState != _memoryPressureState) { + if (MemoryPressureState::Transfer == _memoryPressureState) { + TransferJob::stopTransferLoop(); + } _memoryPressureState = newState; + if (MemoryPressureState::Transfer == _memoryPressureState) { + TransferJob::startTransferLoop(); + } + // Clear the existing queue _transferQueue = WorkQueue(); _promoteQueue = WorkQueue(); @@ -223,20 +348,17 @@ void GL45VariableAllocationTexture::processWorkQueues() { if (!object->canDemote()) { continue; } - //qDebug() << "QQQ executing demote for " << texture->source().c_str(); object->demote(); } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { if (!object->canPromote()) { continue; } - //qDebug() << "QQQ executing promote for " << texture->source().c_str(); object->promote(); } else if (MemoryPressureState::Transfer == _memoryPressureState) { if (!object->hasPendingTransfers()) { continue; } - //qDebug() << "QQQ executing transfer for " << texture->source().c_str(); - object->executeNextTransfer(); + object->executeNextTransfer(texture); } else { Q_UNREACHABLE(); } @@ -265,10 +387,14 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { Backend::updateTextureGPUMemoryUsage(_size, 0); } -void GL45VariableAllocationTexture::executeNextTransfer() { +void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& currentTexture) { if (!_pendingTransfers.empty()) { - _pendingTransfers.front()(); - _pendingTransfers.pop(); + // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture + _currentTransferTexture = currentTexture; + if (_pendingTransfers.front().tryTransfer()) { + _pendingTransfers.pop(); + _currentTransferTexture.reset(); + } } } @@ -394,17 +520,15 @@ void GL45ResourceTexture::demote() { populateTransferQueue(); } + void GL45ResourceTexture::populateTransferQueue() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - _pendingTransfers = std::queue(); if (_populatedMip <= _allocatedMip) { return; } + _pendingTransfers = TransferQueue(); - static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; - static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; const uint8_t maxFace = GLTexture::getFaceCount(_target); - uint16_t sourceMip = _populatedMip; do { --sourceMip; @@ -418,11 +542,7 @@ void GL45ResourceTexture::populateTransferQueue() { // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go - _pendingTransfers.push([=] { - Q_ASSERT(sourceMip >= _allocatedMip); - // FIXME modify the copy mechanism to be incremental - copyMipFaceFromTexture(sourceMip, targetMip, face); - }); + _pendingTransfers.emplace(*this, sourceMip, targetMip, face); continue; } @@ -433,24 +553,19 @@ void GL45ResourceTexture::populateTransferQueue() { auto bytesPerLine = (uint32_t)mipData->getSize() / lines; Q_ASSERT(0 == (mipData->getSize() % lines)); uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); - size_t offset = 0; uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); - uvec3 size { mipDimensions.x, linesToCopy, 1 }; - _pendingTransfers.push([=] { - copyMipFaceLinesFromTexture(sourceMip, targetMip, face, lineOffset, linesToCopy, offset); - }); + _pendingTransfers.emplace(TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; - offset += (linesToCopy * bytesPerLine); } } // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.push([=] { + _pendingTransfers.emplace(TransferJob(*this, [=] { _populatedMip = sourceMip; syncSampler(); - }); + })); } while (sourceMip != _allocatedMip); } From 115251542d1b4c7cbf7ad95b5a1820f23e1923f9 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 15 Feb 2017 00:54:16 -0800 Subject: [PATCH 019/106] 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 894f1b8e665cd6f428a9a3a9bf2c2381e9bd943d Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Feb 2017 16:24:06 -0800 Subject: [PATCH 020/106] 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 cf9fc17c4d640041a16a42296e72ea11a3f472bd Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Feb 2017 17:04:40 -0800 Subject: [PATCH 021/106] 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 d56f982decec045c8f012bf33af7532b1edfc123 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 02:45:53 -0800 Subject: [PATCH 022/106] 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 9db3fb60c3..21c32cc55e 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -1006,3 +1006,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; +} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 3c6c34c68d..fba4c7079f 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 @@ -482,6 +487,9 @@ public: ExternalUpdates getUpdates() const; + static ktx::KTXUniquePointer serialize(const Texture& texture); + static TexturePointer unserialize(const ktx::KTXUniquePointer& srcData); + protected: const TextureUsageType _usageType; 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 6a9446f2ef..24777b7dd6 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 @@ -268,8 +270,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 3f0ea1c889f13045a560ecb683c00046e680d29a Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 16 Feb 2017 13:27:16 -0800 Subject: [PATCH 023/106] Address review comments --- 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 ++- 5 files changed, 156 insertions(+), 42 deletions(-) create mode 100644 libraries/gpu/src/gpu/Texture_ktx.cpp 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 24777b7dd6..cadd985c22 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 @@ -273,7 +276,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 6771cc31e1c39928c30ea880e088b407e15e254d Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 16:03:55 -0800 Subject: [PATCH 024/106] 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 fba4c7079f..acf063e365 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 TexturePointer unserialize(const ktx::KTXUniquePointer& srcData); + static Texture* unserialize(const ktx::KTXUniquePointer& srcData); protected: const TextureUsageType _usageType; 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 cadd985c22..4f8a43412e 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -290,12 +290,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 0d2e764bfd26cea93c5fdb1f4c4b28969be14bff Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 17:28:02 -0800 Subject: [PATCH 025/106] 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 b4745657e0378cf3fdb66b4c5c7bf1401a1aba19 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 17 Feb 2017 01:15:27 -0800 Subject: [PATCH 026/106] progressing on io with ktx --- libraries/gpu/src/gpu/Texture.cpp | 24 ++--- 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, 185 insertions(+), 79 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 21c32cc55e..664a2273a6 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -263,27 +263,27 @@ Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler } Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); } Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, sampler); } Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); } Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TextureUsageType::STRICT_RESOURCE, 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(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 0, sampler); } Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 0, sampler); } Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) @@ -326,7 +326,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) { @@ -387,20 +387,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 acf063e365..9c8ebd627c 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -359,7 +359,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; } @@ -511,12 +516,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 4f8a43412e..03605bd6ef 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; } @@ -273,57 +328,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; } @@ -367,6 +375,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; @@ -453,6 +463,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; @@ -486,8 +498,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; @@ -525,8 +537,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; @@ -562,7 +574,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; @@ -893,6 +905,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 d1b91cb436d1e602452aa358477108bef61ab86a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 09:18:51 -0800 Subject: [PATCH 027/106] Adding abstracted storage for in-memory or file backed data --- libraries/shared/src/shared/Storage.cpp | 86 +++++++++++++++++++++++++ libraries/shared/src/shared/Storage.h | 65 +++++++++++++++++++ tests/shared/src/StorageTests.cpp | 75 +++++++++++++++++++++ tests/shared/src/StorageTests.h | 32 +++++++++ 4 files changed, 258 insertions(+) create mode 100644 libraries/shared/src/shared/Storage.cpp create mode 100644 libraries/shared/src/shared/Storage.h create mode 100644 tests/shared/src/StorageTests.cpp create mode 100644 tests/shared/src/StorageTests.h diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp new file mode 100644 index 0000000000..fe17d678c7 --- /dev/null +++ b/libraries/shared/src/shared/Storage.cpp @@ -0,0 +1,86 @@ +// +// Created by Bradley Austin Davis on 2016/02/17 +// Copyright 2013-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 "Storage.h" + +#include + +using namespace storage; + +MemoryStoragePointer Storage::toMemoryStorage() const { + return std::make_unique(size(), data()); +} + +FileStoragePointer Storage::toFileStorage(const QString& filename) const { + return FileStorage::create(filename, size(), data()); +} + +MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { + _data.resize(size); + memcpy(_data.data(), data, size); +} + +const uint8_t* MemoryStorage::data() const { + return _data.data(); +} + +size_t MemoryStorage::size() const { + return _data.size(); +} + +FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { + QFile file(filename); + if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { + throw std::runtime_error("Unable to open file for writing"); + } + if (!file.resize(size)) { + throw std::runtime_error("Unable to resize file"); + } + { + auto mapped = file.map(0, size); + if (!mapped) { + throw std::runtime_error("Unable to map file"); + } + memcpy(mapped, data, size); + if (!file.unmap(mapped)) { + throw std::runtime_error("Unable to unmap file"); + } + } + file.close(); + return std::make_unique(filename); +} + +FileStorage::FileStorage(const QString& filename) : _file(filename) { + if (!_file.open(QFile::ReadOnly)) { + throw std::runtime_error("Unable to open file"); + } + _mapped = _file.map(0, _file.size()); + if (!_mapped) { + throw std::runtime_error("Unable to map file"); + } +} + +FileStorage::~FileStorage() { + if (_mapped) { + if (!_file.unmap(_mapped)) { + throw std::runtime_error("Unable to unmap file"); + } + } + if (_file.isOpen()) { + _file.close(); + } +} + + +const uint8_t* FileStorage::data() const { + return _mapped; +} + +size_t FileStorage::size() const { + return _file.size(); +} diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h new file mode 100644 index 0000000000..3198172b06 --- /dev/null +++ b/libraries/shared/src/shared/Storage.h @@ -0,0 +1,65 @@ +// +// Created by Bradley Austin Davis on 2016/02/17 +// Copyright 2013-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_Storage_h +#define hifi_Storage_h + +#include +#include +#include +#include +#include + +namespace storage { + class Storage; + using StoragePointer = std::unique_ptr; + class MemoryStorage; + using MemoryStoragePointer = std::unique_ptr; + class FileStorage; + using FileStoragePointer = std::unique_ptr; + + class Storage { + public: + virtual ~Storage() {} + virtual const uint8_t* data() const = 0; + virtual size_t size() const = 0; + + FileStoragePointer toFileStorage(const QString& filename) const; + MemoryStoragePointer toMemoryStorage() const; + }; + + class MemoryStorage : public Storage { + public: + MemoryStorage(size_t size, const uint8_t* data); + const uint8_t* data() const override; + size_t size() const override; + private: + std::vector _data; + }; + + class FileStorage : public Storage { + public: + static FileStoragePointer create(const QString& filename, size_t size, const uint8_t* data); + FileStorage(const QString& filename); + ~FileStorage(); + // Prevent copying + FileStorage(const FileStorage& other) = delete; + FileStorage& operator=(const FileStorage& other) = delete; + + const uint8_t* data() const override; + size_t size() const override; + private: + QFile _file; + uint8_t* _mapped { nullptr }; + }; + + +} + +#endif // hifi_Storage_h diff --git a/tests/shared/src/StorageTests.cpp b/tests/shared/src/StorageTests.cpp new file mode 100644 index 0000000000..fa538f6911 --- /dev/null +++ b/tests/shared/src/StorageTests.cpp @@ -0,0 +1,75 @@ +// +// Created by Bradley Austin Davis on 2016/02/17 +// Copyright 2013-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 "StorageTests.h" + +QTEST_MAIN(StorageTests) + +using namespace storage; + +StorageTests::StorageTests() { + for (size_t i = 0; i < _testData.size(); ++i) { + _testData[i] = (uint8_t)rand(); + } + _testFile = QDir::tempPath() + "/" + QUuid::createUuid().toString(); +} + +StorageTests::~StorageTests() { + QFileInfo fileInfo(_testFile); + if (fileInfo.exists()) { + QFile(_testFile).remove(); + } +} + + +void StorageTests::testConversion() { + { + QFileInfo fileInfo(_testFile); + QCOMPARE(fileInfo.exists(), false); + } + StoragePointer storagePointer = std::make_unique(_testData.size(), _testData.data()); + QCOMPARE(storagePointer->size(), (quint64)_testData.size()); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); + // Convert to a file + storagePointer = storagePointer->toFileStorage(_testFile); + { + QFileInfo fileInfo(_testFile); + QCOMPARE(fileInfo.exists(), true); + QCOMPARE(fileInfo.size(), (qint64)_testData.size()); + } + QCOMPARE(storagePointer->size(), (quint64)_testData.size()); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); + + // Convert to memory + storagePointer = storagePointer->toMemoryStorage(); + QCOMPARE(storagePointer->size(), (quint64)_testData.size()); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); + { + // ensure the file is unaffected + QFileInfo fileInfo(_testFile); + QCOMPARE(fileInfo.exists(), true); + QCOMPARE(fileInfo.size(), (qint64)_testData.size()); + } + + // truncate the data as a new memory object + auto newSize = _testData.size() / 2; + storagePointer = std::make_unique(newSize, storagePointer->data()); + QCOMPARE(storagePointer->size(), (quint64)newSize); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0); + + // Convert back to file + storagePointer = storagePointer->toFileStorage(_testFile); + QCOMPARE(storagePointer->size(), (quint64)newSize); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0); + { + // ensure the file is truncated + QFileInfo fileInfo(_testFile); + QCOMPARE(fileInfo.exists(), true); + QCOMPARE(fileInfo.size(), (qint64)newSize); + } +} diff --git a/tests/shared/src/StorageTests.h b/tests/shared/src/StorageTests.h new file mode 100644 index 0000000000..6a2c153223 --- /dev/null +++ b/tests/shared/src/StorageTests.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis on 2016/02/17 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_StorageTests_h +#define hifi_StorageTests_h + +#include + +#include +#include + +class StorageTests : public QObject { + Q_OBJECT + +public: + StorageTests(); + ~StorageTests(); + +private slots: + void testConversion(); + +private: + std::array _testData; + QString _testFile; +}; + +#endif // hifi_StorageTests_h From 2d7ba45667ff00369cfcb429a88d93e216ace21b Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 17 Feb 2017 12:01:33 -0800 Subject: [PATCH 028/106] FIx creationg issue , need a usageType at creation --- libraries/gpu/src/gpu/Texture.cpp | 25 ------------------------ libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 5 +++-- libraries/model/src/model/TextureMap.cpp | 2 +- 4 files changed, 5 insertions(+), 29 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 664a2273a6..b2cb41e9bb 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -1006,28 +1006,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; -} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9c8ebd627c..cc351de787 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -493,7 +493,7 @@ public: ExternalUpdates getUpdates() const; static ktx::KTXUniquePointer serialize(const Texture& texture); - static Texture* unserialize(const ktx::KTXUniquePointer& srcData); + static Texture* unserialize(TextureUsageType usageType, const ktx::KTXUniquePointer& srcData); protected: const TextureUsageType _usageType; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index c36c0f14d8..d63bffb74f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -80,7 +80,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { +Texture* Texture::unserialize(TextureUsageType usageType, const ktx::KTXUniquePointer& srcData) { if (!srcData) { return nullptr; } @@ -105,7 +105,8 @@ Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { type = TEX_3D; } - auto tex = Texture::create( type, + auto tex = Texture::create( usageType, + type, texelFormat, header.getPixelWidth(), header.getPixelHeight(), diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 03605bd6ef..e4e358c581 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -128,7 +128,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(ktx::KTX::create(storage)); + auto theNewTexure = Texture::unserialize(gpu::TextureUsageType::RESOURCE, ktx::KTX::create(storage)); if (theNewTexure) { returnedTexture = theNewTexure; From e6c6cd2825bd476958917d72bfd79f62b801b5e1 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 17 Feb 2017 12:24:04 -0800 Subject: [PATCH 029/106] cleaner unserialization --- libraries/model/src/model/TextureMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index e4e358c581..3f10f66256 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -128,7 +128,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(gpu::TextureUsageType::RESOURCE, ktx::KTX::create(storage)); + auto theNewTexure = Texture::unserialize(srcTexture->getUsageType(), ktx::KTX::create(storage)); if (theNewTexure) { returnedTexture = theNewTexure; From 285222d4907ebd45efe1a2f17ff9ce8f60d9250f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 12:49:55 -0800 Subject: [PATCH 030/106] Add view storage --- libraries/shared/src/shared/Storage.cpp | 29 +++++++++---------------- libraries/shared/src/shared/Storage.h | 21 +++++++++++++----- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index fe17d678c7..d3f1f3c5d3 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -12,8 +12,16 @@ using namespace storage; +ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const { + auto selfSize = size(); + if ((viewSize + offset) > selfSize) { + throw std::runtime_error("Unable to map file"); + } + return ViewStoragePointer(new ViewStorage(viewSize, data() + offset)); +} + MemoryStoragePointer Storage::toMemoryStorage() const { - return std::make_unique(size(), data()); + return MemoryStoragePointer(new MemoryStorage(size(), data())); } FileStoragePointer Storage::toFileStorage(const QString& filename) const { @@ -25,14 +33,6 @@ MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { memcpy(_data.data(), data, size); } -const uint8_t* MemoryStorage::data() const { - return _data.data(); -} - -size_t MemoryStorage::size() const { - return _data.size(); -} - FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { QFile file(filename); if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { @@ -52,7 +52,7 @@ FileStoragePointer FileStorage::create(const QString& filename, size_t size, con } } file.close(); - return std::make_unique(filename); + return FileStoragePointer(new FileStorage(filename)); } FileStorage::FileStorage(const QString& filename) : _file(filename) { @@ -75,12 +75,3 @@ FileStorage::~FileStorage() { _file.close(); } } - - -const uint8_t* FileStorage::data() const { - return _mapped; -} - -size_t FileStorage::size() const { - return _file.size(); -} diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 3198172b06..8096b631ed 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -23,13 +23,15 @@ namespace storage { using MemoryStoragePointer = std::unique_ptr; class FileStorage; using FileStoragePointer = std::unique_ptr; + class ViewStorage; + using ViewStoragePointer = std::unique_ptr; class Storage { public: virtual ~Storage() {} virtual const uint8_t* data() const = 0; virtual size_t size() const = 0; - + ViewStoragePointer createView(size_t size, size_t offset = 0) const; FileStoragePointer toFileStorage(const QString& filename) const; MemoryStoragePointer toMemoryStorage() const; }; @@ -37,8 +39,8 @@ namespace storage { class MemoryStorage : public Storage { public: MemoryStorage(size_t size, const uint8_t* data); - const uint8_t* data() const override; - size_t size() const override; + const uint8_t* data() const override { return _data.data(); } + size_t size() const override { return _data.size(); } private: std::vector _data; }; @@ -52,13 +54,22 @@ namespace storage { FileStorage(const FileStorage& other) = delete; FileStorage& operator=(const FileStorage& other) = delete; - const uint8_t* data() const override; - size_t size() const override; + const uint8_t* data() const override { return _mapped; } + size_t size() const override { return _file.size(); } private: QFile _file; uint8_t* _mapped { nullptr }; }; + class ViewStorage : public Storage { + public: + ViewStorage(size_t size, const uint8_t* data) : _size(size), _data(data) {} + const uint8_t* data() const override { return _data; } + size_t size() const override { return _size; } + private: + const size_t _size; + const uint8_t* _data; + }; } From eafe0a04d5666b5dd5be01c936be482889c785ec Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 13:00:38 -0800 Subject: [PATCH 031/106] Fix transfer buffering --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 9 +-- .../gpu/gl45/GL45BackendVariableTexture.cpp | 55 +++++++++++-------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 4f299d417f..29e5a59ec5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -108,16 +108,10 @@ public: using VoidLambdaQueue = std::queue; using ThreadPointer = std::shared_ptr; const GL45VariableAllocationTexture& _parent; - const uint16_t _sourceMip; - const uint16_t _targetMip; - const uint8_t _face; - const uint32_t _lines; - const uint32_t _lineOffset; // Holds the contents to transfer to the GPU in CPU memory std::vector _buffer; // Indicates if a transfer from backing storage to interal storage has started bool _bufferingStarted { false }; - bool _transferOnly { false }; bool _bufferingCompleted { false }; VoidLambda _transferLambda; VoidLambda _bufferingLambda; @@ -128,6 +122,7 @@ public: static void bufferLoop(); public: + TransferJob(const TransferJob& other) = delete; TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); bool tryTransfer(); @@ -139,7 +134,7 @@ public: void transfer(); }; - using TransferQueue = std::queue; + using TransferQueue = std::queue>; static MemoryPressureState _memoryPressureState; protected: static std::atomic _memoryPressureStateStale; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index e26a5c262f..8c93ed6a65 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -78,11 +78,18 @@ void TransferJob::stopTransferLoop() { } TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) - : _parent(parent), _sourceMip(sourceMip), _targetMip(targetMip), _face(face), _lines(lines), _lineOffset(lineOffset) { + : _parent(parent) { + + auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + GLenum format; + GLenum type; + auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat()); + format = texelFormat.format; + type = texelFormat.type; if (0 == lines) { - _bufferingLambda = [this] { - auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); + _bufferingLambda = [=] { auto size = mipData->getSize(); _buffer.resize(size); memcpy(&_buffer[0], mipData->readData(), size); @@ -90,38 +97,39 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s }; } else { - _bufferingLambda = [this] { - auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); - auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip); + transferDimensions.y = lines; + _bufferingLambda = [=] { + auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); auto mipSize = mipData->getSize(); auto bytesPerLine = (uint32_t)mipSize / dimensions.y; - auto transferSize = bytesPerLine * _lines; - auto sourceOffset = bytesPerLine * _lineOffset; + auto transferSize = bytesPerLine * lines; + auto sourceOffset = bytesPerLine * lineOffset; _buffer.resize(transferSize); memcpy(&_buffer[0], mipData->readData() + sourceOffset, transferSize); _bufferingCompleted = true; }; } - _transferLambda = [this] { - auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); - auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat()); - _parent.copyMipFaceLinesFromTexture(_targetMip, _face, dimensions, _lineOffset, texelFormat.format, texelFormat.type, &_buffer[0]); + _transferLambda = [=] { + _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); _buffer.swap(std::vector()); }; } TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda) - : _parent(parent), _sourceMip(0), _targetMip(0), _face(0), _lines(0), _lineOffset(0), _bufferingCompleted(true), _transferLambda(transferLambda) { - if (!_bufferThread) { - _bufferThread = std::make_shared([] { - TransferJob::bufferLoop(); - }); - } + : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { } bool TransferJob::tryTransfer() { + // Disable threaded texture transfer for now +#if 1 + if (!_bufferingCompleted) { + _bufferingLambda(); + _bufferingCompleted = true; + } + _transferLambda(); + return true; +#else // Are we ready to transfer if (_bufferingCompleted) { _transferLambda(); @@ -130,6 +138,7 @@ bool TransferJob::tryTransfer() { startBuffering(); return false; +#endif } void TransferJob::startBuffering() { @@ -391,7 +400,7 @@ void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& cu if (!_pendingTransfers.empty()) { // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture _currentTransferTexture = currentTexture; - if (_pendingTransfers.front().tryTransfer()) { + if (_pendingTransfers.front()->tryTransfer()) { _pendingTransfers.pop(); _currentTransferTexture.reset(); } @@ -542,7 +551,7 @@ void GL45ResourceTexture::populateTransferQueue() { // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go - _pendingTransfers.emplace(*this, sourceMip, targetMip, face); + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); continue; } @@ -556,13 +565,13 @@ void GL45ResourceTexture::populateTransferQueue() { uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); - _pendingTransfers.emplace(TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; } } // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.emplace(TransferJob(*this, [=] { + _pendingTransfers.emplace(new TransferJob(*this, [=] { _populatedMip = sourceMip; syncSampler(); })); From 61ce66a03956c8a68a73ce11f99bdea6b6af8667 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 13:01:46 -0800 Subject: [PATCH 032/106] Switching texture backing to opaque storage type --- libraries/gpu/src/gpu/Texture.cpp | 47 +++++---------------------- libraries/gpu/src/gpu/Texture.h | 27 +++++++-------- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- 3 files changed, 20 insertions(+), 56 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index b2cb41e9bb..833647bbda 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -122,36 +122,12 @@ uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : _format(format), - _sysmem(size, bytes), - _isGPULoaded(false) { - Texture::updateTextureCPUMemoryUsage(0, _sysmem.getSize()); + _storage(new storage::MemoryStorage(size, bytes)) { + Texture::updateTextureCPUMemoryUsage(0, _storage->size()); } Texture::Pixels::~Pixels() { - Texture::updateTextureCPUMemoryUsage(_sysmem.getSize(), 0); -} - -Texture::Size Texture::Pixels::resize(Size pSize) { - auto prevSize = _sysmem.getSize(); - auto newSize = _sysmem.resize(pSize); - Texture::updateTextureCPUMemoryUsage(prevSize, newSize); - return newSize; -} - -Texture::Size Texture::Pixels::setData(const Element& format, Size size, const Byte* bytes ) { - _format = format; - auto prevSize = _sysmem.getSize(); - auto newSize = _sysmem.setData(size, bytes); - Texture::updateTextureCPUMemoryUsage(prevSize, newSize); - _isGPULoaded = false; - return newSize; -} - -void Texture::Pixels::notifyGPULoaded() { - _isGPULoaded = true; - auto prevSize = _sysmem.getSize(); - auto newSize = _sysmem.resize(0); - Texture::updateTextureCPUMemoryUsage(prevSize, newSize); + Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); } void Texture::Storage::assignTexture(Texture* texture) { @@ -183,14 +159,6 @@ const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 fa return PixelsPointer(); } -void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const { - PixelsPointer mipFace = getMipFace(level, face); - // Free the mips - if (mipFace) { - mipFace->notifyGPULoaded(); - } -} - bool Texture::Storage::isMipAvailable(uint16 level, uint8 face) const { PixelsPointer mipFace = getMipFace(level, face); return (mipFace && mipFace->getSize()); @@ -229,7 +197,8 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s auto faceBytes = bytes; Size allocated = 0; for (auto& face : mip) { - allocated += face->setData(format, sizePerFace, faceBytes); + face.reset(new Pixels(format, size, bytes)); + allocated += size; faceBytes += sizePerFace; } @@ -242,11 +211,11 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) { allocateMip(level); - auto mip = _mips[level]; + auto& mip = _mips[level]; Size allocated = 0; if (face < mip.size()) { - auto mipFace = mip[face]; - allocated += mipFace->setData(format, size, bytes); + mip[face].reset(new Pixels(format, size, bytes)); + allocated += size; bumpStamp(); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index cc351de787..0a5afe78c3 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -17,6 +17,8 @@ #include #include +#include + #include "Forward.h" #include "Resource.h" @@ -224,26 +226,26 @@ public: bool operator!=(const Usage& usage) { return (_flags != usage._flags); } }; + class Pixels { public: + using StoragePointer = storage::StoragePointer; + Pixels() {} Pixels(const Pixels& pixels) = default; Pixels(const Element& format, Size size, const Byte* bytes); + Pixels(const Element& format, StoragePointer& storage) : _format(format), _storage(storage.release()) {} ~Pixels(); - const Byte* readData() const { return _sysmem.readData(); } - Size getSize() const { return _sysmem.getSize(); } - Size resize(Size pSize); - Size setData(const Element& format, Size size, const Byte* bytes ); + const Byte* readData() const { return _storage->data(); } + Size getSize() const { return _storage->size(); } const Element& getFormat() const { return _format; } - - void notifyGPULoaded(); - + + protected: Element _format; - Sysmem _sysmem; - bool _isGPULoaded; + StoragePointer _storage; friend class Texture; }; @@ -296,10 +298,6 @@ public: const Texture* getTexture() const { return _texture; } friend class Texture; - - // THis should be only called by the Texture from the Backend to notify the storage that the specified mip face pixels - // have been uploaded to the GPU memory. IT is possible for the storage to free the system memory then - virtual void notifyMipFaceGPULoaded(uint16 level, uint8 face) const; }; @@ -481,9 +479,6 @@ public: const Sampler& getSampler() const { return _sampler; } Stamp getSamplerStamp() const { return _samplerStamp; } - // Only callable by the Backend - void notifyMipFaceGPULoaded(uint16 level, uint8 face = 0) const { return _storage->notifyMipFaceGPULoaded(level, face); } - void setExternalTexture(uint32 externalId, void* externalFence); void setExternalRecycler(const ExternalRecycler& recycler); ExternalRecycler getExternalRecycler() const; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d63bffb74f..404aca77a4 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -72,7 +72,7 @@ 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(mip->getSize(), 0, mip->readData())); + images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); } } From 6c03927b97173bc667f8f9f558496ab28c54b61c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 13:53:27 -0800 Subject: [PATCH 033/106] Additional fix to new storage --- libraries/gpu/src/gpu/Texture.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 833647bbda..281d768182 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -127,7 +127,9 @@ Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : } Texture::Pixels::~Pixels() { - Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); + if (_storage) { + Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); + } } void Texture::Storage::assignTexture(Texture* texture) { From 94960a7a80881b7699d7708ff4c0a3397b3092d4 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 17 Feb 2017 16:05:11 -0800 Subject: [PATCH 034/106] Small bug when accessing inexisting storage from the Pixels --- libraries/gpu/src/gpu/Texture.cpp | 4 +++- libraries/model/src/model/TextureMap.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 833647bbda..281d768182 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -127,7 +127,9 @@ Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : } Texture::Pixels::~Pixels() { - Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); + if (_storage) { + Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); + } } void Texture::Storage::assignTexture(Texture* texture) { diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3f10f66256..417fd6992e 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; } From 27e8750b9bf3b5b8039fb3700c50c285891173e1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 17:17:56 -0800 Subject: [PATCH 035/106] Adding new storage backing for gpu::Texture, moving mip format into Storage abstraction --- .../display-plugins/OpenGLDisplayPlugin.cpp | 3 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 3 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 3 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 2 +- .../gpu/gl45/GL45BackendVariableTexture.cpp | 2 +- libraries/gpu/src/gpu/Texture.cpp | 119 ++++++++---------- libraries/gpu/src/gpu/Texture.h | 100 +++++++++------ libraries/gpu/src/gpu/Texture_ktx.cpp | 3 +- .../src/model-networking/TextureCache.cpp | 15 ++- libraries/model/src/model/TextureMap.cpp | 48 +++---- libraries/render-utils/src/text/Font.cpp | 3 +- libraries/shared/src/shared/Storage.cpp | 9 +- libraries/shared/src/shared/Storage.h | 20 +-- tests/render-perf/CMakeLists.txt | 2 +- 14 files changed, 178 insertions(+), 154 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 1bfe4f3dcc..dca5ac4b77 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -362,7 +362,8 @@ void OpenGLDisplayPlugin::customizeContext() { cursorData.texture->setSource("cursor texture"); auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); cursorData.texture->setUsage(usage.build()); - cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + cursorData.texture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + cursorData.texture->assignStoredMip(0, image.byteCount(), image.constBits()); cursorData.texture->autoGenerateMips(-1); } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 24f4e429ef..c55d985a62 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -302,7 +302,8 @@ void HmdDisplayPlugin::internalPresent() { gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); _previewTexture->setSource("HMD Preview Texture"); _previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build()); - _previewTexture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + _previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + _previewTexture->assignStoredMip(0, image.byteCount(), image.constBits()); _previewTexture->autoGenerateMips(-1); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index efbc6903b1..80649c4d64 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -71,7 +71,7 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& : GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) { incrementTextureGPUCount(); withPreservedTexture([&] { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); const Sampler& sampler = _gpuObject.getSampler(); auto numMips = _gpuObject.evalNumMips(); for (uint16_t mipLevel = 0; mipLevel < numMips; ++mipLevel) { @@ -83,7 +83,6 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); mipData = mip->readData(); - texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); } glTexImage2D(target, mipLevel, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, mipData); (void)CHECK_GL_ERROR(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 6dd1d6aea3..95837c16d9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -142,7 +142,7 @@ void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, } auto size = _gpuObject.evalMipDimensions(sourceMip); auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mipData->getFormat()); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 8c93ed6a65..0e2053ad06 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -84,7 +84,7 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s GLenum format; GLenum type; auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat()); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); format = texelFormat.format; type = texelFormat.type; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 281d768182..6afab476de 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -120,18 +120,6 @@ void Texture::setAllowedGPUMemoryUsage(Size size) { uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; -Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : - _format(format), - _storage(new storage::MemoryStorage(size, bytes)) { - Texture::updateTextureCPUMemoryUsage(0, _storage->size()); -} - -Texture::Pixels::~Pixels() { - if (_storage) { - Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); - } -} - void Texture::Storage::assignTexture(Texture* texture) { _texture = texture; if (_texture) { @@ -139,21 +127,12 @@ void Texture::Storage::assignTexture(Texture* texture) { } } -void Texture::Storage::reset() { +void Texture::MemoryStorage::reset() { _mips.clear(); bumpStamp(); } -Texture::PixelsPointer Texture::Storage::editMipFace(uint16 level, uint8 face) { - if (level < _mips.size()) { - assert(face < _mips[level].size()); - bumpStamp(); - return _mips[level][face]; - } - return PixelsPointer(); -} - -const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 face) const { +const Texture::PixelsPointer Texture::MemoryStorage::getMipFace(uint16 level, uint8 face) const { if (level < _mips.size()) { assert(face < _mips[level].size()); return _mips[level][face]; @@ -161,12 +140,12 @@ const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 fa return PixelsPointer(); } -bool Texture::Storage::isMipAvailable(uint16 level, uint8 face) const { +bool Texture::MemoryStorage::isMipAvailable(uint16 level, uint8 face) const { PixelsPointer mipFace = getMipFace(level, face); return (mipFace && mipFace->getSize()); } -bool Texture::Storage::allocateMip(uint16 level) { +bool Texture::MemoryStorage::allocateMip(uint16 level) { bool changed = false; if (level >= _mips.size()) { _mips.resize(level+1, std::vector(Texture::NUM_FACES_PER_TYPE[getType()])); @@ -176,7 +155,6 @@ bool Texture::Storage::allocateMip(uint16 level) { auto& mip = _mips[level]; for (auto& face : mip) { if (!face) { - face = std::make_shared(); changed = true; } } @@ -186,7 +164,7 @@ bool Texture::Storage::allocateMip(uint16 level) { return changed; } -bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes) { +void Texture::MemoryStorage::assignMipData(uint16 level, const storage::StoragePointer& storagePointer) { allocateMip(level); auto& mip = _mips[level]; @@ -195,33 +173,24 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s // The bytes assigned here are supposed to contain all the faces bytes of the mip. // For tex1D, 2D, 3D there is only one face // For Cube, we expect the 6 faces in the order X+, X-, Y+, Y-, Z+, Z- - auto sizePerFace = size / mip.size(); - auto faceBytes = bytes; - Size allocated = 0; + auto sizePerFace = storagePointer->size() / mip.size(); + size_t offset = 0; for (auto& face : mip) { - face.reset(new Pixels(format, size, bytes)); - allocated += size; - faceBytes += sizePerFace; + face = storagePointer->createView(sizePerFace, offset); + offset += sizePerFace; } bumpStamp(); - - return allocated == size; } -bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) { - +void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storagePointer) { allocateMip(level); auto& mip = _mips[level]; - Size allocated = 0; if (face < mip.size()) { - mip[face].reset(new Pixels(format, size, bytes)); - allocated += size; + mip[face] = storagePointer; bumpStamp(); } - - return allocated == size; } Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { @@ -260,7 +229,7 @@ Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sam Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) { Texture* tex = new Texture(usageType); - tex->_storage.reset(new Storage()); + tex->_storage.reset(new MemoryStorage()); tex->_type = type; tex->_storage->assignTexture(tex); tex->_maxMip = 0; @@ -402,69 +371,83 @@ uint16 Texture::evalNumMips() const { return evalNumMips({ _width, _height, _depth }); } -bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes) { +void Texture::setStoredMipFormat(const Element& format) { + _storage->setFormat(format); +} + +const Element& Texture::getStoredMipFormat() const { + return _storage->getFormat(); +} + +void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { + storage::StoragePointer storage { new storage::MemoryStorage(size, bytes) }; + assignStoredMip(level, storage); +} + +void Texture::assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes) { + storage::StoragePointer storage { new storage::MemoryStorage(size, bytes) }; + assignStoredMipFace(level, face, storage); +} + +void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) { // Check that level accessed make sense if (level != 0) { if (_autoGenerateMips) { - return false; + return; } if (level >= evalNumMips()) { - return false; + return; } } // THen check that the mem texture passed make sense with its format - Size expectedSize = evalStoredMipSize(level, format); - if (size == expectedSize) { - _storage->assignMipData(level, format, size, bytes); + Size expectedSize = evalStoredMipSize(level, getStoredMipFormat()); + auto size = storage->size(); + auto bytes = storage->data(); + if (storage->size() == expectedSize) { + _storage->assignMipData(level, storage); _maxMip = std::max(_maxMip, level); _stamp++; - return true; } else if (size > expectedSize) { // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images // and alligning the line of pixels to 32 bits. // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... - _storage->assignMipData(level, format, size, bytes); + _storage->assignMipData(level, storage); _maxMip = std::max(_maxMip, level); _stamp++; - return true; } - - return false; } - -bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) { +void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePointer& storage) { // Check that level accessed make sense if (level != 0) { if (_autoGenerateMips) { - return false; + return; } if (level >= evalNumMips()) { - return false; + return; } } // THen check that the mem texture passed make sense with its format - Size expectedSize = evalStoredMipFaceSize(level, format); + Size expectedSize = evalStoredMipFaceSize(level, getStoredMipFormat()); + auto size = storage->size(); + auto bytes = storage->data(); if (size == expectedSize) { - _storage->assignMipFaceData(level, format, size, bytes, face); + _storage->assignMipFaceData(level, face, storage); _stamp++; - return true; } else if (size > expectedSize) { // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images // and alligning the line of pixels to 32 bits. // 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); + _storage->assignMipFaceData(level, face, storage); _stamp++; - return true; } - - return false; } + uint16 Texture::autoGenerateMips(uint16 maxMip) { bool changed = false; if (!_autoGenerateMips) { @@ -498,7 +481,7 @@ uint16 Texture::getStoredMipHeight(uint16 level) const { if (mip && mip->getSize()) { return evalMipHeight(level); } - return 0; + return 0; } uint16 Texture::getStoredMipDepth(uint16 level) const { @@ -770,8 +753,8 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { PROFILE_RANGE(render_gpu, "ProcessFace"); - auto numComponents = cubeTexture.accessStoredMipFace(0,face)->getFormat().getScalarCount(); - auto data = cubeTexture.accessStoredMipFace(0,face)->readData(); + auto numComponents = cubeTexture.getStoredMipFormat().getScalarCount(); + auto data = cubeTexture.accessStoredMipFace(0,face)->data(); if (data == nullptr) { continue; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 0a5afe78c3..84241442c5 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -226,30 +226,7 @@ public: bool operator!=(const Usage& usage) { return (_flags != usage._flags); } }; - - class Pixels { - public: - using StoragePointer = storage::StoragePointer; - - Pixels() {} - Pixels(const Pixels& pixels) = default; - Pixels(const Element& format, Size size, const Byte* bytes); - Pixels(const Element& format, StoragePointer& storage) : _format(format), _storage(storage.release()) {} - ~Pixels(); - - const Byte* readData() const { return _storage->data(); } - Size getSize() const { return _storage->size(); } - - const Element& getFormat() const { return _format; } - - - protected: - Element _format; - StoragePointer _storage; - - friend class Texture; - }; - typedef std::shared_ptr< Pixels > PixelsPointer; + using PixelsPointer = storage::StoragePointer; enum Type { TEX_1D = 0, @@ -272,35 +249,68 @@ public: NUM_CUBE_FACES, // Not a valid vace index }; + class Storage { public: Storage() {} virtual ~Storage() {} - virtual void reset(); - virtual PixelsPointer editMipFace(uint16 level, uint8 face = 0); - virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const; - virtual bool allocateMip(uint16 level); - virtual bool assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes); - virtual bool assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); - virtual bool isMipAvailable(uint16 level, uint8 face = 0) const; + virtual void reset() = 0; + virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; + virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; + virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; + virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; Texture::Type getType() const { return _type; } - + Stamp getStamp() const { return _stamp; } Stamp bumpStamp() { return ++_stamp; } - protected: - Stamp _stamp = 0; - Texture* _texture = nullptr; // Points to the parent texture (not owned) - Texture::Type _type = Texture::TEX_2D; // The type of texture is needed to know the number of faces to expect - std::vector> _mips; // an array of mips, each mip is an array of faces + void setFormat(const Element& format) { _format = format; } + const Element& getFormat() const { return _format; } + + private: + Stamp _stamp { 0 }; + Element _format; + Texture::Type _type { Texture::TEX_2D }; // The type of texture is needed to know the number of faces to expect + Texture* _texture { nullptr }; // Points to the parent texture (not owned) virtual void assignTexture(Texture* tex); // Texture storage is pointing to ONE corrresponding Texture. const Texture* getTexture() const { return _texture; } - friend class Texture; }; - + class MemoryStorage : public Storage { + public: + void reset() override; + const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; + void assignMipData(uint16 level, const storage::StoragePointer& storage) override; + void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; + bool isMipAvailable(uint16 level, uint8 face = 0) const override; + + protected: + bool allocateMip(uint16 level); + std::vector> _mips; // an array of mips, each mip is an array of faces + }; + + class KtxStorage : public Storage { + public: + KtxStorage(ktx::KTXUniquePointer& ktxData); + const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; + bool isMipAvailable(uint16 level, uint8 face = 0) const override; + + void assignMipData(uint16 level, const storage::StoragePointer& storage) override { + throw std::runtime_error("Invalid call"); + } + + void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override { + throw std::runtime_error("Invalid call"); + } + void reset() override { } + + protected: + ktx::KTXUniquePointer _ktxData; + friend class Texture; + }; + static Texture* create1D(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler()); @@ -444,13 +454,21 @@ public: // Managing Storage and mips + // Mip storage format is constant across all mips + void setStoredMipFormat(const Element& format); + const Element& getStoredMipFormat() const; + // Manually allocate the mips down until the specified maxMip // this is just allocating the sysmem version of it // in case autoGen is on, this doesn't allocate // Explicitely assign mip data for a certain level // If Bytes is NULL then simply allocate the space so mip sysmem can be accessed - bool assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes); - bool assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); + + void assignStoredMip(uint16 level, Size size, const Byte* bytes); + void assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes); + + void assignStoredMip(uint16 level, storage::StoragePointer& storage); + void assignStoredMipFace(uint16 level, uint8 face, storage::StoragePointer& storage); // Access the the sub mips bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 404aca77a4..13d4c2a464 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -116,9 +116,10 @@ Texture* Texture::unserialize(TextureUsageType usageType, const ktx::KTXUniquePo Sampler()); // Assing the mips availables + tex->setStoredMipFormat(mipFormat); uint16_t level = 0; for (auto& image : srcData->_images) { - tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); + tex->assignStoredMip(level, image._imageSize, image._bytes); level++; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 6a84fc960f..1f21e9e78d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -108,7 +108,8 @@ const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() { } _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2)); - _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data); + _permutationNormalTexture->setStoredMipFormat(_permutationNormalTexture->getTexelFormat()); + _permutationNormalTexture->assignStoredMip(0, sizeof(data), data); } return _permutationNormalTexture; } @@ -122,7 +123,8 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (!_whiteTexture) { _whiteTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _whiteTexture->setSource("TextureCache::_whiteTexture"); - _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); + _whiteTexture->setStoredMipFormat(_whiteTexture->getTexelFormat()); + _whiteTexture->assignStoredMip(0, sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } return _whiteTexture; } @@ -131,7 +133,8 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() { if (!_grayTexture) { _grayTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _grayTexture->setSource("TextureCache::_grayTexture"); - _grayTexture->assignStoredMip(0, _grayTexture->getTexelFormat(), sizeof(OPAQUE_GRAY), OPAQUE_GRAY); + _grayTexture->setStoredMipFormat(_grayTexture->getTexelFormat()); + _grayTexture->assignStoredMip(0, sizeof(OPAQUE_GRAY), OPAQUE_GRAY); } return _grayTexture; } @@ -140,7 +143,8 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() { if (!_blueTexture) { _blueTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _blueTexture->setSource("TextureCache::_blueTexture"); - _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); + _blueTexture->setStoredMipFormat(_blueTexture->getTexelFormat()); + _blueTexture->assignStoredMip(0, sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } return _blueTexture; } @@ -149,7 +153,8 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() { if (!_blackTexture) { _blackTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _blackTexture->setSource("TextureCache::_blackTexture"); - _blackTexture->assignStoredMip(0, _blackTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK); + _blackTexture->setStoredMipFormat(_blackTexture->getTexelFormat()); + _blackTexture->assignStoredMip(0, sizeof(OPAQUE_BLACK), OPAQUE_BLACK); } return _blackTexture; } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3f10f66256..0a1eb705eb 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -96,7 +96,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo // Prepare cache directory QString path("hifi_ktx/"); QFileInfo originalFileInfo(path); - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); path = docsLocation + "/" + path; QFileInfo info(path); if (!info.absoluteDir().exists()) { @@ -262,7 +262,7 @@ const QImage& image, bool isLinear, bool doCompress) { #define CPU_MIPMAPS 1 -void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, bool fastResize) { +void generateMips(gpu::Texture* texture, QImage& image, bool fastResize) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateMips"); auto numMips = texture->evalNumMips(); @@ -270,10 +270,10 @@ void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); if (fastResize) { image = image.scaled(mipSize); - texture->assignStoredMip(level, formatMip, image.byteCount(), image.constBits()); + texture->assignStoredMip(level, image.byteCount(), image.constBits()); } else { QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - texture->assignStoredMip(level, formatMip, mipImage.byteCount(), mipImage.constBits()); + texture->assignStoredMip(level, mipImage.byteCount(), mipImage.constBits()); } } #else @@ -281,14 +281,14 @@ void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, #endif } -void generateFaceMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, uint8 face) { +void generateFaceMips(gpu::Texture* texture, QImage& image, uint8 face) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateFaceMips"); auto numMips = texture->evalNumMips(); for (uint16 level = 1; level < numMips; ++level) { QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - texture->assignStoredMipFace(level, formatMip, mipImage.byteCount(), mipImage.constBits(), face); + texture->assignStoredMipFace(level, face, mipImage.byteCount(), mipImage.constBits()); } #else texture->autoGenerateMips(-1); @@ -322,11 +322,11 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag } } theTexture->setUsage(usage.build()); - - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); if (generateMips) { - ::generateMips(theTexture, image, formatMip, false); + ::generateMips(theTexture, image, false); } theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); @@ -373,8 +373,9 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -461,8 +462,9 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -496,8 +498,9 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -535,8 +538,9 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -571,8 +575,9 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -886,11 +891,12 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) { theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); theTexture->setSource(srcImageName); + theTexture->setStoredMipFormat(formatMip); int f = 0; for (auto& face : faces) { - theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f); + theTexture->assignStoredMipFace(0, f, face.byteCount(), face.constBits()); if (generateMips) { - generateFaceMips(theTexture, face, formatMip, f); + generateFaceMips(theTexture, face, f); } f++; } diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 4f4ee12622..c405f6d6ae 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -209,7 +209,8 @@ void Font::read(QIODevice& in) { } _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); - _texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + _texture->setStoredMipFormat(formatMip); + _texture->assignStoredMip(0, image.byteCount(), image.constBits()); } void Font::setupGPU() { diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index d3f1f3c5d3..dacb840de7 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -14,10 +14,13 @@ using namespace storage; ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const { auto selfSize = size(); - if ((viewSize + offset) > selfSize) { - throw std::runtime_error("Unable to map file"); + if (0 == viewSize) { + viewSize = selfSize; } - return ViewStoragePointer(new ViewStorage(viewSize, data() + offset)); + if ((viewSize + offset) > selfSize) { + throw std::runtime_error("Invalid mapping range"); + } + return ViewStoragePointer(new ViewStorage(shared_from_this(), viewSize, data() + offset)); } MemoryStoragePointer Storage::toMemoryStorage() const { diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 8096b631ed..4dbb2a03a5 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -18,22 +18,27 @@ namespace storage { class Storage; - using StoragePointer = std::unique_ptr; + using StoragePointer = std::shared_ptr; class MemoryStorage; - using MemoryStoragePointer = std::unique_ptr; + using MemoryStoragePointer = std::shared_ptr; class FileStorage; - using FileStoragePointer = std::unique_ptr; + using FileStoragePointer = std::shared_ptr; class ViewStorage; - using ViewStoragePointer = std::unique_ptr; + using ViewStoragePointer = std::shared_ptr; - class Storage { + class Storage : public std::enable_shared_from_this { public: virtual ~Storage() {} virtual const uint8_t* data() const = 0; virtual size_t size() const = 0; - ViewStoragePointer createView(size_t size, size_t offset = 0) const; + + ViewStoragePointer createView(size_t size = 0, size_t offset = 0) const; FileStoragePointer toFileStorage(const QString& filename) const; MemoryStoragePointer toMemoryStorage() const; + + // Aliases to prevent having to re-write a ton of code + inline size_t getSize() const { return size(); } + inline const uint8_t* readData() const { return data(); } }; class MemoryStorage : public Storage { @@ -63,10 +68,11 @@ namespace storage { class ViewStorage : public Storage { public: - ViewStorage(size_t size, const uint8_t* data) : _size(size), _data(data) {} + ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data) : _owner(owner), _size(size), _data(data) {} const uint8_t* data() const override { return _data; } size_t size() const override { return _size; } private: + const storage::StoragePointer _owner; const size_t _size; const uint8_t* _data; }; diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index d4f90fdace..96cede9c43 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -10,7 +10,7 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) +link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) package_libraries_for_deployment() From b502b1e200f49a8dede4005504dd7b6e13aec9e7 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 20 Feb 2017 19:40:39 -0800 Subject: [PATCH 036/106] 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 037/106] 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; From 5e307f247ef4b449d1c6c59cc281fa25efa2fda7 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 21 Feb 2017 10:46:49 -0800 Subject: [PATCH 038/106] Add support for the R8 textures --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 3 ++ libraries/gpu/src/gpu/Format.cpp | 2 + libraries/gpu/src/gpu/Format.h | 2 + libraries/gpu/src/gpu/Texture_ktx.cpp | 37 +++++++++++++++---- libraries/ktx/src/ktx/KTX.h | 8 ++++ libraries/model/src/model/TextureMap.cpp | 12 +++--- 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index bd945cbaaa..fa639aab11 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -17,6 +17,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { switch (dstFormat.getDimension()) { case gpu::SCALAR: { switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -262,6 +263,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: texel.internalFormat = GL_R8; @@ -403,6 +405,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_COMPRESSED_RED_RGTC1; break; } + case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 0f5e85c907..5b61a3a5a4 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -10,6 +10,8 @@ using namespace gpu; +const Element Element::COLOR_R_8 { SCALAR, NUINT8, RED }; + const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index c7dc2eed8d..4610597a56 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -133,6 +133,7 @@ static const int SCALAR_COUNT[NUM_DIMENSIONS] = { enum Semantic { RAW = 0, // used as RAW memory + RED, RGB, RGBA, BGRA, @@ -227,6 +228,7 @@ public: return getRaw() != right.getRaw(); } + static const Element COLOR_R_8; static const Element COLOR_RGBA_32; static const Element COLOR_SRGBA_32; static const Element COLOR_BGRA_32; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 8b47c7409e..f1d91d39ab 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -20,13 +20,18 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { // 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); + auto mipFormat = texture.getStoredMipFormat(); + if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_BGRA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); + } else { + return nullptr; + } + // Set Dimensions uint32_t numFaces = 1; switch (texture.getType()) { @@ -97,9 +102,27 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx } const auto& header = *srcData->getHeader(); - Format mipFormat = Format::COLOR_SBGRA_32; + Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; + if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { + mipFormat = Format::COLOR_BGRA_32; + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + texelFormat = Format::COLOR_RGBA_32; + } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + texelFormat = Format::COLOR_SRGBA_32; + } else { + return nullptr; + } + } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { + mipFormat = Format::COLOR_R_8; + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { + texelFormat = Format::COLOR_R_8; + } else { + return nullptr; + } + } + // Find Texture Type based on dimensions Type type = TEX_1D; if (header.pixelWidth == 0) { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 52c65c9cd4..128d92ccc8 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -394,6 +394,14 @@ namespace ktx { glBaseInternalFormat = (uint32_t) baseInternalFormat; } + GLType getGLType() const { return (GLType)glType; } + uint32_t getTypeSize() const { return glTypeSize; } + GLFormat getGLFormat() const { return (GLFormat)glFormat; } + GLInternalFormat_Uncompressed getGLInternaFormat_Uncompressed() const { return (GLInternalFormat_Uncompressed)glInternalFormat; } + GLInternalFormat_Compressed getGLInternaFormat_Compressed() const { return (GLInternalFormat_Compressed)glInternalFormat; } + GLBaseInternalFormat getGLBaseInternalFormat() const { return (GLBaseInternalFormat)glBaseInternalFormat; } + + 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; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 41d1ba5e12..760cd98898 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -504,9 +504,9 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element::COLOR_R_8; #endif - gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element::COLOR_R_8; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); @@ -545,9 +545,9 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element::COLOR_R_8; #endif - gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element::COLOR_R_8; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); @@ -583,9 +583,9 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element::COLOR_R_8; #endif - gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element::COLOR_R_8; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); From 666be6912bd38bfe4bb0f244d619c78c2f100b27 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 21 Feb 2017 14:46:25 -0500 Subject: [PATCH 039/106] fix KTX gcc errors --- libraries/ktx/src/ktx/KTX.h | 1 + libraries/ktx/src/ktx/Reader.cpp | 6 ++---- libraries/ktx/src/ktx/Writer.cpp | 4 +--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 128d92ccc8..94529d6e68 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -16,6 +16,7 @@ #include #include #include +#include #include /* KTX Spec: diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index dffe6cf828..584bf219c4 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -17,9 +17,7 @@ namespace ktx { class ReaderException: public std::exception { public: ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} - const char* what() const override { - return _explanation.c_str(); - } + const char* what() const noexcept override { return _explanation.c_str(); } private: const std::string _explanation; }; @@ -179,4 +177,4 @@ namespace ktx { return result; } -} \ No newline at end of file +} diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index edd9a9ec97..7cc6f9972d 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -16,9 +16,7 @@ namespace ktx { class WriterException : public std::exception { public: WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} - const char* what() const override { - return _explanation.c_str(); - } + const char* what() const noexcept override { return _explanation.c_str(); } private: const std::string _explanation; }; From be4a9dcd5a3e78152a087c512218af8cf0cb5575 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 21 Feb 2017 14:50:12 -0500 Subject: [PATCH 040/106] clear buffer without rref --- libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 0e2053ad06..92cc945a86 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -112,7 +112,8 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s _transferLambda = [=] { _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); - _buffer.swap(std::vector()); + std::vector emptyVector; + _buffer.swap(emptyVector); }; } From 748d7c0ce45ff90b42e644982e50f0f1d0074922 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 22 Feb 2017 15:06:01 -0500 Subject: [PATCH 041/106] macro NOEXCEPT for ktx on msvc --- libraries/ktx/src/ktx/Reader.cpp | 8 +++++++- libraries/ktx/src/ktx/Writer.cpp | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 584bf219c4..e977f9ab4f 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -13,11 +13,17 @@ #include #include +#ifndef _MSC_VER +#define NOEXCEPT noexcept +#else +#define NOEXCEPT +#endif + namespace ktx { class ReaderException: public std::exception { public: ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} - const char* what() const noexcept override { return _explanation.c_str(); } + const char* what() const NOEXCEPT override { return _explanation.c_str(); } private: const std::string _explanation; }; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 7cc6f9972d..1839624038 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -10,13 +10,18 @@ // #include "KTX.h" +#ifndef _MSC_VER +#define NOEXCEPT noexcept +#else +#define NOEXCEPT +#endif namespace ktx { class WriterException : public std::exception { public: WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} - const char* what() const noexcept override { return _explanation.c_str(); } + const char* what() const NOEXCEPT override { return _explanation.c_str(); } private: const std::string _explanation; }; From 3f67f05b02fdf6cf447471fe5c75c7db782a88bd Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 13:06:34 -0800 Subject: [PATCH 042/106] Remove the noexcept --- libraries/ktx/src/ktx/Reader.cpp | 2 +- libraries/ktx/src/ktx/Writer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 584bf219c4..a00bfd90e3 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -17,7 +17,7 @@ namespace ktx { class ReaderException: public std::exception { public: ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} - const char* what() const noexcept override { return _explanation.c_str(); } + const char* what() const override { return _explanation.c_str(); } private: const std::string _explanation; }; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 7cc6f9972d..63f8fb3988 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -16,7 +16,7 @@ namespace ktx { class WriterException : public std::exception { public: WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} - const char* what() const noexcept override { return _explanation.c_str(); } + const char* what() const override { return _explanation.c_str(); } private: const std::string _explanation; }; From c6b72dfe3c8c76534c3e79641633c56e5f423faf Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 12:04:29 -0800 Subject: [PATCH 043/106] Make memory storage mutable --- libraries/shared/src/shared/Storage.cpp | 4 +++- libraries/shared/src/shared/Storage.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index dacb840de7..3b83676bc8 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -33,7 +33,9 @@ FileStoragePointer Storage::toFileStorage(const QString& filename) const { MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { _data.resize(size); - memcpy(_data.data(), data, size); + if (data) { + memcpy(_data.data(), data, size); + } } FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 4dbb2a03a5..b79b9d6080 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -43,8 +43,9 @@ namespace storage { class MemoryStorage : public Storage { public: - MemoryStorage(size_t size, const uint8_t* data); + MemoryStorage(size_t size, const uint8_t* data = nullptr); const uint8_t* data() const override { return _data.data(); } + uint8_t* data() { return _data.data(); } size_t size() const override { return _data.size(); } private: std::vector _data; From a2269f488f99175cb603a9ff8afbcc6d24f4ab0c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 12:07:06 -0800 Subject: [PATCH 044/106] Add some testing / validation for KTX --- libraries/gpu/src/gpu/Texture_ktx.cpp | 21 +++++ libraries/ktx/src/ktx/KTX.cpp | 20 ++--- libraries/ktx/src/ktx/KTX.h | 55 ++---------- libraries/ktx/src/ktx/Writer.cpp | 43 ++-------- scripts/developer/tests/.gitignore | 1 + tests/ktx/CMakeLists.txt | 15 ++++ tests/ktx/src/main.cpp | 117 ++++++++++++++++++++++++++ 7 files changed, 176 insertions(+), 96 deletions(-) create mode 100644 scripts/developer/tests/.gitignore create mode 100644 tests/ktx/CMakeLists.txt create mode 100644 tests/ktx/src/main.cpp diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index f1d91d39ab..47e7bde333 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -93,6 +93,27 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } auto ktxBuffer = ktx::KTX::create(header, images); + + assert(0 == memcmp(&header, ktxBuffer->getHeader(), sizeof(ktx::Header))); + assert(ktxBuffer->_images.size() == images.size()); + auto start = ktxBuffer->_storage->data(); + for (size_t i = 0; i < images.size(); ++i) { + auto expected = images[i]; + auto actual = ktxBuffer->_images[i]; + assert(expected._padding == actual._padding); + assert(expected._numFaces == actual._numFaces); + assert(expected._imageSize == actual._imageSize); + assert(expected._faceSize == actual._faceSize); + assert(actual._faceBytes.size() == actual._numFaces); + for (uint32_t face = 0; face < expected._numFaces; ++face) { + auto expectedFace = expected._faceBytes[face]; + auto actualFace = actual._faceBytes[face]; + auto offset = actualFace - start; + assert(offset % 4 == 0); + assert(expectedFace != actualFace); + assert(0 == memcmp(expectedFace, actualFace, expected._faceSize)); + } + } return ktxBuffer; } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 8635fbb684..ea7653e1ed 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -86,11 +86,10 @@ void KTX::resetStorage(Storage* storage) { } const Header* KTX::getHeader() const { - if (_storage) { - return reinterpret_cast (_storage->_bytes); - } else { + if (!_storage) { return nullptr; - } + } + return reinterpret_cast(_storage->data()); } @@ -105,7 +104,7 @@ size_t KTX::getKeyValueDataSize() const { size_t KTX::getTexelsDataSize() const { if (_storage) { //return _storage->size() - (sizeof(Header) + getKeyValueDataSize()); - return (_storage->_bytes + _storage->_size) - getTexelsData(); + return (_storage->data() + _storage->size()) - getTexelsData(); } else { return 0; } @@ -113,7 +112,7 @@ size_t KTX::getTexelsDataSize() const { const Byte* KTX::getKeyValueData() const { if (_storage) { - return (_storage->_bytes + sizeof(Header)); + return (_storage->data() + sizeof(Header)); } else { return nullptr; } @@ -121,16 +120,9 @@ const Byte* KTX::getKeyValueData() const { const Byte* KTX::getTexelsData() const { if (_storage) { - return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); + return (_storage->data() + sizeof(Header) + getKeyValueDataSize()); } else { return nullptr; } } -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 94529d6e68..c1521a52b9 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -19,6 +19,8 @@ #include #include +#include + /* KTX Spec: Byte[12] identifier @@ -291,46 +293,8 @@ namespace ktx { NUM_CUBEMAPFACES = 6, }; - - // 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; } - } - 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()) - {} - - }; + using Storage = storage::Storage; + using StoragePointer = std::unique_ptr; // Header struct Header { @@ -382,7 +346,8 @@ namespace ktx { void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat_Uncompressed internalFormat, GLBaseInternalFormat baseInternalFormat) { glType = (uint32_t) type; - glTypeSize = 0; + // FIXME this should correspond to the size of glType + glTypeSize = 1; glFormat = (uint32_t) format; glInternalFormat = (uint32_t) internalFormat; glBaseInternalFormat = (uint32_t) baseInternalFormat; @@ -456,13 +421,9 @@ namespace ktx { }; using Images = std::vector; - class KTX { void resetStorage(Storage* src); - void resetHeader(const Header& header); - void resetImages(const Images& images); - KTX(); public: @@ -498,12 +459,12 @@ namespace ktx { const Header* getHeader() const; const Byte* getKeyValueData() const; const Byte* getTexelsData() const; - Byte* getTexelsData(); + const StoragePointer& getStorage() const { return _storage; } size_t getKeyValueDataSize() const; size_t getTexelsDataSize() const; - std::unique_ptr _storage; + StoragePointer _storage; KeyValues _keyValues; Images _images; }; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 1839624038..7fcf4c60d2 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -26,40 +26,15 @@ namespace ktx { const std::string _explanation; }; - - 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(); - - // 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; + std::unique_ptr storagePointer; + { + auto storageSize = ktx::KTX::evalStorageSize(header, images); + auto memoryStorage = new storage::MemoryStorage(storageSize); + ktx::KTX::write(memoryStorage->data(), memoryStorage->size(), header, images); + storagePointer.reset(memoryStorage); + } + return create(storagePointer); } size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { @@ -117,8 +92,6 @@ namespace ktx { size_t currentDataSize = 0; auto currentPtr = imagesDataPtr; - - for (uint32_t l = 0; l < srcImages.size(); l++) { if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { size_t imageSize = srcImages[l]._imageSize; diff --git a/scripts/developer/tests/.gitignore b/scripts/developer/tests/.gitignore new file mode 100644 index 0000000000..7cacbf042c --- /dev/null +++ b/scripts/developer/tests/.gitignore @@ -0,0 +1 @@ +cube_texture.ktx \ No newline at end of file diff --git a/tests/ktx/CMakeLists.txt b/tests/ktx/CMakeLists.txt new file mode 100644 index 0000000000..d72379efd6 --- /dev/null +++ b/tests/ktx/CMakeLists.txt @@ -0,0 +1,15 @@ + +set(TARGET_NAME ktx-test) + +if (WIN32) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217") +endif() + +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Quick Gui OpenGL) +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") + +# link in the shared libraries +link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) + +package_libraries_for_deployment() diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp new file mode 100644 index 0000000000..a3c3b99960 --- /dev/null +++ b/tests/ktx/src/main.cpp @@ -0,0 +1,117 @@ +// +// Created by Bradley Austin Davis on 2016/07/01 +// 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 +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +QSharedPointer logger; + +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true); + + +void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { + QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message); + + if (!logMessage.isEmpty()) { +#ifdef Q_OS_WIN + OutputDebugStringA(logMessage.toLocal8Bit().constData()); + OutputDebugStringA("\n"); +#endif + logger->addMessage(qPrintable(logMessage + "\n")); + } +} + +const char * LOG_FILTER_RULES = R"V0G0N( +hifi.gpu=true +)V0G0N"; + +QString getRootPath() { + static std::once_flag once; + static QString result; + std::call_once(once, [&] { + QFileInfo file(__FILE__); + QDir parent = file.absolutePath(); + result = QDir::cleanPath(parent.currentPath() + "/../../.."); + }); + return result; +} + +const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png"; +const QString TEST_IMAGE_KTX = getRootPath() + "/scripts/developer/tests/cube_texture.ktx"; + +int main(int argc, char** argv) { + QApplication app(argc, argv); + QCoreApplication::setApplicationName("KTX"); + QCoreApplication::setOrganizationName("High Fidelity"); + QCoreApplication::setOrganizationDomain("highfidelity.com"); + logger.reset(new FileLogger()); + + DependencyManager::set(); + qInstallMessageHandler(messageHandler); + QLoggingCategory::setFilterRules(LOG_FILTER_RULES); + + QImage image(TEST_IMAGE); + gpu::Texture* testTexture = model::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true, false, true); + + auto ktxPtr = gpu::Texture::serialize(*testTexture); + const auto& ktxStorage = ktxPtr->getStorage(); + auto header = ktxPtr->getHeader(); + assert(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); + QFile outFile(TEST_IMAGE_KTX); + if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { + throw std::runtime_error("Unable to open file"); + } + //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() + auto ktxSize = ktxStorage->size(); + outFile.resize(ktxSize); + auto dest = outFile.map(0, ktxSize); + memcpy(dest, ktxStorage->data(), ktxSize); + outFile.unmap(dest); + outFile.close(); +// gpu::Texture* ktxTexture = cacheTexture(TEST_IMAGE.toStdString(), testTexture, true, true); + return 0; +} + +#include "main.moc" + From 61ba2dd11e303045cea9b29fca2ad542d5e17209 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 13:24:54 -0800 Subject: [PATCH 045/106] Disable texture cache processing for now --- libraries/model/src/model/TextureMap.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 760cd98898..dcc685ca22 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -91,6 +91,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } gpu::Texture* returnedTexture = srcTexture; +#if 0 auto theKTX = Texture::serialize(*srcTexture); if (theKTX) { // Prepare cache directory @@ -147,6 +148,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } } } +#endif return returnedTexture; } From 9ca4926c1d40520bd1a59f414ade94000973092c Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 13:43:23 -0800 Subject: [PATCH 046/106] Fixing the bad assignment for the srgb textures --- libraries/gpu/src/gpu/Texture_ktx.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index f1d91d39ab..e2da959a1a 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -106,10 +106,11 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx Format texelFormat = Format::COLOR_SRGBA_32; if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { - mipFormat = Format::COLOR_BGRA_32; if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + mipFormat = Format::COLOR_BGRA_32; texelFormat = Format::COLOR_RGBA_32; } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + mipFormat = Format::COLOR_BGRA_32; texelFormat = Format::COLOR_SRGBA_32; } else { return nullptr; From c2831a513bde3aeda2eb6350fa9bd43c1980a3ae Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 14:41:43 -0800 Subject: [PATCH 047/106] Adjusting the representation of the format in the KTX for sRGB, unfortunately not working with PicoPixel --- libraries/gpu/src/gpu/Texture_ktx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index e2da959a1a..d5e9122a4f 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) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_BGRA_32) { + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); @@ -110,7 +110,7 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx mipFormat = Format::COLOR_BGRA_32; texelFormat = Format::COLOR_RGBA_32; } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { - mipFormat = Format::COLOR_BGRA_32; + mipFormat = Format::COLOR_SBGRA_32; texelFormat = Format::COLOR_SRGBA_32; } else { return nullptr; From 05efac9ddfc3262a6fe0c2ab562f4ab5b55235b4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 15:42:51 -0800 Subject: [PATCH 048/106] Add compile time toggle for threaded buffering --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 9 ++++++- .../gpu/gl45/GL45BackendVariableTexture.cpp | 26 ++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 29e5a59ec5..28425433c4 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -115,22 +115,29 @@ public: bool _bufferingCompleted { false }; VoidLambda _transferLambda; VoidLambda _bufferingLambda; - static ThreadPointer _bufferThread; +#if THREADED_TEXTURE_BUFFERING static Mutex _mutex; static VoidLambdaQueue _bufferLambdaQueue; + static ThreadPointer _bufferThread; static std::atomic _shutdownBufferingThread; static void bufferLoop(); +#endif public: TransferJob(const TransferJob& other) = delete; TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); bool tryTransfer(); + +#if THREADED_TEXTURE_BUFFERING static void startTransferLoop(); static void stopTransferLoop(); +#endif private: +#if THREADED_TEXTURE_BUFFERING void startBuffering(); +#endif void transfer(); }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 92cc945a86..62f1a3c248 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -52,6 +52,7 @@ using TransferJob = GL45VariableAllocationTexture::TransferJob; static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; +#if THREADED_TEXTURE_BUFFERING std::shared_ptr TransferJob::_bufferThread { nullptr }; std::atomic TransferJob::_shutdownBufferingThread { false }; Mutex TransferJob::_mutex; @@ -76,6 +77,7 @@ void TransferJob::stopTransferLoop() { _bufferThread.reset(); _shutdownBufferingThread = false; } +#endif TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) : _parent(parent) { @@ -123,14 +125,7 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::funct bool TransferJob::tryTransfer() { // Disable threaded texture transfer for now -#if 1 - if (!_bufferingCompleted) { - _bufferingLambda(); - _bufferingCompleted = true; - } - _transferLambda(); - return true; -#else +#if THREADED_TEXTURE_BUFFERING // Are we ready to transfer if (_bufferingCompleted) { _transferLambda(); @@ -139,9 +134,18 @@ bool TransferJob::tryTransfer() { startBuffering(); return false; +#else + if (!_bufferingCompleted) { + _bufferingLambda(); + _bufferingCompleted = true; + } + _transferLambda(); + return true; #endif } +#if THREADED_TEXTURE_BUFFERING + void TransferJob::startBuffering() { if (_bufferingStarted) { return; @@ -172,6 +176,7 @@ void TransferJob::bufferLoop() { } } } +#endif void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { @@ -318,6 +323,7 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { } if (newState != _memoryPressureState) { +#if THREADED_TEXTURE_BUFFERING if (MemoryPressureState::Transfer == _memoryPressureState) { TransferJob::stopTransferLoop(); } @@ -325,7 +331,9 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { if (MemoryPressureState::Transfer == _memoryPressureState) { TransferJob::startTransferLoop(); } - +#else + _memoryPressureState = newState; +#endif // Clear the existing queue _transferQueue = WorkQueue(); _promoteQueue = WorkQueue(); From 62422690d05e037a7ea7680db3563755a68bfad1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 15:44:06 -0800 Subject: [PATCH 049/106] More KTX testing, functionality --- libraries/gpu/src/gpu/Texture.cpp | 33 +++++++++++++--- libraries/gpu/src/gpu/Texture.h | 12 ++++-- libraries/ktx/src/ktx/KTX.cpp | 12 ++++++ libraries/ktx/src/ktx/KTX.h | 1 + tests/ktx/src/main.cpp | 65 ++++++++++++++++++++++++------- 5 files changed, 98 insertions(+), 25 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index aa81b34914..4b2156ec79 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include "GPULogging.h" @@ -120,19 +121,24 @@ void Texture::setAllowedGPUMemoryUsage(Size size) { uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; -void Texture::Storage::assignTexture(Texture* texture) { +using Storage = Texture::Storage; +using PixelsPointer = Texture::PixelsPointer; +using MemoryStorage = Texture::MemoryStorage; +using KtxStorage = Texture::KtxStorage; + +void Storage::assignTexture(Texture* texture) { _texture = texture; if (_texture) { _type = _texture->getType(); } } -void Texture::MemoryStorage::reset() { +void MemoryStorage::reset() { _mips.clear(); bumpStamp(); } -const Texture::PixelsPointer Texture::MemoryStorage::getMipFace(uint16 level, uint8 face) const { +PixelsPointer MemoryStorage::getMipFace(uint16 level, uint8 face) const { if (level < _mips.size()) { assert(face < _mips[level].size()); return _mips[level][face]; @@ -140,12 +146,12 @@ const Texture::PixelsPointer Texture::MemoryStorage::getMipFace(uint16 level, ui return PixelsPointer(); } -bool Texture::MemoryStorage::isMipAvailable(uint16 level, uint8 face) const { +bool MemoryStorage::isMipAvailable(uint16 level, uint8 face) const { PixelsPointer mipFace = getMipFace(level, face); return (mipFace && mipFace->getSize()); } -bool Texture::MemoryStorage::allocateMip(uint16 level) { +bool MemoryStorage::allocateMip(uint16 level) { bool changed = false; if (level >= _mips.size()) { _mips.resize(level+1, std::vector(Texture::NUM_FACES_PER_TYPE[getType()])); @@ -164,7 +170,7 @@ bool Texture::MemoryStorage::allocateMip(uint16 level) { return changed; } -void Texture::MemoryStorage::assignMipData(uint16 level, const storage::StoragePointer& storagePointer) { +void MemoryStorage::assignMipData(uint16 level, const storage::StoragePointer& storagePointer) { allocateMip(level); auto& mip = _mips[level]; @@ -193,6 +199,13 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s } } +KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) : _ktxData(ktxData.release()) { +} + +PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { + return _ktxData->getMipFaceTexelsData(level, face); +} + Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { Texture* tex = new Texture(TextureUsageType::EXTERNAL); tex->_type = TEX_2D; @@ -963,3 +976,11 @@ Texture::ExternalUpdates Texture::getUpdates() const { return result; } +void Texture::setStorage(std::unique_ptr& newStorage) { + _storage.swap(newStorage); +} + +void Texture::setKtxBacking(ktx::KTXUniquePointer& ktxBacking) { + auto newBacking = std::unique_ptr(new KtxStorage(ktxBacking)); + setStorage(newBacking); +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index a8fd9e3b0d..637b098504 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -256,7 +256,7 @@ public: virtual ~Storage() {} virtual void reset() = 0; - virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; + virtual PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; @@ -281,7 +281,7 @@ public: class MemoryStorage : public Storage { public: void reset() override; - const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; + PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; void assignMipData(uint16 level, const storage::StoragePointer& storage) override; void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; bool isMipAvailable(uint16 level, uint8 face = 0) const override; @@ -294,8 +294,9 @@ public: class KtxStorage : public Storage { public: KtxStorage(ktx::KTXUniquePointer& ktxData); - const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; - bool isMipAvailable(uint16 level, uint8 face = 0) const override; + PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; + // By convention, all mip levels and faces MUST be populated when using KTX backing + bool isMipAvailable(uint16 level, uint8 face = 0) const override { return true; } void assignMipData(uint16 level, const storage::StoragePointer& storage) override { throw std::runtime_error("Invalid call"); @@ -474,6 +475,9 @@ public: bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } + void setStorage(std::unique_ptr& newStorage); + void setKtxBacking(ktx::KTXUniquePointer& newBacking); + // access sizes for the stored mips uint16 getStoredMipWidth(uint16 level) const; uint16 getStoredMipHeight(uint16 level) const; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index ea7653e1ed..e30bde64d6 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -126,3 +126,15 @@ const Byte* KTX::getTexelsData() const { } } +storage::StoragePointer KTX::getMipFaceTexelsData(uint16_t mip, uint8_t face) const { + storage::StoragePointer result; + if (mip < _images.size()) { + const auto& faces = _images[mip]; + if (face < faces._numFaces) { + auto faceOffset = faces._faceBytes[face] - _storage->data(); + auto faceSize = faces._faceSize; + result = _storage->createView(faceSize, faceOffset); + } + } + return result; +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index c1521a52b9..783f7e428c 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -459,6 +459,7 @@ namespace ktx { const Header* getHeader() const; const Byte* getKeyValueData() const; const Byte* getTexelsData() const; + storage::StoragePointer getMipFaceTexelsData(uint16_t mip = 0, uint8_t face = 0) const; const StoragePointer& getStorage() const { return _storage; } size_t getKeyValueDataSize() const; diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index a3c3b99960..2dbf2f60d7 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -87,6 +87,8 @@ int main(int argc, char** argv) { QCoreApplication::setOrganizationDomain("highfidelity.com"); logger.reset(new FileLogger()); + Q_ASSERT(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); + DependencyManager::set(); qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); @@ -94,22 +96,55 @@ int main(int argc, char** argv) { QImage image(TEST_IMAGE); gpu::Texture* testTexture = model::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true, false, true); - auto ktxPtr = gpu::Texture::serialize(*testTexture); - const auto& ktxStorage = ktxPtr->getStorage(); - auto header = ktxPtr->getHeader(); - assert(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); - QFile outFile(TEST_IMAGE_KTX); - if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { - throw std::runtime_error("Unable to open file"); + auto ktxMemory = gpu::Texture::serialize(*testTexture); + { + const auto& ktxStorage = ktxMemory->getStorage(); + auto header = ktxMemory->getHeader(); + QFile outFile(TEST_IMAGE_KTX); + if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { + throw std::runtime_error("Unable to open file"); + } + //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() + auto ktxSize = ktxStorage->size(); + outFile.resize(ktxSize); + auto dest = outFile.map(0, ktxSize); + memcpy(dest, ktxStorage->data(), ktxSize); + outFile.unmap(dest); + outFile.close(); } - //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() - auto ktxSize = ktxStorage->size(); - outFile.resize(ktxSize); - auto dest = outFile.map(0, ktxSize); - memcpy(dest, ktxStorage->data(), ktxSize); - outFile.unmap(dest); - outFile.close(); -// gpu::Texture* ktxTexture = cacheTexture(TEST_IMAGE.toStdString(), testTexture, true, true); + + auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); + { + const auto& memStorage = ktxMemory->getStorage(); + const auto& fileStorage = ktxFile->getStorage(); + Q_ASSERT(memStorage->size() == fileStorage->size()); + Q_ASSERT(memStorage->data() != fileStorage->data()); + Q_ASSERT(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size())); + Q_ASSERT(ktxFile->_images.size() == ktxMemory->_images.size()); + auto imageCount = ktxFile->_images.size(); + auto startMemory = ktxMemory->_storage->data(); + auto startFile = ktxFile->_storage->data(); + for (size_t i = 0; i < imageCount; ++i) { + auto memImages = ktxMemory->_images[i]; + auto fileImages = ktxFile->_images[i]; + Q_ASSERT(memImages._padding == fileImages._padding); + Q_ASSERT(memImages._numFaces == fileImages._numFaces); + Q_ASSERT(memImages._imageSize == fileImages._imageSize); + Q_ASSERT(memImages._faceSize == fileImages._faceSize); + Q_ASSERT(memImages._faceBytes.size() == memImages._numFaces); + Q_ASSERT(fileImages._faceBytes.size() == fileImages._numFaces); + auto faceCount = fileImages._numFaces; + for (uint32_t face = 0; face < faceCount; ++face) { + auto memFace = memImages._faceBytes[face]; + auto memOffset = memFace - startMemory; + auto fileFace = fileImages._faceBytes[face]; + auto fileOffset = fileFace - startFile; + Q_ASSERT(memOffset % 4 == 0); + Q_ASSERT(memOffset == fileOffset); + } + } + } + testTexture->setKtxBacking(ktxFile); return 0; } From 81cee57bb1bfe5a77be4edf371843fd50009eac7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 15:44:27 -0800 Subject: [PATCH 050/106] Add gamma-aware scaling test image --- scripts/developer/tests/scaling.png | Bin 0 -> 3172 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/developer/tests/scaling.png diff --git a/scripts/developer/tests/scaling.png b/scripts/developer/tests/scaling.png new file mode 100644 index 0000000000000000000000000000000000000000..1e6a7df45d8440cc52d3b45501b909419fed2f1b GIT binary patch literal 3172 zcmd5;c~p~E7XQ63A%rEG0zw6aOe>MPK~IBJisaL=qAd_A)CCaGwo@pwR8gZC_|%SD z7o4J$st8ugBC@Cz1PuvBYq45TM5xH3-~b|o9YRPlCwSUFX3m*2?O*fPeed4;yT5ne zyYHTRFu>o3XKrr}fVXnRvQ+>DfPl*ZaO3bV9|M+iS1wx;Bqh%uyiNeFQgFJcjPsAZ zQ+zK$=}JukxPSm)@JBWj{=Z@I&oh>M-7es>42Jw255u%;9b7_Ar+anY4|u#h;LQ2T zXWDU%Msp|eqeX1oR5;Ed z%2}=L#||eS>yaPipfV=$G*|spnb+iuN)}3+#M!v_fuM66=g`gr46y9i{f9JUb}sR=GCq% zR(P3YRJ{JO!mW$0%9Jz@eYk?q7U{t?rcyCMhi z6{Z`TtYfCa`{>n@WsR%lQ;+N=tEm?r;P=t9QTaV#{6N zh1Pmom+C}u<3gMv&9tVn=P0E-_pG{G3+9@Qtto#hLS%otuAK(%ys9nwp}|Zmc#fx{ z@4e;VgALMJ*0VC@D40W;6Y8yVt(VamH@OrP?uF{bEryGZ>yr$b7q$3XH8k5~RT|1?GJ0JWv*9}?nbf>SGfY8XT`;`MnhRsYzrz*~= z*A&XdI*PFZ)w71p)L&UCq1xqkU@kKXfq;M)EJ!w0C{PvwOE%?t%W$GO* zQv5VVq%*X-k;SH=>(8gm)6xC*3s9bQ+sx>5RT7 zd4@9yS6et(OIvWcdDMMG9_j>>&9K$pDm%-Ru&KU$#B5$#Qtn?aEa-mH*Htd;G0_(7mw3bheDNh9_>{ z^Qy-bHmNZfzG!*8rbotKzSn=mCMS0TmwUqEoo9E0*ZsPG0VuUUW9$J8a9P8m5Z~r{ znNSEYOmn9J|Bl24<@*GEGdIz|qp>9b*Z}=!1OfPyUG2Y#)@lD4`*&c4Ogob4Bu+Xq z_pKif4jc5(+ssD9x+{L8cDah|fwsBUq1F8w&M`yF?qNW+?YVekiy}GmSVryVJ^eS| zh;`M+4z^hYRd^Q_9@K?wTb$A{7Xri|9nTYI6X~FIpsJ+#E>F2LwJt$rXE|;LH{VE| z;Xo7yWI3rZm-dT5(ROTHRc9U&zROcKs$;mhfnn zo9B75R?*7_8w>h>xgX>%Gl`By(!f8X>z@^CVZb)o~%L;_f%|NAB(nv;`jxp6AniR}RWt zOD#%0&=$Nt`J&_#-1=(EQqWKi8Lkb8!WuZfDcvVI0&)GgVqLa^bHM_2#0)@K3 zdjq1dUpco+wDwFy&qUnnvkH^scz73k? z*0^wSGR<<^RCVlhggmC)f!R|PKFR46xws_6p1H3d{JG8pRmZZE`8aUG%TL(o%%i$p34%Ee%*y;?&!bVsIIHVCU`&x67@V=oz80bPkRU#~ z>gCI6fJ#&z?)_yH7DU1RsqTCaah zxRduvIgME=l>hJbq$|g`^ed--g)kOKgQTUv^iBC-opi?Po#V+ zm|^$~;%#`!C&53@XE@4QRL7AQRrS7~vVf;t!Q#)z7t-jQjVzM8iRz8TkG3?+?X(#m z<7SA&gQxS2ubgGVr}+3Khj9P?+aBJ>0aLF{GTn+ZpK29FWaXm}S4>*R@TAF$j? zP`PuvJC1>5odjZPH}|b$E>yXWsgLIvw%Rj3?!mY;P72a{OGk~S zzi8qDb@f4?vTADBHjg5jCSF2k=JgQ|XpW2w2Br6wKArRmOB~8C&>lM*hdIYVKp2Y| z6JeT!ai>eA(2mL#^M^8v9P}5>P@G3x7M0vnt4+sq z^pA=!`D%4M<-@eFMq`Z_C+h!Q7-w(pixK<}i+|%%4w63`O{v~IOI3Pm{_;5hu<~vH KWra&4_WTP0c9{JD literal 0 HcmV?d00001 From 055db531b1b1dad9413bce4bfc318aa3ce69e720 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 15:59:05 -0800 Subject: [PATCH 051/106] Update 'caching' of textures to KTX file backing --- libraries/model/src/model/TextureMap.cpp | 82 ++++++++++-------------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index dcc685ca22..81d768e91d 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -89,66 +89,54 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo if (!srcTexture) { return nullptr; } - gpu::Texture* returnedTexture = srcTexture; -#if 0 - auto theKTX = Texture::serialize(*srcTexture); - if (theKTX) { + static QString ktxCacheFolder; + static std::once_flag once; + std::call_once(once, [&] { // Prepare cache directory - QString path("hifi_ktx/"); - QFileInfo originalFileInfo(path); + static const QString HIFI_KTX_FOLDER("hifi_ktx"); QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - path = docsLocation + "/" + path; - QFileInfo info(path); - if (!info.absoluteDir().exists()) { - QString originalRelativePath = originalFileInfo.path(); - QDir(docsLocation).mkpath(originalRelativePath); + ktxCacheFolder = docsLocation + "/" + HIFI_KTX_FOLDER; + QFileInfo info(ktxCacheFolder); + if (!info.exists()) { + QDir(docsLocation).mkpath(HIFI_KTX_FOLDER); } + }); - std::string cleanedName = name; - cleanedName = cleanedName.substr(cleanedName.find_last_of('//') + 1); + std::string cleanedName = name; + cleanedName = cleanedName.substr(cleanedName.find_last_of('//') + 1); + std::string cacheFilename(ktxCacheFolder.toStdString()); + cacheFilename += cleanedName; + cacheFilename += ".ktx"; - std::string filename(path.toStdString()); - filename += cleanedName; - filename += ".ktx"; - - if (write) { - /* FILE *file = fopen(name.c_str(), "r"); - if (file != nullptr) { - fclose(file); - } else*/ { - FILE *file = fopen (filename.c_str(),"wb"); - if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); + gpu::Texture* returnedTexture = srcTexture; + { + if (write && !QFileInfo(cacheFilename.c_str()).exists()) { + auto ktxMemory = gpu::Texture::serialize(*srcTexture); + if (ktxMemory) { + const auto& ktxStorage = ktxMemory->getStorage(); + auto header = ktxMemory->getHeader(); + QFile outFile(cacheFilename.c_str()); + if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { + throw std::runtime_error("Unable to open file"); } + //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() + auto ktxSize = ktxStorage->size(); + outFile.resize(ktxSize); + auto dest = outFile.map(0, ktxSize); + memcpy(dest, ktxStorage->data(), ktxSize); + outFile.unmap(dest); + outFile.close(); } } - 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(srcTexture->getUsage(), srcTexture->getUsageType(), ktx::KTX::create(storage), srcTexture->getSampler()); - - if (theNewTexure) { - returnedTexture = theNewTexure; - delete srcTexture; - } - } + if (read && QFileInfo(cacheFilename.c_str()).exists()) { + auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); + returnedTexture->setKtxBacking(ktxFile); } } -#endif + return returnedTexture; } From ad40e2d7d95a57385a517559b5de4442acf3aed6 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 17:16:37 -0800 Subject: [PATCH 052/106] Trying to address the ktx serialization problem with normal, still have a bug --- libraries/gpu/src/gpu/Texture_ktx.cpp | 14 ++++++++++++++ libraries/ktx/src/ktx/KTX.cpp | 4 ---- libraries/ktx/src/ktx/KTX.h | 1 - libraries/ktx/src/ktx/Reader.cpp | 4 ++-- libraries/ktx/src/ktx/Writer.cpp | 4 ++-- libraries/model/src/model/TextureMap.cpp | 15 ++++++++------- libraries/render-utils/src/MaterialTextures.slh | 2 +- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d5e9122a4f..7512f9ddb7 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -24,8 +24,12 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_RGBA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SRGBA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); } else { @@ -115,6 +119,16 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx } else { return nullptr; } + } else if (header.getGLFormat() == ktx::GLFormat::RGBA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + mipFormat = Format::COLOR_RGBA_32; + texelFormat = Format::COLOR_RGBA_32; + } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + mipFormat = Format::COLOR_SRGBA_32; + texelFormat = Format::COLOR_SRGBA_32; + } else { + return nullptr; + } } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { mipFormat = Format::COLOR_R_8; if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 8635fbb684..7613747e5c 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -34,10 +34,6 @@ uint32_t Header::evalMaxDimension() const { return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); } -uint32_t Header::evalMaxLevel() const { - return 1 + log2(evalMaxDimension()); -} - uint32_t Header::evalPixelWidth(uint32_t level) const { return std::max(getPixelWidth() >> level, 1U); } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 94529d6e68..7dc1963090 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -370,7 +370,6 @@ namespace ktx { uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } 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; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index e977f9ab4f..e9e0f2760c 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -149,10 +149,10 @@ namespace ktx { faces[face] = currentPtr; currentPtr += faceSize; } - images.emplace_back(Image(faceSize, padding, faces)); + images.emplace_back(Image((uint32_t) faceSize, padding, faces)); currentPtr += padding; } else { - images.emplace_back(Image(imageSize, padding, currentPtr)); + images.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize + padding; } } else { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 1839624038..89b1d975a2 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -122,7 +122,7 @@ namespace ktx { 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; + *(reinterpret_cast (currentPtr)) = (uint32_t) imageSize; currentPtr += sizeof(uint32_t); currentDataSize += sizeof(uint32_t); @@ -133,7 +133,7 @@ namespace ktx { // 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)); + destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize; } else { Image::FaceBytes faceBytes(6); diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 760cd98898..f3d6918453 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -106,7 +106,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo std::string cleanedName = name; - cleanedName = cleanedName.substr(cleanedName.find_last_of('//') + 1); + cleanedName = cleanedName.substr(cleanedName.find_last_of((char) '//') + 1); std::string filename(path.toStdString()); filename += cleanedName; @@ -378,8 +378,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; + gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); @@ -388,7 +388,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); + theTexture = cacheTexture(theTexture->source(), theTexture, true, false); } return theTexture; @@ -468,8 +468,9 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + + gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; + gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); @@ -478,7 +479,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); + theTexture = cacheTexture(theTexture->source(), theTexture, true, true); } return theTexture; diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 6d2ad23c21..7b73896cc5 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -64,7 +64,7 @@ float fetchRoughnessMap(vec2 uv) { uniform sampler2D normalMap; vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out - return normalize(texture(normalMap, uv).xzy -vec3(0.5, 0.5, 0.5)); + return normalize(texture(normalMap, uv).rbg -vec3(0.5, 0.5, 0.5)); } <@endif@> From e8835b34f446d0b55eee2686566d67d002ce61d8 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 19:05:20 -0800 Subject: [PATCH 053/106] fix issues while testing the ktxStorage --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 8 ++++++-- libraries/ktx/src/ktx/KTX.h | 2 +- libraries/model/src/model/TextureMap.cpp | 1 + libraries/shared/src/shared/Storage.cpp | 3 ++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 95837c16d9..f6c40259ea 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -142,8 +142,12 @@ void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, } auto size = _gpuObject.evalMipDimensions(sourceMip); auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); + if (mipData) { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); + copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); + } else { + qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + } } void GL45Texture::syncSampler() const { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 0ea8d4896d..0e0bb3831e 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -294,7 +294,7 @@ namespace ktx { }; using Storage = storage::Storage; - using StoragePointer = std::unique_ptr; + using StoragePointer = std::shared_ptr; // Header struct Header { diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index f0e8074aaa..43c8a8fbc2 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -106,6 +106,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo std::string cleanedName = name; cleanedName = cleanedName.substr(cleanedName.find_last_of((char) '//') + 1); std::string cacheFilename(ktxCacheFolder.toStdString()); + cacheFilename += "/"; cacheFilename += cleanedName; cacheFilename += ".ktx"; diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 3b83676bc8..7075d9c6f7 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -57,7 +57,8 @@ FileStoragePointer FileStorage::create(const QString& filename, size_t size, con } } file.close(); - return FileStoragePointer(new FileStorage(filename)); + //return FileStoragePointer(new FileStorage(filename)); + return std::make_shared(filename); } FileStorage::FileStorage(const QString& filename) : _file(filename) { From bbded6aa517e79876ba4069ae9f421bbf168105f Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 19:07:01 -0800 Subject: [PATCH 054/106] fix issues while testing the ktxStorage --- libraries/model/src/model/TextureMap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 43c8a8fbc2..d2cdeb0a0b 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) { // FIXME: set read to false for a working state if (!srcTexture) { return nullptr; } @@ -469,7 +469,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture, true, true); + theTexture = cacheTexture(theTexture->source(), theTexture, true, false); } return theTexture; From fbb0a24c4f45a42db917a1491f849eccb33d145b Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 23 Feb 2017 02:08:46 -0800 Subject: [PATCH 055/106] FIxing the bug preventing to deserialize normals, clean up the ktx usage of the storage::Storage class and cleaning up somewhat the Qt pixel formats used --- .../src/gpu/gl45/GL45BackendTexture.cpp | 2 +- libraries/gpu/src/gpu/Format.cpp | 1 + libraries/gpu/src/gpu/Format.h | 2 + libraries/ktx/src/ktx/KTX.cpp | 4 +- libraries/ktx/src/ktx/KTX.h | 7 ++-- libraries/ktx/src/ktx/Reader.cpp | 4 +- libraries/ktx/src/ktx/Writer.cpp | 2 +- .../src/model-networking/TextureCache.cpp | 4 ++ libraries/model/src/model/TextureMap.cpp | 41 +++++++++++++++---- tests/ktx/src/main.cpp | 2 +- 10 files changed, 50 insertions(+), 19 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index f6c40259ea..d5ad0204bf 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -146,7 +146,7 @@ void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); } else { - qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); } } diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 5b61a3a5a4..de202911e3 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -11,6 +11,7 @@ using namespace gpu; const Element Element::COLOR_R_8 { SCALAR, NUINT8, RED }; +const Element Element::COLOR_SR_8 { SCALAR, NUINT8, SRED }; const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 4610597a56..493a2de3c2 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -150,6 +150,7 @@ enum Semantic { STENCIL, // Stencil only buffer DEPTH_STENCIL, // Depth Stencil buffer + SRED, SRGB, SRGBA, SBGRA, @@ -229,6 +230,7 @@ public: } static const Element COLOR_R_8; + static const Element COLOR_SR_8; static const Element COLOR_RGBA_32; static const Element COLOR_SRGBA_32; static const Element COLOR_BGRA_32; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index b03f855783..cc9c1069b1 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -77,8 +77,8 @@ KTX::KTX() { KTX::~KTX() { } -void KTX::resetStorage(Storage* storage) { - _storage.reset(storage); +void KTX::resetStorage(StoragePointer& storage) { + _storage = storage; } const Header* KTX::getHeader() const { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 0e0bb3831e..7aef33704e 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -345,8 +345,7 @@ namespace ktx { void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat_Uncompressed internalFormat, GLBaseInternalFormat baseInternalFormat) { glType = (uint32_t) type; - // FIXME this should correspond to the size of glType - glTypeSize = 1; + glTypeSize = typeSize; glFormat = (uint32_t) format; glInternalFormat = (uint32_t) internalFormat; glBaseInternalFormat = (uint32_t) baseInternalFormat; @@ -421,7 +420,7 @@ namespace ktx { using Images = std::vector; class KTX { - void resetStorage(Storage* src); + void resetStorage(StoragePointer& src); KTX(); public: @@ -449,7 +448,7 @@ 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(std::unique_ptr& src); + static std::unique_ptr create(StoragePointer& src); static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index e9e0f2760c..d74b45c01c 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -163,7 +163,7 @@ namespace ktx { return images; } - std::unique_ptr KTX::create(std::unique_ptr& src) { + std::unique_ptr KTX::create(StoragePointer& src) { if (!src) { return nullptr; } @@ -173,7 +173,7 @@ namespace ktx { } std::unique_ptr result(new KTX()); - result->resetStorage(src.release()); + result->resetStorage(src); // read metadata // result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 747e1e7bac..901571f804 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -27,7 +27,7 @@ namespace ktx { }; std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { - std::unique_ptr storagePointer; + StoragePointer storagePointer; { auto storageSize = ktx::KTX::evalStorageSize(header, images); auto memoryStorage = new storage::MemoryStorage(storageSize); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1f21e9e78d..f4bb3707e8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -232,6 +232,10 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return model::TextureUsage::createMetallicTextureFromImage; break; } + case Type::OCCLUSION_TEXTURE: { + return model::TextureUsage::create2DTextureFromImage; + break; + } case Type::STRICT_TEXTURE: { return model::TextureUsage::createStrict2DTextureFromImage; break; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d2cdeb0a0b..c2bca9d1be 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) { // FIXME: set read to false for a working state +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { if (!srcTexture) { return nullptr; } @@ -132,8 +132,32 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } if (read && QFileInfo(cacheFilename.c_str()).exists()) { - auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); - returnedTexture->setKtxBacking(ktxFile); + { + FILE* file = fopen(cacheFilename.c_str(), "rb"); + if (file != nullptr) { + // obtain file size: + fseek (file , 0 , SEEK_END); + auto size = ftell(file); + rewind(file); + + auto storage = std::make_shared(size); + fread(storage->data(), 1, storage->size(), file); + fclose (file); + + //then create a new texture out of the ktx + auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), + ktx::KTX::create(std::static_pointer_cast(storage)), srcTexture->getSampler()); + + if (theNewTexure) { + returnedTexture = theNewTexure; + delete srcTexture; + } + } + } + + // auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); + // returnedTexture->setKtxBacking(ktxFile); + } } @@ -360,15 +384,16 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src PROFILE_RANGE(resource_parse, "createNormalTextureFromNormalImage"); QImage image = processSourceImage(srcImage, false); - // Make sure the normal map source image is RGBA32 - if (image.format() != QImage::Format_RGBA8888) { - image = image.convertToFormat(QImage::Format_RGBA8888); + // Make sure the normal map source image is ARGB32 + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); } + gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; + gpu::Element formatMip = gpu::Element::COLOR_BGRA_32; gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -378,7 +403,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture, true, false); + theTexture = cacheTexture(theTexture->source(), theTexture, true, true); } return theTexture; diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index 2dbf2f60d7..34280cb263 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -113,7 +113,7 @@ int main(int argc, char** argv) { outFile.close(); } - auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); + auto ktxFile = ktx::KTX::create(std::shared_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); { const auto& memStorage = ktxMemory->getStorage(); const auto& fileStorage = ktxFile->getStorage(); From d7cb479dbe21547c56eb5e4007ce496cb097c299 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 23 Feb 2017 02:17:18 -0800 Subject: [PATCH 056/106] clean the clean ups --- .../src/model-networking/TextureCache.cpp | 4 ---- libraries/model/src/model/TextureMap.cpp | 10 ++++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f4bb3707e8..1f21e9e78d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -232,10 +232,6 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return model::TextureUsage::createMetallicTextureFromImage; break; } - case Type::OCCLUSION_TEXTURE: { - return model::TextureUsage::create2DTextureFromImage; - break; - } case Type::STRICT_TEXTURE: { return model::TextureUsage::createStrict2DTextureFromImage; break; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index c2bca9d1be..a3bd5fc7c1 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -132,6 +132,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } if (read && QFileInfo(cacheFilename.c_str()).exists()) { +#define DEBUG_KTX_LOADING 1 +#ifdef DEBUG_KTX_LOADING { FILE* file = fopen(cacheFilename.c_str(), "rb"); if (file != nullptr) { @@ -154,10 +156,10 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } } } - - // auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); - // returnedTexture->setKtxBacking(ktxFile); - +#else + auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); + returnedTexture->setKtxBacking(ktxFile); +#endif } } From f32817beacad647222aa4a89e943b18194b2ba40 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 10:19:20 -0800 Subject: [PATCH 057/106] Fix number of mips calculation --- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 83d8e20b1c..d18440bbc6 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -77,7 +77,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } // Number level of mips coming - header.numberOfMipmapLevels = texture.maxMip(); + header.numberOfMipmapLevels = texture.maxMip() + 1; ktx::Images images; for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { From a168bbf2dda4d3b628b43c3830cfa4cce8f78b7a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 10:26:59 -0800 Subject: [PATCH 058/106] Fix ktx file location, unique->shared pointer --- libraries/model/src/model/TextureMap.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index a3bd5fc7c1..dd0ae402d5 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -96,7 +96,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo // Prepare cache directory static const QString HIFI_KTX_FOLDER("hifi_ktx"); QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - ktxCacheFolder = docsLocation + "/" + HIFI_KTX_FOLDER; + ktxCacheFolder = docsLocation + "/" + HIFI_KTX_FOLDER + "/"; QFileInfo info(ktxCacheFolder); if (!info.exists()) { QDir(docsLocation).mkpath(HIFI_KTX_FOLDER); @@ -132,8 +132,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } if (read && QFileInfo(cacheFilename.c_str()).exists()) { -#define DEBUG_KTX_LOADING 1 -#ifdef DEBUG_KTX_LOADING +#define DEBUG_KTX_LOADING 0 +#if DEBUG_KTX_LOADING { FILE* file = fopen(cacheFilename.c_str(), "rb"); if (file != nullptr) { @@ -157,12 +157,12 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } } #else - auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); + ktx::StoragePointer storage = std::make_shared(cacheFilename.c_str()); + auto ktxFile = ktx::KTX::create(storage); returnedTexture->setKtxBacking(ktxFile); #endif } } - return returnedTexture; } From 48c7d17140f10605019ddadf960c12332d21a9f1 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 23 Feb 2017 12:43:52 -0800 Subject: [PATCH 059/106] make sure to assign the proper mipFOrmat to the KTXStorage, and fix the glTypeSize again --- libraries/gpu/src/gpu/Texture.cpp | 12 --- libraries/gpu/src/gpu/Texture.h | 4 + libraries/gpu/src/gpu/Texture_ktx.cpp | 125 ++++++++++++++++++-------- 3 files changed, 90 insertions(+), 51 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 4b2156ec79..44e35aa387 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -124,7 +124,6 @@ uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; using Storage = Texture::Storage; using PixelsPointer = Texture::PixelsPointer; using MemoryStorage = Texture::MemoryStorage; -using KtxStorage = Texture::KtxStorage; void Storage::assignTexture(Texture* texture) { _texture = texture; @@ -199,13 +198,6 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s } } -KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) : _ktxData(ktxData.release()) { -} - -PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { - return _ktxData->getMipFaceTexelsData(level, face); -} - Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { Texture* tex = new Texture(TextureUsageType::EXTERNAL); tex->_type = TEX_2D; @@ -980,7 +972,3 @@ void Texture::setStorage(std::unique_ptr& newStorage) { _storage.swap(newStorage); } -void Texture::setKtxBacking(ktx::KTXUniquePointer& ktxBacking) { - auto newBacking = std::unique_ptr(new KtxStorage(ktxBacking)); - setStorage(newBacking); -} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 637b098504..b0054472fe 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -25,6 +25,7 @@ namespace ktx { class KTX; using KTXUniquePointer = std::unique_ptr; + struct Header; } namespace gpu { @@ -509,8 +510,11 @@ public: ExternalUpdates getUpdates() const; + // Textures can be serialized directly to ktx data file, here is how static ktx::KTXUniquePointer serialize(const Texture& texture); static Texture* unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler = Sampler()); + static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); + static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); protected: const TextureUsageType _usageType; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d18440bbc6..3b56b1e8c5 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -15,6 +15,36 @@ #include using namespace gpu; +using PixelsPointer = Texture::PixelsPointer; +using KtxStorage = Texture::KtxStorage; + +KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) { + + // if the source ktx is valid let's config this KtxStorage correctly + if (ktxData && ktxData->getHeader()) { + + // now that we know the ktx, let's get the header info to configure this Texture::Storage: + Format mipFormat = Format::COLOR_BGRA_32; + Format texelFormat = Format::COLOR_SRGBA_32; + if (Texture::evalTextureFormat(*ktxData->getHeader(), mipFormat, texelFormat)) { + _format = mipFormat; + } + + + } + + _ktxData.reset(ktxData.release()); +} + +PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { + return _ktxData->getMipFaceTexelsData(level, face); +} + +void Texture::setKtxBacking(ktx::KTXUniquePointer& ktxBacking) { + auto newBacking = std::unique_ptr(new KtxStorage(ktxBacking)); + setStorage(newBacking); +} + ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { ktx::Header header; @@ -22,17 +52,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto texelFormat = texture.getTexelFormat(); auto mipFormat = texture.getStoredMipFormat(); - if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_RGBA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SRGBA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); - } else { + if (!Texture::evalKTXFormat(mipFormat, texelFormat, header)) { return nullptr; } @@ -130,33 +150,8 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; - if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { - mipFormat = Format::COLOR_BGRA_32; - texelFormat = Format::COLOR_RGBA_32; - } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { - mipFormat = Format::COLOR_SBGRA_32; - texelFormat = Format::COLOR_SRGBA_32; - } else { - return nullptr; - } - } else if (header.getGLFormat() == ktx::GLFormat::RGBA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { - mipFormat = Format::COLOR_RGBA_32; - texelFormat = Format::COLOR_RGBA_32; - } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { - mipFormat = Format::COLOR_SRGBA_32; - texelFormat = Format::COLOR_SRGBA_32; - } else { - return nullptr; - } - } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { - mipFormat = Format::COLOR_R_8; - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { - texelFormat = Format::COLOR_R_8; - } else { - return nullptr; - } + if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) { + return nullptr; } // Find Texture Type based on dimensions @@ -198,4 +193,56 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx } return tex; -} \ No newline at end of file +} + +bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { + if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_RGBA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SRGBA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); + } else { + return false; + } + + return true; +} + +bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat) { + if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + mipFormat = Format::COLOR_BGRA_32; + texelFormat = Format::COLOR_RGBA_32; + } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + mipFormat = Format::COLOR_SBGRA_32; + texelFormat = Format::COLOR_SRGBA_32; + } else { + return false; + } + } else if (header.getGLFormat() == ktx::GLFormat::RGBA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + mipFormat = Format::COLOR_RGBA_32; + texelFormat = Format::COLOR_RGBA_32; + } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + mipFormat = Format::COLOR_SRGBA_32; + texelFormat = Format::COLOR_SRGBA_32; + } else { + return false; + } + } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { + mipFormat = Format::COLOR_R_8; + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { + texelFormat = Format::COLOR_R_8; + } else { + return false; + } + } else { + return false; + } + return true; +} From cd8f3e1b0187a524411af0f01742da050f0a28f7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 13:04:04 -0800 Subject: [PATCH 060/106] Prevent name collisions in KTX from causing crashes --- libraries/gpu/src/gpu/Texture_ktx.cpp | 3 +++ libraries/model/src/model/TextureMap.cpp | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 3b56b1e8c5..c7da499e98 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -117,6 +117,9 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } auto ktxBuffer = ktx::KTX::create(header, images); + auto expectedMipCount = texture.evalNumMips(); + assert(expectedMipCount == ktxBuffer->_images.size()); + assert(expectedMipCount == header.numberOfMipmapLevels); assert(0 == memcmp(&header, ktxBuffer->getHeader(), sizeof(ktx::Header))); assert(ktxBuffer->_images.size() == images.size()); diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index dd0ae402d5..7b2e363eff 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -103,8 +103,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } }); - std::string cleanedName = name; - cleanedName = cleanedName.substr(cleanedName.find_last_of((char) '//') + 1); + std::string cleanedName = QUrl::toPercentEncoding(name.c_str()).toStdString(); std::string cacheFilename(ktxCacheFolder.toStdString()); cacheFilename += "/"; cacheFilename += cleanedName; From b8e1340a0d587972a88e15d4976bbc13173ecaa9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 16:08:29 -0800 Subject: [PATCH 061/106] Enable threaded texture buffering --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 28425433c4..cfc4dc39da 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -17,6 +17,7 @@ #include #define INCREMENTAL_TRANSFER 0 +#define THREADED_TEXTURE_BUFFERING 1 namespace gpu { namespace gl45 { From 594fe9b1bef52f1be053e8fd7b8ae6176455bd78 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 22:01:53 -0800 Subject: [PATCH 062/106] Use hashes of URLs for filenames, due to length constraints --- libraries/model/src/model/TextureMap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 7b2e363eff..ce602b7b9b 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -18,7 +18,7 @@ #include #include #include - +#include #include #include "ModelLogging.h" @@ -103,7 +103,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } }); - std::string cleanedName = QUrl::toPercentEncoding(name.c_str()).toStdString(); + + std::string cleanedName = QCryptographicHash::hash(QUrl::toPercentEncoding(name.c_str()), QCryptographicHash::Sha1).toHex().toStdString(); std::string cacheFilename(ktxCacheFolder.toStdString()); cacheFilename += "/"; cacheFilename += cleanedName; From 3d2e6713eea32c6553113bfe1422c4b6ed2bb89a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 24 Feb 2017 12:04:17 -0800 Subject: [PATCH 063/106] Ensure complete population of allocated mip levels --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 +- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index cfc4dc39da..a320008dc5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -168,7 +168,7 @@ public: //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } bool canPromote() const { return _allocatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return !_pendingTransfers.empty(); } + bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); uint32 size() const override { return _size; } virtual void populateTransferQueue() = 0; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 62f1a3c248..f21530ff9e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -338,6 +338,7 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { _transferQueue = WorkQueue(); _promoteQueue = WorkQueue(); _demoteQueue = WorkQueue(); + // Populate the existing textures into the queue for (const auto& texture : strongTextures) { addToWorkQueue(texture); @@ -387,7 +388,7 @@ void GL45VariableAllocationTexture::processWorkQueues() { } if (workQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; + _memoryPressureStateStale = true; } } @@ -406,6 +407,14 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { } void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& currentTexture) { + if (_populatedMip <= _allocatedMip) { + return; + } + + if (_pendingTransfers.empty()) { + populateTransferQueue(); + } + if (!_pendingTransfers.empty()) { // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture _currentTransferTexture = currentTexture; From cd8bba47cf3821c8986763c22ed08a077f6cd44a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 24 Feb 2017 12:39:38 -0800 Subject: [PATCH 064/106] Add pending texture transfer size to tracked stats --- interface/resources/qml/Stats.qml | 3 +++ interface/src/ui/Stats.cpp | 1 + interface/src/ui/Stats.h | 2 ++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 ++ .../gpu/gl45/GL45BackendVariableTexture.cpp | 27 ++++++++++++------- libraries/gpu/src/gpu/Context.cpp | 17 ++++++++++++ libraries/gpu/src/gpu/Context.h | 4 +++ libraries/gpu/src/gpu/Texture.cpp | 4 +++ libraries/gpu/src/gpu/Texture.h | 1 + 9 files changed, 51 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 5e0eac37bd..25c2e3903e 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -252,6 +252,9 @@ Item { StatText { text: " Decimated: " + root.decimatedTextureCount; } + StatText { + text: " Pending Transfer: " + root.texturePendingTransfers + " MB"; + } StatText { text: " Resource Memory: " + root.gpuTextureMemory + " MB"; } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 49602833e6..9ba5170856 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -321,6 +321,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(glContextSwapchainMemory, (int)BYTES_TO_MB(gl::Context::getSwapchainMemoryUsage())); STAT_UPDATE(qmlTextureMemory, (int)BYTES_TO_MB(OffscreenQmlSurface::getUsedTextureMemory())); + STAT_UPDATE(texturePendingTransfers, (int)BYTES_TO_MB(gpu::Texture::getTextureTransferPendingSize())); STAT_UPDATE(gpuTextureMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUMemoryUsage())); STAT_UPDATE(gpuTextureVirtualMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUVirtualMemoryUsage())); STAT_UPDATE(gpuTextureFramebufferMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUFramebufferMemoryUsage())); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 569e737117..8e01f39e07 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -105,6 +105,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, gpuTexturesSparse, 0) STATS_PROPERTY(int, glContextSwapchainMemory, 0) STATS_PROPERTY(int, qmlTextureMemory, 0) + STATS_PROPERTY(int, texturePendingTransfers, 0) STATS_PROPERTY(int, gpuTextureMemory, 0) STATS_PROPERTY(int, gpuTextureVirtualMemory, 0) STATS_PROPERTY(int, gpuTextureFramebufferMemory, 0) @@ -210,6 +211,7 @@ signals: void timingStatsChanged(); void glContextSwapchainMemoryChanged(); void qmlTextureMemoryChanged(); + void texturePendingTransfersChanged(); void gpuBuffersChanged(); void gpuBufferMemoryChanged(); void gpuTexturesChanged(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index a320008dc5..cfdcc356a6 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -128,6 +128,7 @@ public: TransferJob(const TransferJob& other) = delete; TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); + ~TransferJob(); bool tryTransfer(); #if THREADED_TEXTURE_BUFFERING @@ -136,6 +137,7 @@ public: #endif private: + size_t _transferSize { 0 }; #if THREADED_TEXTURE_BUFFERING void startBuffering(); #endif diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index f21530ff9e..92251bd381 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -91,27 +91,29 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s type = texelFormat.type; if (0 == lines) { + _transferSize = mipData->getSize(); _bufferingLambda = [=] { - auto size = mipData->getSize(); - _buffer.resize(size); - memcpy(&_buffer[0], mipData->readData(), size); + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData(), _transferSize); _bufferingCompleted = true; }; } else { transferDimensions.y = lines; + auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + auto mipSize = mipData->getSize(); + auto bytesPerLine = (uint32_t)mipSize / dimensions.y; + _transferSize = bytesPerLine * lines; + auto sourceOffset = bytesPerLine * lineOffset; _bufferingLambda = [=] { - auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - auto mipSize = mipData->getSize(); - auto bytesPerLine = (uint32_t)mipSize / dimensions.y; - auto transferSize = bytesPerLine * lines; - auto sourceOffset = bytesPerLine * lineOffset; - _buffer.resize(transferSize); - memcpy(&_buffer[0], mipData->readData() + sourceOffset, transferSize); + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData() + sourceOffset, _transferSize); _bufferingCompleted = true; }; } + Backend::updateTextureTransferPendingSize(0, _transferSize); + _transferLambda = [=] { _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); std::vector emptyVector; @@ -123,6 +125,11 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::funct : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { } +TransferJob::~TransferJob() { + Backend::updateTextureTransferPendingSize(_transferSize, 0); +} + + bool TransferJob::tryTransfer() { // Disable threaded texture transfer for now #if THREADED_TEXTURE_BUFFERING diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 78b472bdae..cc570f696f 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -241,6 +241,7 @@ std::atomic Context::_bufferGPUMemoryUsage { 0 }; std::atomic Context::_textureGPUCount{ 0 }; std::atomic Context::_textureGPUSparseCount { 0 }; +std::atomic Context::_textureTransferPendingSize { 0 }; std::atomic Context::_textureGPUMemoryUsage { 0 }; std::atomic Context::_textureGPUVirtualMemoryUsage { 0 }; std::atomic Context::_textureGPUFramebufferMemoryUsage { 0 }; @@ -317,6 +318,17 @@ void Context::decrementTextureGPUSparseCount() { --_textureGPUSparseCount; } +void Context::updateTextureTransferPendingSize(Size prevObjectSize, Size newObjectSize) { + if (prevObjectSize == newObjectSize) { + return; + } + if (newObjectSize > prevObjectSize) { + _textureTransferPendingSize.fetch_add(newObjectSize - prevObjectSize); + } else { + _textureTransferPendingSize.fetch_sub(prevObjectSize - newObjectSize); + } +} + void Context::updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { if (prevObjectSize == newObjectSize) { return; @@ -390,6 +402,10 @@ uint32_t Context::getTextureGPUSparseCount() { return _textureGPUSparseCount.load(); } +Context::Size Context::getTextureTransferPendingSize() { + return _textureTransferPendingSize.load(); +} + Context::Size Context::getTextureGPUMemoryUsage() { return _textureGPUMemoryUsage.load(); } @@ -419,6 +435,7 @@ void Backend::incrementTextureGPUCount() { Context::incrementTextureGPUCount(); void Backend::decrementTextureGPUCount() { Context::decrementTextureGPUCount(); } void Backend::incrementTextureGPUSparseCount() { Context::incrementTextureGPUSparseCount(); } void Backend::decrementTextureGPUSparseCount() { Context::decrementTextureGPUSparseCount(); } +void Backend::updateTextureTransferPendingSize(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureTransferPendingSize(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUMemoryUsage(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUVirtualMemoryUsage(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUFramebufferMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUFramebufferMemoryUsage(prevObjectSize, newObjectSize); } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 01c841992d..102c754cd7 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -101,6 +101,7 @@ public: static void decrementTextureGPUCount(); static void incrementTextureGPUSparseCount(); static void decrementTextureGPUSparseCount(); + static void updateTextureTransferPendingSize(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUSparseMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); @@ -220,6 +221,7 @@ public: static uint32_t getTextureGPUSparseCount(); static Size getFreeGPUMemory(); static Size getUsedGPUMemory(); + static Size getTextureTransferPendingSize(); static Size getTextureGPUMemoryUsage(); static Size getTextureGPUVirtualMemoryUsage(); static Size getTextureGPUFramebufferMemoryUsage(); @@ -263,6 +265,7 @@ protected: static void decrementTextureGPUCount(); static void incrementTextureGPUSparseCount(); static void decrementTextureGPUSparseCount(); + static void updateTextureTransferPendingSize(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUSparseMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newObjectSize); @@ -279,6 +282,7 @@ protected: static std::atomic _textureGPUCount; static std::atomic _textureGPUSparseCount; + static std::atomic _textureTransferPendingSize; static std::atomic _textureGPUMemoryUsage; static std::atomic _textureGPUSparseMemoryUsage; static std::atomic _textureGPUVirtualMemoryUsage; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 44e35aa387..6b9087333c 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -89,6 +89,10 @@ uint32_t Texture::getTextureGPUSparseCount() { return Context::getTextureGPUSparseCount(); } +Texture::Size Texture::getTextureTransferPendingSize() { + return Context::getTextureTransferPendingSize(); +} + Texture::Size Texture::getTextureGPUMemoryUsage() { return Context::getTextureGPUMemoryUsage(); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index b0054472fe..d840687af2 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -166,6 +166,7 @@ public: static Size getTextureCPUMemoryUsage(); static uint32_t getTextureGPUCount(); static uint32_t getTextureGPUSparseCount(); + static Size getTextureTransferPendingSize(); static Size getTextureGPUMemoryUsage(); static Size getTextureGPUVirtualMemoryUsage(); static Size getTextureGPUFramebufferMemoryUsage(); From 8969d7df1d9cd61718e45043b2c172b7d4684b43 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 23 Feb 2017 16:26:52 -0500 Subject: [PATCH 065/106] FileCache --- libraries/networking/src/FileCache.cpp | 246 +++++++++++++++++++++++++ libraries/networking/src/FileCache.h | 149 +++++++++++++++ 2 files changed, 395 insertions(+) create mode 100644 libraries/networking/src/FileCache.cpp create mode 100644 libraries/networking/src/FileCache.h diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp new file mode 100644 index 0000000000..a717546de4 --- /dev/null +++ b/libraries/networking/src/FileCache.cpp @@ -0,0 +1,246 @@ +// +// FileCache.cpp +// libraries/model-networking/src +// +// Created by Zach Pomerantz on 2/21/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 "FileCache.h" + +#include +#include +#include + +#include + +#include + +Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache") + +static const std::string MANIFEST_NAME = "manifest"; + +void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { + _unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE); + reserve(0); + emit dirty(); +} + +void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) { + _offlineFilesMaxSize = std::min(offlineFilesMaxSize, MAX_UNUSED_MAX_SIZE); +} + +FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) : + QObject(parent), + _dir(createDir(dirname)), + _ext(ext) {} + +FileCache::~FileCache() { + clear(); +} + +void fileDeleter(File* file) { + file->deleter(); +} + +FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length, void* extra) { + std::string filepath = getFilepath(key); + + Lock lock(_filesMutex); + + // if file already exists, return it + FilePointer file = getFile(key); + if (file) { + qCWarning(file_cache) << "Attempted to overwrite" << filepath.c_str(); + return file; + } + + // write the new file + FILE* saveFile = fopen(filepath.c_str(), "wb"); + if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { + file.reset(createFile(key, filepath, length, extra), &fileDeleter); + fclose(saveFile); + file->_cache = this; + _files[key] = file; + _numTotalFiles += 1; + _totalFilesSize += length; + + emit dirty(); + } else { + qCWarning(file_cache, "Failed to write %s (%s)", filepath.c_str(), strerror(errno)); + errno = 0; + } + + return file; +} + +FilePointer FileCache::getFile(const Key& key) { + FilePointer file; + + Lock lock(_filesMutex); + + // check if file already exists + const auto it = _files.find(key); + if (it != _files.cend()) { + file = it->second.lock(); + if (file) { + // if it exists, it is active - remove it from the cache + removeUnusedFile(file); + emit dirty(); + } else { + // if not, remove the weak_ptr + _files.erase(it); + } + } + + return file; +} + +File* FileCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { + return new File(key, filepath, length); +} + +std::string FileCache::createDir(const std::string& dirname) { + QString dirpath = ServerPathUtils::getDataFilePath(dirname.c_str()); + QDir dir(dirpath); + + if (dir.exists()) { + std::unordered_set persistedEntries; + if (dir.exists(MANIFEST_NAME.c_str())) { + std::ifstream manifest; + manifest.open(dir.absoluteFilePath(MANIFEST_NAME.c_str()).toStdString()); + while (manifest.good()) { + std::string entry; + manifest >> entry; + persistedEntries.insert(entry); + + // ZZMP: rm + for (const auto& entry : persistedEntries) + qDebug() << "ZZMP" << entry.c_str(); + qDebug() << "ZZMP" << "---"; + } + } + + foreach(QString filename, dir.entryList()) { + if (persistedEntries.find(filename.toStdString()) == persistedEntries.cend()) { + dir.remove(filename); + } + } + } else { + dir.mkpath(dirpath); + } + + return dirpath.toStdString(); +} + +std::string FileCache::getFilepath(const Key& key) { + return _dir + key + '.' + _ext; +} + +void FileCache::addUnusedFile(const FilePointer file) { + { + Lock lock(_filesMutex); + _files[file->getKey()] = file; + } + + reserve(file->getLength()); + file->_LRUKey = ++_lastLRUKey; + + { + Lock lock(_unusedFilesMutex); + _unusedFiles.insert({ file->_LRUKey, file }); + _numUnusedFiles += 1; + _unusedFilesSize += file->getLength(); + } + + emit dirty(); +} + +void FileCache::removeUnusedFile(const FilePointer file) { + Lock lock(_unusedFilesMutex); + const auto it = _unusedFiles.find(file->_LRUKey); + if (it != _unusedFiles.cend()) { + _unusedFiles.erase(it); + _numUnusedFiles -= 1; + _unusedFilesSize -= file->getLength(); + } +} + +void FileCache::reserve(size_t length) { + Lock unusedLock(_unusedFilesMutex); + while (!_unusedFiles.empty() && + _unusedFilesSize + length > _unusedFilesMaxSize) { + auto it = _unusedFiles.begin(); + auto file = it->second; + auto length = file->getLength(); + + unusedLock.unlock(); + { + file->_cache = nullptr; + Lock lock(_filesMutex); + _files.erase(file->getKey()); + } + unusedLock.lock(); + + _unusedFiles.erase(it); + _numTotalFiles -= 1; + _numUnusedFiles -= 1; + _totalFilesSize -= length; + _unusedFilesSize -= length; + + unusedLock.unlock(); + evictedFile(file); + unusedLock.lock(); + } +} + +void FileCache::clear() { + std::string manifestPath= _dir + MANIFEST_NAME; + FILE* manifest = fopen(manifestPath.c_str(), "wb"); + + Lock lock(_unusedFilesMutex); + for (const auto& val : _unusedFiles) { + const FilePointer& file = val.second; + file->_cache = nullptr; + + if (_unusedFilesSize > _offlineFilesMaxSize) { + _unusedFilesSize -= file->getLength(); + } else { + std::string key = file->getKey() + '.' + _ext + '\n'; + if (manifest != nullptr && !fwrite(key.c_str(), key.length(), 1, manifest)) { + manifest = nullptr; // to prevent future writes + } + file->_shouldPersist = true; + } + } + + if (manifest == nullptr || fclose(manifest) != 0) { + for (const auto& val : _unusedFiles) { + val.second->_shouldPersist = false; + } + + qCWarning(file_cache, "Failed to write %s (%s)", manifestPath.c_str(), strerror(errno)); + errno = 0; + } + + _unusedFiles.clear(); +} + +void File::deleter() { + if (_cache) { + FilePointer self(this, &fileDeleter); + _cache->addUnusedFile(self); + } else { + deleteLater(); + } +} + +File::~File() { + QFile file(getFilepath().c_str()); + if (file.exists() && !_shouldPersist) { + file.remove(); + } +} diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h new file mode 100644 index 0000000000..f068f6e7d5 --- /dev/null +++ b/libraries/networking/src/FileCache.h @@ -0,0 +1,149 @@ +// +// FileCache.h +// libraries/networking/src +// +// Created by Zach Pomerantz on 2/21/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 +// + +#ifndef hifi_FileCache_h +#define hifi_FileCache_h + +#include +#include +#include +#include +#include +#include + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(file_cache) + +class File; +using FilePointer = std::shared_ptr; + +class FileCache : public QObject { + Q_OBJECT + Q_PROPERTY(size_t numTotal READ getNumTotalFiles NOTIFY dirty) + Q_PROPERTY(size_t numCached READ getNumCachedFiles NOTIFY dirty) + Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty) + Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty) + + static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; + static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; + static const size_t DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB + static const size_t MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB + static const size_t DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB + +public: + size_t getNumTotalFiles() const { return _numTotalFiles; } + size_t getNumCachedFiles() const { return _numUnusedFiles; } + size_t getSizeTotalFiles() const { return _totalFilesSize; } + size_t getSizeCachedFiles() const { return _unusedFilesSize; } + + void setUnusedFileCacheSize(size_t unusedFilesMaxSize); + size_t getUnusedFileCacheSize() const { return _unusedFilesSize; } + + void setOfflineFileCacheSize(size_t offlineFilesMaxSize); + + // initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg") + FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); + // precondition: there should be no references to Files when FileCache is destroyed + virtual ~FileCache(); + + // derived classes are left to implement hashing of the files on their own + using Key = std::string; + + // derived classes should implement a setter/getter, for example, for a FileCache backing a network cache: + // + // DerivedFilePointer writeFile(const DerivedData& data) { + // return writeFile(data->key, data->data, data->length, &data); + // } + // + // DerivedFilePointer getFile(const QUrl& url) { + // // assuming storage/removal of url->hash in createFile/evictedFile overrides + // auto key = lookup_hash_for(url); + // return getFile(key); + // } + +signals: + void dirty(); + +protected: + FilePointer writeFile(const Key& key, const char* data, size_t length, void* extra); + FilePointer getFile(const Key& key); + + virtual File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra); + virtual void evictedFile(const FilePointer& file) = 0; + +private: + using Mutex = std::recursive_mutex; + using Lock = std::unique_lock; + + friend class File; + + std::string createDir(const std::string& dirname); + std::string getFilepath(const Key& key); + + void addUnusedFile(const FilePointer file); + void removeUnusedFile(const FilePointer file); + void reserve(size_t length); + void clear(); + + std::atomic _numTotalFiles { 0 }; + std::atomic _numUnusedFiles { 0 }; + std::atomic _totalFilesSize { 0 }; + std::atomic _unusedFilesSize { 0 }; + + std::string _dir; + std::string _ext; + + std::unordered_map> _files; + Mutex _filesMutex; + + std::map _unusedFiles; + Mutex _unusedFilesMutex; + size_t _unusedFilesMaxSize { DEFAULT_UNUSED_MAX_SIZE }; + int _lastLRUKey { 0 }; + + size_t _offlineFilesMaxSize { DEFAULT_OFFLINE_MAX_SIZE }; +}; + +class File : public QObject { + Q_OBJECT + +public: + using Key = FileCache::Key; + + std::string getFilepath() const { return _filepath; } + Key getKey() const { return _key; } + size_t getLength() const { return _length; } + + // overrides should call File::deleter to maintain caching behavior + virtual void deleter(); + +protected: + // when constructed, the file has already been created/written + File(const Key& key, const std::string& filepath, size_t length) : + _filepath(filepath), _key(key), _length(length) {} + // the destructor should handle unlinking of the actual filepath + virtual ~File(); + + const std::string _filepath; + +private: + friend class FileCache; + + const Key _key; + const size_t _length; + + FileCache* _cache; + int _LRUKey { 0 }; + + bool _shouldPersist { false }; +}; + +#endif // hifi_FileCache_h From cfe14518a1ba08884a8c1399b3ac78051cab9815 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 23 Feb 2017 16:27:08 -0500 Subject: [PATCH 066/106] KTXCache --- .../src/model-networking/KTXCache.cpp | 49 +++++++++++++ .../src/model-networking/KTXCache.h | 69 +++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 libraries/model-networking/src/model-networking/KTXCache.cpp create mode 100644 libraries/model-networking/src/model-networking/KTXCache.h diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp new file mode 100644 index 0000000000..1ab32698b6 --- /dev/null +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -0,0 +1,49 @@ +// +// KTXCache.cpp +// libraries/model-networking/src +// +// Created by Zach Pomerantz on 2/22/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 "KTXCache.h" + +#include + +KTXFilePointer KTXCache::writeFile(Data data) { + return std::static_pointer_cast(FileCache::writeFile(data.key, data.data, data.length, (void*)&data)); +} + +KTXFilePointer KTXCache::getFile(const QUrl& url) { + Key key; + { + Lock lock(_urlMutex); + const auto it = _urlMap.find(url); + if (it != _urlMap.cend()) { + key = it->second; + } + } + + KTXFilePointer file; + if (!key.empty()) { + file = std::static_pointer_cast(FileCache::getFile(key)); + } + + return file; +} + +File* KTXCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { + const QUrl& url = reinterpret_cast(extra)->url; + Lock lock(_urlMutex); + _urlMap[url] = key; + return new KTXFile(key, filepath, length, url); +} + +void KTXCache::evictedFile(const FilePointer& file) { + const QUrl url = std::static_pointer_cast(file)->getUrl(); + Lock lock(_urlMutex); + _urlMap.erase(url); +} diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h new file mode 100644 index 0000000000..5b9cb04061 --- /dev/null +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -0,0 +1,69 @@ +// +// KTXCache.h +// libraries/model-networking/src +// +// Created by Zach Pomerantz 2/22/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 +// + +#ifndef hifi_KTXCache_h +#define hifi_KTXCache_h + +#include + +#include + +class KTXFile; +using KTXFilePointer = std::shared_ptr; + +class KTXCache : public FileCache { + Q_OBJECT + +public: + KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) {} + + struct Data { + Data(const QUrl& url, const Key& key, const char* data, size_t length) : + url(url), key(key), data(data), length(length) {} + const QUrl url; + const Key key; + const char* data; + size_t length; + }; + + KTXFilePointer writeFile(Data data); + KTXFilePointer getFile(const QUrl& url); + +protected: + File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; + void evictedFile(const FilePointer& file) override final; + +private: + using Mutex = std::mutex; + using Lock = std::lock_guard; + struct QUrlHasher { std::size_t operator()(QUrl const& url) const { return qHash(url); } }; + + std::unordered_map _urlMap; + Mutex _urlMutex; +}; + +class KTXFile : public File { + Q_OBJECT + +public: + QUrl getUrl() const { return _url; } + +protected: + KTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) : + File(key, filepath, length), _url(url) {} + +private: + friend class KTXCache; + + const QUrl _url; +}; + +#endif // hifi_KTXCache_h From c044daf7b22953211197e7cef6d30102b47f0e0f Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 23 Feb 2017 16:27:28 -0500 Subject: [PATCH 067/106] TextureCache --- libraries/model-networking/CMakeLists.txt | 2 +- .../src/model-networking/TextureCache.cpp | 383 +++++++++++------- .../src/model-networking/TextureCache.h | 16 +- libraries/model/src/model/TextureMap.cpp | 88 ---- 4 files changed, 247 insertions(+), 242 deletions(-) diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt index ed8cd7b5f9..00aa17ff57 100644 --- a/libraries/model-networking/CMakeLists.txt +++ b/libraries/model-networking/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME model-networking) setup_hifi_library() -link_hifi_libraries(shared networking model fbx) +link_hifi_libraries(shared networking model fbx ktx) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1f21e9e78d..4224cf076c 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -18,27 +18,39 @@ #include #include #include + +#if DEBUG_DUMP_TEXTURE_LOADS #include #include +#endif #include #include #include +#include + #include #include #include #include +#include #include "ModelNetworkingLogging.h" #include #include Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image") +Q_LOGGING_CATEGORY(trace_resource_parse_ktx, "trace.resource.parse.ktx") +Q_LOGGING_CATEGORY(trace_resource_cache_ktx, "trace.resource.cache.ktx") -TextureCache::TextureCache() { +const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; +const std::string TextureCache::KTX_EXT { "ktx" }; + +TextureCache::TextureCache() : + _ktxCache(KTX_DIRNAME, KTX_EXT) { setUnusedResourceCacheSize(0); setObjectName("TextureCache"); @@ -61,7 +73,7 @@ TextureCache::~TextureCache() { // this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at // http://mrl.nyu.edu/~perlin/noise/ -const int permutation[256] = +const int permutation[256] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, @@ -241,7 +253,7 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return NetworkTexture::TextureLoaderFunc(); break; } - + case Type::DEFAULT_TEXTURE: default: { return model::TextureUsage::create2DTextureFromImage; @@ -259,12 +271,32 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) { + KTXFilePointer file = _ktxCache.getFile(url); const TextureExtra* textureExtra = static_cast(extra); auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE; - auto content = textureExtra ? textureExtra->content : QByteArray(); - auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; - return QSharedPointer(new NetworkTexture(url, type, content, maxNumPixels), - &Resource::deleter); + + NetworkTexture* texture; + if (file) { + texture = new NetworkTexture(url, type, file); + } else { + auto content = textureExtra ? textureExtra->content : QByteArray(); + auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; + texture = new NetworkTexture(url, type, content, maxNumPixels); + } + + return QSharedPointer(texture, &Resource::deleter); +} + +NetworkTexture::NetworkTexture(const QUrl& url, Type type, const KTXFilePointer& file) : + Resource(url), + _type(type), + _file(file) { + _textureSource = std::make_shared(); + + if (file) { + _startedLoading = true; + QMetaObject::invokeMethod(this, "loadFile", Qt::QueuedConnection); + } } NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) : @@ -278,7 +310,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& con _loaded = true; } - std::string theName = url.toString().toStdString(); // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; @@ -299,149 +330,6 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { return getTextureLoaderForType(_type); } - -class ImageReader : public QRunnable { -public: - - ImageReader(const QWeakPointer& resource, const QByteArray& data, - const QUrl& url = QUrl(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); - - virtual void run() override; - -private: - static void listSupportedImageFormats(); - - QWeakPointer _resource; - QUrl _url; - QByteArray _content; - int _maxNumPixels; -}; - -void NetworkTexture::downloadFinished(const QByteArray& data) { - // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new ImageReader(_self, data, _url)); -} - -void NetworkTexture::loadContent(const QByteArray& content) { - QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url, _maxNumPixels)); -} - -ImageReader::ImageReader(const QWeakPointer& resource, const QByteArray& data, - const QUrl& url, int maxNumPixels) : - _resource(resource), - _url(url), - _content(data), - _maxNumPixels(maxNumPixels) -{ -#if DEBUG_DUMP_TEXTURE_LOADS - static auto start = usecTimestampNow() / USECS_PER_MSEC; - auto now = usecTimestampNow() / USECS_PER_MSEC - start; - QString urlStr = _url.toString(); - auto dot = urlStr.lastIndexOf("."); - QString outFileName = QString(QCryptographicHash::hash(urlStr.toLocal8Bit(), QCryptographicHash::Md5).toHex()) + urlStr.right(urlStr.length() - dot); - QFile loadRecord("h:/textures/loads.txt"); - loadRecord.open(QFile::Text | QFile::Append | QFile::ReadWrite); - loadRecord.write(QString("%1 %2\n").arg(now).arg(outFileName).toLocal8Bit()); - outFileName = "h:/textures/" + outFileName; - QFileInfo outInfo(outFileName); - if (!outInfo.exists()) { - QFile outFile(outFileName); - outFile.open(QFile::WriteOnly | QFile::Truncate); - outFile.write(data); - outFile.close(); - } -#endif - DependencyManager::get()->incrementStat("PendingProcessing"); -} - -void ImageReader::listSupportedImageFormats() { - static std::once_flag once; - std::call_once(once, []{ - auto supportedFormats = QImageReader::supportedImageFormats(); - qCDebug(modelnetworking) << "List of supported Image formats:" << supportedFormats.join(", "); - }); -} - -void ImageReader::run() { - DependencyManager::get()->decrementStat("PendingProcessing"); - - CounterStat counter("Processing"); - - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - auto originalPriority = QThread::currentThread()->priority(); - if (originalPriority == QThread::InheritPriority) { - originalPriority = QThread::NormalPriority; - } - QThread::currentThread()->setPriority(QThread::LowPriority); - Finally restorePriority([originalPriority]{ - QThread::currentThread()->setPriority(originalPriority); - }); - - if (!_resource.data()) { - qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - return; - } - listSupportedImageFormats(); - - // Help the QImage loader by extracting the image file format from the url filename ext. - // Some tga are not created properly without it. - auto filename = _url.fileName().toStdString(); - auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); - QImage image = QImage::fromData(_content, filenameExtension.c_str()); - - // Note that QImage.format is the pixel format which is different from the "format" of the image file... - auto imageFormat = image.format(); - int imageWidth = image.width(); - int imageHeight = image.height(); - - if (imageWidth == 0 || imageHeight == 0 || imageFormat == QImage::Format_Invalid) { - if (filenameExtension.empty()) { - qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url; - } else { - qCDebug(modelnetworking) << "QImage failed to create from content" << _url; - } - return; - } - - if (imageWidth * imageHeight > _maxNumPixels) { - float scaleFactor = sqrtf(_maxNumPixels / (float)(imageWidth * imageHeight)); - int originalWidth = imageWidth; - int originalHeight = imageHeight; - imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f); - imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f); - QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - image.swap(newImage); - qCDebug(modelnetworking) << "Downscale image" << _url - << "from" << originalWidth << "x" << originalHeight - << "to" << imageWidth << "x" << imageHeight; - } - - gpu::TexturePointer texture = nullptr; - { - // Double-check the resource still exists between long operations. - auto resource = _resource.toStrongRef(); - if (!resource) { - qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - return; - } - - auto url = _url.toString().toStdString(); - - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffffff00, 0); - texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); - } - - // Ensure the resource has not been deleted - auto resource = _resource.toStrongRef(); - if (!resource) { - qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - } else { - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, imageWidth), Q_ARG(int, imageHeight)); - } -} - void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight) { _originalWidth = originalWidth; @@ -464,3 +352,196 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, emit networkTextureCreated(qWeakPointerCast (_self)); } + +class Reader : public QRunnable { +public: + Reader(const QWeakPointer& resource, const QUrl& url) : _resource(resource), _url(url) { + DependencyManager::get()->incrementStat("PendingProcessing"); + } + void run() override final { + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); + + // Run this with low priority, then restore thread priority + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; + } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority]{ + QThread::currentThread()->setPriority(originalPriority); + }); + + if (!_resource.lock()) { // to ensure the resource is still needed + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + read(); + } + virtual void read() = 0; + +protected: + QWeakPointer _resource; + QUrl _url; +}; + +class FileReader : public Reader { +public: + FileReader(const QWeakPointer& resource, const QUrl& url) : Reader(resource, url) {} + void read() override final; +}; + +class ImageReader : public Reader { +public: + ImageReader(const QWeakPointer& resource, const QUrl& url, + const QByteArray& data, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + void read() override final; + +private: + static void listSupportedImageFormats(); + + QByteArray _content; + int _maxNumPixels; +}; + +void NetworkTexture::downloadFinished(const QByteArray& data) { + // send the reader off to the thread pool + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, data)); +} + +void NetworkTexture::loadFile() { + QThreadPool::globalInstance()->start(new FileReader(_self, _url)); +} + +void NetworkTexture::loadContent(const QByteArray& content) { + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); +} + +ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, + const QByteArray& data, int maxNumPixels) : + Reader(resource, url), _content(data), _maxNumPixels(maxNumPixels) { + listSupportedImageFormats(); +#if DEBUG_DUMP_TEXTURE_LOADS + static auto start = usecTimestampNow() / USECS_PER_MSEC; + auto now = usecTimestampNow() / USECS_PER_MSEC - start; + QString urlStr = _url.toString(); + auto dot = urlStr.lastIndexOf("."); + QString outFileName = QString(QCryptographicHash::hash(urlStr.toLocal8Bit(), QCryptographicHash::Md5).toHex()) + urlStr.right(urlStr.length() - dot); + QFile loadRecord("h:/textures/loads.txt"); + loadRecord.open(QFile::Text | QFile::Append | QFile::ReadWrite); + loadRecord.write(QString("%1 %2\n").arg(now).arg(outFileName).toLocal8Bit()); + outFileName = "h:/textures/" + outFileName; + QFileInfo outInfo(outFileName); + if (!outInfo.exists()) { + QFile outFile(outFileName); + outFile.open(QFile::WriteOnly | QFile::Truncate); + outFile.write(data); + outFile.close(); + } +#endif +} + +void ImageReader::listSupportedImageFormats() { + static std::once_flag once; + std::call_once(once, []{ + auto supportedFormats = QImageReader::supportedImageFormats(); + qCDebug(modelnetworking) << "List of supported Image formats:" << supportedFormats.join(", "); + }); +} + +void FileReader::read() { + PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0); + + // TODO: + // auto ktx = ktx::KTX::create(); + // auto texture = gpu::Texture::unserialize(getUsage(), getUsageType(), ktx, getSampler()); + // FIXME: do I need to set the file as a backing file here? +} + +void ImageReader::read() { + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + // Help the QImage loader by extracting the image file format from the url filename ext. + // Some tga are not created properly without it. + auto filename = _url.fileName().toStdString(); + auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); + QImage image = QImage::fromData(_content, filenameExtension.c_str()); + int imageWidth = image.width(); + int imageHeight = image.height(); + + // Validate that the image loaded + if (imageWidth == 0 || imageHeight == 0 || image.format() == QImage::Format_Invalid) { + QString reason(filenameExtension.empty() ? "" : "(no file extension)"); + qCWarning(modelnetworking) << "Failed to load" << _url << reason; + return; + } + + // Validate the image is less than _maxNumPixels, and downscale if necessary + if (imageWidth * imageHeight > _maxNumPixels) { + float scaleFactor = sqrtf(_maxNumPixels / (float)(imageWidth * imageHeight)); + int originalWidth = imageWidth; + int originalHeight = imageHeight; + imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f); + imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f); + QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + image.swap(newImage); + qCDebug(modelnetworking).nospace() << "Downscaled " << _url << " (" << + QSize(originalWidth, originalHeight) << " to " << + QSize(imageWidth, imageHeight) << ")"; + } + + // Load the image into a gpu::Texture + gpu::TexturePointer texture = nullptr; + { + auto resource = _resource.lock(); // to ensure the resource is still needed + if (!resource) { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + auto url = _url.toString().toStdString(); + + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0); + texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); + texture->setSource(url); + } + + // Hash the source image to use as a filename for on-disk caching + std::string hash; + { + QCryptographicHash hasher(QCryptographicHash::Md5); + hasher.addData((const char*)image.bits(), image.byteCount() * sizeof(char)); + hash = hasher.result().toHex().toStdString(); + } + + { + auto resource = _resource.lock(); // to ensure the resource is still needed + if (!resource) { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + PROFILE_RANGE_EX(resource_cache_ktx, __FUNCTION__, 0xffffff00, 0); + auto ktx = gpu::Texture::serialize(*texture); + const char* data = reinterpret_cast(ktx->_storage->data()); + size_t length = ktx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = DependencyManager::get()->_ktxCache; + if (!ktx || !(file = ktxCache.writeFile({ _url, hash, data, length }))) { + qCWarning(modelnetworking) << _url << "file cache failed"; + } else { + resource.dynamicCast()->_file = file; + // FIXME: do I need to set the file as a backing file here? + } + } + + auto resource = _resource.toStrongRef(); // to ensure the resource is still needed + if (resource) { + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, imageWidth), Q_ARG(int, imageHeight)); + } else { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + } +} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 749b5a2ebb..f05ab6b220 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -23,6 +23,8 @@ #include #include +#include "KTXCache.h" + const int ABSOLUTE_MAX_TEXTURE_NUM_PIXELS = 8192 * 8192; namespace gpu { @@ -63,6 +65,7 @@ public: typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName); using TextureLoaderFunc = std::function; + NetworkTexture(const QUrl& url, Type type, const KTXFilePointer& file); NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels); NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); @@ -80,17 +83,20 @@ signals: void networkTextureCreated(const QWeakPointer& self); protected: - virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; Q_INVOKABLE void loadContent(const QByteArray& content); + Q_INVOKABLE void loadFile(); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: + friend class ImageReader; + Type _type; TextureLoaderFunc _textureLoader { [](const QImage&, const std::string&){ return nullptr; } }; + KTXFilePointer _file; int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; @@ -143,9 +149,15 @@ protected: const void* extra) override; private: + friend class ImageReader; + friend class DilatableNetworkTexture; + TextureCache(); virtual ~TextureCache(); - friend class DilatableNetworkTexture; + + static const std::string KTX_DIRNAME; + static const std::string KTX_EXT; + KTXCache _ktxCache; gpu::TexturePointer _permutationNormalTexture; gpu::TexturePointer _whiteTexture; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index ce602b7b9b..e8d6d4cd6f 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,87 +85,6 @@ 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; - } - - static QString ktxCacheFolder; - static std::once_flag once; - std::call_once(once, [&] { - // Prepare cache directory - static const QString HIFI_KTX_FOLDER("hifi_ktx"); - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - ktxCacheFolder = docsLocation + "/" + HIFI_KTX_FOLDER + "/"; - QFileInfo info(ktxCacheFolder); - if (!info.exists()) { - QDir(docsLocation).mkpath(HIFI_KTX_FOLDER); - } - }); - - - std::string cleanedName = QCryptographicHash::hash(QUrl::toPercentEncoding(name.c_str()), QCryptographicHash::Sha1).toHex().toStdString(); - std::string cacheFilename(ktxCacheFolder.toStdString()); - cacheFilename += "/"; - cacheFilename += cleanedName; - cacheFilename += ".ktx"; - - gpu::Texture* returnedTexture = srcTexture; - { - if (write && !QFileInfo(cacheFilename.c_str()).exists()) { - auto ktxMemory = gpu::Texture::serialize(*srcTexture); - if (ktxMemory) { - const auto& ktxStorage = ktxMemory->getStorage(); - auto header = ktxMemory->getHeader(); - QFile outFile(cacheFilename.c_str()); - if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { - throw std::runtime_error("Unable to open file"); - } - //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() - auto ktxSize = ktxStorage->size(); - outFile.resize(ktxSize); - auto dest = outFile.map(0, ktxSize); - memcpy(dest, ktxStorage->data(), ktxSize); - outFile.unmap(dest); - outFile.close(); - } - } - - if (read && QFileInfo(cacheFilename.c_str()).exists()) { -#define DEBUG_KTX_LOADING 0 -#if DEBUG_KTX_LOADING - { - FILE* file = fopen(cacheFilename.c_str(), "rb"); - if (file != nullptr) { - // obtain file size: - fseek (file , 0 , SEEK_END); - auto size = ftell(file); - rewind(file); - - auto storage = std::make_shared(size); - fread(storage->data(), 1, storage->size(), file); - fclose (file); - - //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), - ktx::KTX::create(std::static_pointer_cast(storage)), srcTexture->getSampler()); - - if (theNewTexure) { - returnedTexture = theNewTexure; - delete srcTexture; - } - } - } -#else - ktx::StoragePointer storage = std::make_shared(cacheFilename.c_str()); - auto ktxFile = ktx::KTX::create(storage); - returnedTexture->setKtxBacking(ktxFile); -#endif - } - } - return returnedTexture; -} - void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { _textureSource = textureSource; } @@ -355,7 +274,6 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag ::generateMips(theTexture, image, false); } theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -405,7 +323,6 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture, true, true); } return theTexture; @@ -496,7 +413,6 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture, true, false); } return theTexture; @@ -533,7 +449,6 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -574,7 +489,6 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -612,7 +526,6 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -946,7 +859,6 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm } theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } } From b03f51dd26c740bbd086994a2422ad6ed2a66548 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 28 Feb 2017 16:08:54 -0500 Subject: [PATCH 068/106] fix double fclose --- libraries/networking/src/FileCache.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index a717546de4..20b78c281a 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -62,7 +62,6 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length FILE* saveFile = fopen(filepath.c_str(), "wb"); if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { file.reset(createFile(key, filepath, length, extra), &fileDeleter); - fclose(saveFile); file->_cache = this; _files[key] = file; _numTotalFiles += 1; From de23a11dee7a01525cbee3a0e8ec41d967e428be Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 28 Feb 2017 17:25:56 -0500 Subject: [PATCH 069/106] wip --- libraries/networking/src/FileCache.cpp | 69 ++++++++++++++++---------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 20b78c281a..e4c715d020 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -54,13 +54,14 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length // if file already exists, return it FilePointer file = getFile(key); if (file) { - qCWarning(file_cache) << "Attempted to overwrite" << filepath.c_str(); + qCWarning(file_cache, "Attempted to overwrite %", key.c_str()); return file; } // write the new file FILE* saveFile = fopen(filepath.c_str(), "wb"); if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { + qCInfo(file_cache, "Wrote %s", key.c_str()); file.reset(createFile(key, filepath, length, extra), &fileDeleter); file->_cache = this; _files[key] = file; @@ -69,7 +70,7 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length emit dirty(); } else { - qCWarning(file_cache, "Failed to write %s (%s)", filepath.c_str(), strerror(errno)); + qCWarning(file_cache, "Failed to write %s (%s)", key.c_str(), strerror(errno)); errno = 0; } @@ -88,6 +89,7 @@ FilePointer FileCache::getFile(const Key& key) { if (file) { // if it exists, it is active - remove it from the cache removeUnusedFile(file); + qCInfo(file_cache, "Found %s", key.c_str()); emit dirty(); } else { // if not, remove the weak_ptr @@ -115,28 +117,30 @@ std::string FileCache::createDir(const std::string& dirname) { std::string entry; manifest >> entry; persistedEntries.insert(entry); - - // ZZMP: rm - for (const auto& entry : persistedEntries) - qDebug() << "ZZMP" << entry.c_str(); - qDebug() << "ZZMP" << "---"; + qCInfo(file_cache, "Manifest contents: %s", entry.c_str()); } + } else { + qCWarning(file_cache, "Missing manifest"); } + foreach(QString filename, dir.entryList()) { if (persistedEntries.find(filename.toStdString()) == persistedEntries.cend()) { dir.remove(filename); + qCInfo(file_cache) << "Cleaned" << filename; } } + qCDebug(file_cache, "Initiated %s", dirpath.data()); } else { dir.mkpath(dirpath); + qCDebug(file_cache, "Created %s", dirpath.data()); } return dirpath.toStdString(); } std::string FileCache::getFilepath(const Key& key) { - return _dir + key + '.' + _ext; + return _dir + '/' + key + '.' + _ext; } void FileCache::addUnusedFile(const FilePointer file) { @@ -197,34 +201,46 @@ void FileCache::reserve(size_t length) { } void FileCache::clear() { - std::string manifestPath= _dir + MANIFEST_NAME; - FILE* manifest = fopen(manifestPath.c_str(), "wb"); + try { + std::string manifestPath= _dir + '/' + MANIFEST_NAME; + std::ofstream manifest(manifestPath); - Lock lock(_unusedFilesMutex); - for (const auto& val : _unusedFiles) { - const FilePointer& file = val.second; - file->_cache = nullptr; + bool firstEntry = true; - if (_unusedFilesSize > _offlineFilesMaxSize) { - _unusedFilesSize -= file->getLength(); - } else { - std::string key = file->getKey() + '.' + _ext + '\n'; - if (manifest != nullptr && !fwrite(key.c_str(), key.length(), 1, manifest)) { - manifest = nullptr; // to prevent future writes + { + Lock lock(_unusedFilesMutex); + for (const auto& val : _unusedFiles) { + const FilePointer& file = val.second; + file->_cache = nullptr; + + if (_unusedFilesSize > _offlineFilesMaxSize) { + _unusedFilesSize -= file->getLength(); + } else { + if (!firstEntry) { + manifest << '\n'; + } + firstEntry = false; + manifest << file->getKey(); + + file->_shouldPersist = true; + qCInfo(file_cache, "Persisting %s", file->getKey().c_str()); + } } - file->_shouldPersist = true; } - } - if (manifest == nullptr || fclose(manifest) != 0) { + { + Lock lock(_filesMutex); + for (const auto& val : _files) { + const FilePointer& file = val.second + } + } catch (std::exception& e) { + qCWarning(file_cache, "Failed to write manifest (%s)", e.what()); for (const auto& val : _unusedFiles) { val.second->_shouldPersist = false; } - - qCWarning(file_cache, "Failed to write %s (%s)", manifestPath.c_str(), strerror(errno)); - errno = 0; } + Lock lock(_unusedFilesMutex); _unusedFiles.clear(); } @@ -240,6 +256,7 @@ void File::deleter() { File::~File() { QFile file(getFilepath().c_str()); if (file.exists() && !_shouldPersist) { + qCInfo(file_cache, "Unlinked %s", getFilepath().c_str()); file.remove(); } } From a6a0fd3851a46b3e2f9b6c48d6c75a29928ce7ed Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 1 Mar 2017 11:32:11 -0500 Subject: [PATCH 070/106] fix FileCache persistence --- .../src/model-networking/KTXCache.cpp | 1 + libraries/networking/src/FileCache.cpp | 92 ++++++++++--------- libraries/networking/src/FileCache.h | 3 +- 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 1ab32698b6..74926d12a1 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -39,6 +39,7 @@ File* KTXCache::createFile(const Key& key, const std::string& filepath, size_t l const QUrl& url = reinterpret_cast(extra)->url; Lock lock(_urlMutex); _urlMap[url] = key; + qCInfo(file_cache) << "Wrote KTX" << key.c_str() << url; return new KTXFile(key, filepath, length, url); } diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index e4c715d020..61712b383b 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -35,8 +35,9 @@ void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) { FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) : QObject(parent), - _dir(createDir(dirname)), - _ext(ext) {} + _ext(ext), + _dirname(dirname), + _dir(createDir(_dirname)) {} FileCache::~FileCache() { clear(); @@ -54,14 +55,13 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length // if file already exists, return it FilePointer file = getFile(key); if (file) { - qCWarning(file_cache, "Attempted to overwrite %", key.c_str()); + qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), key.c_str()); return file; } // write the new file FILE* saveFile = fopen(filepath.c_str(), "wb"); if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { - qCInfo(file_cache, "Wrote %s", key.c_str()); file.reset(createFile(key, filepath, length, extra), &fileDeleter); file->_cache = this; _files[key] = file; @@ -70,7 +70,7 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length emit dirty(); } else { - qCWarning(file_cache, "Failed to write %s (%s)", key.c_str(), strerror(errno)); + qCWarning(file_cache, "[%s] Failed to write %s (%s)", _dirname.c_str(), key.c_str(), strerror(errno)); errno = 0; } @@ -89,7 +89,7 @@ FilePointer FileCache::getFile(const Key& key) { if (file) { // if it exists, it is active - remove it from the cache removeUnusedFile(file); - qCInfo(file_cache, "Found %s", key.c_str()); + qCInfo(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); emit dirty(); } else { // if not, remove the weak_ptr @@ -101,6 +101,7 @@ FilePointer FileCache::getFile(const Key& key) { } File* FileCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { + qCInfo(file_cache, "Wrote %s", key.c_str()); return new File(key, filepath, length); } @@ -116,24 +117,26 @@ std::string FileCache::createDir(const std::string& dirname) { while (manifest.good()) { std::string entry; manifest >> entry; - persistedEntries.insert(entry); - qCInfo(file_cache, "Manifest contents: %s", entry.c_str()); + if (!entry.empty()) { + qCInfo(file_cache, "[%s] Manifest contains %s", _dirname.c_str(), entry.c_str()); + persistedEntries.insert(entry + '.' + _ext); + } } } else { - qCWarning(file_cache, "Missing manifest"); + qCWarning(file_cache, "[%s] Missing manifest", _dirname.c_str()); } - foreach(QString filename, dir.entryList()) { + foreach(QString filename, dir.entryList(QDir::Filters(QDir::NoDotAndDotDot | QDir::Files))) { if (persistedEntries.find(filename.toStdString()) == persistedEntries.cend()) { dir.remove(filename); - qCInfo(file_cache) << "Cleaned" << filename; + qCInfo(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); } } - qCDebug(file_cache, "Initiated %s", dirpath.data()); + qCDebug(file_cache) << "Initiated" << dirpath; } else { dir.mkpath(dirpath); - qCDebug(file_cache, "Created %s", dirpath.data()); + qCDebug(file_cache) << "Created" << dirpath; } return dirpath.toStdString(); @@ -201,43 +204,46 @@ void FileCache::reserve(size_t length) { } void FileCache::clear() { + auto forAllFiles = [&](std::function functor) { + Lock unusedFilesLock(_unusedFilesMutex); + for (const auto& pair : _unusedFiles) { + functor(pair.second); + } + // clear files so they are not reiterated from _files + _unusedFiles.clear(); + unusedFilesLock.unlock(); + + Lock filesLock(_filesMutex); + for (const auto& pair : _files) { + FilePointer file; + if ((file = pair.second.lock())) { + functor(file); + } + } + }; + try { std::string manifestPath= _dir + '/' + MANIFEST_NAME; std::ofstream manifest(manifestPath); - bool firstEntry = true; + forAllFiles([&](const FilePointer& file) { + file->_cache = nullptr; - { - Lock lock(_unusedFilesMutex); - for (const auto& val : _unusedFiles) { - const FilePointer& file = val.second; - file->_cache = nullptr; - - if (_unusedFilesSize > _offlineFilesMaxSize) { - _unusedFilesSize -= file->getLength(); - } else { - if (!firstEntry) { - manifest << '\n'; - } - firstEntry = false; - manifest << file->getKey(); - - file->_shouldPersist = true; - qCInfo(file_cache, "Persisting %s", file->getKey().c_str()); - } + if (_totalFilesSize > _offlineFilesMaxSize) { + _totalFilesSize -= file->getLength(); + } else { + manifest << file->getKey() << '\n'; + file->_shouldPersist = true; + qCInfo(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); } - } - - { - Lock lock(_filesMutex); - for (const auto& val : _files) { - const FilePointer& file = val.second - } + }); } catch (std::exception& e) { - qCWarning(file_cache, "Failed to write manifest (%s)", e.what()); - for (const auto& val : _unusedFiles) { - val.second->_shouldPersist = false; - } + qCWarning(file_cache, "[%s] Failed to write manifest (%s)", _dirname.c_str(), e.what()); + + forAllFiles([](const FilePointer& file) { + file->_cache = nullptr; + file->_shouldPersist = false; + }); } Lock lock(_unusedFilesMutex); diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index f068f6e7d5..09e1760ae5 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -98,8 +98,9 @@ private: std::atomic _totalFilesSize { 0 }; std::atomic _unusedFilesSize { 0 }; - std::string _dir; std::string _ext; + std::string _dirname; + std::string _dir; std::unordered_map> _files; Mutex _filesMutex; From e8319f967dd7c376dbf0418aa5c1aefdd3d07664 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 1 Mar 2017 17:28:51 -0500 Subject: [PATCH 071/106] add loading from KTXCache --- .../src/model-networking/KTXCache.cpp | 7 +++- .../src/model-networking/KTXCache.h | 5 +++ .../src/model-networking/TextureCache.cpp | 39 ++++++++++++++----- .../src/model-networking/TextureCache.h | 1 + 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 74926d12a1..3e288c6b27 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -11,7 +11,7 @@ #include "KTXCache.h" -#include +#include KTXFilePointer KTXCache::writeFile(Data data) { return std::static_pointer_cast(FileCache::writeFile(data.key, data.data, data.length, (void*)&data)); @@ -48,3 +48,8 @@ void KTXCache::evictedFile(const FilePointer& file) { Lock lock(_urlMutex); _urlMap.erase(url); } + +std::unique_ptr KTXFile::getKTX() const { + ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); + return ktx::KTX::create(storage); +} diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 5b9cb04061..835c28e6db 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -16,6 +16,10 @@ #include +namespace ktx { + class KTX; +} + class KTXFile; using KTXFilePointer = std::shared_ptr; @@ -55,6 +59,7 @@ class KTXFile : public File { public: QUrl getUrl() const { return _url; } + std::unique_ptr getKTX() const; protected: KTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) : diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4224cf076c..aef0a6d56f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -451,12 +451,32 @@ void ImageReader::listSupportedImageFormats() { } void FileReader::read() { - PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0); + gpu::TexturePointer texture; + { + auto resource = _resource.lock(); // to ensure the resource is still needed + if (!resource) { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0); + auto ktx = resource.staticCast()->_file->getKTX(); + gpu::Texture::Usage usage; + gpu::TextureUsageType usageType(gpu::TextureUsageType::RESOURCE); + gpu::Sampler sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); + texture.reset(gpu::Texture::unserialize(usage, usageType, ktx, sampler)); + texture->setKtxBacking(ktx); + } + + auto resource = _resource.lock(); // to ensure the resource is still needed + if (resource) { + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); + } else { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + } - // TODO: - // auto ktx = ktx::KTX::create(); - // auto texture = gpu::Texture::unserialize(getUsage(), getUsageType(), ktx, getSampler()); - // FIXME: do I need to set the file as a backing file here? } void ImageReader::read() { @@ -503,7 +523,7 @@ void ImageReader::read() { auto url = _url.toString().toStdString(); PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0); - texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); + texture.reset(resource.staticCast()->getTextureLoader()(image, url)); texture->setSource(url); } @@ -531,12 +551,13 @@ void ImageReader::read() { if (!ktx || !(file = ktxCache.writeFile({ _url, hash, data, length }))) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { - resource.dynamicCast()->_file = file; - // FIXME: do I need to set the file as a backing file here? + resource.staticCast()->_file = file; + auto ktx = file->getKTX(); + texture->setKtxBacking(ktx); } } - auto resource = _resource.toStrongRef(); // to ensure the resource is still needed + auto resource = _resource.lock(); // to ensure the resource is still needed if (resource) { QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, texture), diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index f05ab6b220..bc3baa6423 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -92,6 +92,7 @@ protected: Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: + friend class FileReader; friend class ImageReader; Type _type; From c71e614dd59e9ece4492cffc77621d05d520781a Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 1 Mar 2017 20:18:31 -0500 Subject: [PATCH 072/106] add loading from persisted files --- .../src/model-networking/KTXCache.cpp | 11 ++ .../src/model-networking/KTXCache.h | 5 +- .../src/model-networking/TextureCache.cpp | 3 +- libraries/networking/src/FileCache.cpp | 112 ++++++++++-------- libraries/networking/src/FileCache.h | 16 ++- 5 files changed, 95 insertions(+), 52 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 3e288c6b27..036e520af3 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -43,12 +43,23 @@ File* KTXCache::createFile(const Key& key, const std::string& filepath, size_t l return new KTXFile(key, filepath, length, url); } +File* KTXCache::loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) { + const QUrl url = QString(metadata.c_str()); + _urlMap[url] = key; + qCInfo(file_cache) << "Loaded KTX" << key.c_str() << url; + return new KTXFile(key, filepath, length, url); +} + void KTXCache::evictedFile(const FilePointer& file) { const QUrl url = std::static_pointer_cast(file)->getUrl(); Lock lock(_urlMutex); _urlMap.erase(url); } +std::string KTXFile::getMetadata() const { + return _url.toString().toStdString(); +} + std::unique_ptr KTXFile::getKTX() const { ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); return ktx::KTX::create(storage); diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 835c28e6db..7fe3ed872b 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -27,7 +27,7 @@ class KTXCache : public FileCache { Q_OBJECT public: - KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) {} + KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) { initialize(); } struct Data { Data(const QUrl& url, const Key& key, const char* data, size_t length) : @@ -43,6 +43,7 @@ public: protected: File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; + File* loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) override final; void evictedFile(const FilePointer& file) override final; private: @@ -65,6 +66,8 @@ protected: KTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) : File(key, filepath, length), _url(url) {} + std::string getMetadata() const override final; + private: friend class KTXCache; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index aef0a6d56f..56325614ff 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -451,6 +451,8 @@ void ImageReader::listSupportedImageFormats() { } void FileReader::read() { + PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + gpu::TexturePointer texture; { auto resource = _resource.lock(); // to ensure the resource is still needed @@ -459,7 +461,6 @@ void FileReader::read() { return; } - PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0); auto ktx = resource.staticCast()->_file->getKTX(); gpu::Texture::Usage usage; gpu::TextureUsageType usageType(gpu::TextureUsageType::RESOURCE); diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 61712b383b..40a2509b7c 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -12,6 +12,7 @@ #include "FileCache.h" #include +#include #include #include @@ -37,7 +38,7 @@ FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject QObject(parent), _ext(ext), _dirname(dirname), - _dir(createDir(_dirname)) {} + _dirpath(ServerPathUtils::getDataFilePath(dirname.c_str()).toStdString()) {} FileCache::~FileCache() { clear(); @@ -47,7 +48,63 @@ void fileDeleter(File* file) { file->deleter(); } +void FileCache::initialize() { + QDir dir(_dirpath.c_str()); + + if (dir.exists()) { + std::unordered_map> persistedEntries; + if (dir.exists(MANIFEST_NAME.c_str())) { + std::ifstream manifest; + manifest.open(dir.absoluteFilePath(MANIFEST_NAME.c_str()).toStdString()); + while (manifest.good()) { + std::string key, metadata; + std::getline(manifest, key, '\t'); + std::getline(manifest, metadata, '\n'); + if (!key.empty()) { + qCInfo(file_cache, "[%s] Manifest contains %s (%s)", _dirname.c_str(), key.c_str(), metadata.c_str()); + auto filename = key + '.' + _ext; + persistedEntries[filename] = { key, metadata }; + } + } + } else { + qCWarning(file_cache, "[%s] Missing manifest", _dirname.c_str()); + } + + std::unordered_map entries; + + foreach(QString filename, dir.entryList(QDir::Filters(QDir::NoDotAndDotDot | QDir::Files))) { + const auto& it = persistedEntries.find(filename.toStdString()); + if (it == persistedEntries.cend()) { + // unlink extra files + dir.remove(filename); + qCInfo(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); + } else { + // load existing files + const Key& key = it->second.first; + const std::string& metadata = it->second.second; + const std::string filepath = dir.filePath(filename).toStdString(); + const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); + + FilePointer file(loadFile(key, filepath, length, metadata), &fileDeleter); + file->_cache = this; + _files[key] = file; + _numTotalFiles += 1; + _totalFilesSize += length; + } + } + + qCDebug(file_cache, "[%s] Initialized %s", _dirname.c_str(), _dirpath.c_str()); + } else { + dir.mkpath(_dirpath.c_str()); + qCDebug(file_cache, "[%s] Created %s", _dirname.c_str(), _dirpath.c_str()); + } + + _initialized = true; +} + FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length, void* extra) { + assert(_initialized); + std::string filepath = getFilepath(key); Lock lock(_filesMutex); @@ -78,6 +135,8 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length } FilePointer FileCache::getFile(const Key& key) { + assert(_initialized); + FilePointer file; Lock lock(_filesMutex); @@ -100,50 +159,8 @@ FilePointer FileCache::getFile(const Key& key) { return file; } -File* FileCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { - qCInfo(file_cache, "Wrote %s", key.c_str()); - return new File(key, filepath, length); -} - -std::string FileCache::createDir(const std::string& dirname) { - QString dirpath = ServerPathUtils::getDataFilePath(dirname.c_str()); - QDir dir(dirpath); - - if (dir.exists()) { - std::unordered_set persistedEntries; - if (dir.exists(MANIFEST_NAME.c_str())) { - std::ifstream manifest; - manifest.open(dir.absoluteFilePath(MANIFEST_NAME.c_str()).toStdString()); - while (manifest.good()) { - std::string entry; - manifest >> entry; - if (!entry.empty()) { - qCInfo(file_cache, "[%s] Manifest contains %s", _dirname.c_str(), entry.c_str()); - persistedEntries.insert(entry + '.' + _ext); - } - } - } else { - qCWarning(file_cache, "[%s] Missing manifest", _dirname.c_str()); - } - - - foreach(QString filename, dir.entryList(QDir::Filters(QDir::NoDotAndDotDot | QDir::Files))) { - if (persistedEntries.find(filename.toStdString()) == persistedEntries.cend()) { - dir.remove(filename); - qCInfo(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); - } - } - qCDebug(file_cache) << "Initiated" << dirpath; - } else { - dir.mkpath(dirpath); - qCDebug(file_cache) << "Created" << dirpath; - } - - return dirpath.toStdString(); -} - std::string FileCache::getFilepath(const Key& key) { - return _dir + '/' + key + '.' + _ext; + return _dirpath + '/' + key + '.' + _ext; } void FileCache::addUnusedFile(const FilePointer file) { @@ -223,7 +240,7 @@ void FileCache::clear() { }; try { - std::string manifestPath= _dir + '/' + MANIFEST_NAME; + std::string manifestPath= _dirpath + '/' + MANIFEST_NAME; std::ofstream manifest(manifestPath); forAllFiles([&](const FilePointer& file) { @@ -232,9 +249,10 @@ void FileCache::clear() { if (_totalFilesSize > _offlineFilesMaxSize) { _totalFilesSize -= file->getLength(); } else { - manifest << file->getKey() << '\n'; + manifest << file->getKey() << '\t' << file->getMetadata() << '\n'; file->_shouldPersist = true; - qCInfo(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); + qCInfo(file_cache, "[%s] Persisting %s (%s)", + _dirname.c_str(), file->getKey().c_str(), file->getMetadata().c_str()); } }); } catch (std::exception& e) { diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 09e1760ae5..c5d8ce3fc2 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -73,10 +73,17 @@ signals: void dirty(); protected: + /// must be called after construction to create the cache on the fs and restore persisted files + void initialize(); + FilePointer writeFile(const Key& key, const char* data, size_t length, void* extra); FilePointer getFile(const Key& key); - virtual File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra); + /// create a file (ex.: create a class derived from File and store it in a secondary map with extra->url) + virtual File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) = 0; + /// load a file + virtual File* loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) = 0; + /// take action when a file is evicted from the cache (ex.: evict it from a secondary map) virtual void evictedFile(const FilePointer& file) = 0; private: @@ -85,7 +92,6 @@ private: friend class File; - std::string createDir(const std::string& dirname); std::string getFilepath(const Key& key); void addUnusedFile(const FilePointer file); @@ -100,7 +106,8 @@ private: std::string _ext; std::string _dirname; - std::string _dir; + std::string _dirpath; + bool _initialized { false }; std::unordered_map> _files; Mutex _filesMutex; @@ -133,6 +140,9 @@ protected: // the destructor should handle unlinking of the actual filepath virtual ~File(); + /// get metadata to store with a file between instances (ex.: return the url of a hash) + virtual std::string getMetadata() const = 0; + const std::string _filepath; private: From febeeeca3ac28e4d4e9d9be953016c84da039476 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 1 Mar 2017 20:58:00 -0500 Subject: [PATCH 073/106] namespace cache::FileCache and use unique_ptr --- .../src/model-networking/KTXCache.cpp | 20 ++++++++++++------- .../src/model-networking/KTXCache.h | 12 ++++++----- libraries/networking/src/FileCache.cpp | 6 ++++-- libraries/networking/src/FileCache.h | 12 +++++++---- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 036e520af3..d0380c7635 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -13,6 +13,9 @@ #include +using File = cache::File; +using FilePointer = cache::FilePointer; + KTXFilePointer KTXCache::writeFile(Data data) { return std::static_pointer_cast(FileCache::writeFile(data.key, data.data, data.length, (void*)&data)); } @@ -35,19 +38,22 @@ KTXFilePointer KTXCache::getFile(const QUrl& url) { return file; } -File* KTXCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { - const QUrl& url = reinterpret_cast(extra)->url; +std::unique_ptr KTXCache::createKTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) { Lock lock(_urlMutex); _urlMap[url] = key; - qCInfo(file_cache) << "Wrote KTX" << key.c_str() << url; - return new KTXFile(key, filepath, length, url); + return std::unique_ptr(new KTXFile(key, filepath, length, url)); } -File* KTXCache::loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) { +std::unique_ptr KTXCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { + const QUrl& url = reinterpret_cast(extra)->url; + qCInfo(file_cache) << "Wrote KTX" << key.c_str() << url; + return createKTXFile(key, filepath, length, url); +} + +std::unique_ptr KTXCache::loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) { const QUrl url = QString(metadata.c_str()); - _urlMap[url] = key; qCInfo(file_cache) << "Loaded KTX" << key.c_str() << url; - return new KTXFile(key, filepath, length, url); + return createKTXFile(key, filepath, length, url); } void KTXCache::evictedFile(const FilePointer& file) { diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 7fe3ed872b..84dda48ee2 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -23,7 +23,7 @@ namespace ktx { class KTXFile; using KTXFilePointer = std::shared_ptr; -class KTXCache : public FileCache { +class KTXCache : public cache::FileCache { Q_OBJECT public: @@ -42,11 +42,13 @@ public: KTXFilePointer getFile(const QUrl& url); protected: - File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; - File* loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) override final; - void evictedFile(const FilePointer& file) override final; + std::unique_ptr createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; + std::unique_ptr loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) override final; + void evictedFile(const cache::FilePointer& file) override final; private: + std::unique_ptr createKTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url); + using Mutex = std::mutex; using Lock = std::lock_guard; struct QUrlHasher { std::size_t operator()(QUrl const& url) const { return qHash(url); } }; @@ -55,7 +57,7 @@ private: Mutex _urlMutex; }; -class KTXFile : public File { +class KTXFile : public cache::File { Q_OBJECT public: diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 40a2509b7c..034e24c8cd 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -22,6 +22,8 @@ Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache") +using namespace cache; + static const std::string MANIFEST_NAME = "manifest"; void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { @@ -85,7 +87,7 @@ void FileCache::initialize() { const std::string filepath = dir.filePath(filename).toStdString(); const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); - FilePointer file(loadFile(key, filepath, length, metadata), &fileDeleter); + FilePointer file(loadFile(key, filepath, length, metadata).release(), &fileDeleter); file->_cache = this; _files[key] = file; _numTotalFiles += 1; @@ -119,7 +121,7 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length // write the new file FILE* saveFile = fopen(filepath.c_str(), "wb"); if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { - file.reset(createFile(key, filepath, length, extra), &fileDeleter); + file.reset(createFile(key, filepath, length, extra).release(), &fileDeleter); file->_cache = this; _files[key] = file; _numTotalFiles += 1; diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index c5d8ce3fc2..7e751d56be 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -22,6 +22,8 @@ Q_DECLARE_LOGGING_CATEGORY(file_cache) +namespace cache { + class File; using FilePointer = std::shared_ptr; @@ -80,9 +82,9 @@ protected: FilePointer getFile(const Key& key); /// create a file (ex.: create a class derived from File and store it in a secondary map with extra->url) - virtual File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) = 0; + virtual std::unique_ptr createFile(const Key& key, const std::string& filepath, size_t length, void* extra) = 0; /// load a file - virtual File* loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) = 0; + virtual std::unique_ptr loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) = 0; /// take action when a file is evicted from the cache (ex.: evict it from a secondary map) virtual void evictedFile(const FilePointer& file) = 0; @@ -130,6 +132,8 @@ public: Key getKey() const { return _key; } size_t getLength() const { return _length; } + // the destructor should handle unlinking of the actual filepath + virtual ~File(); // overrides should call File::deleter to maintain caching behavior virtual void deleter(); @@ -137,8 +141,6 @@ protected: // when constructed, the file has already been created/written File(const Key& key, const std::string& filepath, size_t length) : _filepath(filepath), _key(key), _length(length) {} - // the destructor should handle unlinking of the actual filepath - virtual ~File(); /// get metadata to store with a file between instances (ex.: return the url of a hash) virtual std::string getMetadata() const = 0; @@ -157,4 +159,6 @@ private: bool _shouldPersist { false }; }; +} + #endif // hifi_FileCache_h From cca1f2fb257fb921bdb168d565e0b0a6873618ce Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 2 Mar 2017 11:28:49 -0500 Subject: [PATCH 074/106] include in FileCache --- libraries/networking/src/FileCache.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 7e751d56be..b19f2d10cd 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -10,6 +10,7 @@ #ifndef hifi_FileCache_h #define hifi_FileCache_h +#include #include #include #include From 6a7ee4321ba5e17904aaf88c9c650ef773f7857c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 2 Mar 2017 13:30:51 -0500 Subject: [PATCH 075/106] suppress logs for file_cache --- libraries/networking/src/FileCache.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 034e24c8cd..c94d0e1b8c 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -20,7 +20,7 @@ #include -Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache") +Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) using namespace cache; @@ -63,7 +63,7 @@ void FileCache::initialize() { std::getline(manifest, key, '\t'); std::getline(manifest, metadata, '\n'); if (!key.empty()) { - qCInfo(file_cache, "[%s] Manifest contains %s (%s)", _dirname.c_str(), key.c_str(), metadata.c_str()); + qCDebug(file_cache, "[%s] Manifest contains %s (%s)", _dirname.c_str(), key.c_str(), metadata.c_str()); auto filename = key + '.' + _ext; persistedEntries[filename] = { key, metadata }; } @@ -79,7 +79,7 @@ void FileCache::initialize() { if (it == persistedEntries.cend()) { // unlink extra files dir.remove(filename); - qCInfo(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); + qCDebug(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); } else { // load existing files const Key& key = it->second.first; @@ -150,7 +150,7 @@ FilePointer FileCache::getFile(const Key& key) { if (file) { // if it exists, it is active - remove it from the cache removeUnusedFile(file); - qCInfo(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); + qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); emit dirty(); } else { // if not, remove the weak_ptr @@ -253,7 +253,7 @@ void FileCache::clear() { } else { manifest << file->getKey() << '\t' << file->getMetadata() << '\n'; file->_shouldPersist = true; - qCInfo(file_cache, "[%s] Persisting %s (%s)", + qCDebug(file_cache, "[%s] Persisting %s (%s)", _dirname.c_str(), file->getKey().c_str(), file->getMetadata().c_str()); } }); From 8db74413fd306e67adebf2c4641b3706c8d584c5 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 2 Mar 2017 17:40:54 -0800 Subject: [PATCH 076/106] Fix comment in GLBackend --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 76cc64f3e3..0800c27839 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -207,7 +207,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } } - { // Sync all the buffers + { // Sync all the transform states PROFILE_RANGE(render_gpu_gl_detail, "syncCPUTransform"); _transform._cameras.clear(); _transform._cameraOffsets.clear(); @@ -275,7 +275,7 @@ void GLBackend::renderPassDraw(const Batch& batch) { updateInput(); updateTransform(batch); updatePipeline(); - + CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); break; From 47087add154867caae91939a283a9fbfd7a3b727 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Mar 2017 08:58:55 -0800 Subject: [PATCH 077/106] Add support for fallback textures, throttling texture creation --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 6 +++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 + .../src/gpu/gl45/GL45BackendTexture.cpp | 30 ++++++------ .../gpu/gl45/GL45BackendVariableTexture.cpp | 3 ++ libraries/gpu/src/gpu/Texture.h | 4 ++ .../src/model-networking/TextureCache.cpp | 46 ++++++++++++++++++- .../src/model-networking/TextureCache.h | 1 + 7 files changed, 75 insertions(+), 16 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index f0ef2ac7a8..12c4b818f7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -18,6 +18,12 @@ Q_LOGGING_CATEGORY(gpugl45logging, "hifi.gpu.gl45") using namespace gpu; using namespace gpu::gl45; +void GL45Backend::recycle() const { + Parent::recycle(); + GL45VariableAllocationTexture::manageMemory(); + GL45VariableAllocationTexture::_frameTexturesCreated = 0; +} + void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index cfdcc356a6..6a9811b055 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -147,6 +147,7 @@ public: using TransferQueue = std::queue>; static MemoryPressureState _memoryPressureState; protected: + static size_t _frameTexturesCreated; static std::atomic _memoryPressureStateStale; static std::list _memoryManagedTextures; static WorkQueue _transferQueue; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index d5ad0204bf..36aaf75e81 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -28,6 +28,7 @@ using namespace gpu::gl; using namespace gpu::gl45; #define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f +#define MAX_RESOURCE_TEXTURES_PER_FRAME 2 GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (!texturePointer) { @@ -57,19 +58,23 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { break; case TextureUsageType::RESOURCE: { - - GL45VariableAllocationTexture* varObject { nullptr }; + if (GL45VariableAllocationTexture::_frameTexturesCreated < MAX_RESOURCE_TEXTURES_PER_FRAME) { #if 0 - if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { - varObject = new GL45SparseResourceTexture(shared_from_this(), texture); - } else { - varObject = new GL45ResourceTexture(shared_from_this(), texture); - } + if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { + object = new GL45SparseResourceTexture(shared_from_this(), texture); + } else { + object = new GL45ResourceTexture(shared_from_this(), texture); + } #else - varObject = new GL45ResourceTexture(shared_from_this(), texture); + object = new GL45ResourceTexture(shared_from_this(), texture); #endif - GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); - object = varObject; + GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); + } else { + auto fallback = texturePointer->getFallbackTexture(); + if (fallback) { + object = static_cast(syncGPUObject(fallback)); + } + } break; } @@ -81,11 +86,6 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { return object; } -void GL45Backend::recycle() const { - Parent::recycle(); - GL45VariableAllocationTexture::manageMemory(); -} - void GL45Backend::initTextureManagementStage() { // enable the Sparse Texture on gl45 _textureManagement._sparseCapable = true; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 92251bd381..d54ad1ea4b 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -405,7 +405,10 @@ void GL45VariableAllocationTexture::manageMemory() { processWorkQueues(); } +size_t GL45VariableAllocationTexture::_frameTexturesCreated { 0 }; + GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { + ++_frameTexturesCreated; } GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index d840687af2..9996026254 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -503,6 +503,9 @@ public: const Sampler& getSampler() const { return _sampler; } Stamp getSamplerStamp() const { return _samplerStamp; } + void setFallbackTexture(const TexturePointer& fallback) { _fallback = fallback; } + TexturePointer getFallbackTexture() const { return _fallback.lock(); } + void setExternalTexture(uint32 externalId, void* externalFence); void setExternalRecycler(const ExternalRecycler& recycler); ExternalRecycler getExternalRecycler() const; @@ -526,6 +529,7 @@ protected: ExternalRecycler _externalRecycler; + std::weak_ptr _fallback; // Not strictly necessary, but incredibly useful for debugging std::string _source; std::unique_ptr< Storage > _storage; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1f21e9e78d..f4473ceb39 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -186,6 +186,39 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const return ResourceCache::getResource(url, QUrl(), &extra).staticCast(); } +gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) { + auto textureCache = DependencyManager::get(); + + gpu::TexturePointer result; + switch (type) { + case NetworkTexture::DEFAULT_TEXTURE: + case NetworkTexture::ALBEDO_TEXTURE: + case NetworkTexture::ROUGHNESS_TEXTURE: + case NetworkTexture::OCCLUSION_TEXTURE: + result = textureCache->getWhiteTexture(); + break; + + case NetworkTexture::NORMAL_TEXTURE: + result = textureCache->getBlueTexture(); + break; + + case NetworkTexture::EMISSIVE_TEXTURE: + case NetworkTexture::LIGHTMAP_TEXTURE: + result = textureCache->getBlackTexture(); + break; + + case NetworkTexture::BUMP_TEXTURE: + case NetworkTexture::SPECULAR_TEXTURE: + case NetworkTexture::GLOSS_TEXTURE: + case NetworkTexture::CUBE_TEXTURE: + case NetworkTexture::CUSTOM_TEXTURE: + case NetworkTexture::STRICT_TEXTURE: + default: + break; + } + return result; +} + NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, const QVariantMap& options = QVariantMap()) { @@ -299,6 +332,13 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { return getTextureLoaderForType(_type); } +gpu::TexturePointer NetworkTexture::getFallbackTexture() const { + if (_type == CUSTOM_TEXTURE) { + return gpu::TexturePointer(); + } + return getFallbackTextureForType(_type); +} + class ImageReader : public QRunnable { public: @@ -428,7 +468,11 @@ void ImageReader::run() { auto url = _url.toString().toStdString(); PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffffff00, 0); - texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); + auto networkTexture = resource.dynamicCast(); + texture.reset(networkTexture->getTextureLoader()(image, url)); + if (texture) { + texture->setFallbackTexture(networkTexture->getFallbackTexture()); + } } // Ensure the resource has not been deleted diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 749b5a2ebb..c77bafe447 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -75,6 +75,7 @@ public: Type getTextureType() const { return _type; } TextureLoaderFunc getTextureLoader() const; + gpu::TexturePointer getFallbackTexture() const; signals: void networkTextureCreated(const QWeakPointer& self); From 5b1cd5443dc18aabb304b2e96e9a88fbfbf971de Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Mar 2017 11:13:11 -0800 Subject: [PATCH 078/106] Fix KTX includes --- tests/ktx/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index 34280cb263..c461f3e316 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -38,7 +38,7 @@ #include #include -#include + #include #include #include From de564d92b985ea443065f3f3c8d3068af4424dd5 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Mar 2017 13:23:13 -0800 Subject: [PATCH 079/106] Fix KTX compile errors --- libraries/ktx/src/ktx/KTX.cpp | 2 +- libraries/ktx/src/ktx/KTX.h | 4 ++-- libraries/ktx/src/ktx/Reader.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index cc9c1069b1..bee31e3200 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -77,7 +77,7 @@ KTX::KTX() { KTX::~KTX() { } -void KTX::resetStorage(StoragePointer& storage) { +void KTX::resetStorage(const StoragePointer& storage) { _storage = storage; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 7aef33704e..76920717bf 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -420,7 +420,7 @@ namespace ktx { using Images = std::vector; class KTX { - void resetStorage(StoragePointer& src); + void resetStorage(const StoragePointer& src); KTX(); public: @@ -448,7 +448,7 @@ 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(StoragePointer& src); + static std::unique_ptr create(const StoragePointer& src); static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index d74b45c01c..7321686de8 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -163,7 +163,7 @@ namespace ktx { return images; } - std::unique_ptr KTX::create(StoragePointer& src) { + std::unique_ptr KTX::create(const StoragePointer& src) { if (!src) { return nullptr; } From 9051c84b6dcab7e240468389f7b32c49f46a5600 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Mar 2017 13:42:22 -0800 Subject: [PATCH 080/106] Fix warnings --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 1 - libraries/gpu/src/gpu/Texture.cpp | 2 -- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 ++ libraries/ktx/src/ktx/KTX.h | 6 +++--- libraries/ktx/src/ktx/Reader.cpp | 8 ++++---- libraries/ktx/src/ktx/Writer.cpp | 4 ++-- libraries/model/src/model/TextureMap.cpp | 2 -- tests/ktx/src/main.cpp | 2 -- 8 files changed, 11 insertions(+), 16 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 80649c4d64..84806d82c3 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -72,7 +72,6 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& incrementTextureGPUCount(); withPreservedTexture([&] { GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - const Sampler& sampler = _gpuObject.getSampler(); auto numMips = _gpuObject.evalNumMips(); for (uint16_t mipLevel = 0; mipLevel < numMips; ++mipLevel) { // Get the mip level dimensions, accounting for the downgrade level diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 6b9087333c..5cd1ebd31f 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -412,7 +412,6 @@ void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) { // THen check that the mem texture passed make sense with its format Size expectedSize = evalStoredMipSize(level, getStoredMipFormat()); auto size = storage->size(); - auto bytes = storage->data(); if (storage->size() == expectedSize) { _storage->assignMipData(level, storage); _maxMip = std::max(_maxMip, level); @@ -442,7 +441,6 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin // THen check that the mem texture passed make sense with its format Size expectedSize = evalStoredMipFaceSize(level, getStoredMipFormat()); auto size = storage->size(); - auto bytes = storage->data(); if (size == expectedSize) { _storage->assignMipFaceData(level, face, storage); _maxMip = std::max(_maxMip, level); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index c7da499e98..bd34246f51 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -117,6 +117,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } auto ktxBuffer = ktx::KTX::create(header, images); +#if 0 auto expectedMipCount = texture.evalNumMips(); assert(expectedMipCount == ktxBuffer->_images.size()); assert(expectedMipCount == header.numberOfMipmapLevels); @@ -141,6 +142,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { assert(0 == memcmp(expectedFace, actualFace, expected._faceSize)); } } +#endif return ktxBuffer; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 76920717bf..3ea14b2cb5 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -402,15 +402,15 @@ namespace ktx { Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : _numFaces(1), _imageSize(imageSize), - _padding(padding), _faceSize(imageSize), + _padding(padding), _faceBytes(1, bytes) {} Image(uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : _numFaces(NUM_CUBEMAPFACES), _imageSize(pageSize * NUM_CUBEMAPFACES), - _padding(padding), - _faceSize(pageSize) + _faceSize(pageSize), + _padding(padding) { if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { _faceBytes = cubeFaceBytes; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 7321686de8..f8004cf21a 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -12,6 +12,7 @@ #include #include +#include #ifndef _MSC_VER #define NOEXCEPT noexcept @@ -68,7 +69,7 @@ namespace ktx { } // find the first null character \0 - int keyLength = 0; + uint32_t keyLength = 0; while (reinterpret_cast(src[++keyLength]) != '\0') { if (keyLength == keyValueByteSize) { // key must be null-terminated, and there must be space for the value @@ -119,8 +120,8 @@ namespace ktx { return true; } - catch (ReaderException& e) { - qWarning(e.what()); + catch (const ReaderException& e) { + qWarning() << e.what(); return false; } } @@ -128,7 +129,6 @@ namespace ktx { Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { 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 diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 901571f804..005b8e9e45 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -105,14 +105,14 @@ namespace ktx { // Single face vs cubes if (srcImages[l]._numFaces == 1) { - auto copied = memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); + memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); destImages.emplace_back(Image((uint32_t) 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); + memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); faceBytes[face] = currentPtr; currentPtr += faceSize; } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 23e892ba2b..4f9d57b21b 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -116,12 +116,10 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo auto ktxMemory = gpu::Texture::serialize(*srcTexture); if (ktxMemory) { const auto& ktxStorage = ktxMemory->getStorage(); - auto header = ktxMemory->getHeader(); QFile outFile(cacheFilename.c_str()); if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { throw std::runtime_error("Unable to open file"); } - //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() auto ktxSize = ktxStorage->size(); outFile.resize(ktxSize); auto dest = outFile.map(0, ktxSize); diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index c461f3e316..aa6795e17b 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -99,12 +99,10 @@ int main(int argc, char** argv) { auto ktxMemory = gpu::Texture::serialize(*testTexture); { const auto& ktxStorage = ktxMemory->getStorage(); - auto header = ktxMemory->getHeader(); QFile outFile(TEST_IMAGE_KTX); if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { throw std::runtime_error("Unable to open file"); } - //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() auto ktxSize = ktxStorage->size(); outFile.resize(ktxSize); auto dest = outFile.map(0, ktxSize); From c267b5cd099a8dee6f670a02c8a7d5a249519aa3 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 6 Mar 2017 18:26:31 -0800 Subject: [PATCH 081/106] Adding KeyValues read/write, work in progress --- libraries/ktx/src/ktx/KTX.cpp | 29 +++++++++++++ libraries/ktx/src/ktx/KTX.h | 27 ++++++++++-- libraries/ktx/src/ktx/Reader.cpp | 72 +++++++++++++++++++------------- libraries/ktx/src/ktx/Writer.cpp | 23 ++++++---- 4 files changed, 111 insertions(+), 40 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index bee31e3200..7035d8fc54 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -71,6 +71,35 @@ size_t Header::evalImageSize(uint32_t level) const { } +KeyValue::KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value) : + _byteSize((uint32_t) key.size() + valueByteSize), + _key(key), + _value(valueByteSize) +{ + if (_value.size() && value) { + memcpy(_value.data(), value, valueByteSize); + } +} + +KeyValue::KeyValue(const std::string& key, const std::string& value) : + KeyValue(key, (uint32_t) value.size(), (const Byte*) value.data()) +{ + +} + +uint32_t KeyValue::serializedByteSize() const { + return (uint32_t) (sizeof(uint32_t) + _byteSize + Header::evalPadding(_byteSize)); +} + +uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { + size_t keyValuesSize = 0; + for (auto& keyval : keyValues) { + keyValuesSize += keyval.serializedByteSize(); + } + return (keyValuesSize + Header::evalPadding(keyValuesSize)); +} + + KTX::KTX() { } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 3ea14b2cb5..8fdbe25ac9 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -385,9 +386,27 @@ namespace ktx { }; // Key Values - using KeyValue = std::pair; - using KeyValues = std::list; - + struct KeyValue { + uint32_t _byteSize { 0 }; + std::string _key; + std::vector _value; + + + KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value); + + KeyValue(const std::string& key, const std::string& value); + + uint32_t serializedByteSize() const; + + static KeyValue parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes); + static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes); + + using KeyValues = std::list; + static uint32_t serializedKeyValuesByteSize(const KeyValues& keyValues); + + }; + using KeyValues = KeyValue::KeyValues; + struct Image { using FaceBytes = std::vector; @@ -445,12 +464,14 @@ namespace ktx { // This is exactly what is done in the create function static size_t evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); static size_t write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues); static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const StoragePointer& src); static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); + static KeyValues parseKeyValues(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 diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index f8004cf21a..10a75ddfe7 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -56,38 +56,52 @@ namespace ktx { return true; } - 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 ReaderException("invalid key-value size"); + KeyValue KeyValue::parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes) { + // find the first null character \0 + uint32_t keyLength = 0; + while (reinterpret_cast(bytes[++keyLength]) != '\0') { + if (keyLength == keyAndValueByteSize) { + // key must be null-terminated, and there must be space for the value + throw ReaderException("invalid key-value " + std::string(reinterpret_cast(bytes), keyLength)); } - - // find the first null character \0 - uint32_t 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 ReaderException("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) % PACKING_SIZE); - offset += keyValueByteSize + keyValuePadding; - src += keyValueByteSize + keyValuePadding; } + return KeyValue(std::string(reinterpret_cast(bytes), keyLength), keyAndValueByteSize - keyLength, bytes + keyLength); + } + + static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes) { + uint32_t keyValueByteSize; + memcpy(&keyValueByteSize, bytes, sizeof(uint32_t)); + if (keyValueByteSize > byteSizeAhead) { + throw ReaderException("invalid key-value size"); + } + + auto keyValueBytes = bytes + sizeof(uint32_t); + + // parse the key-value + return KeyValue::parseKeyAndValue(keyValueByteSize, keyValueBytes); + } + + static KeyValues parseKeyValues(size_t srcSize, const Byte* srcBytes); + + KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* src) { + KeyValues keyValues; + try { + uint32_t length = (uint32_t) srcSize; + uint32_t offset = 0; + while (offset < length) { + auto keyValue = parseSerializedKeyAndValue(length - offset, src); + keyValues.emplace_back(keyValue); + + // advance offset/src + offset += keyValue.serializedByteSize(); + src += keyValue.serializedByteSize(); + } + } + catch (const ReaderException& e) { + qWarning() << e.what(); + } return keyValues; } @@ -176,7 +190,7 @@ namespace ktx { result->resetStorage(src); // read metadata - // result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + result->_keyValues = parseKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); // populate image table result->_images = parseImages(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 005b8e9e45..1a1c39e2c6 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -29,9 +29,9 @@ namespace ktx { std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { StoragePointer storagePointer; { - auto storageSize = ktx::KTX::evalStorageSize(header, images); + auto storageSize = ktx::KTX::evalStorageSize(header, images, keyValues); auto memoryStorage = new storage::MemoryStorage(storageSize); - ktx::KTX::write(memoryStorage->data(), memoryStorage->size(), header, images); + ktx::KTX::write(memoryStorage->data(), memoryStorage->size(), header, images, keyValues); storagePointer.reset(memoryStorage); } return create(storagePointer); @@ -40,8 +40,9 @@ namespace ktx { size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { size_t storageSize = sizeof(Header); - if (header.bytesOfKeyValueData && !keyValues.empty()) { - + if (!keyValues.empty()) { + size_t keyValuesSize = KeyValue::serializedKeyValuesByteSize(keyValues); + storageSize += keyValuesSize; } auto numMips = header.getNumberOfLevels(); @@ -68,11 +69,12 @@ namespace ktx { currentDestPtr += sizeof(Header); // KeyValues - // Skip for now - if (header.bytesOfKeyValueData && !keyValues.empty()) { - + if (!keyValues.empty()) { + destHeader->bytesOfKeyValueData = writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); + } else { + // Make sure the header contains the right bytesOfKeyValueData size + destHeader->bytesOfKeyValueData = 0; } - destHeader->bytesOfKeyValueData = 0; currentDestPtr += destHeader->bytesOfKeyValueData; // Images @@ -82,6 +84,11 @@ namespace ktx { return destByteSize; } + static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { + + + } + Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { Images destImages; auto imagesDataPtr = destBytes; From 1b036b7225665ee7e052d33c7b84396bea2c87b0 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 7 Mar 2017 00:58:22 -0800 Subject: [PATCH 082/106] Implement the ktx keyvalue feature for read and write and use it with gpu::Texture to store meat data required for full serialization --- interface/src/avatar/MyAvatar.cpp | 4 +- libraries/gpu/src/gpu/Texture.h | 3 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 46 ++++++++++-- libraries/ktx/src/ktx/KTX.cpp | 4 +- libraries/ktx/src/ktx/KTX.h | 4 +- libraries/ktx/src/ktx/Reader.cpp | 93 +++++++++++------------- libraries/ktx/src/ktx/Writer.cpp | 39 +++++++++- libraries/model/src/model/TextureMap.cpp | 4 +- 8 files changed, 131 insertions(+), 66 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 969268c549..3a34e4f434 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1617,8 +1617,8 @@ void MyAvatar::postUpdate(float deltaTime) { } } - DebugDraw::getInstance().updateMyAvatarPos(getPosition()); - DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); +// DebugDraw::getInstance().updateMyAvatarPos(getPosition()); + // DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); AnimPose postUpdateRoomPose(_sensorToWorldMatrix); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9996026254..c7fb496fc0 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -143,6 +143,7 @@ public: uint8 getMinMip() const { return _desc._minMip; } uint8 getMaxMip() const { return _desc._maxMip; } + const Desc& getDesc() const { return _desc; } protected: Desc _desc; }; @@ -516,7 +517,7 @@ public: // Textures can be serialized directly to ktx data file, here is how static ktx::KTXUniquePointer serialize(const Texture& texture); - static Texture* unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler = Sampler()); + static Texture* unserialize(const ktx::KTXUniquePointer& srcData, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index bd34246f51..5fed4d82bc 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -18,6 +18,30 @@ using namespace gpu; using PixelsPointer = Texture::PixelsPointer; using KtxStorage = Texture::KtxStorage; +struct GPUKTXPayload { + Sampler::Desc _samplerDesc; + Texture::Usage _usage; + TextureUsageType _usageType; + + static std::string KEY; + static bool isGPUKTX(const ktx::KeyValue& val) { + return (val._key.compare(KEY) == 0); + } + + static bool findInKeyValues(const ktx::KeyValues& keyValues, GPUKTXPayload& payload) { + auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); + if (found != keyValues.end()) { + if ((*found)._value.size() == sizeof(GPUKTXPayload)) { + memcpy(&payload, (*found)._value.data(), sizeof(GPUKTXPayload)); + return true; + } + } + return false; + } +}; + +std::string GPUKTXPayload::KEY { "hifi.gpu" }; + KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) { // if the source ktx is valid let's config this KtxStorage correctly @@ -116,7 +140,14 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } } - auto ktxBuffer = ktx::KTX::create(header, images); + GPUKTXPayload keyval; + keyval._samplerDesc = texture.getSampler().getDesc(); + keyval._usage = texture.getUsage(); + keyval._usageType = texture.getUsageType(); + ktx::KeyValues keyValues; + keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval)); + + auto ktxBuffer = ktx::KTX::create(header, images, keyValues); #if 0 auto expectedMipCount = texture.evalNumMips(); assert(expectedMipCount == ktxBuffer->_images.size()); @@ -146,7 +177,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler) { +Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { if (!srcData) { return nullptr; } @@ -175,7 +206,12 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx type = TEX_3D; } - auto tex = Texture::create( usageType, + + // If found, use the + GPUKTXPayload gpuktxKeyValue; + bool isGPUKTXPayload = GPUKTXPayload::findInKeyValues(srcData->_keyValues, gpuktxKeyValue); + + auto tex = Texture::create( (isGPUKTXPayload ? gpuktxKeyValue._usageType : usageType), type, texelFormat, header.getPixelWidth(), @@ -183,9 +219,9 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx header.getPixelDepth(), 1, // num Samples header.getNumberOfSlices(), - sampler); + (isGPUKTXPayload ? gpuktxKeyValue._samplerDesc : sampler)); - tex->setUsage(usage); + tex->setUsage((isGPUKTXPayload ? gpuktxKeyValue._usage : usage)); // Assing the mips availables tex->setStoredMipFormat(mipFormat); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 7035d8fc54..f9bfb377f5 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -72,7 +72,7 @@ size_t Header::evalImageSize(uint32_t level) const { KeyValue::KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value) : - _byteSize((uint32_t) key.size() + valueByteSize), + _byteSize((uint32_t) key.size() + 1 + valueByteSize), // keyString size + '\0' ending char + the value size _key(key), _value(valueByteSize) { @@ -92,7 +92,7 @@ uint32_t KeyValue::serializedByteSize() const { } uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { - size_t keyValuesSize = 0; + uint32_t keyValuesSize = 0; for (auto& keyval : keyValues) { keyValuesSize += keyval.serializedByteSize(); } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 8fdbe25ac9..8e901b1105 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -398,8 +398,8 @@ namespace ktx { uint32_t serializedByteSize() const; - static KeyValue parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes); - static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes); + static KeyValue parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes); + static uint32_t writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval); using KeyValues = std::list; static uint32_t serializedKeyValuesByteSize(const KeyValues& keyValues); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 10a75ddfe7..81f523cb69 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -56,55 +56,6 @@ namespace ktx { return true; } - - KeyValue KeyValue::parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes) { - // find the first null character \0 - uint32_t keyLength = 0; - while (reinterpret_cast(bytes[++keyLength]) != '\0') { - if (keyLength == keyAndValueByteSize) { - // key must be null-terminated, and there must be space for the value - throw ReaderException("invalid key-value " + std::string(reinterpret_cast(bytes), keyLength)); - } - } - - return KeyValue(std::string(reinterpret_cast(bytes), keyLength), keyAndValueByteSize - keyLength, bytes + keyLength); - } - - static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes) { - uint32_t keyValueByteSize; - memcpy(&keyValueByteSize, bytes, sizeof(uint32_t)); - if (keyValueByteSize > byteSizeAhead) { - throw ReaderException("invalid key-value size"); - } - - auto keyValueBytes = bytes + sizeof(uint32_t); - - // parse the key-value - return KeyValue::parseKeyAndValue(keyValueByteSize, keyValueBytes); - } - - static KeyValues parseKeyValues(size_t srcSize, const Byte* srcBytes); - - KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* src) { - KeyValues keyValues; - try { - uint32_t length = (uint32_t) srcSize; - uint32_t offset = 0; - while (offset < length) { - auto keyValue = parseSerializedKeyAndValue(length - offset, src); - keyValues.emplace_back(keyValue); - - // advance offset/src - offset += keyValue.serializedByteSize(); - src += keyValue.serializedByteSize(); - } - } - catch (const ReaderException& e) { - qWarning() << e.what(); - } - return keyValues; - } - bool KTX::checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes) { try { // validation @@ -140,6 +91,50 @@ namespace ktx { } } + KeyValue KeyValue::parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes) { + uint32_t keyAndValueByteSize; + memcpy(&keyAndValueByteSize, srcBytes, sizeof(uint32_t)); + if (keyAndValueByteSize + sizeof(uint32_t) > srcSize) { + throw ReaderException("invalid key-value size"); + } + auto keyValueBytes = srcBytes + sizeof(uint32_t); + + // find the first null character \0 and extract the key + uint32_t keyLength = 0; + while (reinterpret_cast(keyValueBytes[++keyLength]) != '\0') { + if (keyLength == keyAndValueByteSize) { + // key must be null-terminated, and there must be space for the value + throw ReaderException("invalid key-value " + std::string(reinterpret_cast(keyValueBytes), keyLength)); + } + } + uint32_t valueStartOffset = keyLength + 1; + + // parse the key-value + return KeyValue(std::string(reinterpret_cast(keyValueBytes), keyLength), + keyAndValueByteSize - valueStartOffset, keyValueBytes + valueStartOffset); + } + + KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* srcBytes) { + KeyValues keyValues; + try { + auto src = srcBytes; + uint32_t length = (uint32_t) srcSize; + uint32_t offset = 0; + while (offset < length) { + auto keyValue = KeyValue::parseSerializedKeyAndValue(length - offset, src); + keyValues.emplace_back(keyValue); + + // advance offset/src + offset += keyValue.serializedByteSize(); + src += keyValue.serializedByteSize(); + } + } + catch (const ReaderException& e) { + qWarning() << e.what(); + } + return keyValues; + } + Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { Images images; auto currentPtr = srcBytes; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 1a1c39e2c6..06fba326d5 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -10,6 +10,9 @@ // #include "KTX.h" + +#include +#include #ifndef _MSC_VER #define NOEXCEPT noexcept #else @@ -70,7 +73,7 @@ namespace ktx { // KeyValues if (!keyValues.empty()) { - destHeader->bytesOfKeyValueData = writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); + destHeader->bytesOfKeyValueData = (uint32_t) writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); } else { // Make sure the header contains the right bytesOfKeyValueData size destHeader->bytesOfKeyValueData = 0; @@ -84,9 +87,39 @@ namespace ktx { return destByteSize; } - static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { - + uint32_t KeyValue::writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval) { + uint32_t keyvalSize = keyval.serializedByteSize(); + if (keyvalSize > destByteSize) { + throw WriterException("invalid key-value size"); + } + *((uint32_t*) destBytes) = keyval._byteSize; + + auto dest = destBytes + sizeof(uint32_t); + + auto keySize = keyval._key.size() + 1; // Add 1 for the '\0' character at the end of the string + memcpy(dest, keyval._key.data(), keySize); + dest += keySize; + + memcpy(dest, keyval._value.data(), keyval._value.size()); + + return keyvalSize; + } + + size_t KTX::writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { + size_t writtenByteSize = 0; + try { + auto dest = destBytes; + for (auto& keyval : keyValues) { + size_t keyvalSize = KeyValue::writeSerializedKeyAndValue(dest, (uint32_t) (destByteSize - writtenByteSize), keyval); + writtenByteSize += keyvalSize; + dest += keyvalSize; + } + } + catch (const WriterException& e) { + qWarning() << e.what(); + } + return writtenByteSize; } Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 4f9d57b21b..56b86f22d6 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -145,8 +145,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo fclose (file); //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), - ktx::KTX::create(std::static_pointer_cast(storage)), srcTexture->getSampler()); + auto theNewTexure = Texture::unserialize(ktx::KTX::create(std::static_pointer_cast(storage)), + srcTexture->getUsageType(), srcTexture->getUsage(), srcTexture->getSampler().getDesc()); if (theNewTexure) { returnedTexture = theNewTexure; From 4b4f573f8ea682208a0f66b381b4a83cc65f29e8 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 7 Mar 2017 11:44:40 -0800 Subject: [PATCH 083/106] Removing the comments to work in debug --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3a34e4f434..969268c549 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1617,8 +1617,8 @@ void MyAvatar::postUpdate(float deltaTime) { } } -// DebugDraw::getInstance().updateMyAvatarPos(getPosition()); - // DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); + DebugDraw::getInstance().updateMyAvatarPos(getPosition()); + DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); AnimPose postUpdateRoomPose(_sensorToWorldMatrix); From 62fcd238ae8a9012458d8968b563a12ed1adced4 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 7 Mar 2017 13:36:51 -0800 Subject: [PATCH 084/106] Fix shared pointer usage in storage abstraction --- libraries/gpu/src/gpu/Texture.cpp | 7 ++++--- libraries/shared/src/shared/Storage.cpp | 17 +++++++++++------ libraries/shared/src/shared/Storage.h | 16 +++++----------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 5cd1ebd31f..2be495dcdb 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -389,12 +389,14 @@ const Element& Texture::getStoredMipFormat() const { } void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { - storage::StoragePointer storage { new storage::MemoryStorage(size, bytes) }; + auto rawStoragePointer = new storage::MemoryStorage(size, bytes); + storage::StoragePointer storage = rawStoragePointer->shared_from_this(); assignStoredMip(level, storage); } void Texture::assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes) { - storage::StoragePointer storage { new storage::MemoryStorage(size, bytes) }; + auto rawStoragePointer = new storage::MemoryStorage(size, bytes); + storage::StoragePointer storage = rawStoragePointer->shared_from_this(); assignStoredMipFace(level, face, storage); } @@ -973,4 +975,3 @@ Texture::ExternalUpdates Texture::getUpdates() const { void Texture::setStorage(std::unique_ptr& newStorage) { _storage.swap(newStorage); } - diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 7075d9c6f7..f887ec1181 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -12,7 +12,10 @@ using namespace storage; -ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const { +ViewStorage::ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data) + : _owner(owner), _size(size), _data(data) {} + +StoragePointer Storage::createView(size_t viewSize, size_t offset) const { auto selfSize = size(); if (0 == viewSize) { viewSize = selfSize; @@ -20,14 +23,16 @@ ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const { if ((viewSize + offset) > selfSize) { throw std::runtime_error("Invalid mapping range"); } - return ViewStoragePointer(new ViewStorage(shared_from_this(), viewSize, data() + offset)); + auto viewPointer = new ViewStorage(shared_from_this(), viewSize, data() + offset); + return viewPointer->shared_from_this(); } -MemoryStoragePointer Storage::toMemoryStorage() const { - return MemoryStoragePointer(new MemoryStorage(size(), data())); +StoragePointer Storage::toMemoryStorage() const { + auto rawPointer = new MemoryStorage(size(), data()); + return rawPointer->shared_from_this(); } -FileStoragePointer Storage::toFileStorage(const QString& filename) const { +StoragePointer Storage::toFileStorage(const QString& filename) const { return FileStorage::create(filename, size(), data()); } @@ -38,7 +43,7 @@ MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { } } -FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { +StoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { QFile file(filename); if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { throw std::runtime_error("Unable to open file for writing"); diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index b79b9d6080..4b97e14178 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -19,12 +19,6 @@ namespace storage { class Storage; using StoragePointer = std::shared_ptr; - class MemoryStorage; - using MemoryStoragePointer = std::shared_ptr; - class FileStorage; - using FileStoragePointer = std::shared_ptr; - class ViewStorage; - using ViewStoragePointer = std::shared_ptr; class Storage : public std::enable_shared_from_this { public: @@ -32,9 +26,9 @@ namespace storage { virtual const uint8_t* data() const = 0; virtual size_t size() const = 0; - ViewStoragePointer createView(size_t size = 0, size_t offset = 0) const; - FileStoragePointer toFileStorage(const QString& filename) const; - MemoryStoragePointer toMemoryStorage() const; + StoragePointer createView(size_t size = 0, size_t offset = 0) const; + StoragePointer toFileStorage(const QString& filename) const; + StoragePointer toMemoryStorage() const; // Aliases to prevent having to re-write a ton of code inline size_t getSize() const { return size(); } @@ -53,7 +47,7 @@ namespace storage { class FileStorage : public Storage { public: - static FileStoragePointer create(const QString& filename, size_t size, const uint8_t* data); + static StoragePointer create(const QString& filename, size_t size, const uint8_t* data); FileStorage(const QString& filename); ~FileStorage(); // Prevent copying @@ -69,7 +63,7 @@ namespace storage { class ViewStorage : public Storage { public: - ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data) : _owner(owner), _size(size), _data(data) {} + ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); const uint8_t* data() const override { return _data; } size_t size() const override { return _size; } private: From 399270d24387cc4a7b441b59538c761fa389e57c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 7 Mar 2017 17:21:54 -0800 Subject: [PATCH 085/106] Still trying to fix OSX build --- libraries/gpu/src/gpu/Texture.cpp | 6 ++---- libraries/shared/src/shared/Storage.cpp | 7 ++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 2be495dcdb..28373c4856 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -389,14 +389,12 @@ const Element& Texture::getStoredMipFormat() const { } void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { - auto rawStoragePointer = new storage::MemoryStorage(size, bytes); - storage::StoragePointer storage = rawStoragePointer->shared_from_this(); + storage::StoragePointer storage = std::make_shared(size, bytes); assignStoredMip(level, storage); } void Texture::assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes) { - auto rawStoragePointer = new storage::MemoryStorage(size, bytes); - storage::StoragePointer storage = rawStoragePointer->shared_from_this(); + storage::StoragePointer storage = std::make_shared(size, bytes); assignStoredMipFace(level, face, storage); } diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index f887ec1181..6754854c40 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -23,13 +23,11 @@ StoragePointer Storage::createView(size_t viewSize, size_t offset) const { if ((viewSize + offset) > selfSize) { throw std::runtime_error("Invalid mapping range"); } - auto viewPointer = new ViewStorage(shared_from_this(), viewSize, data() + offset); - return viewPointer->shared_from_this(); + return std::make_shared(shared_from_this(), viewSize, data() + offset); } StoragePointer Storage::toMemoryStorage() const { - auto rawPointer = new MemoryStorage(size(), data()); - return rawPointer->shared_from_this(); + return std::make_shared(size(), data()); } StoragePointer Storage::toFileStorage(const QString& filename) const { @@ -62,7 +60,6 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u } } file.close(); - //return FileStoragePointer(new FileStorage(filename)); return std::make_shared(filename); } From 9b8a5314390a4c0750478f70c6b0d59bd1c3ce69 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 7 Mar 2017 18:13:05 -0800 Subject: [PATCH 086/106] Fix iteration --- libraries/ktx/src/ktx/Reader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 81f523cb69..5aa6919844 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -101,7 +101,7 @@ namespace ktx { // find the first null character \0 and extract the key uint32_t keyLength = 0; - while (reinterpret_cast(keyValueBytes[++keyLength]) != '\0') { + while (reinterpret_cast(keyValueBytes)[++keyLength] != '\0') { if (keyLength == keyAndValueByteSize) { // key must be null-terminated, and there must be space for the value throw ReaderException("invalid key-value " + std::string(reinterpret_cast(keyValueBytes), keyLength)); From 9d56001341a6fdcfd1e252448ba0a6f87fd2f64b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 8 Mar 2017 12:11:25 -0800 Subject: [PATCH 087/106] Fix rebase --- .../model-networking/src/model-networking/TextureCache.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b1ed4fcc5d..c58c157da2 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -497,7 +497,7 @@ void FileReader::read() { gpu::Texture::Usage usage; gpu::TextureUsageType usageType(gpu::TextureUsageType::RESOURCE); gpu::Sampler sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - texture.reset(gpu::Texture::unserialize(usage, usageType, ktx, sampler)); + texture.reset(gpu::Texture::unserialize(ktx, usageType, usage, sampler.getDesc())); texture->setKtxBacking(ktx); } @@ -556,7 +556,8 @@ void ImageReader::read() { auto url = _url.toString().toStdString(); PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0); - texture.reset(resource.staticCast()->getTextureLoader()(image, url)); + auto networkTexture = resource.staticCast(); + texture.reset(networkTexture->getTextureLoader()(image, url)); texture->setSource(url); if (texture) { texture->setFallbackTexture(networkTexture->getFallbackTexture()); From ffdaa090728332c4e9b4cd893ef80d0ea3c6d905 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 8 Mar 2017 15:54:32 -0500 Subject: [PATCH 088/106] use stored Usage(Type), Sampler for KTX --- .../model-networking/src/model-networking/TextureCache.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c58c157da2..16d84b69df 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -494,10 +494,7 @@ void FileReader::read() { } auto ktx = resource.staticCast()->_file->getKTX(); - gpu::Texture::Usage usage; - gpu::TextureUsageType usageType(gpu::TextureUsageType::RESOURCE); - gpu::Sampler sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - texture.reset(gpu::Texture::unserialize(ktx, usageType, usage, sampler.getDesc())); + texture.reset(gpu::Texture::unserialize(ktx)); texture->setKtxBacking(ktx); } From 8a438948b977bdca4076054708aadb3cb5f7a2cc Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 9 Mar 2017 10:42:56 -0800 Subject: [PATCH 089/106] Fix OSX link failure --- libraries/networking/src/FileCache.cpp | 7 +++++++ libraries/networking/src/FileCache.h | 8 +++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index c94d0e1b8c..88f2c048e5 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -19,6 +19,7 @@ #include #include +#include Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) @@ -26,6 +27,12 @@ using namespace cache; static const std::string MANIFEST_NAME = "manifest"; +static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; +static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; +const size_t FileCache::DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB +const size_t FileCache::MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB +const size_t FileCache::DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB + void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { _unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE); reserve(0); diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index b19f2d10cd..945d43b224 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -35,11 +35,9 @@ class FileCache : public QObject { Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty) Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty) - static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; - static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; - static const size_t DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB - static const size_t MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB - static const size_t DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB + static const size_t DEFAULT_UNUSED_MAX_SIZE; + static const size_t MAX_UNUSED_MAX_SIZE; + static const size_t DEFAULT_OFFLINE_MAX_SIZE; public: size_t getNumTotalFiles() const { return _numTotalFiles; } From 43d8433d9a3c92ccc8dfb8910019cb068452fdc7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Mar 2017 09:19:00 -0800 Subject: [PATCH 090/106] Removing unused include --- libraries/networking/src/FileCache.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 88f2c048e5..b851539b1b 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -19,7 +19,6 @@ #include #include -#include Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) From a0c56618b36ef9032ef90e920b407ce53f473e53 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 10 Mar 2017 03:57:36 +0000 Subject: [PATCH 091/106] update FileCache to rm manifest --- libraries/networking/src/FileCache.cpp | 145 ++++++++----------------- libraries/networking/src/FileCache.h | 43 ++++---- 2 files changed, 66 insertions(+), 122 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index b851539b1b..78008562ed 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -60,45 +60,17 @@ void FileCache::initialize() { QDir dir(_dirpath.c_str()); if (dir.exists()) { - std::unordered_map> persistedEntries; - if (dir.exists(MANIFEST_NAME.c_str())) { - std::ifstream manifest; - manifest.open(dir.absoluteFilePath(MANIFEST_NAME.c_str()).toStdString()); - while (manifest.good()) { - std::string key, metadata; - std::getline(manifest, key, '\t'); - std::getline(manifest, metadata, '\n'); - if (!key.empty()) { - qCDebug(file_cache, "[%s] Manifest contains %s (%s)", _dirname.c_str(), key.c_str(), metadata.c_str()); - auto filename = key + '.' + _ext; - persistedEntries[filename] = { key, metadata }; - } - } - } else { - qCWarning(file_cache, "[%s] Missing manifest", _dirname.c_str()); - } + auto nameFilters = QStringList(("*." + _ext).c_str()); + auto filters = QDir::Filters(QDir::NoDotAndDotDot | QDir::Files); + auto sort = QDir::SortFlags(QDir::Time); + auto files = dir.entryList(nameFilters, filters, sort); - std::unordered_map entries; - - foreach(QString filename, dir.entryList(QDir::Filters(QDir::NoDotAndDotDot | QDir::Files))) { - const auto& it = persistedEntries.find(filename.toStdString()); - if (it == persistedEntries.cend()) { - // unlink extra files - dir.remove(filename); - qCDebug(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); - } else { - // load existing files - const Key& key = it->second.first; - const std::string& metadata = it->second.second; - const std::string filepath = dir.filePath(filename).toStdString(); - const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); - - FilePointer file(loadFile(key, filepath, length, metadata).release(), &fileDeleter); - file->_cache = this; - _files[key] = file; - _numTotalFiles += 1; - _totalFilesSize += length; - } + // load persisted files + foreach(QString filename, files) { + const Key key = filename.section('.', 0, 1).toStdString(); + const std::string filepath = dir.filePath(filename).toStdString(); + const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); + addFile(Metadata(key, length), filepath); } qCDebug(file_cache, "[%s] Initialized %s", _dirname.c_str(), _dirpath.c_str()); @@ -110,32 +82,40 @@ void FileCache::initialize() { _initialized = true; } -FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length, void* extra) { +FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) { + FilePointer file(createFile(std::move(metadata), filepath).release(), &fileDeleter); + if (file) { + _numTotalFiles += 1; + _totalFilesSize += file->getLength(); + file->_cache = this; + emit dirty(); + + Lock lock(_filesMutex); + _files[file->getKey()] = file; + } + return file; +} + +FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata) { assert(_initialized); - std::string filepath = getFilepath(key); + std::string filepath = getFilepath(metadata.key); Lock lock(_filesMutex); // if file already exists, return it - FilePointer file = getFile(key); + FilePointer file = getFile(metadata.key); if (file) { - qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), key.c_str()); + qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), metadata.key.c_str()); return file; } // write the new file FILE* saveFile = fopen(filepath.c_str(), "wb"); - if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { - file.reset(createFile(key, filepath, length, extra).release(), &fileDeleter); - file->_cache = this; - _files[key] = file; - _numTotalFiles += 1; - _totalFilesSize += length; - - emit dirty(); + if (saveFile != nullptr && fwrite(data, metadata.length, 1, saveFile) && fclose(saveFile) == 0) { + file = addFile(std::move(metadata), filepath); } else { - qCWarning(file_cache, "[%s] Failed to write %s (%s)", _dirname.c_str(), key.c_str(), strerror(errno)); + qCWarning(file_cache, "[%s] Failed to write %s (%s)", _dirname.c_str(), metadata.key.c_str(), strerror(errno)); errno = 0; } @@ -149,7 +129,7 @@ FilePointer FileCache::getFile(const Key& key) { Lock lock(_filesMutex); - // check if file already exists + // check if file exists const auto it = _files.find(key); if (it != _files.cend()) { file = it->second.lock(); @@ -221,58 +201,22 @@ void FileCache::reserve(size_t length) { _numUnusedFiles -= 1; _totalFilesSize -= length; _unusedFilesSize -= length; - - unusedLock.unlock(); - evictedFile(file); - unusedLock.lock(); } } void FileCache::clear() { - auto forAllFiles = [&](std::function functor) { - Lock unusedFilesLock(_unusedFilesMutex); - for (const auto& pair : _unusedFiles) { - functor(pair.second); + Lock unusedFilesLock(_unusedFilesMutex); + for (const auto& pair : _unusedFiles) { + auto& file = pair.second; + file->_cache = nullptr; + + if (_totalFilesSize > _offlineFilesMaxSize) { + _totalFilesSize -= file->getLength(); + } else { + file->_shouldPersist = true; + qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); } - // clear files so they are not reiterated from _files - _unusedFiles.clear(); - unusedFilesLock.unlock(); - - Lock filesLock(_filesMutex); - for (const auto& pair : _files) { - FilePointer file; - if ((file = pair.second.lock())) { - functor(file); - } - } - }; - - try { - std::string manifestPath= _dirpath + '/' + MANIFEST_NAME; - std::ofstream manifest(manifestPath); - - forAllFiles([&](const FilePointer& file) { - file->_cache = nullptr; - - if (_totalFilesSize > _offlineFilesMaxSize) { - _totalFilesSize -= file->getLength(); - } else { - manifest << file->getKey() << '\t' << file->getMetadata() << '\n'; - file->_shouldPersist = true; - qCDebug(file_cache, "[%s] Persisting %s (%s)", - _dirname.c_str(), file->getKey().c_str(), file->getMetadata().c_str()); - } - }); - } catch (std::exception& e) { - qCWarning(file_cache, "[%s] Failed to write manifest (%s)", _dirname.c_str(), e.what()); - - forAllFiles([](const FilePointer& file) { - file->_cache = nullptr; - file->_shouldPersist = false; - }); } - - Lock lock(_unusedFilesMutex); _unusedFiles.clear(); } @@ -285,6 +229,11 @@ void File::deleter() { } } +File::File(Metadata&& metadata, const std::string& filepath) : + _key(std::move(metadata.key)), + _length(metadata.length), + _filepath(filepath) {} + File::~File() { QFile file(getFilepath().c_str()); if (file.exists() && !_shouldPersist) { diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 945d43b224..f77db555bc 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -52,21 +52,24 @@ public: // initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg") FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); - // precondition: there should be no references to Files when FileCache is destroyed virtual ~FileCache(); - // derived classes are left to implement hashing of the files on their own using Key = std::string; + struct Metadata { + Metadata(const Key& key, size_t length) : + key(key), length(length) {} + Key key; + size_t length; + }; // derived classes should implement a setter/getter, for example, for a FileCache backing a network cache: // - // DerivedFilePointer writeFile(const DerivedData& data) { - // return writeFile(data->key, data->data, data->length, &data); + // DerivedFilePointer writeFile(const char* data, DerivedMetadata&& metadata) { + // return writeFile(data, std::forward(metadata)); // } // // DerivedFilePointer getFile(const QUrl& url) { - // // assuming storage/removal of url->hash in createFile/evictedFile overrides - // auto key = lookup_hash_for(url); + // auto key = lookup_hash_for(url); // assuming hashing url in create/evictedFile overrides // return getFile(key); // } @@ -77,15 +80,11 @@ protected: /// must be called after construction to create the cache on the fs and restore persisted files void initialize(); - FilePointer writeFile(const Key& key, const char* data, size_t length, void* extra); + FilePointer writeFile(const char* data, Metadata&& metadata); FilePointer getFile(const Key& key); - /// create a file (ex.: create a class derived from File and store it in a secondary map with extra->url) - virtual std::unique_ptr createFile(const Key& key, const std::string& filepath, size_t length, void* extra) = 0; - /// load a file - virtual std::unique_ptr loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) = 0; - /// take action when a file is evicted from the cache (ex.: evict it from a secondary map) - virtual void evictedFile(const FilePointer& file) = 0; + /// create a file + virtual std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) = 0; private: using Mutex = std::recursive_mutex; @@ -95,6 +94,7 @@ private: std::string getFilepath(const Key& key); + FilePointer addFile(Metadata&& metadata, const std::string& filepath); void addUnusedFile(const FilePointer file); void removeUnusedFile(const FilePointer file); void reserve(size_t length); @@ -126,31 +126,26 @@ class File : public QObject { public: using Key = FileCache::Key; + using Metadata = FileCache::Metadata; - std::string getFilepath() const { return _filepath; } Key getKey() const { return _key; } size_t getLength() const { return _length; } + std::string getFilepath() const { return _filepath; } - // the destructor should handle unlinking of the actual filepath virtual ~File(); - // overrides should call File::deleter to maintain caching behavior + /// overrides should call File::deleter to maintain caching behavior virtual void deleter(); protected: - // when constructed, the file has already been created/written - File(const Key& key, const std::string& filepath, size_t length) : - _filepath(filepath), _key(key), _length(length) {} - - /// get metadata to store with a file between instances (ex.: return the url of a hash) - virtual std::string getMetadata() const = 0; - - const std::string _filepath; + /// when constructed, the file has already been created/written + File(Metadata&& metadata, const std::string& filepath); private: friend class FileCache; const Key _key; const size_t _length; + const std::string _filepath; FileCache* _cache; int _LRUKey { 0 }; From a27d88da1a3acafc35f9abf5765815d3c7743a3d Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 12 Mar 2017 20:14:38 -0400 Subject: [PATCH 092/106] update TextureCache to use FileCache --- .../src/model-networking/KTXCache.cpp | 54 ++--- .../src/model-networking/KTXCache.h | 38 +-- .../src/model-networking/TextureCache.cpp | 220 ++++++++---------- .../src/model-networking/TextureCache.h | 6 +- 4 files changed, 120 insertions(+), 198 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index d0380c7635..908d3245ce 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -16,55 +16,27 @@ using File = cache::File; using FilePointer = cache::FilePointer; -KTXFilePointer KTXCache::writeFile(Data data) { - return std::static_pointer_cast(FileCache::writeFile(data.key, data.data, data.length, (void*)&data)); +KTXCache::KTXCache(const std::string& dir, const std::string& ext) : + FileCache(dir, ext) { + initialize(); } -KTXFilePointer KTXCache::getFile(const QUrl& url) { - Key key; - { - Lock lock(_urlMutex); - const auto it = _urlMap.find(url); - if (it != _urlMap.cend()) { - key = it->second; - } - } - - KTXFilePointer file; - if (!key.empty()) { - file = std::static_pointer_cast(FileCache::getFile(key)); - } - - return file; +KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) { + FilePointer file = FileCache::writeFile(data, std::move(metadata)); + return std::static_pointer_cast(file); } -std::unique_ptr KTXCache::createKTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) { - Lock lock(_urlMutex); - _urlMap[url] = key; - return std::unique_ptr(new KTXFile(key, filepath, length, url)); +KTXFilePointer KTXCache::getFile(const Key& key) { + return std::static_pointer_cast(FileCache::getFile(key)); } -std::unique_ptr KTXCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { - const QUrl& url = reinterpret_cast(extra)->url; - qCInfo(file_cache) << "Wrote KTX" << key.c_str() << url; - return createKTXFile(key, filepath, length, url); +std::unique_ptr KTXCache::createFile(Metadata&& metadata, const std::string& filepath) { + qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str(); + return std::unique_ptr(new KTXFile(std::move(metadata), filepath)); } -std::unique_ptr KTXCache::loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) { - const QUrl url = QString(metadata.c_str()); - qCInfo(file_cache) << "Loaded KTX" << key.c_str() << url; - return createKTXFile(key, filepath, length, url); -} - -void KTXCache::evictedFile(const FilePointer& file) { - const QUrl url = std::static_pointer_cast(file)->getUrl(); - Lock lock(_urlMutex); - _urlMap.erase(url); -} - -std::string KTXFile::getMetadata() const { - return _url.toString().toStdString(); -} +KTXFile::KTXFile(Metadata&& metadata, const std::string& filepath) : + cache::File(std::move(metadata), filepath) {} std::unique_ptr KTXFile::getKTX() const { ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 84dda48ee2..4ef5e52721 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -27,53 +27,25 @@ class KTXCache : public cache::FileCache { Q_OBJECT public: - KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) { initialize(); } + KTXCache(const std::string& dir, const std::string& ext); - struct Data { - Data(const QUrl& url, const Key& key, const char* data, size_t length) : - url(url), key(key), data(data), length(length) {} - const QUrl url; - const Key key; - const char* data; - size_t length; - }; - - KTXFilePointer writeFile(Data data); - KTXFilePointer getFile(const QUrl& url); + KTXFilePointer writeFile(const char* data, Metadata&& metadata); + KTXFilePointer getFile(const Key& key); protected: - std::unique_ptr createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; - std::unique_ptr loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) override final; - void evictedFile(const cache::FilePointer& file) override final; - -private: - std::unique_ptr createKTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url); - - using Mutex = std::mutex; - using Lock = std::lock_guard; - struct QUrlHasher { std::size_t operator()(QUrl const& url) const { return qHash(url); } }; - - std::unordered_map _urlMap; - Mutex _urlMutex; + std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) override final; }; class KTXFile : public cache::File { Q_OBJECT public: - QUrl getUrl() const { return _url; } std::unique_ptr getKTX() const; protected: - KTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) : - File(key, filepath, length), _url(url) {} - - std::string getMetadata() const override final; - -private: friend class KTXCache; - const QUrl _url; + KTXFile(Metadata&& metadata, const std::string& filepath); }; #endif // hifi_KTXCache_h diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 16d84b69df..b9977e33d8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -43,8 +43,8 @@ #include Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image") -Q_LOGGING_CATEGORY(trace_resource_parse_ktx, "trace.resource.parse.ktx") -Q_LOGGING_CATEGORY(trace_resource_cache_ktx, "trace.resource.cache.ktx") +Q_LOGGING_CATEGORY(trace_resource_parse_image_raw, "trace.resource.parse.image.raw") +Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.ktx") const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; @@ -296,34 +296,14 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) { - KTXFilePointer file = _ktxCache.getFile(url); const TextureExtra* textureExtra = static_cast(extra); auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE; - - NetworkTexture* texture; - if (file) { - texture = new NetworkTexture(url, type, file); - } else { - auto content = textureExtra ? textureExtra->content : QByteArray(); - auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; - texture = new NetworkTexture(url, type, content, maxNumPixels); - } - + auto content = textureExtra ? textureExtra->content : QByteArray(); + auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; + NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels); return QSharedPointer(texture, &Resource::deleter); } -NetworkTexture::NetworkTexture(const QUrl& url, Type type, const KTXFilePointer& file) : - Resource(url), - _type(type), - _file(file) { - _textureSource = std::make_shared(); - - if (file) { - _startedLoading = true; - QMetaObject::invokeMethod(this, "loadFile", Qt::QueuedConnection); - } -} - NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) : Resource(url), _type(type), @@ -342,12 +322,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& con } } -NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content) : - NetworkTexture(url, CUSTOM_TEXTURE, content, ABSOLUTE_MAX_TEXTURE_NUM_PIXELS) -{ - _textureLoader = textureLoader; -} - NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { if (_type == CUSTOM_TEXTURE) { return _textureLoader; @@ -387,30 +361,8 @@ gpu::TexturePointer NetworkTexture::getFallbackTexture() const { class Reader : public QRunnable { public: - Reader(const QWeakPointer& resource, const QUrl& url) : _resource(resource), _url(url) { - DependencyManager::get()->incrementStat("PendingProcessing"); - } - void run() override final { - DependencyManager::get()->decrementStat("PendingProcessing"); - CounterStat counter("Processing"); - - // Run this with low priority, then restore thread priority - auto originalPriority = QThread::currentThread()->priority(); - if (originalPriority == QThread::InheritPriority) { - originalPriority = QThread::NormalPriority; - } - QThread::currentThread()->setPriority(QThread::LowPriority); - Finally restorePriority([originalPriority]{ - QThread::currentThread()->setPriority(originalPriority); - }); - - if (!_resource.lock()) { // to ensure the resource is still needed - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - read(); - } + Reader(const QWeakPointer& resource, const QUrl& url); + void run() override final; virtual void read() = 0; protected: @@ -418,42 +370,83 @@ protected: QUrl _url; }; -class FileReader : public Reader { -public: - FileReader(const QWeakPointer& resource, const QUrl& url) : Reader(resource, url) {} - void read() override final; -}; - class ImageReader : public Reader { public: ImageReader(const QWeakPointer& resource, const QUrl& url, - const QByteArray& data, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + const QByteArray& data, const std::string& hash, int maxNumPixels); void read() override final; private: static void listSupportedImageFormats(); QByteArray _content; + std::string _hash; int _maxNumPixels; }; -void NetworkTexture::downloadFinished(const QByteArray& data) { - // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new ImageReader(_self, _url, data)); -} +class KTXReader : public Reader { +public: + KTXReader(const QWeakPointer& resource, const QUrl& url, const KTXFilePointer& ktxFile); + void read() override final; -void NetworkTexture::loadFile() { - QThreadPool::globalInstance()->start(new FileReader(_self, _url)); +private: + KTXFilePointer _file; +}; + +void NetworkTexture::downloadFinished(const QByteArray& data) { + loadContent(data); } void NetworkTexture::loadContent(const QByteArray& content) { - QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); + // Hash the source image to for KTX caching + std::string hash; + { + QCryptographicHash hasher(QCryptographicHash::Md5); + hasher.addData(content); + hash = hasher.result().toHex().toStdString(); + } + + std::unique_ptr reader; + KTXFilePointer ktxFile; + if (!_cache.isNull() && (ktxFile = static_cast(_cache.data())->_ktxCache.getFile(hash))) { + reader.reset(new KTXReader(_self, _url, ktxFile)); + } else { + reader.reset(new ImageReader(_self, _url, content, hash, _maxNumPixels)); + } + + QThreadPool::globalInstance()->start(reader.release()); +} + +Reader::Reader(const QWeakPointer& resource, const QUrl& url) : + _resource(resource), _url(url) { + DependencyManager::get()->incrementStat("PendingProcessing"); +} + +void Reader::run() { + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); + + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; + } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority]{ QThread::currentThread()->setPriority(originalPriority); }); + + if (!_resource.data()) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + return; + } + + read(); } ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, - const QByteArray& data, int maxNumPixels) : - Reader(resource, url), _content(data), _maxNumPixels(maxNumPixels) { + const QByteArray& data, const std::string& hash, int maxNumPixels) : + Reader(resource, url), _content(data), _hash(hash), _maxNumPixels(maxNumPixels) { listSupportedImageFormats(); + #if DEBUG_DUMP_TEXTURE_LOADS static auto start = usecTimestampNow() / USECS_PER_MSEC; auto now = usecTimestampNow() / USECS_PER_MSEC - start; @@ -482,36 +475,7 @@ void ImageReader::listSupportedImageFormats() { }); } -void FileReader::read() { - PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - - gpu::TexturePointer texture; - { - auto resource = _resource.lock(); // to ensure the resource is still needed - if (!resource) { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - auto ktx = resource.staticCast()->_file->getKTX(); - texture.reset(gpu::Texture::unserialize(ktx)); - texture->setKtxBacking(ktx); - } - - auto resource = _resource.lock(); // to ensure the resource is still needed - if (resource) { - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); - } else { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - } - -} - void ImageReader::read() { - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - // Help the QImage loader by extracting the image file format from the url filename ext. // Some tga are not created properly without it. auto filename = _url.fileName().toStdString(); @@ -541,7 +505,6 @@ void ImageReader::read() { QSize(imageWidth, imageHeight) << ")"; } - // Load the image into a gpu::Texture gpu::TexturePointer texture = nullptr; { auto resource = _resource.lock(); // to ensure the resource is still needed @@ -552,37 +515,22 @@ void ImageReader::read() { auto url = _url.toString().toStdString(); - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0); + PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0); + // Load the image into a gpu::Texture auto networkTexture = resource.staticCast(); texture.reset(networkTexture->getTextureLoader()(image, url)); texture->setSource(url); if (texture) { texture->setFallbackTexture(networkTexture->getFallbackTexture()); } - } - // Hash the source image to use as a filename for on-disk caching - std::string hash; - { - QCryptographicHash hasher(QCryptographicHash::Md5); - hasher.addData((const char*)image.bits(), image.byteCount() * sizeof(char)); - hash = hasher.result().toHex().toStdString(); - } - - { - auto resource = _resource.lock(); // to ensure the resource is still needed - if (!resource) { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - PROFILE_RANGE_EX(resource_cache_ktx, __FUNCTION__, 0xffffff00, 0); + // Save the image into a KTXFile auto ktx = gpu::Texture::serialize(*texture); const char* data = reinterpret_cast(ktx->_storage->data()); size_t length = ktx->_storage->size(); KTXFilePointer file; auto& ktxCache = DependencyManager::get()->_ktxCache; - if (!ktx || !(file = ktxCache.writeFile({ _url, hash, data, length }))) { + if (!ktx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { resource.staticCast()->_file = file; @@ -600,3 +548,35 @@ void ImageReader::read() { qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; } } + +KTXReader::KTXReader(const QWeakPointer& resource, const QUrl& url, + const KTXFilePointer& ktxFile) : + Reader(resource, url), _file(ktxFile) {} + +void KTXReader::read() { + PROFILE_RANGE_EX(resource_parse_image_ktx, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + gpu::TexturePointer texture; + { + auto resource = _resource.lock(); // to ensure the resource is still needed + if (!resource) { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + resource.staticCast()->_file = _file; + auto ktx = _file->getKTX(); + texture.reset(gpu::Texture::unserialize(ktx)); + texture->setKtxBacking(ktx); + } + + auto resource = _resource.lock(); // to ensure the resource is still needed + if (resource) { + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); + } else { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + } + +} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index ad628c6f2c..8ee3f59416 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -65,9 +65,7 @@ public: typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName); using TextureLoaderFunc = std::function; - NetworkTexture(const QUrl& url, Type type, const KTXFilePointer& file); NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels); - NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); QString getType() const override { return "NetworkTexture"; } @@ -89,11 +87,10 @@ protected: virtual void downloadFinished(const QByteArray& data) override; Q_INVOKABLE void loadContent(const QByteArray& content); - Q_INVOKABLE void loadFile(); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: - friend class FileReader; + friend class KTXReader; friend class ImageReader; Type _type; @@ -149,6 +146,7 @@ protected: private: friend class ImageReader; + friend class NetworkTexture; friend class DilatableNetworkTexture; TextureCache(); From 172a638ef0dac11f9587ddf8a83db579cdda1818 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 12 Mar 2017 20:44:17 -0400 Subject: [PATCH 093/106] move resource paths to PathUtils --- assignment-client/src/assets/AssetServer.cpp | 4 +-- assignment-client/src/octree/OctreeServer.cpp | 8 ++--- domain-server/src/DomainServer.cpp | 4 +-- .../src/model-networking/TextureCache.cpp | 2 -- libraries/networking/src/Assignment.cpp | 1 - libraries/networking/src/FileCache.cpp | 4 +-- libraries/shared/src/HifiConfigVariantMap.cpp | 6 ++-- libraries/shared/src/PathUtils.cpp | 18 +++++++++-- libraries/shared/src/PathUtils.h | 5 ++- libraries/shared/src/ServerPathUtils.cpp | 31 ------------------- libraries/shared/src/ServerPathUtils.h | 22 ------------- 11 files changed, 33 insertions(+), 72 deletions(-) delete mode 100644 libraries/shared/src/ServerPathUtils.cpp delete mode 100644 libraries/shared/src/ServerPathUtils.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 82dd23a9de..4cdeddb943 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include "NetworkLogging.h" #include "NodeType.h" @@ -162,7 +162,7 @@ void AssetServer::completeSetup() { if (assetsPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - absoluteFilePath = ServerPathUtils::getDataFilePath("assets/" + assetsPathString); + absoluteFilePath = PathUtils::getDataFilePath("assets/" + assetsPathString); } _resourcesDirectory = QDir(absoluteFilePath); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 2eee2ee229..d657fafaeb 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -29,7 +29,7 @@ #include "OctreeQueryNode.h" #include "OctreeServerConsts.h" #include -#include +#include #include int OctreeServer::_clientCount = 0; @@ -280,7 +280,7 @@ OctreeServer::~OctreeServer() { void OctreeServer::initHTTPManager(int port) { // setup the embedded web server - QString documentRoot = QString("%1/web").arg(ServerPathUtils::getDataDirectory()); + QString documentRoot = QString("%1/web").arg(PathUtils::getDataDirectory()); // setup an httpManager with us as the request handler and the parent _httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this); @@ -1179,7 +1179,7 @@ void OctreeServer::domainSettingsRequestComplete() { if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - persistAbsoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + persistAbsoluteFilePath = QDir(PathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; @@ -1245,7 +1245,7 @@ void OctreeServer::domainSettingsRequestComplete() { QDir backupDirectory { _backupDirectoryPath }; QString absoluteBackupDirectory; if (backupDirectory.isRelative()) { - absoluteBackupDirectory = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); + absoluteBackupDirectory = QDir(PathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath(); } else { absoluteBackupDirectory = backupDirectory.absolutePath(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c741c22b83..9b8d2842e6 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include "DomainServerNodeData.h" @@ -1618,7 +1618,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QDir pathForAssignmentScriptsDirectory() { static const QString SCRIPTS_DIRECTORY_NAME = "/scripts/"; - QDir directory(ServerPathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME); + QDir directory(PathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME); if (!directory.exists()) { directory.mkpath("."); qInfo() << "Created path to " << directory.path(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b9977e33d8..7c355ee3b2 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -35,8 +35,6 @@ #include #include -#include -#include #include "ModelNetworkingLogging.h" #include diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 9efad15398..27d4a31ccf 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -12,7 +12,6 @@ #include "udt/PacketHeaders.h" #include "SharedUtil.h" #include "UUID.h" -#include "ServerPathUtils.h" #include diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 78008562ed..8c71e6b632 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -18,7 +18,7 @@ #include -#include +#include Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) @@ -46,7 +46,7 @@ FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject QObject(parent), _ext(ext), _dirname(dirname), - _dirpath(ServerPathUtils::getDataFilePath(dirname.c_str()).toStdString()) {} + _dirpath(PathUtils::getDataFilePath(dirname.c_str()).toStdString()) {} FileCache::~FileCache() { clear(); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 5be6b2cd74..95fbb67942 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -21,7 +21,7 @@ #include #include -#include "ServerPathUtils.h" +#include "PathUtils.h" #include "SharedLogging.h" QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) { @@ -127,7 +127,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { _userConfigFilename = argumentList[userConfigIndex + 1]; } else { // we weren't passed a user config path - _userConfigFilename = ServerPathUtils::getDataFilePath(USER_CONFIG_FILE_NAME); + _userConfigFilename = PathUtils::getDataFilePath(USER_CONFIG_FILE_NAME); // as of 1/19/2016 this path was moved so we attempt a migration for first run post migration here @@ -153,7 +153,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { // we have the old file and not the new file - time to copy the file // make the destination directory if it doesn't exist - auto dataDirectory = ServerPathUtils::getDataDirectory(); + auto dataDirectory = PathUtils::getDataDirectory(); if (QDir().mkpath(dataDirectory)) { if (oldConfigFile.copy(_userConfigFilename)) { qCDebug(shared) << "Migrated config file from" << oldConfigFilename << "to" << _userConfigFilename; diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 265eaaa5b6..ee43749a87 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -30,11 +30,15 @@ const QString& PathUtils::resourcesPath() { return staticResourcePath; } -QString PathUtils::getRootDataDirectory() { +QString PathUtils::getRootDataDirectory(bool roaming) { auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); #ifdef Q_OS_WIN - dataPath += "/AppData/Roaming/"; + if (roaming) { + dataPath += "/AppData/Roaming/"; + } else { + dataPath += "/AppData/Local/"; + } #elif defined(Q_OS_OSX) dataPath += "/Library/Application Support/"; #else @@ -44,6 +48,16 @@ QString PathUtils::getRootDataDirectory() { return dataPath; } +QString PathUtils::getDataDirectory(bool roaming) { + auto dataPath = getRootDataDirectory(roaming); + dataPath += qApp->organizationName() + "/" + qApp->applicationName(); + return QDir::cleanPath(dataPath); +} + +QString PathUtils::getDataFilePath(QString filename, bool roaming) { + return QDir(getDataDirectory(roaming)).absoluteFilePath(filename); +} + QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { QString fileNameLowered = fileName.toLower(); foreach (const QString possibleExtension, possibleExtensions) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 1f7dcbe466..e9f1a7f78d 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -27,7 +27,10 @@ class PathUtils : public QObject, public Dependency { Q_PROPERTY(QString resources READ resourcesPath) public: static const QString& resourcesPath(); - static QString getRootDataDirectory(); + static QString getRootDataDirectory(bool roaming = true); + + static QString getDataDirectory(bool roaming = true); + static QString getDataFilePath(QString filename, bool roaming = true); static Qt::CaseSensitivity getFSCaseSensitivity(); static QString stripFilename(const QUrl& url); diff --git a/libraries/shared/src/ServerPathUtils.cpp b/libraries/shared/src/ServerPathUtils.cpp deleted file mode 100644 index cf52875c5f..0000000000 --- a/libraries/shared/src/ServerPathUtils.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// ServerPathUtils.cpp -// libraries/shared/src -// -// Created by Ryan Huffman on 01/12/16. -// Copyright 2016 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 "ServerPathUtils.h" - -#include -#include -#include -#include - -#include "PathUtils.h" - -QString ServerPathUtils::getDataDirectory() { - auto dataPath = PathUtils::getRootDataDirectory(); - - dataPath += qApp->organizationName() + "/" + qApp->applicationName(); - - return QDir::cleanPath(dataPath); -} - -QString ServerPathUtils::getDataFilePath(QString filename) { - return QDir(getDataDirectory()).absoluteFilePath(filename); -} - diff --git a/libraries/shared/src/ServerPathUtils.h b/libraries/shared/src/ServerPathUtils.h deleted file mode 100644 index 28a9a71f0d..0000000000 --- a/libraries/shared/src/ServerPathUtils.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// ServerPathUtils.h -// libraries/shared/src -// -// Created by Ryan Huffman on 01/12/16. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ServerPathUtils_h -#define hifi_ServerPathUtils_h - -#include - -namespace ServerPathUtils { - QString getDataDirectory(); - QString getDataFilePath(QString filename); -} - -#endif // hifi_ServerPathUtils_h \ No newline at end of file From 463f6454f887044648a780121a7845446ada0a97 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 12 Mar 2017 20:44:28 -0400 Subject: [PATCH 094/106] use Local instead of Roaming for FileCache --- libraries/networking/src/FileCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 8c71e6b632..82d3baa4e2 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -46,7 +46,7 @@ FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject QObject(parent), _ext(ext), _dirname(dirname), - _dirpath(PathUtils::getDataFilePath(dirname.c_str()).toStdString()) {} + _dirpath(PathUtils::getDataFilePath(dirname.c_str(), false).toStdString()) {} FileCache::~FileCache() { clear(); From 808973d7d25087341e9058f509ecfe8544243f68 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 13 Mar 2017 17:24:57 -0400 Subject: [PATCH 095/106] use QStandardPaths instead of hardcoded paths --- assignment-client/src/assets/AssetServer.cpp | 2 +- assignment-client/src/octree/OctreeServer.cpp | 7 ++-- domain-server/src/DomainServer.cpp | 2 +- interface/src/Application.cpp | 2 +- libraries/networking/src/FileCache.cpp | 2 +- libraries/shared/src/HifiConfigVariantMap.cpp | 4 +-- libraries/shared/src/PathUtils.cpp | 32 ++++++------------- libraries/shared/src/PathUtils.h | 8 +++-- 8 files changed, 24 insertions(+), 35 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 4cdeddb943..3886ff8d92 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -162,7 +162,7 @@ void AssetServer::completeSetup() { if (assetsPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - absoluteFilePath = PathUtils::getDataFilePath("assets/" + assetsPathString); + absoluteFilePath = PathUtils::getAppDataFilePath("assets/" + assetsPathString); } _resourcesDirectory = QDir(absoluteFilePath); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index d657fafaeb..f2dbe5d1d2 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -279,8 +279,7 @@ OctreeServer::~OctreeServer() { void OctreeServer::initHTTPManager(int port) { // setup the embedded web server - - QString documentRoot = QString("%1/web").arg(PathUtils::getDataDirectory()); + QString documentRoot = QString("%1/web").arg(PathUtils::getAppDataPath()); // setup an httpManager with us as the request handler and the parent _httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this); @@ -1179,7 +1178,7 @@ void OctreeServer::domainSettingsRequestComplete() { if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - persistAbsoluteFilePath = QDir(PathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + persistAbsoluteFilePath = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; @@ -1245,7 +1244,7 @@ void OctreeServer::domainSettingsRequestComplete() { QDir backupDirectory { _backupDirectoryPath }; QString absoluteBackupDirectory; if (backupDirectory.isRelative()) { - absoluteBackupDirectory = QDir(PathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); + absoluteBackupDirectory = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath(); } else { absoluteBackupDirectory = backupDirectory.absolutePath(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 9b8d2842e6..620b11d8ad 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1618,7 +1618,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QDir pathForAssignmentScriptsDirectory() { static const QString SCRIPTS_DIRECTORY_NAME = "/scripts/"; - QDir directory(PathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME); + QDir directory(PathUtils::getAppDataPath() + SCRIPTS_DIRECTORY_NAME); if (!directory.exists()) { directory.mkpath("."); qInfo() << "Created path to " << directory.path(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f870bd9f83..1d1adcdf96 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1439,7 +1439,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo scriptEngines->loadScript(testScript, false); } else { // Get sandbox content set version, if available - auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; + auto acDirPath = PathUtils::getAppDataPath() + "../../" + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; auto contentVersionPath = acDirPath + "content-version.txt"; qCDebug(interfaceapp) << "Checking " << contentVersionPath << " for content version"; auto contentVersion = 0; diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 82d3baa4e2..f8a86903cb 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -46,7 +46,7 @@ FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject QObject(parent), _ext(ext), _dirname(dirname), - _dirpath(PathUtils::getDataFilePath(dirname.c_str(), false).toStdString()) {} + _dirpath(PathUtils::getAppLocalDataFilePath(dirname.c_str()).toStdString()) {} FileCache::~FileCache() { clear(); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 95fbb67942..d0fb14e104 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -127,7 +127,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { _userConfigFilename = argumentList[userConfigIndex + 1]; } else { // we weren't passed a user config path - _userConfigFilename = PathUtils::getDataFilePath(USER_CONFIG_FILE_NAME); + _userConfigFilename = PathUtils::getAppDataFilePath(USER_CONFIG_FILE_NAME); // as of 1/19/2016 this path was moved so we attempt a migration for first run post migration here @@ -153,7 +153,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { // we have the old file and not the new file - time to copy the file // make the destination directory if it doesn't exist - auto dataDirectory = PathUtils::getDataDirectory(); + auto dataDirectory = PathUtils::getAppDataPath(); if (QDir().mkpath(dataDirectory)) { if (oldConfigFile.copy(_userConfigFilename)) { qCDebug(shared) << "Migrated config file from" << oldConfigFilename << "to" << _userConfigFilename; diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index ee43749a87..6e3acc5e99 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -30,32 +30,20 @@ const QString& PathUtils::resourcesPath() { return staticResourcePath; } -QString PathUtils::getRootDataDirectory(bool roaming) { - auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); - -#ifdef Q_OS_WIN - if (roaming) { - dataPath += "/AppData/Roaming/"; - } else { - dataPath += "/AppData/Local/"; - } -#elif defined(Q_OS_OSX) - dataPath += "/Library/Application Support/"; -#else - dataPath += "/.local/share/"; -#endif - - return dataPath; +QString PathUtils::getAppDataPath() { + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/"; } -QString PathUtils::getDataDirectory(bool roaming) { - auto dataPath = getRootDataDirectory(roaming); - dataPath += qApp->organizationName() + "/" + qApp->applicationName(); - return QDir::cleanPath(dataPath); +QString PathUtils::getAppLocalDataPath() { + return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/"; } -QString PathUtils::getDataFilePath(QString filename, bool roaming) { - return QDir(getDataDirectory(roaming)).absoluteFilePath(filename); +QString PathUtils::getAppDataFilePath(const QString& filename) { + return QDir(getAppDataPath()).absoluteFilePath(filename); +} + +QString PathUtils::getAppLocalDataFilePath(const QString& filename) { + return QDir(getAppLocalDataPath()).absoluteFilePath(filename); } QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index e9f1a7f78d..a7af44221c 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -27,10 +27,12 @@ class PathUtils : public QObject, public Dependency { Q_PROPERTY(QString resources READ resourcesPath) public: static const QString& resourcesPath(); - static QString getRootDataDirectory(bool roaming = true); - static QString getDataDirectory(bool roaming = true); - static QString getDataFilePath(QString filename, bool roaming = true); + static QString getAppDataPath(); + static QString getAppLocalDataPath(); + + static QString getAppDataFilePath(const QString& filename); + static QString getAppLocalDataFilePath(const QString& filename); static Qt::CaseSensitivity getFSCaseSensitivity(); static QString stripFilename(const QUrl& url); From 1ccf715e3b8a4378926c423166772e53aeab9177 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 14 Mar 2017 09:09:14 -0700 Subject: [PATCH 096/106] Don't crash when failing to convert a texture to KTX format --- .../src/model-networking/TextureCache.cpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b9977e33d8..386e9239a4 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -526,16 +526,20 @@ void ImageReader::read() { // Save the image into a KTXFile auto ktx = gpu::Texture::serialize(*texture); - const char* data = reinterpret_cast(ktx->_storage->data()); - size_t length = ktx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = DependencyManager::get()->_ktxCache; - if (!ktx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { - qCWarning(modelnetworking) << _url << "file cache failed"; + if (ktx) { + const char* data = reinterpret_cast(ktx->_storage->data()); + size_t length = ktx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = DependencyManager::get()->_ktxCache; + if (!ktx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { + qCWarning(modelnetworking) << _url << "file cache failed"; + } else { + resource.staticCast()->_file = file; + auto ktx = file->getKTX(); + texture->setKtxBacking(ktx); + } } else { - resource.staticCast()->_file = file; - auto ktx = file->getKTX(); - texture->setKtxBacking(ktx); + qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; } } From 5e716f15b544a7fe2bb308e339c072a48b61168a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 14 Mar 2017 13:48:24 -0700 Subject: [PATCH 097/106] Protect against missing texture cache on shutdown --- .../src/model-networking/TextureCache.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 85513f038a..e51b016904 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -189,9 +189,13 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const } gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) { - auto textureCache = DependencyManager::get(); - gpu::TexturePointer result; + auto textureCache = DependencyManager::get(); + // Since this can be called on a background thread, there's a chance that the cache + // will be destroyed by the time we request it + if (!textureCache) { + return result; + } switch (type) { case NetworkTexture::DEFAULT_TEXTURE: case NetworkTexture::ALBEDO_TEXTURE: From 900615fcd5ac071eed64046c16c3f496786390d1 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 14 Mar 2017 15:53:29 -0700 Subject: [PATCH 098/106] Fix the unsupported ktx format, it was coming from the BUmp map case --- libraries/model/src/model/TextureMap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index e41b5d0454..d07eae2166 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -354,7 +354,6 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm const double pStrength = 2.0; int width = image.width(); int height = image.height(); - // QImage result(width, height, QImage::Format_RGB888); QImage result(width, height, QImage::Format_ARGB32); From 813e3a1bcaa147db3f236373f58287390a736cb8 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 16 Mar 2017 20:47:56 -0700 Subject: [PATCH 099/106] Fix issue with scaling prior to KTX serialization --- libraries/fbx/src/FBXReader.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index fcaef90527..52a51daac3 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1468,6 +1468,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // Create the Material Library consolidateFBXMaterials(mapping); + // We can't scale allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image + // Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key +#if 0 // HACK: until we get proper LOD management we're going to cap model textures // according to how many unique textures the model uses: // 1 - 8 textures --> 2048 @@ -1481,6 +1484,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS int numTextures = uniqueTextures.size(); const int MAX_NUM_TEXTURES_AT_MAX_RESOLUTION = 8; int maxWidth = sqrt(MAX_NUM_PIXELS_FOR_FBX_TEXTURE); + if (numTextures > MAX_NUM_TEXTURES_AT_MAX_RESOLUTION) { int numTextureThreshold = MAX_NUM_TEXTURES_AT_MAX_RESOLUTION; const int MIN_MIP_TEXTURE_WIDTH = 64; @@ -1494,7 +1498,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS material.setMaxNumPixelsPerTexture(maxWidth * maxWidth); } } - +#endif geometry.materials = _fbxMaterials; // see if any materials have texture children From 14c8c3f4db766ad3f7f09a3955bfea565cd8406c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 20 Mar 2017 09:39:29 -0700 Subject: [PATCH 100/106] Return existing textures when matching hash for a previously found live texture --- .../src/model-networking/KTXCache.cpp | 5 +- .../src/model-networking/TextureCache.cpp | 140 ++++++++++-------- .../src/model-networking/TextureCache.h | 7 + libraries/shared/src/shared/Storage.cpp | 21 ++- libraries/shared/src/shared/Storage.h | 5 + 5 files changed, 112 insertions(+), 66 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 908d3245ce..63d35fe4a4 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -40,5 +40,8 @@ KTXFile::KTXFile(Metadata&& metadata, const std::string& filepath) : std::unique_ptr KTXFile::getKTX() const { ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); - return ktx::KTX::create(storage); + if (*storage) { + return ktx::KTX::create(storage); + } + return {}; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e51b016904..5dfaddd471 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -188,6 +188,35 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const return ResourceCache::getResource(url, QUrl(), &extra).staticCast(); } +gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) { + std::weak_ptr weakPointer; + { + std::unique_lock lock(_texturesByHashesMutex); + weakPointer = _texturesByHashes[hash]; + } + auto result = weakPointer.lock(); + if (result) { + qCWarning(modelnetworking) << "QQQ Returning live texture for hash " << hash.c_str(); + } + return result; +} + +gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) { + gpu::TexturePointer result; + { + std::unique_lock lock(_texturesByHashesMutex); + result = _texturesByHashes[hash].lock(); + if (!result) { + _texturesByHashes[hash] = texture; + result = texture; + } else { + qCWarning(modelnetworking) << "QQQ Swapping out texture with previous live texture in hash " << hash.c_str(); + } + } + return result; +} + + gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) { gpu::TexturePointer result; auto textureCache = DependencyManager::get(); @@ -386,15 +415,6 @@ private: int _maxNumPixels; }; -class KTXReader : public Reader { -public: - KTXReader(const QWeakPointer& resource, const QUrl& url, const KTXFilePointer& ktxFile); - void read() override final; - -private: - KTXFilePointer _file; -}; - void NetworkTexture::downloadFinished(const QByteArray& data) { loadContent(data); } @@ -408,15 +428,39 @@ void NetworkTexture::loadContent(const QByteArray& content) { hash = hasher.result().toHex().toStdString(); } - std::unique_ptr reader; - KTXFilePointer ktxFile; - if (!_cache.isNull() && (ktxFile = static_cast(_cache.data())->_ktxCache.getFile(hash))) { - reader.reset(new KTXReader(_self, _url, ktxFile)); - } else { - reader.reset(new ImageReader(_self, _url, content, hash, _maxNumPixels)); + auto textureCache = static_cast(_cache.data()); + + if (textureCache != nullptr) { + // If we already have a live texture with the same hash, use it + auto texture = textureCache->getTextureByHash(hash); + + // If there is no live texture, check if there's an existing KTX file + if (!texture) { + KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); + if (ktxFile) { + // Ensure that the KTX deserialization worked + auto ktx = ktxFile->getKTX(); + if (ktx) { + texture.reset(gpu::Texture::unserialize(ktx)); + // Ensure that the texture population worked + if (texture) { + texture->setKtxBacking(ktx); + texture = textureCache->cacheTextureByHash(hash, texture); + } + } + } + } + + // If we found the texture either because it's in use or via KTX deserialization, + // set the image and return immediately. + if (texture) { + setImage(texture, texture->getWidth(), texture->getHeight()); + return; + } } - QThreadPool::globalInstance()->start(reader.release()); + // We failed to find an existing live or KTX texture, so trigger an image reader + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, hash, _maxNumPixels)); } Reader::Reader(const QWeakPointer& resource, const QUrl& url) : @@ -526,22 +570,34 @@ void ImageReader::read() { texture->setFallbackTexture(networkTexture->getFallbackTexture()); } + auto textureCache = DependencyManager::get(); // Save the image into a KTXFile - auto ktx = gpu::Texture::serialize(*texture); - if (ktx) { - const char* data = reinterpret_cast(ktx->_storage->data()); - size_t length = ktx->_storage->size(); + auto memKtx = gpu::Texture::serialize(*texture); + if (!memKtx) { + qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; + } + + if (memKtx && textureCache) { + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); KTXFilePointer file; - auto& ktxCache = DependencyManager::get()->_ktxCache; - if (!ktx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { resource.staticCast()->_file = file; - auto ktx = file->getKTX(); - texture->setKtxBacking(ktx); + auto fileKtx = file->getKTX(); + if (fileKtx) { + texture->setKtxBacking(fileKtx); + } } - } else { - qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; + } + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + if (textureCache) { + texture = textureCache->cacheTextureByHash(_hash, texture); } } @@ -554,35 +610,3 @@ void ImageReader::read() { qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; } } - -KTXReader::KTXReader(const QWeakPointer& resource, const QUrl& url, - const KTXFilePointer& ktxFile) : - Reader(resource, url), _file(ktxFile) {} - -void KTXReader::read() { - PROFILE_RANGE_EX(resource_parse_image_ktx, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - - gpu::TexturePointer texture; - { - auto resource = _resource.lock(); // to ensure the resource is still needed - if (!resource) { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - resource.staticCast()->_file = _file; - auto ktx = _file->getKTX(); - texture.reset(gpu::Texture::unserialize(ktx)); - texture->setKtxBacking(ktx); - } - - auto resource = _resource.lock(); // to ensure the resource is still needed - if (resource) { - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); - } else { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - } - -} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 8ee3f59416..6005cc1226 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -137,6 +137,10 @@ public: NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE, const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + + gpu::TexturePointer getTextureByHash(const std::string& hash); + gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); + protected: // Overload ResourceCache::prefetch to allow specifying texture type for loads Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); @@ -155,6 +159,9 @@ private: static const std::string KTX_DIRNAME; static const std::string KTX_EXT; KTXCache _ktxCache; + // Map from image hashes to texture weak pointers + std::unordered_map> _texturesByHashes; + std::mutex _texturesByHashesMutex; gpu::TexturePointer _permutationNormalTexture; gpu::TexturePointer _whiteTexture; diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 6754854c40..3c46347a49 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -8,7 +8,11 @@ #include "Storage.h" -#include +#include +#include +#include + +Q_LOGGING_CATEGORY(storagelogging, "hifi.core.storage") using namespace storage; @@ -64,12 +68,15 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u } FileStorage::FileStorage(const QString& filename) : _file(filename) { - if (!_file.open(QFile::ReadOnly)) { - throw std::runtime_error("Unable to open file"); - } - _mapped = _file.map(0, _file.size()); - if (!_mapped) { - throw std::runtime_error("Unable to map file"); + if (_file.open(QFile::ReadOnly)) { + _mapped = _file.map(0, _file.size()); + if (_mapped) { + _valid = true; + } else { + qCWarning(storagelogging) << "Failed to map file " << filename; + } + } else { + qCWarning(storagelogging) << "Failed to open file " << filename; } } diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 4b97e14178..306984040f 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -25,6 +25,7 @@ namespace storage { virtual ~Storage() {} virtual const uint8_t* data() const = 0; virtual size_t size() const = 0; + virtual operator bool() const { return true; } StoragePointer createView(size_t size = 0, size_t offset = 0) const; StoragePointer toFileStorage(const QString& filename) const; @@ -41,6 +42,7 @@ namespace storage { const uint8_t* data() const override { return _data.data(); } uint8_t* data() { return _data.data(); } size_t size() const override { return _data.size(); } + operator bool() const override { return true; } private: std::vector _data; }; @@ -56,7 +58,9 @@ namespace storage { const uint8_t* data() const override { return _mapped; } size_t size() const override { return _file.size(); } + operator bool() const override { return _valid; } private: + bool _valid { false }; QFile _file; uint8_t* _mapped { nullptr }; }; @@ -66,6 +70,7 @@ namespace storage { ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); const uint8_t* data() const override { return _data; } size_t size() const override { return _size; } + operator bool() const override { return *_owner; } private: const storage::StoragePointer _owner; const size_t _size; From a3828ce45c508fc115adc14cd9e1aa02a049760d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 21 Mar 2017 13:42:24 -0700 Subject: [PATCH 101/106] Fixing comment --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 52a51daac3..d5236558a4 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1468,7 +1468,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // Create the Material Library consolidateFBXMaterials(mapping); - // We can't scale allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image + // We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image // Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key #if 0 // HACK: until we get proper LOD management we're going to cap model textures From e60108cc8c5dd21ec08ce2b68d0f1c23b8f5860f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 21 Mar 2017 16:57:53 -0700 Subject: [PATCH 102/106] PR comments --- libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 6 +++--- libraries/ktx/src/ktx/KTX.cpp | 2 +- libraries/ktx/src/ktx/Reader.cpp | 8 ++++---- libraries/ktx/src/ktx/Writer.cpp | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index c7fb496fc0..0212e1845f 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -163,6 +163,7 @@ class Texture : public Resource { static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); public: + static const uint32_t CUBE_FACE_COUNT { 6 }; static uint32_t getTextureCPUCount(); static Size getTextureCPUMemoryUsage(); static uint32_t getTextureGPUCount(); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 5fed4d82bc..5f0ededee7 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -113,7 +113,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } else { header.setCube(texture.getWidth(), texture.getHeight()); } - numFaces = 6; + numFaces = Texture::CUBE_FACE_COUNT; break; } default: @@ -130,9 +130,9 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { if (numFaces == 1) { images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); } else { - ktx::Image::FaceBytes cubeFaces(6); + ktx::Image::FaceBytes cubeFaces(Texture::CUBE_FACE_COUNT); cubeFaces[0] = mip->readData(); - for (int face = 1; face < 6; face++) { + for (uint32_t face = 1; face < Texture::CUBE_FACE_COUNT; face++) { cubeFaces[face] = texture.accessStoredMipFace(level, face)->readData(); } images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, cubeFaces)); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index f9bfb377f5..bbd4e1bc86 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -63,7 +63,7 @@ size_t Header::evalFaceSize(uint32_t level) const { } size_t Header::evalImageSize(uint32_t level) const { auto faceSize = evalFaceSize(level); - if (numberOfFaces == 6 && numberOfArrayElements == 0) { + if (numberOfFaces == NUM_CUBEMAPFACES && numberOfArrayElements == 0) { return faceSize; } else { return (getNumberOfSlices() * numberOfFaces * faceSize); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 5aa6919844..277ce42e69 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -151,10 +151,10 @@ namespace ktx { if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { auto padding = Header::evalPadding(imageSize); - if (numFaces == 6) { - size_t faceSize = imageSize / 6; - Image::FaceBytes faces(6); - for (uint32_t face = 0; face < 6; face++) { + if (numFaces == NUM_CUBEMAPFACES) { + size_t faceSize = imageSize / NUM_CUBEMAPFACES; + Image::FaceBytes faces(NUM_CUBEMAPFACES); + for (uint32_t face = 0; face < NUM_CUBEMAPFACES; face++) { faces[face] = currentPtr; currentPtr += faceSize; } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 06fba326d5..25b363d31b 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -149,9 +149,9 @@ namespace ktx { destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize; } else { - Image::FaceBytes faceBytes(6); + Image::FaceBytes faceBytes(NUM_CUBEMAPFACES); auto faceSize = srcImages[l]._faceSize; - for (int face = 0; face < 6; face++) { + for (int face = 0; face < NUM_CUBEMAPFACES; face++) { memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); faceBytes[face] = currentPtr; currentPtr += faceSize; From 9a3686fc1a3a53d2c0255e1dd2b57df2f5fa2db5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 14:55:11 -0700 Subject: [PATCH 103/106] Fixing merge conflict --- libraries/gpu/src/gpu/Texture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 872a78fc9c..1f66b2900e 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -762,7 +762,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { PROFILE_RANGE(render_gpu, "ProcessFace"); - auto mipFormat = cubeTexture.accessStoredMipFace(0, face)->getFormat(); + auto mipFormat = cubeTexture.getStoredMipFormat(); auto numComponents = mipFormat.getScalarCount(); int roffset { 0 }; int goffset { 1 }; From 893fcba283093f8a5b1af7db5dd18e3af2dfed3b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 17:14:40 -0700 Subject: [PATCH 104/106] Return the correct format and internal format combo for depth textures --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index fa639aab11..7e26e65e02 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -274,8 +274,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::DEPTH: + texel.format = GL_DEPTH_COMPONENT; texel.internalFormat = GL_DEPTH_COMPONENT32; break; + case gpu::DEPTH_STENCIL: texel.type = GL_UNSIGNED_INT_24_8; texel.format = GL_DEPTH_STENCIL; From 4868a1618a751c66da58ed2c376a276a8ff3ecc6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 18:10:54 -0700 Subject: [PATCH 105/106] Add initialization to sampler stamp --- libraries/gpu/src/gpu/Texture.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 0212e1845f..f7297b3280 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -536,12 +536,12 @@ protected: std::string _source; std::unique_ptr< Storage > _storage; - Stamp _stamp = 0; + Stamp _stamp { 0 }; Sampler _sampler; - Stamp _samplerStamp; + Stamp _samplerStamp { 0 }; - uint32 _size = 0; + uint32 _size { 0 }; Element _texelFormat; uint16 _width { 1 }; @@ -554,7 +554,7 @@ protected: uint16 _maxMip { 0 }; uint16 _minMip { 0 }; - Type _type = TEX_1D; + Type _type { TEX_1D }; Usage _usage; From 3a40fd886f8062a39258e199dd565a7538747f15 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 18:11:20 -0700 Subject: [PATCH 106/106] Fix comparison between GL stamps and GPU stamps --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 84806d82c3..8dbef09f06 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -53,12 +53,12 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { // FIXME internalize to GL41Texture 'sync' function if (object->isOutdated()) { object->withPreservedTexture([&] { - if (object->_contentStamp < texture.getDataStamp()) { + if (object->_contentStamp <= texture.getDataStamp()) { // FIXME implement synchronous texture transfer here object->syncContent(); } - if (object->_samplerStamp < texture.getSamplerStamp()) { + if (object->_samplerStamp <= texture.getSamplerStamp()) { object->syncSampler(); } });