diff --git a/libraries/ktx/src/khronos/KHR.h b/libraries/ktx/src/khronos/KHR.h index 7b3e3453c7..d710ca7b40 100644 --- a/libraries/ktx/src/khronos/KHR.h +++ b/libraries/ktx/src/khronos/KHR.h @@ -209,6 +209,44 @@ namespace khronos { COMPRESSED_SIGNED_RG11_EAC = 0x9273, }; + template + inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) { + // FIXME add static assert that ALIGNMENT is a power of 2 + return (value + (ALIGNMENT - 1) / ALIGNMENT); + } + + inline uint8_t evalBlockAlignemnt(InternalFormat format, uint32_t value) { + switch (format) { + case InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT: // BC1 + case InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // BC1A + case InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC3 + case InternalFormat::COMPRESSED_RED_RGTC1: // BC4 + case InternalFormat::COMPRESSED_RG_RGTC2: // BC5 + case InternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM: // BC7 + return evalAlignedCompressedBlockCount<4>(value); + + default: + throw std::runtime_error("Unknown format"); + } + } + + inline uint8_t evalCompressedBlockSize(InternalFormat format) { + switch (format) { + case InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT: + case InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + case InternalFormat::COMPRESSED_RED_RGTC1: + return 8; + + case InternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM: + case InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + case InternalFormat::COMPRESSED_RG_RGTC2: + return 16; + + default: + return 0; + } + } + enum class BaseInternalFormat : uint32_t { // GL 4.4 Table 8.11 DEPTH_COMPONENT = 0x1902, @@ -220,19 +258,46 @@ namespace khronos { STENCIL_INDEX = 0x1901, }; - enum CubeMapFace { - POS_X = 0, - NEG_X = 1, - POS_Y = 2, - NEG_Y = 3, - POS_Z = 4, - NEG_Z = 5, - NUM_CUBEMAPFACES = 6, - }; + inline uint8_t evalComponentCount(BaseInternalFormat format) { + switch (format) { + case BaseInternalFormat::DEPTH_COMPONENT: + case BaseInternalFormat::STENCIL_INDEX: + case BaseInternalFormat::RED: + return 1; + + case BaseInternalFormat::DEPTH_STENCIL: + case BaseInternalFormat::RG: + return 2; + + case BaseInternalFormat::RGB: + return 3; + + case BaseInternalFormat::RGBA: + return 4; + + default: + break; + } + + return 0; + } + + namespace cubemap { + enum Constants { + NUM_CUBEMAPFACES = 6, + }; + + enum class Face { + POSITIVE_X = 0x8515, + NEGATIVE_X = 0x8516, + POSITIVE_Y = 0x8517, + NEGATIVE_Y = 0x8518, + POSITIVE_Z = 0x8519, + NEGATIVE_Z = 0x851A, + }; + } } - } - } #endif // khronos_khr_hpp diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 1900c4a4b1..d6faee4cc7 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -28,70 +28,45 @@ uint32_t Header::evalMaxDimension() const { return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); } -uint32_t Header::evalPixelOrBlockWidth(uint32_t level) const { - auto pixelWidth = std::max(getPixelWidth() >> level, 1U); +uint32_t Header::evalPixelOrBlockDimension(uint32_t pixelDimension) const { if (isCompressed()) { - return evalAlignedCount(pixelWidth); - } else { - return pixelWidth; - } + return khronos::gl::texture::evalBlockAlignemnt(getGLInternaFormat(), pixelDimension); + } + return pixelDimension; } + +uint32_t Header::evalMipPixelOrBlockDimension(uint32_t mipLevel, uint32_t pixelDimension) const { + uint32_t mipPixelDimension = evalMipDimension(mipLevel, pixelDimension); + return evalPixelOrBlockDimension(mipPixelDimension); +} + +uint32_t Header::evalPixelOrBlockWidth(uint32_t level) const { + return evalMipPixelOrBlockDimension(level, getPixelWidth()); +} + uint32_t Header::evalPixelOrBlockHeight(uint32_t level) const { - auto pixelWidth = std::max(getPixelHeight() >> level, 1U); - if (glType == COMPRESSED_TYPE) { - auto format = getGLInternaFormat(); - switch (format) { - case GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT: // BC1 - case GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // BC1A - case GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC3 - case GLInternalFormat::COMPRESSED_RED_RGTC1: // BC4 - case GLInternalFormat::COMPRESSED_RG_RGTC2: // BC5 - case GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM: // BC7 - return evalAlignedCount(pixelWidth); - default: - throw std::runtime_error("Unknown format"); - } - } else { - return pixelWidth; - } + return evalMipPixelOrBlockDimension(level, getPixelHeight()); } + uint32_t Header::evalPixelOrBlockDepth(uint32_t level) const { - return std::max(getPixelDepth() >> level, 1U); + return evalMipDimension(level, getPixelDepth()); } size_t Header::evalPixelOrBlockSize() const { + size_t result = 0; if (isCompressed()) { auto format = getGLInternaFormat(); - switch (format) { - case GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT: - case GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: - case GLInternalFormat::COMPRESSED_RED_RGTC1: - return 8; - case GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM: - case GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: - case GLInternalFormat::COMPRESSED_RG_RGTC2: - return 16; - default: - break; - } + result = khronos::gl::texture::evalCompressedBlockSize(format); } else { + // FIXME should really be using the internal format, not the base internal format auto baseFormat = getGLBaseInternalFormat(); - switch (baseFormat) { - case GLBaseInternalFormat::RED: - return 1; - case GLBaseInternalFormat::RG: - return 2; - case GLBaseInternalFormat::RGB: - return 3; - case GLBaseInternalFormat::RGBA: - return 4; - default: - break; - } + result = khronos::gl::texture::evalComponentCount(baseFormat); } - qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; - return 0; + if (0 == result) { + qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; + } + return result; } size_t Header::evalRowSize(uint32_t level) const { @@ -100,7 +75,7 @@ size_t Header::evalRowSize(uint32_t level) const { if (pixSize == 0) { return 0; } - return evalPadded(pixWidth * pixSize); + return evalPaddedSize(pixWidth * pixSize); } size_t Header::evalFaceSize(uint32_t level) const { @@ -184,7 +159,7 @@ KeyValue::KeyValue(const std::string& key, const std::string& value) : } uint32_t KeyValue::serializedByteSize() const { - return (uint32_t)sizeof(uint32_t) + evalPadded(_byteSize); + return (uint32_t)sizeof(uint32_t) + evalPaddedSize(_byteSize); } uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index c6570f87c6..b02e2ada75 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -82,7 +82,7 @@ namespace ktx { // Alignment constants static const uint32_t ALIGNMENT { sizeof(uint32_t) }; static const uint32_t ALIGNMENT_REMAINDER { ALIGNMENT - 1 }; - static const uint32_t NUM_CUBEMAPFACES = khronos::gl::texture::CubeMapFace::NUM_CUBEMAPFACES; + static const uint32_t NUM_CUBEMAPFACES = khronos::gl::texture::cubemap::NUM_CUBEMAPFACES; // FIXME move out of this header, not specific to ktx const std::string HIFI_MIN_POPULATED_MIP_KEY { "hifi.minMip" }; @@ -94,7 +94,6 @@ namespace ktx { using GLFormat = khronos::gl::texture::Format; using GLInternalFormat = khronos::gl::texture::InternalFormat; using GLBaseInternalFormat = khronos::gl::texture::BaseInternalFormat; - using CubeMapFace = khronos::gl::texture::CubeMapFace; using Storage = storage::Storage; using StoragePointer = std::shared_ptr; @@ -112,7 +111,7 @@ namespace ktx { // Returns the passed value rounded up to the next 4 byte aligned value, if it's not already 4 byte aligned template - inline T evalPadded(T value) { + inline T evalPaddedSize(T value) { return (value + ALIGNMENT_REMAINDER) & ~(T)ALIGNMENT_REMAINDER; } @@ -123,7 +122,7 @@ namespace ktx { template inline bool checkAlignment(T value) { - return ((value & 0x3) == 0); + return ((value & ALIGNMENT_REMAINDER) == 0); } @@ -216,6 +215,13 @@ namespace ktx { ImageDescriptors generateImageDescriptors() const; private: + uint32_t evalPixelOrBlockDimension(uint32_t pixelDimension) const; + uint32_t evalMipPixelOrBlockDimension(uint32_t level, uint32_t pixelDimension) const; + + static inline uint32_t evalMipDimension(uint32_t mipLevel, uint32_t pixelDimension) { + return std::max(pixelDimension >> mipLevel, 1U); + } + 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/ktx/src/ktx/Validation.cpp b/libraries/ktx/src/ktx/Validation.cpp index a59dde92f1..c54a259ab1 100644 --- a/libraries/ktx/src/ktx/Validation.cpp +++ b/libraries/ktx/src/ktx/Validation.cpp @@ -281,9 +281,7 @@ struct AlignedStreamBuffer { } bool skip(size_t skipSize) { - if ((skipSize % 4) != 0) { - skipSize += (3 - ((skipSize + 3) % 4)); - } + skipSize = ktx::evalPaddedSize(skipSize); if (skipSize > _size) { return false; } @@ -323,7 +321,7 @@ bool validateKeyValueData(AlignedStreamBuffer kvbuffer) { } bool KTX::validate(const StoragePointer& src) { - if ((src->size() % 4) != 0) { + if (!checkAlignment(src->size())) { // All KTX data is 4-byte aligned qDebug() << "Invalid size, not 4 byte aligned"; return false; @@ -363,17 +361,14 @@ bool KTX::validate(const StoragePointer& src) { return false; } - if (header.numberOfArrayElements == 0 && header.numberOfFaces == 6) { - for (uint8_t face = 0; face < NUM_CUBEMAPFACES; ++face) { + uint32_t arrayElements = header.numberOfArrayElements == 0 ? 1 : header.numberOfArrayElements; + for (uint32_t arrayElement = 0; arrayElement < arrayElements; ++arrayElement) { + for (uint8_t face = 0; face < header.numberOfFaces; ++face) { if (!buffer.skip(imageSize)) { - qDebug() << "Unable to skip past cubemap data"; + qDebug() << "Unable to skip past image data"; return false; } } - } else { - if (!buffer.skip(imageSize)) { - return false; - } } } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index e18a3f1adb..c94856e598 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -70,7 +70,7 @@ namespace ktx { for (uint32_t l = 0; l < numMips; l++) { if (images.size() > l) { storageSize += sizeof(uint32_t); - storageSize += evalPadded(images[l]._imageSize); + storageSize += evalPaddedSize(images[l]._imageSize); } } return storageSize; @@ -88,7 +88,7 @@ namespace ktx { for (uint32_t l = 0; l < numMips; l++) { if (imageDescriptors.size() > l) { storageSize += sizeof(uint32_t); - storageSize += evalPadded(imageDescriptors[l]._imageSize); + storageSize += evalPaddedSize(imageDescriptors[l]._imageSize); } } return storageSize; diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index 282c537ed2..3b62b89948 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -96,13 +96,13 @@ int main(int argc, char** argv) { Q_ASSERT(ktx::evalPadding(4) == 0); Q_ASSERT(ktx::evalPadding(1024) == 0); Q_ASSERT(ktx::evalPadding(1025) == 3); - Q_ASSERT(ktx::evalPadded(0) == 0); - Q_ASSERT(ktx::evalPadded(1) == 4); - Q_ASSERT(ktx::evalPadded(2) == 4); - Q_ASSERT(ktx::evalPadded(3) == 4); - Q_ASSERT(ktx::evalPadded(4) == 4); - Q_ASSERT(ktx::evalPadded(1024) == 1024); - Q_ASSERT(ktx::evalPadded(1025) == 1028); + Q_ASSERT(ktx::evalPaddedSize(0) == 0); + Q_ASSERT(ktx::evalPaddedSize(1) == 4); + Q_ASSERT(ktx::evalPaddedSize(2) == 4); + Q_ASSERT(ktx::evalPaddedSize(3) == 4); + Q_ASSERT(ktx::evalPaddedSize(4) == 4); + Q_ASSERT(ktx::evalPaddedSize(1024) == 1024); + Q_ASSERT(ktx::evalPaddedSize(1025) == 1028); Q_ASSERT(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); DependencyManager::set();