From 191b633081e1c8a89d475c4c820f7fdba10d9f3a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 15 May 2017 10:23:08 -0700 Subject: [PATCH] Refactor KTX defines and validation --- libraries/gpu/src/gpu/Texture_ktx.cpp | 50 ++-- libraries/ktx/src/khronos/KHR.h | 238 +++++++++++++++++++ libraries/ktx/src/ktx/KTX.cpp | 98 ++++---- libraries/ktx/src/ktx/KTX.h | 327 +++++++------------------- libraries/ktx/src/ktx/Reader.cpp | 4 +- libraries/ktx/src/ktx/Validation.cpp | 265 +++++++++++++-------- libraries/ktx/src/ktx/Writer.cpp | 8 +- tests/ktx/src/main.cpp | 24 +- 8 files changed, 570 insertions(+), 444 deletions(-) create mode 100644 libraries/ktx/src/khronos/KHR.h diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 5f677d7424..f455fde009 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -510,29 +510,29 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe 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); + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat::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); + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat::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); + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat::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); + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat::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); + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat::R8, ktx::GLBaseInternalFormat::RED); } else if (texelFormat == Format::VEC2NU8_XY && mipFormat == Format::VEC2NU8_XY) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RG, ktx::GLInternalFormat_Uncompressed::RG8, ktx::GLBaseInternalFormat::RG); + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RG, ktx::GLInternalFormat::RG8, ktx::GLBaseInternalFormat::RG); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) { - header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB); + header.setCompressed(ktx::GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_MASK && mipFormat == Format::COLOR_COMPRESSED_SRGBA_MASK) { - header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGBA); + header.setCompressed(ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA && mipFormat == Format::COLOR_COMPRESSED_SRGBA) { - header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, ktx::GLBaseInternalFormat::RGBA); + header.setCompressed(ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_COMPRESSED_RED && mipFormat == Format::COLOR_COMPRESSED_RED) { - header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1, ktx::GLBaseInternalFormat::RED); + header.setCompressed(ktx::GLInternalFormat::COMPRESSED_RED_RGTC1, ktx::GLBaseInternalFormat::RED); } else if (texelFormat == Format::COLOR_COMPRESSED_XY && mipFormat == Format::COLOR_COMPRESSED_XY) { - header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2, ktx::GLBaseInternalFormat::RG); + header.setCompressed(ktx::GLInternalFormat::COMPRESSED_RG_RGTC2, ktx::GLBaseInternalFormat::RG); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_HIGH && mipFormat == Format::COLOR_COMPRESSED_SRGBA_HIGH) { - header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM, ktx::GLBaseInternalFormat::RGBA); + header.setCompressed(ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM, ktx::GLBaseInternalFormat::RGBA); } else { return false; } @@ -542,20 +542,20 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat 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) { + if (header.getGLInternaFormat() == ktx::GLInternalFormat::RGBA8) { mipFormat = Format::COLOR_BGRA_32; texelFormat = Format::COLOR_RGBA_32; - } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::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) { + if (header.getGLInternaFormat() == ktx::GLInternalFormat::RGBA8) { mipFormat = Format::COLOR_RGBA_32; texelFormat = Format::COLOR_RGBA_32; - } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::SRGB8_ALPHA8) { mipFormat = Format::COLOR_SRGBA_32; texelFormat = Format::COLOR_SRGBA_32; } else { @@ -563,35 +563,35 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E } } 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) { + if (header.getGLInternaFormat() == ktx::GLInternalFormat::R8) { texelFormat = Format::COLOR_R_8; } else { return false; } } else if (header.getGLFormat() == ktx::GLFormat::RG && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { mipFormat = Format::VEC2NU8_XY; - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RG8) { + if (header.getGLInternaFormat() == ktx::GLInternalFormat::RG8) { texelFormat = Format::VEC2NU8_XY; } else { return false; } - } else if (header.getGLFormat() == ktx::GLFormat::COMPRESSED_FORMAT && header.getGLType() == ktx::GLType::COMPRESSED_TYPE) { - if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT) { + } else if (header.isCompressed()) { + if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT) { mipFormat = Format::COLOR_COMPRESSED_SRGB; texelFormat = Format::COLOR_COMPRESSED_SRGB; - } else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) { + } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) { mipFormat = Format::COLOR_COMPRESSED_SRGBA_MASK; texelFormat = Format::COLOR_COMPRESSED_SRGBA_MASK; - } else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) { + } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) { mipFormat = Format::COLOR_COMPRESSED_SRGBA; texelFormat = Format::COLOR_COMPRESSED_SRGBA; - } else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1) { + } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RED_RGTC1) { mipFormat = Format::COLOR_COMPRESSED_RED; texelFormat = Format::COLOR_COMPRESSED_RED; - } else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) { + } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RG_RGTC2) { mipFormat = Format::COLOR_COMPRESSED_XY; texelFormat = Format::COLOR_COMPRESSED_XY; - } else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) { + } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) { mipFormat = Format::COLOR_COMPRESSED_SRGBA_HIGH; texelFormat = Format::COLOR_COMPRESSED_SRGBA_HIGH; } else { diff --git a/libraries/ktx/src/khronos/KHR.h b/libraries/ktx/src/khronos/KHR.h new file mode 100644 index 0000000000..7b3e3453c7 --- /dev/null +++ b/libraries/ktx/src/khronos/KHR.h @@ -0,0 +1,238 @@ +// +// Created by Bradley Austin Davis on 2017/05/13 +// 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 khronos_khr_hpp +#define khronos_khr_hpp + +namespace khronos { + + namespace gl { + + enum class Type : uint32_t { + // 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, + }; + + namespace texture { + + enum class Format : 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, + }; + + enum class InternalFormat : 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, + + // 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_ETC1_RGB8_OES = 0x8D64, + + COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C, + COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D, + COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E, + COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F, + + 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, + }; + + enum class BaseInternalFormat : uint32_t { + // GL 4.4 Table 8.11 + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, + RED = 0x1903, + RG = 0x8227, + RGB = 0x1907, + RGBA = 0x1908, + 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, + }; + } + + } + +} + +#endif // khronos_khr_hpp diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index c366daf7ed..1900c4a4b1 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -16,16 +16,6 @@ using namespace ktx; -uint32_t Header::evalPadding(size_t byteSize) { - //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); -} - -bool Header::checkAlignment(size_t byteSize) { - return ((byteSize & 0x3) == 0); -} - const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; @@ -40,24 +30,24 @@ uint32_t Header::evalMaxDimension() const { uint32_t Header::evalPixelOrBlockWidth(uint32_t level) const { auto pixelWidth = std::max(getPixelWidth() >> level, 1U); - if (getGLType() == GLType::COMPRESSED_TYPE) { - return (pixelWidth + 3) / 4; + if (isCompressed()) { + return evalAlignedCount(pixelWidth); } else { return pixelWidth; } } uint32_t Header::evalPixelOrBlockHeight(uint32_t level) const { auto pixelWidth = std::max(getPixelHeight() >> level, 1U); - if (getGLType() == GLType::COMPRESSED_TYPE) { - auto format = getGLInternaFormat_Compressed(); + if (glType == COMPRESSED_TYPE) { + auto format = getGLInternaFormat(); switch (format) { - case GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT: // BC1 - case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // BC1A - case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC3 - case GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1: // BC4 - case GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2: // BC5 - case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM: // BC7 - return (pixelWidth + 3) / 4; + 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"); } @@ -70,31 +60,33 @@ uint32_t Header::evalPixelOrBlockDepth(uint32_t level) const { } size_t Header::evalPixelOrBlockSize() const { - if (getGLType() == GLType::COMPRESSED_TYPE) { - auto format = getGLInternaFormat_Compressed(); - if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT) { - return 8; - } else if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) { - return 8; - } else if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) { - return 16; - } else if (format == GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1) { - return 8; - } else if (format == GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) { - return 16; - } else if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) { - return 16; + 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; } } else { auto baseFormat = getGLBaseInternalFormat(); - if (baseFormat == GLBaseInternalFormat::RED) { - return 1; - } else if (baseFormat == GLBaseInternalFormat::RG) { - return 2; - } else if (baseFormat == GLBaseInternalFormat::RGB) { - return 3; - } else if (baseFormat == GLBaseInternalFormat::RGBA) { - return 4; + switch (baseFormat) { + case GLBaseInternalFormat::RED: + return 1; + case GLBaseInternalFormat::RG: + return 2; + case GLBaseInternalFormat::RGB: + return 3; + case GLBaseInternalFormat::RGBA: + return 4; + default: + break; } } @@ -108,16 +100,16 @@ size_t Header::evalRowSize(uint32_t level) const { if (pixSize == 0) { return 0; } - auto netSize = pixWidth * pixSize; - auto padding = evalPadding(netSize); - return netSize + padding; + return evalPadded(pixWidth * pixSize); } + size_t Header::evalFaceSize(uint32_t level) const { auto pixHeight = evalPixelOrBlockHeight(level); auto pixDepth = evalPixelOrBlockDepth(level); auto rowSize = evalRowSize(level); return pixDepth * pixHeight * rowSize; } + size_t Header::evalImageSize(uint32_t level) const { auto faceSize = evalFaceSize(level); if (!checkAlignment(faceSize)) { @@ -192,7 +184,7 @@ KeyValue::KeyValue(const std::string& key, const std::string& value) : } uint32_t KeyValue::serializedByteSize() const { - return (uint32_t) (sizeof(uint32_t) + _byteSize + Header::evalPadding(_byteSize)); + return (uint32_t)sizeof(uint32_t) + evalPadded(_byteSize); } uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { @@ -200,14 +192,8 @@ uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { for (auto& keyval : keyValues) { keyValuesSize += keyval.serializedByteSize(); } - return (keyValuesSize + Header::evalPadding(keyValuesSize)); -} - - -KTX::KTX() { -} - -KTX::~KTX() { + Q_ASSERT(keyValuesSize % 4 == 0); + return keyValuesSize; } void KTX::resetStorage(const StoragePointer& storage) { @@ -230,7 +216,7 @@ size_t KTX::getTexelsDataSize() const { if (!_storage) { return 0; } - return (_storage->data() + _storage->size()) - getTexelsData(); + return _storage->size() - sizeof(Header) - getKeyValueDataSize(); } const Byte* KTX::getKeyValueData() const { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 6dc8e01131..c6570f87c6 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -22,7 +22,14 @@ #include -/* KTX Spec: +#include "../khronos/KHR.h" + +/* + +KTX Specification: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + + +**** A KTX header is 64 bytes layed out as follows Byte[12] identifier UInt32 endianness @@ -38,6 +45,8 @@ UInt32 numberOfArrayElements UInt32 numberOfFaces UInt32 numberOfMipmapLevels UInt32 bytesOfKeyValueData + +**** Each KTX key value pair block is 4 byte aligned for each keyValuePair that fits in bytesOfKeyValueData UInt32 keyAndValueByteSize @@ -45,6 +54,8 @@ for each keyValuePair that fits in bytesOfKeyValueData Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)] end +**** Each mip and cube face is 4 byte aligned + for each mipmap_level in numberOfMipmapLevels* UInt32 imageSize; for each array_element in numberOfArrayElements* @@ -67,231 +78,23 @@ end ** Uncompressed texture data matches a GL_UNPACK_ALIGNMENT of 4. */ - - namespace ktx { - const uint32_t PACKING_SIZE { sizeof(uint32_t) }; - const std::string HIFI_MIN_POPULATED_MIP_KEY{ "hifi.minMip" }; + // 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; + + // FIXME move out of this header, not specific to ktx + const std::string HIFI_MIN_POPULATED_MIP_KEY { "hifi.minMip" }; + using Byte = uint8_t; - enum class 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, - }; - - enum class 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, - }; - - enum class 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, - }; - - enum class 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_ETC1_RGB8_OES = 0x8D64, - - COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C, - COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D, - COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E, - COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F, - - 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, - }; - - enum class GLBaseInternalFormat : uint32_t { - // GL 4.4 Table 8.11 - DEPTH_COMPONENT = 0x1902, - DEPTH_STENCIL = 0x84F9, - RED = 0x1903, - RG = 0x8227, - RGB = 0x1907, - RGBA = 0x1908, - 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, - }; + using GLType = khronos::gl::Type; + 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; @@ -301,18 +104,41 @@ namespace ktx { bool checkIdentifier(const Byte* identifier); + // Returns the number of bytes required be added to the passed value to make it 4 byte aligned + template + inline uint8_t evalPadding(T value) { + return ALIGNMENT_REMAINDER - ((value + ALIGNMENT_REMAINDER) % ALIGNMENT); + } + + // 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) { + return (value + ALIGNMENT_REMAINDER) & ~(T)ALIGNMENT_REMAINDER; + } + + template + inline T evalAlignedCount(T value) { + return (value + ALIGNMENT_REMAINDER) / ALIGNMENT; + } + + template + inline bool checkAlignment(T value) { + return ((value & 0x3) == 0); + } + + // Header struct Header { - static const size_t IDENTIFIER_LENGTH = 12; + static const uint32_t COMPRESSED_FORMAT { 0 }; + static const uint32_t COMPRESSED_TYPE { 0 }; + static const uint32_t COMPRESSED_TYPE_SIZE { 1 }; + 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; - static uint32_t evalPadding(size_t byteSize); - static bool checkAlignment(size_t byteSize); - Header(); Byte identifier[IDENTIFIER_LENGTH]; @@ -321,7 +147,7 @@ namespace ktx { uint32_t glType { static_cast(GLType::UNSIGNED_BYTE) }; uint32_t glTypeSize { 0 }; uint32_t glFormat { static_cast(GLFormat::RGBA) }; - uint32_t glInternalFormat { static_cast(GLInternalFormat_Uncompressed::RGBA8) }; + uint32_t glInternalFormat { static_cast(GLInternalFormat::RGBA8) }; uint32_t glBaseInternalFormat { static_cast(GLBaseInternalFormat::RGBA) }; uint32_t pixelWidth { 1 }; @@ -338,6 +164,7 @@ namespace ktx { uint32_t getPixelDepth() const { return (pixelDepth ? pixelDepth : 1); } uint32_t getNumberOfSlices() const { return (numberOfArrayElements ? numberOfArrayElements : 1); } uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } + bool isCompressed() const { return glFormat == COMPRESSED_FORMAT; } uint32_t evalMaxDimension() const; uint32_t evalPixelOrBlockWidth(uint32_t level) const; @@ -349,17 +176,21 @@ 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) { + // FIXME base internal format should automatically be determined by internal format + // FIXME type size should automatically be determined by type + void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat internalFormat, GLBaseInternalFormat baseInternalFormat) { glType = (uint32_t) type; glTypeSize = typeSize; 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; + + // FIXME base internal format should automatically be determined by internal format + void setCompressed(GLInternalFormat internalFormat, GLBaseInternalFormat baseInternalFormat) { + glType = COMPRESSED_TYPE; + glFormat = COMPRESSED_FORMAT; + glTypeSize = COMPRESSED_TYPE_SIZE; glInternalFormat = (uint32_t) internalFormat; glBaseInternalFormat = (uint32_t) baseInternalFormat; } @@ -367,18 +198,9 @@ namespace ktx { 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; } + GLInternalFormat getGLInternaFormat() const { return (GLInternalFormat)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; - 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); } @@ -392,12 +214,22 @@ namespace ktx { // Generate a set of image descriptors based on the assumption that the full mip pyramid is populated ImageDescriptors generateImageDescriptors() const; + + private: + 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; + } }; - static const size_t KTX_HEADER_SIZE = 64; + // Size as specified by the KTX specification + static const size_t KTX_HEADER_SIZE { 64 }; static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static and should not change from the spec"); - static const size_t KV_SIZE_WIDTH = 4; // Number of bytes for keyAndValueByteSize - static const size_t IMAGE_SIZE_WIDTH = 4; // Number of bytes for imageSize + static const size_t KV_SIZE_WIDTH { ALIGNMENT }; // Number of bytes for keyAndValueByteSize + static const size_t IMAGE_SIZE_WIDTH { ALIGNMENT }; // Number of bytes for imageSize // Key Values struct KeyValue { @@ -488,11 +320,9 @@ namespace ktx { class KTX { void resetStorage(const StoragePointer& src); - KTX(); + KTX() {} KTX(const StoragePointer& storage, const Header& header, const KeyValues& keyValues, const Images& images); public: - ~KTX(); - static bool validate(const StoragePointer& src); // Define a KTX object manually to write it somewhere (in a file on disk?) @@ -539,6 +369,7 @@ namespace ktx { KTXDescriptor toDescriptor() const; size_t getKeyValueDataSize() const; size_t getTexelsDataSize() const; + bool isValid() const; Header _header; StoragePointer _storage; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 1b63af5262..cfd9111ee3 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -151,7 +151,7 @@ namespace ktx { auto expectedImageSize = header.evalImageSize((uint32_t) images.size()); if (imageSize != expectedImageSize) { break; - } else if (!Header::checkAlignment(imageSize)) { + } else if (!checkAlignment(imageSize)) { break; } @@ -163,7 +163,7 @@ namespace ktx { // If enough data ahead then capture the pointer if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { - auto padding = Header::evalPadding(imageSize); + auto padding = evalPadding(imageSize); if (numFaces == NUM_CUBEMAPFACES) { Image::FaceBytes faces(NUM_CUBEMAPFACES); diff --git a/libraries/ktx/src/ktx/Validation.cpp b/libraries/ktx/src/ktx/Validation.cpp index 74af58d311..a59dde92f1 100644 --- a/libraries/ktx/src/ktx/Validation.cpp +++ b/libraries/ktx/src/ktx/Validation.cpp @@ -63,111 +63,111 @@ static const std::unordered_set VALID_GL_FORMATS { }; static const std::unordered_set VALID_GL_INTERNAL_FORMATS { - (uint32_t)GLInternalFormat_Uncompressed::R8, - (uint32_t)GLInternalFormat_Uncompressed::R8_SNORM, - (uint32_t)GLInternalFormat_Uncompressed::R16, - (uint32_t)GLInternalFormat_Uncompressed::R16_SNORM, - (uint32_t)GLInternalFormat_Uncompressed::RG8, - (uint32_t)GLInternalFormat_Uncompressed::RG8_SNORM, - (uint32_t)GLInternalFormat_Uncompressed::RG16, - (uint32_t)GLInternalFormat_Uncompressed::RG16_SNORM, - (uint32_t)GLInternalFormat_Uncompressed::R3_G3_B2, - (uint32_t)GLInternalFormat_Uncompressed::RGB4, - (uint32_t)GLInternalFormat_Uncompressed::RGB5, - (uint32_t)GLInternalFormat_Uncompressed::RGB565, - (uint32_t)GLInternalFormat_Uncompressed::RGB8, - (uint32_t)GLInternalFormat_Uncompressed::RGB8_SNORM, - (uint32_t)GLInternalFormat_Uncompressed::RGB10, - (uint32_t)GLInternalFormat_Uncompressed::RGB12, - (uint32_t)GLInternalFormat_Uncompressed::RGB16, - (uint32_t)GLInternalFormat_Uncompressed::RGB16_SNORM, - (uint32_t)GLInternalFormat_Uncompressed::RGBA2, - (uint32_t)GLInternalFormat_Uncompressed::RGBA4, - (uint32_t)GLInternalFormat_Uncompressed::RGB5_A1, - (uint32_t)GLInternalFormat_Uncompressed::RGBA8, - (uint32_t)GLInternalFormat_Uncompressed::RGBA8_SNORM, - (uint32_t)GLInternalFormat_Uncompressed::RGB10_A2, - (uint32_t)GLInternalFormat_Uncompressed::RGB10_A2UI, - (uint32_t)GLInternalFormat_Uncompressed::RGBA12, - (uint32_t)GLInternalFormat_Uncompressed::RGBA16, - (uint32_t)GLInternalFormat_Uncompressed::RGBA16_SNORM, - (uint32_t)GLInternalFormat_Uncompressed::SRGB8, - (uint32_t)GLInternalFormat_Uncompressed::SRGB8_ALPHA8, - (uint32_t)GLInternalFormat_Uncompressed::R16F, - (uint32_t)GLInternalFormat_Uncompressed::RG16F, - (uint32_t)GLInternalFormat_Uncompressed::RGB16F, - (uint32_t)GLInternalFormat_Uncompressed::RGBA16F, - (uint32_t)GLInternalFormat_Uncompressed::R32F, - (uint32_t)GLInternalFormat_Uncompressed::RG32F, - (uint32_t)GLInternalFormat_Uncompressed::RGBA32F, - (uint32_t)GLInternalFormat_Uncompressed::R11F_G11F_B10F, - (uint32_t)GLInternalFormat_Uncompressed::RGB9_E5, - (uint32_t)GLInternalFormat_Uncompressed::R8I, - (uint32_t)GLInternalFormat_Uncompressed::R8UI, - (uint32_t)GLInternalFormat_Uncompressed::R16I, - (uint32_t)GLInternalFormat_Uncompressed::R16UI, - (uint32_t)GLInternalFormat_Uncompressed::R32I, - (uint32_t)GLInternalFormat_Uncompressed::R32UI, - (uint32_t)GLInternalFormat_Uncompressed::RG8I, - (uint32_t)GLInternalFormat_Uncompressed::RG8UI, - (uint32_t)GLInternalFormat_Uncompressed::RG16I, - (uint32_t)GLInternalFormat_Uncompressed::RG16UI, - (uint32_t)GLInternalFormat_Uncompressed::RG32I, - (uint32_t)GLInternalFormat_Uncompressed::RG32UI, - (uint32_t)GLInternalFormat_Uncompressed::RGB8I, - (uint32_t)GLInternalFormat_Uncompressed::RGB8UI, - (uint32_t)GLInternalFormat_Uncompressed::RGB16I, - (uint32_t)GLInternalFormat_Uncompressed::RGB16UI, - (uint32_t)GLInternalFormat_Uncompressed::RGB32I, - (uint32_t)GLInternalFormat_Uncompressed::RGB32UI, - (uint32_t)GLInternalFormat_Uncompressed::RGBA8I, - (uint32_t)GLInternalFormat_Uncompressed::RGBA8UI, - (uint32_t)GLInternalFormat_Uncompressed::RGBA16I, - (uint32_t)GLInternalFormat_Uncompressed::RGBA16UI, - (uint32_t)GLInternalFormat_Uncompressed::RGBA32I, - (uint32_t)GLInternalFormat_Uncompressed::RGBA32UI, - (uint32_t)GLInternalFormat_Uncompressed::DEPTH_COMPONENT16, - (uint32_t)GLInternalFormat_Uncompressed::DEPTH_COMPONENT24, - (uint32_t)GLInternalFormat_Uncompressed::DEPTH_COMPONENT32, - (uint32_t)GLInternalFormat_Uncompressed::DEPTH_COMPONENT32F, - (uint32_t)GLInternalFormat_Uncompressed::DEPTH24_STENCIL8, - (uint32_t)GLInternalFormat_Uncompressed::DEPTH32F_STENCIL8, - (uint32_t)GLInternalFormat_Uncompressed::STENCIL_INDEX1, - (uint32_t)GLInternalFormat_Uncompressed::STENCIL_INDEX4, - (uint32_t)GLInternalFormat_Uncompressed::STENCIL_INDEX8, - (uint32_t)GLInternalFormat_Uncompressed::STENCIL_INDEX16, + (uint32_t)GLInternalFormat::R8, + (uint32_t)GLInternalFormat::R8_SNORM, + (uint32_t)GLInternalFormat::R16, + (uint32_t)GLInternalFormat::R16_SNORM, + (uint32_t)GLInternalFormat::RG8, + (uint32_t)GLInternalFormat::RG8_SNORM, + (uint32_t)GLInternalFormat::RG16, + (uint32_t)GLInternalFormat::RG16_SNORM, + (uint32_t)GLInternalFormat::R3_G3_B2, + (uint32_t)GLInternalFormat::RGB4, + (uint32_t)GLInternalFormat::RGB5, + (uint32_t)GLInternalFormat::RGB565, + (uint32_t)GLInternalFormat::RGB8, + (uint32_t)GLInternalFormat::RGB8_SNORM, + (uint32_t)GLInternalFormat::RGB10, + (uint32_t)GLInternalFormat::RGB12, + (uint32_t)GLInternalFormat::RGB16, + (uint32_t)GLInternalFormat::RGB16_SNORM, + (uint32_t)GLInternalFormat::RGBA2, + (uint32_t)GLInternalFormat::RGBA4, + (uint32_t)GLInternalFormat::RGB5_A1, + (uint32_t)GLInternalFormat::RGBA8, + (uint32_t)GLInternalFormat::RGBA8_SNORM, + (uint32_t)GLInternalFormat::RGB10_A2, + (uint32_t)GLInternalFormat::RGB10_A2UI, + (uint32_t)GLInternalFormat::RGBA12, + (uint32_t)GLInternalFormat::RGBA16, + (uint32_t)GLInternalFormat::RGBA16_SNORM, + (uint32_t)GLInternalFormat::SRGB8, + (uint32_t)GLInternalFormat::SRGB8_ALPHA8, + (uint32_t)GLInternalFormat::R16F, + (uint32_t)GLInternalFormat::RG16F, + (uint32_t)GLInternalFormat::RGB16F, + (uint32_t)GLInternalFormat::RGBA16F, + (uint32_t)GLInternalFormat::R32F, + (uint32_t)GLInternalFormat::RG32F, + (uint32_t)GLInternalFormat::RGBA32F, + (uint32_t)GLInternalFormat::R11F_G11F_B10F, + (uint32_t)GLInternalFormat::RGB9_E5, + (uint32_t)GLInternalFormat::R8I, + (uint32_t)GLInternalFormat::R8UI, + (uint32_t)GLInternalFormat::R16I, + (uint32_t)GLInternalFormat::R16UI, + (uint32_t)GLInternalFormat::R32I, + (uint32_t)GLInternalFormat::R32UI, + (uint32_t)GLInternalFormat::RG8I, + (uint32_t)GLInternalFormat::RG8UI, + (uint32_t)GLInternalFormat::RG16I, + (uint32_t)GLInternalFormat::RG16UI, + (uint32_t)GLInternalFormat::RG32I, + (uint32_t)GLInternalFormat::RG32UI, + (uint32_t)GLInternalFormat::RGB8I, + (uint32_t)GLInternalFormat::RGB8UI, + (uint32_t)GLInternalFormat::RGB16I, + (uint32_t)GLInternalFormat::RGB16UI, + (uint32_t)GLInternalFormat::RGB32I, + (uint32_t)GLInternalFormat::RGB32UI, + (uint32_t)GLInternalFormat::RGBA8I, + (uint32_t)GLInternalFormat::RGBA8UI, + (uint32_t)GLInternalFormat::RGBA16I, + (uint32_t)GLInternalFormat::RGBA16UI, + (uint32_t)GLInternalFormat::RGBA32I, + (uint32_t)GLInternalFormat::RGBA32UI, + (uint32_t)GLInternalFormat::DEPTH_COMPONENT16, + (uint32_t)GLInternalFormat::DEPTH_COMPONENT24, + (uint32_t)GLInternalFormat::DEPTH_COMPONENT32, + (uint32_t)GLInternalFormat::DEPTH_COMPONENT32F, + (uint32_t)GLInternalFormat::DEPTH24_STENCIL8, + (uint32_t)GLInternalFormat::DEPTH32F_STENCIL8, + (uint32_t)GLInternalFormat::STENCIL_INDEX1, + (uint32_t)GLInternalFormat::STENCIL_INDEX4, + (uint32_t)GLInternalFormat::STENCIL_INDEX8, + (uint32_t)GLInternalFormat::STENCIL_INDEX16, }; static const std::unordered_set VALID_GL_INTERNAL_COMPRESSED_FORMATS { - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RED, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RG, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGBA, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_ETC1_RGB8_OES, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SIGNED_RED_RGTC1, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SIGNED_RG_RGTC2, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGBA_BPTC_UNORM, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB_BPTC_SIGNED_FLOAT, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB8_ETC2, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB8_ETC2, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGBA8_ETC2_EAC, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_R11_EAC, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SIGNED_R11_EAC, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_RG11_EAC, - (uint32_t)GLInternalFormat_Compressed::COMPRESSED_SIGNED_RG11_EAC, + (uint32_t)GLInternalFormat::COMPRESSED_RED, + (uint32_t)GLInternalFormat::COMPRESSED_RG, + (uint32_t)GLInternalFormat::COMPRESSED_RGB, + (uint32_t)GLInternalFormat::COMPRESSED_RGBA, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB_ALPHA, + (uint32_t)GLInternalFormat::COMPRESSED_ETC1_RGB8_OES, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, + (uint32_t)GLInternalFormat::COMPRESSED_RED_RGTC1, + (uint32_t)GLInternalFormat::COMPRESSED_SIGNED_RED_RGTC1, + (uint32_t)GLInternalFormat::COMPRESSED_RG_RGTC2, + (uint32_t)GLInternalFormat::COMPRESSED_SIGNED_RG_RGTC2, + (uint32_t)GLInternalFormat::COMPRESSED_RGBA_BPTC_UNORM, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM, + (uint32_t)GLInternalFormat::COMPRESSED_RGB_BPTC_SIGNED_FLOAT, + (uint32_t)GLInternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, + (uint32_t)GLInternalFormat::COMPRESSED_RGB8_ETC2, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB8_ETC2, + (uint32_t)GLInternalFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, + (uint32_t)GLInternalFormat::COMPRESSED_RGBA8_ETC2_EAC, + (uint32_t)GLInternalFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, + (uint32_t)GLInternalFormat::COMPRESSED_R11_EAC, + (uint32_t)GLInternalFormat::COMPRESSED_SIGNED_R11_EAC, + (uint32_t)GLInternalFormat::COMPRESSED_RG11_EAC, + (uint32_t)GLInternalFormat::COMPRESSED_SIGNED_RG11_EAC, }; static const std::unordered_set VALID_GL_BASE_INTERNAL_FORMATS { @@ -199,7 +199,17 @@ bool Header::isValid() const { return false; } - if (glFormat == (uint32_t)GLFormat::COMPRESSED_FORMAT && glType == (uint32_t)GLType::COMPRESSED_TYPE) { + if (isCompressed()) { + if (glType != COMPRESSED_TYPE) { + qDebug("Invalid type for compressed texture 0x%x", glType); + return false; + } + + if (glTypeSize != COMPRESSED_TYPE_SIZE) { + qDebug("Invalid type size for compressed texture %d", glTypeSize); + return false; + } + if (VALID_GL_INTERNAL_COMPRESSED_FORMATS.count(glInternalFormat) != 1) { qDebug("Invalid compressed internal format 0x%x", glInternalFormat); return false; @@ -241,7 +251,7 @@ bool Header::isValid() const { // FIXME validate numberOfMipmapLevels based on the dimensions? if ((bytesOfKeyValueData % 4) != 0) { - qDebug() << "Invalid keyvalue data size " << numberOfFaces; + qDebug() << "Invalid keyvalue data size " << bytesOfKeyValueData; return false; } @@ -374,3 +384,50 @@ bool KTX::validate(const StoragePointer& src) { return true; } + + + +bool KTX::isValid() const { + if (!_header.isValid()) { + return false; + } + + if (_images.size() != _header.numberOfMipmapLevels) { + return false; + } + + const auto start = _storage->data(); + const auto end = start + _storage->size(); + + // FIXME, do key value checks? + + for (const auto& image : _images) { + if (image._numFaces != _header.numberOfFaces) { + return false; + } + + for (const auto& facePointer : image._faceBytes) { + if (facePointer + image._faceSize > end) { + return false; + } + } + } + + + for (uint8_t mip = 0; mip < _header.numberOfMipmapLevels; ++mip) { + for (uint8_t face = 0; face < _header.numberOfFaces; ++face) { + auto faceStorage = getMipFaceTexelsData(mip, face); + // The face start offset must be 4 byte aligned + if (!checkAlignment(faceStorage->data() - start)) { + return false; + } + + // The face size must be 4 byte aligned + if (!checkAlignment(faceStorage->size())) { + return false; + } + } + } + + return true; +} \ No newline at end of file diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 23f9d05596..e18a3f1adb 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -70,8 +70,7 @@ namespace ktx { 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); + storageSize += evalPadded(images[l]._imageSize); } } return storageSize; @@ -89,8 +88,7 @@ namespace ktx { for (uint32_t l = 0; l < numMips; l++) { if (imageDescriptors.size() > l) { storageSize += sizeof(uint32_t); - storageSize += imageDescriptors[l]._imageSize; - storageSize += Header::evalPadding(imageDescriptors[l]._imageSize); + storageSize += evalPadded(imageDescriptors[l]._imageSize); } } return storageSize; @@ -221,7 +219,7 @@ namespace ktx { // If enough data ahead then capture the copy source pointer if (currentDataSize + imageSize <= (allocatedImagesDataSize)) { - auto padding = Header::evalPadding(imageSize); + auto padding = evalPadding(imageSize); // Single face vs cubes if (srcImages[l]._numFaces == 1) { diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index 92b9091533..282c537ed2 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -88,6 +89,20 @@ int main(int argc, char** argv) { QCoreApplication::setOrganizationDomain("highfidelity.com"); logger.reset(new FileLogger()); + Q_ASSERT(ktx::evalPadding(0) == 0); + Q_ASSERT(ktx::evalPadding(1) == 3); + Q_ASSERT(ktx::evalPadding(2) == 2); + Q_ASSERT(ktx::evalPadding(3) == 1); + 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(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); DependencyManager::set(); @@ -100,8 +115,10 @@ int main(int argc, char** argv) { auto ktxMemory = gpu::Texture::serialize(*testTexture); { const auto& ktxStorage = ktxMemory->getStorage(); - QFile outFile(TEST_IMAGE_KTX); - if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { + Q_ASSERT_X(ktx::KTX::validate(ktxStorage), __FUNCTION__, "KTX storage validation failed"); + Q_ASSERT_X(ktxMemory->isValid(), __FUNCTION__, "KTX self-validation failed"); + QSaveFile outFile(TEST_IMAGE_KTX); + if (!outFile.open(QFile::WriteOnly)) { throw std::runtime_error("Unable to open file"); } auto ktxSize = ktxStorage->size(); @@ -109,7 +126,7 @@ int main(int argc, char** argv) { auto dest = outFile.map(0, ktxSize); memcpy(dest, ktxStorage->data(), ktxSize); outFile.unmap(dest); - outFile.close(); + outFile.commit(); } { @@ -169,7 +186,6 @@ int mainTemp(int, char**) { auto ktxFile = ktx::KTX::create(storage); ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor(); - assert(ktxFile->_keyValues == ktxDescriptor.keyValues); qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs"; for (const auto& kv : ktxDescriptor.keyValues) {