From 077e56a96fc98d0892485a25b1c94e2882ef9ff3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 13 May 2017 18:07:35 -0700 Subject: [PATCH] Add KTX validation routines --- libraries/ktx/src/ktx/KTX.h | 169 ++++++------ libraries/ktx/src/ktx/Validation.cpp | 376 +++++++++++++++++++++++++++ tests/ktx/src/main.cpp | 60 ++++- 3 files changed, 522 insertions(+), 83 deletions(-) create mode 100644 libraries/ktx/src/ktx/Validation.cpp diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 3f220abac3..6dc8e01131 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -180,105 +180,107 @@ namespace ktx { RGB16F = 0x881B, RGBA16F = 0x881A, - R32F = 0x822E, - RG32F = 0x8230, - RGB32F = 0x8815, - RGBA32F = 0x8814, + R32F = 0x822E, + RG32F = 0x8230, + RGB32F = 0x8815, + RGBA32F = 0x8814, - R11F_G11F_B10F = 0x8C3A, - RGB9_E5 = 0x8C3D, + 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, + 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, + RGB8I = 0x8D8F, + RGB8UI = 0x8D7D, + RGB16I = 0x8D89, + RGB16UI = 0x8D77, - RGB32I = 0x8D83, - RGB32UI = 0x8D71, - RGBA8I = 0x8D8E, - RGBA8UI = 0x8D7C, - RGBA16I = 0x8D88, - RGBA16UI = 0x8D76, - RGBA32I = 0x8D82, + RGB32I = 0x8D83, + RGB32UI = 0x8D71, + RGBA8I = 0x8D8E, + RGBA8UI = 0x8D7C, + RGBA16I = 0x8D88, + RGBA16UI = 0x8D76, + RGBA32I = 0x8D82, - RGBA32UI = 0x8D70, + RGBA32UI = 0x8D70, // GL 4.4 Table 8.13 - DEPTH_COMPONENT16 = 0x81A5, - DEPTH_COMPONENT24 = 0x81A6, - DEPTH_COMPONENT32 = 0x81A7, + DEPTH_COMPONENT16 = 0x81A5, + DEPTH_COMPONENT24 = 0x81A6, + DEPTH_COMPONENT32 = 0x81A7, - DEPTH_COMPONENT32F = 0x8CAC, - DEPTH24_STENCIL8 = 0x88F0, - DEPTH32F_STENCIL8 = 0x8CAD, + DEPTH_COMPONENT32F = 0x8CAC, + DEPTH24_STENCIL8 = 0x88F0, + DEPTH32F_STENCIL8 = 0x8CAD, - STENCIL_INDEX1 = 0x8D46, - STENCIL_INDEX4 = 0x8D47, - STENCIL_INDEX8 = 0x8D48, - STENCIL_INDEX16 = 0x8D49, + 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_RED = 0x8225, + COMPRESSED_RG = 0x8226, + COMPRESSED_RGB = 0x84ED, + COMPRESSED_RGBA = 0x84EE, - COMPRESSED_SRGB = 0x8C48, - COMPRESSED_SRGB_ALPHA = 0x8C49, + COMPRESSED_SRGB = 0x8C48, + COMPRESSED_SRGB_ALPHA = 0x8C49, - 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_ETC1_RGB8_OES = 0x8D64, - COMPRESSED_RED_RGTC1 = 0x8DBB, - COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC, - COMPRESSED_RG_RGTC2 = 0x8DBD, - COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE, + 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_RGBA_BPTC_UNORM = 0x8E8C, - COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D, - COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, - COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, + COMPRESSED_RED_RGTC1 = 0x8DBB, + COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC, + COMPRESSED_RG_RGTC2 = 0x8DBD, + COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE, - 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_RGBA_BPTC_UNORM = 0x8E8C, + COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D, + COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, + COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, - COMPRESSED_R11_EAC = 0x9270, - COMPRESSED_SIGNED_R11_EAC = 0x9271, - COMPRESSED_RG11_EAC = 0x9272, - COMPRESSED_SIGNED_RG11_EAC = 0x9273, + 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, + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, + RED = 0x1903, + RG = 0x8227, + RGB = 0x1907, + RGBA = 0x1908, + STENCIL_INDEX = 0x1901, }; enum CubeMapFace { @@ -316,14 +318,14 @@ namespace ktx { Byte identifier[IDENTIFIER_LENGTH]; uint32_t endianness { ENDIAN_TEST }; - uint32_t glType; + uint32_t glType { static_cast(GLType::UNSIGNED_BYTE) }; uint32_t glTypeSize { 0 }; - uint32_t glFormat; - uint32_t glInternalFormat; - uint32_t glBaseInternalFormat; + uint32_t glFormat { static_cast(GLFormat::RGBA) }; + uint32_t glInternalFormat { static_cast(GLInternalFormat_Uncompressed::RGBA8) }; + uint32_t glBaseInternalFormat { static_cast(GLBaseInternalFormat::RGBA) }; uint32_t pixelWidth { 1 }; - uint32_t pixelHeight { 0 }; + uint32_t pixelHeight { 1 }; uint32_t pixelDepth { 0 }; uint32_t numberOfArrayElements { 0 }; uint32_t numberOfFaces { 1 }; @@ -386,8 +388,12 @@ namespace ktx { 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); } + bool isValid() const; + + // Generate a set of image descriptors based on the assumption that the full mip pyramid is populated ImageDescriptors generateImageDescriptors() const; }; + 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 @@ -426,6 +432,7 @@ namespace ktx { const uint32_t _imageSize; const uint32_t _faceSize; const uint32_t _padding; + ImageHeader(bool cube, size_t imageOffset, uint32_t imageSize, uint32_t padding) : _numFaces(cube ? NUM_CUBEMAPFACES : 1), _imageOffset(imageOffset), @@ -486,6 +493,8 @@ namespace ktx { public: ~KTX(); + static bool validate(const StoragePointer& src); + // 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 diff --git a/libraries/ktx/src/ktx/Validation.cpp b/libraries/ktx/src/ktx/Validation.cpp new file mode 100644 index 0000000000..74af58d311 --- /dev/null +++ b/libraries/ktx/src/ktx/Validation.cpp @@ -0,0 +1,376 @@ +// +// 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 +// + +#include "KTX.h" + +#include +#include + +using namespace ktx; + +static const std::unordered_set VALID_GL_TYPES { + (uint32_t)GLType::UNSIGNED_BYTE, + (uint32_t)GLType::BYTE, + (uint32_t)GLType::UNSIGNED_SHORT, + (uint32_t)GLType::SHORT, + (uint32_t)GLType::UNSIGNED_INT, + (uint32_t)GLType::INT, + (uint32_t)GLType::HALF_FLOAT, + (uint32_t)GLType::FLOAT, + (uint32_t)GLType::UNSIGNED_BYTE_3_3_2, + (uint32_t)GLType::UNSIGNED_BYTE_2_3_3_REV, + (uint32_t)GLType::UNSIGNED_SHORT_5_6_5, + (uint32_t)GLType::UNSIGNED_SHORT_5_6_5_REV, + (uint32_t)GLType::UNSIGNED_SHORT_4_4_4_4, + (uint32_t)GLType::UNSIGNED_SHORT_4_4_4_4_REV, + (uint32_t)GLType::UNSIGNED_SHORT_5_5_5_1, + (uint32_t)GLType::UNSIGNED_SHORT_1_5_5_5_REV, + (uint32_t)GLType::UNSIGNED_INT_8_8_8_8, + (uint32_t)GLType::UNSIGNED_INT_8_8_8_8_REV, + (uint32_t)GLType::UNSIGNED_INT_10_10_10_2, + (uint32_t)GLType::UNSIGNED_INT_2_10_10_10_REV, + (uint32_t)GLType::UNSIGNED_INT_24_8, + (uint32_t)GLType::UNSIGNED_INT_10F_11F_11F_REV, + (uint32_t)GLType::UNSIGNED_INT_5_9_9_9_REV, + (uint32_t)GLType::FLOAT_32_UNSIGNED_INT_24_8_REV, +}; + +static const std::unordered_set VALID_GL_FORMATS { + (uint32_t)GLFormat::STENCIL_INDEX, + (uint32_t)GLFormat::DEPTH_COMPONENT, + (uint32_t)GLFormat::DEPTH_STENCIL, + (uint32_t)GLFormat::RED, + (uint32_t)GLFormat::GREEN, + (uint32_t)GLFormat::BLUE, + (uint32_t)GLFormat::RG, + (uint32_t)GLFormat::RGB, + (uint32_t)GLFormat::RGBA, + (uint32_t)GLFormat::BGR, + (uint32_t)GLFormat::BGRA, + (uint32_t)GLFormat::RG_INTEGER, + (uint32_t)GLFormat::RED_INTEGER, + (uint32_t)GLFormat::GREEN_INTEGER, + (uint32_t)GLFormat::BLUE_INTEGER, + (uint32_t)GLFormat::RGB_INTEGER, + (uint32_t)GLFormat::RGBA_INTEGER, + (uint32_t)GLFormat::BGR_INTEGER, + (uint32_t)GLFormat::BGRA_INTEGER, +}; + +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, +}; + +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, +}; + +static const std::unordered_set VALID_GL_BASE_INTERNAL_FORMATS { + (uint32_t)GLBaseInternalFormat::DEPTH_COMPONENT, + (uint32_t)GLBaseInternalFormat::DEPTH_STENCIL, + (uint32_t)GLBaseInternalFormat::RED, + (uint32_t)GLBaseInternalFormat::RG, + (uint32_t)GLBaseInternalFormat::RGB, + (uint32_t)GLBaseInternalFormat::RGBA, + (uint32_t)GLBaseInternalFormat::STENCIL_INDEX, +}; + +bool Header::isValid() const { + if (0 != memcmp(identifier, IDENTIFIER.data(), IDENTIFIER_LENGTH)) { + qDebug() << "Invalid header identifier"; + return false; + } + + if (endianness != ENDIAN_TEST && endianness != REVERSE_ENDIAN_TEST) { + qDebug("Invalid endian marker 0x%x", endianness); + return false; + } + + // + // GL enum validity + // + if (VALID_GL_BASE_INTERNAL_FORMATS.count(glBaseInternalFormat) != 1) { + qDebug("Invalid base internal format 0x%x", glBaseInternalFormat); + return false; + } + + if (glFormat == (uint32_t)GLFormat::COMPRESSED_FORMAT && glType == (uint32_t)GLType::COMPRESSED_TYPE) { + if (VALID_GL_INTERNAL_COMPRESSED_FORMATS.count(glInternalFormat) != 1) { + qDebug("Invalid compressed internal format 0x%x", glInternalFormat); + return false; + } + } else { + if (VALID_GL_TYPES.count(glType) != 1) { + qDebug("Invalid type 0x%x", glType); + return false; + } + + if (VALID_GL_FORMATS.count(glFormat) != 1) { + qDebug("Invalid format 0x%x", glFormat); + return false; + } + + if (VALID_GL_INTERNAL_FORMATS.count(glInternalFormat) != 1) { + qDebug("Invalid internal format 0x%x", glInternalFormat); + return false; + } + } + + // + // Dimensions validity + // + + // Textures must at least have a width + // If they have a depth, they must have a height + if ((pixelWidth == 0) || (pixelDepth != 0 && pixelHeight == 0)) { + qDebug() << "Invalid dimensions " << pixelWidth << "x" << pixelHeight << "x" << pixelDepth; + return false; + } + + + if (numberOfFaces != 1 && numberOfFaces != NUM_CUBEMAPFACES) { + qDebug() << "Invalid number of faces " << numberOfFaces; + return false; + } + + // FIXME validate numberOfMipmapLevels based on the dimensions? + + if ((bytesOfKeyValueData % 4) != 0) { + qDebug() << "Invalid keyvalue data size " << numberOfFaces; + return false; + } + + return true; +} + +struct AlignedStreamBuffer { + AlignedStreamBuffer(size_t size, const uint8_t* data) + : _size(size), _data(data) { } + + AlignedStreamBuffer(const StoragePointer& storage) + : AlignedStreamBuffer(storage->size(), storage->data()) { } + + + template + bool read(T& t) { + // Ensure we don't read more than we have + if (sizeof(T) > _size) { + return false; + } + + // Grab the data + memcpy(&t, _data, sizeof(T)); + + // Advance the pointer + return skip(sizeof(T)); + } + + bool skip(size_t skipSize) { + if ((skipSize % 4) != 0) { + skipSize += (3 - ((skipSize + 3) % 4)); + } + if (skipSize > _size) { + return false; + } + _data += skipSize; + _size -= skipSize; + return true; + } + + AlignedStreamBuffer front(size_t size) const { + return AlignedStreamBuffer { std::min(size, _size), _data }; + } + + bool empty() const { + return _size == 0; + } + +private: + size_t _size; + const uint8_t* _data; +}; + +bool validateKeyValueData(AlignedStreamBuffer kvbuffer) { + while (!kvbuffer.empty()) { + uint32_t keyValueSize; + // Try to fetch the size of the next key value block + if (!kvbuffer.read(keyValueSize)) { + qDebug() << "Unable to read past key value size"; + return false; + } + if (!kvbuffer.skip(keyValueSize)) { + qDebug() << "Unable to skip past key value data"; + return false; + } + } + + return true; +} + +bool KTX::validate(const StoragePointer& src) { + if ((src->size() % 4) != 0) { + // All KTX data is 4-byte aligned + qDebug() << "Invalid size, not 4 byte aligned"; + return false; + } + + Header header; + AlignedStreamBuffer buffer { src }; + if (!buffer.read(header)) { + qDebug() << "Unable to read header"; + return false; + } + + // Basic header validation, are the enums and size valid? + if (!header.isValid()) { + qDebug() << "Invalid header"; + return false; + } + + // Validate the key value pairs + if (!validateKeyValueData(buffer.front(header.bytesOfKeyValueData))) { + qDebug() << "Invalid key value data"; + return false; + } + + // now skip the KV data + if (!buffer.skip(header.bytesOfKeyValueData)) { + qDebug() << "Unable to read past key value data"; + return false; + } + + + // Validate the images + for (uint32_t mip = 0; mip < header.numberOfMipmapLevels; ++mip) { + uint32_t imageSize; + if (!buffer.read(imageSize)) { + qDebug() << "Unable to read image size"; + return false; + } + + if (header.numberOfArrayElements == 0 && header.numberOfFaces == 6) { + for (uint8_t face = 0; face < NUM_CUBEMAPFACES; ++face) { + if (!buffer.skip(imageSize)) { + qDebug() << "Unable to skip past cubemap data"; + return false; + } + } + } else { + if (!buffer.skip(imageSize)) { + return false; + } + } + } + + // The buffer should be empty afer we've skipped all of the KTX data + if (!buffer.empty()) { + return false; + } + + return true; +} diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index 225fcbb2ed..92b9091533 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -38,14 +38,12 @@ #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); @@ -59,7 +57,9 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt OutputDebugStringA(logMessage.toLocal8Bit().constData()); OutputDebugStringA("\n"); #endif - logger->addMessage(qPrintable(logMessage + "\n")); + if (logger) { + logger->addMessage(qPrintable(logMessage + "\n")); + } } } @@ -149,5 +149,59 @@ int main(int argc, char** argv) { return 0; } +#if 0 +static const QString TEST_FOLDER { "H:/ktx_cacheold" }; +//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" }; + +//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" }; +static const QString EXTENSIONS { "*.ktx" }; + +int mainTemp(int, char**) { + qInstallMessageHandler(messageHandler); + auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS }); + for (auto fileInfo : fileInfoList) { + qDebug() << fileInfo.filePath(); + std::shared_ptr storage { new storage::FileStorage { fileInfo.filePath() } }; + + if (!ktx::KTX::validate(storage)) { + qDebug() << "KTX invalid"; + } + + 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) { + qDebug() << "\t" << kv._key.c_str(); + } + + auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); + if (offsetToMinMipKV) { + auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV; + auto minMipLevelAvailable = *data; + qDebug() << "\tMin mip available " << minMipLevelAvailable; + assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels); + } + auto storageSize = storage->size(); + for (const auto& faceImageDesc : ktxDescriptor.images) { + //assert(0 == (faceImageDesc._faceSize % 4)); + for (const auto& faceOffset : faceImageDesc._faceOffsets) { + assert(0 == (faceOffset % 4)); + auto faceEndOffset = faceOffset + faceImageDesc._faceSize; + assert(faceEndOffset <= storageSize); + } + } + + for (const auto& faceImage : ktxFile->_images) { + for (const ktx::Byte* faceBytes : faceImage._faceBytes) { + assert(0 == (reinterpret_cast(faceBytes) % 4)); + } + } + } + return 0; +} +#endif + #include "main.moc"