This commit is contained in:
2026-06-14 19:09:18 +01:00
parent 14bd1a9271
commit 13fa90a0e9
3958 changed files with 999286 additions and 4 deletions
+437
View File
@@ -0,0 +1,437 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Helper class for writer tests.
*
* @author Mark Callow, github.com/MarkCallow
*/
#include "gl_format.h"
#include "vkformat_enum.h"
#include "vk_format.h"
#include "ktx.h"
extern "C" {
#include "ktxint.h"
#include "texture2.h"
}
#include "unused.h"
struct wthImageInfo {
GLsizei size; // Size of the image data in bytes.
GLubyte* data; // Pointer to the image data.
};
class wthTexInfo : public ktxTextureCreateInfo {
public:
ktx_uint32_t glTypeSize;
ktx_uint32_t glType;
ktx_uint32_t glFormat;
ktx_uint32_t glBaseInternalformat;
ktx_uint32_t headerPixelHeight;
ktx_uint32_t headerPixelDepth;
ktx_uint32_t headerNumLayers;
};
extern "C" KTX_error_code appendLibId(ktxHashList* head,
ktxHashListEntry* writerEntry);
/**
* @internal @~English
* @brief Template class for creating writer test helpers.
*
* @tparam component_type the primitive type of a color component.
* @tparam numComponents the number of components in a color.
* @tparam internalformat the OpenGL internal format enum for the color.
*/
template<typename component_type,
ktx_uint32_t numComponents, GLenum internalformat>
class WriterTestHelper {
public:
enum createFlagBits {
eNone = 0x00,
eMipmapped = 0x01,
eGenerateMipmaps = 0x02,
eArray = 0x04
};
typedef ktx_uint32_t createFlags;
WriterTestHelper() : writer_ktx2("WriteTestHelper 1.0 __default__") {
}
~WriterTestHelper() {
ktxHashList_Destruct(&kvHash);
ktxHashList_Destruct(&kvHash_ktx2);
}
void resize(createFlags flags,
ktx_uint32_t layers, ktx_uint32_t faces,
ktx_uint32_t dimensions,
ktx_uint32_t w, ktx_uint32_t h, ktx_uint32_t d,
std::vector<component_type>* requestedColor = nullptr)
{
assert(faces == 1 || d == 1);
assert(requestedColor == nullptr || requestedColor->size() >= numComponents);
this->width = w;
this->height = h;
this->depth = d;
numLevels = flags & eMipmapped? levelsFromSize(w, h, d): 1;
this->numLayers = layers;
this->numFaces=faces;
isArray = flags & eArray ? true : false;
texinfo.resize(numLevels, layers, faces, dimensions,
isArray, w, h, d);
// Create the image set.
imageDataSize = 0;
images.resize(numLevels);
imageList.resize(numLevels * numLayers * numFaces * depth);
std::vector<component_type> color;
color.resize(numComponents);
if (requestedColor != nullptr) {
for (ktx_uint32_t i = 0; i < numComponents; i++) {
color[i] = (*requestedColor)[i];
}
}
for (ktx_uint32_t level = 0, count = 0; level < numLevels; level++) {
ktx_uint32_t levelWidth = MAX(1, width >> level);
ktx_uint32_t levelHeight = MAX(1, height >> level);
ktx_uint32_t levelDepth = MAX(1, depth >> level);
images[level].resize(numLayers);
for (ktx_uint32_t layer = 0; layer < numLayers; layer++) {
ktx_uint32_t numImages = numFaces == 6 ? numFaces : levelDepth;
images[level][layer].resize(numImages);
for (ktx_uint32_t faceSlice = 0; faceSlice < numImages; faceSlice++) {
ktx_uint32_t componentCount, pixelCount;
pixelCount = levelWidth * levelHeight;
componentCount = pixelCount * numComponents;
images[level][layer][faceSlice].resize(componentCount);
// Using std::vector avoids warnings in the following
// switch due to access past the end of the array, if we
// were using an array and numComponents < 4.
if (requestedColor == nullptr) {
switch (numComponents) {
case 4:
color[3] = (component_type)0.5;
FALLTHROUGH;
case 3:
color[2] = (component_type)faceSlice;
FALLTHROUGH;
case 2:
color[1] = (component_type)layer;
FALLTHROUGH;
case 1:
color[0] = (component_type)level;
break;
}
}
for (ktx_uint32_t i = 0; i < pixelCount; i++) {
for (ktx_uint32_t j = 0; j < numComponents; j++) {
ktx_uint32_t ci = i * numComponents + j;
images[level][layer][faceSlice][ci] = color[j];
}
}
imageList[count].size
= pixelCount * numComponents * sizeof(component_type);
imageDataSize += imageList[count].size;
imageList[count++].data
= (GLubyte*)images[level][layer][faceSlice].data();
}
}
}
switch (dimensions) {
case 1:
assert(strlen(KTX_ORIENTATION1_FMT) < sizeof(orientation));
snprintf(orientation, sizeof(orientation), KTX_ORIENTATION1_FMT,
'r');
break;
case 2:
assert(strlen(KTX_ORIENTATION2_FMT) < sizeof(orientation));
snprintf(orientation, sizeof(orientation), KTX_ORIENTATION2_FMT,
'r', 'd');
break;
case 3:
assert(strlen(KTX_ORIENTATION3_FMT) < sizeof(orientation));
snprintf(orientation, sizeof(orientation), KTX_ORIENTATION3_FMT,
'r', 'd', 'i');
break;
}
assert(4 <= sizeof(orientation_ktx2));
orientation_ktx2[0] = 'r';
orientation_ktx2[1] = 'd';
orientation_ktx2[2] = 'i';
orientation_ktx2[3] = 0;
orientation_ktx2[dimensions] = 0; // Ensure terminating NULL.
ktxHashList_Construct(&kvHash);
ktxHashList_AddKVPair(&kvHash, KTX_ORIENTATION_KEY,
(unsigned int)strlen(orientation) + 1,
orientation);
ktxHashList_Serialize(&kvHash, &kvDataLen, &kvData);
ktxHashList_Construct(&kvHash_ktx2);
ktxHashList_AddKVPair(&kvHash_ktx2, KTX_WRITER_KEY,
(ktx_uint32_t)writer_ktx2.size(),
writer_ktx2.data());
// Get the library to add its Id to the writer key so it will be
// included in the serialized data.
ktxHashListEntry* pWriter;
ktxHashList_FindEntry(&kvHash_ktx2, KTX_WRITER_KEY,
&pWriter);
appendLibId(&kvHash_ktx2, pWriter);
ktxHashList_Serialize(&kvHash_ktx2, &kvDataLenWriter_ktx2, &kvDataWriter_ktx2);
ktxHashList_AddKVPair(&kvHash_ktx2, KTX_ORIENTATION_KEY,
dimensions + 1,
orientation_ktx2);
ktxHashList_Sort(&kvHash_ktx2);
ktxHashList_Serialize(&kvHash_ktx2, &kvDataLenAll_ktx2, &kvDataAll_ktx2);
}
// Compare the raw images, which are tightly packed, with potentially
// row padded images from KTX texture.
bool compareRawImages(ktx_uint8_t* pData) {
for (ktx_uint32_t level = 0; level < numLevels; level++) {
ktx_uint32_t faceLodSize = *(ktx_uint32_t*)pData;
ktx_uint32_t levelWidth = MAX(1, width >> level);
ktx_uint32_t levelHeight = MAX(1, height >> level);
ktx_uint32_t levelDepth = MAX(1, depth >> level);
ktx_uint32_t numImages;
ktx_uint32_t rowPadding;
ktx_size_t paddedImageBytes;
ktx_size_t paddedRowBytes, rowBytes;
ktx_size_t expectedFaceLodSize;
rowBytes = levelWidth
* sizeof(component_type)
* numComponents;
rowPadding = 3 - ((rowBytes + KTX_GL_UNPACK_ALIGNMENT-1) % KTX_GL_UNPACK_ALIGNMENT);
paddedRowBytes = rowBytes + rowPadding;
paddedImageBytes = paddedRowBytes * levelHeight;
if (numFaces == 6 && !isArray) {
// Non-array cubemap.
numImages = numFaces;
expectedFaceLodSize = paddedImageBytes;
} else {
numImages = numFaces == 6 ? numFaces : levelDepth;
expectedFaceLodSize = paddedImageBytes * numImages * numLayers;
}
if (faceLodSize != expectedFaceLodSize)
return false;
pData += sizeof(ktx_uint32_t);
for (ktx_uint32_t layer = 0; layer < numLayers; layer++) {
for (ktx_uint32_t faceSlice = 0; faceSlice < numImages; faceSlice++) {
if (rowPadding == 0) {
if (memcmp(images[level][layer][faceSlice].data(),
pData,
images[level][layer][faceSlice].size() * sizeof(component_type)))
return false;
pData += paddedImageBytes;
} else {
ktx_uint8_t* pImage = (ktx_uint8_t*)images[level][layer][faceSlice].data();
for (ktx_uint32_t row = 0; row < levelHeight; row++) {
if (memcmp(pImage, pData, rowBytes))
return false;
pImage += rowBytes;
pData += paddedRowBytes;
}
}
}
}
}
return true;
}
// Compare the raw images, which are tightly packed, with the images from
// a KTX 2 texture, which are also tightly packed but have reversed order
// for mip levels.
bool compareRawImages(ktxLevelIndexEntry levelIndex[], ktx_uint8_t* baseAddr) {
for (ktx_uint32_t level = 0; level < numLevels; level++) {
ktx_uint64_t levelSize = levelIndex[level].uncompressedByteLength;
ktx_uint32_t levelDepth = MAX(1, depth >> level);
ktx_uint32_t numImages;
ktx_size_t imageBytes;
ktx_size_t expectedLevelSize;
imageBytes = images[level][0][0].size() * sizeof(component_type);
numImages = numFaces == 6 ? numFaces : levelDepth;
expectedLevelSize = imageBytes * numImages * numLayers;
if (levelSize != expectedLevelSize)
return false;
ktx_uint8_t* pData = baseAddr + levelIndex[level].byteOffset;
for (ktx_uint32_t layer = 0; layer < numLayers; layer++) {
for (ktx_uint32_t faceSlice = 0; faceSlice < numImages; faceSlice++) {
#if 0 //DUMP_IMAGE
fprintf(stdout, "Reading level %d, layer %d, faceSlice %d at offset %#" PRIx64 "\n",
level, layer, faceSlice, levelIndex[level].offset);
for (ktx_uint32_t i = 0; i < imageBytes; i++)
fprintf(stdout, "%#x, ", *(pData + i));
fprintf(stdout, "\n");
#endif
if (memcmp(images[level][layer][faceSlice].data(), pData,
imageBytes))
return false;
pData += imageBytes;
}
}
}
return true;
}
KTX_error_code
copyImagesToTexture(ktxTexture* texture) {
KTX_error_code result = KTX_SUCCESS;
for (ktx_uint32_t level = 0; level < images.size(); level++) {
for (ktx_uint32_t layer = 0; layer < images[level].size(); layer++) {
for (ktx_uint32_t faceSlice = 0; faceSlice < images[level][layer].size(); faceSlice++) {
ktx_size_t imageBytes = images[level][layer][faceSlice].size() * sizeof(component_type);
ktx_uint8_t* imageDataPtr = (ktx_uint8_t*)(images[level][layer][faceSlice].data());
result = ktxTexture_SetImageFromMemory(texture,
level, layer,
faceSlice,
imageDataPtr,
imageBytes);
if (result != KTX_SUCCESS)
break;
}
}
}
return result;
}
static ktx_uint32_t
levelsFromSize(ktx_uint32_t width, ktx_uint32_t height, ktx_uint32_t depth) {
ktx_uint32_t mipLevels;
ktx_uint32_t max_dim = MAX(MAX(width, height), depth);
for (mipLevels = 1; max_dim != 1; mipLevels++, max_dim >>= 1) { }
return mipLevels;
}
ktx_uint32_t numLevels;
ktx_uint32_t numLayers;
ktx_uint32_t numFaces;
ktx_uint32_t width;
ktx_uint32_t height;
ktx_uint32_t depth;
bool isArray;
ktx_uint8_t* kvData;
ktx_uint32_t kvDataLen;
char orientation[15];
ktx_uint8_t* kvDataWriter_ktx2;
ktx_uint32_t kvDataLenWriter_ktx2;
ktx_uint8_t* kvDataAll_ktx2;
ktx_uint32_t kvDataLenAll_ktx2;
ktxHashList kvHash;
ktxHashList kvHash_ktx2;
char orientation_ktx2[4];
std::string writer_ktx2;
std::string comparisonWriter_ktx2;
ktx_size_t imageDataSize;
std::vector< std::vector < std::vector < std::vector<component_type> > > > images;
std::vector<wthImageInfo> imageList;
class texinfo : public wthTexInfo {
public:
texinfo() {
glType = glGetTypeFromInternalFormat(internalformat);
glTypeSize = glGetTypeSizeFromType(glType);
glFormat = glGetFormatFromInternalFormat(internalformat);
glInternalformat = internalformat;
glBaseInternalformat = glFormat;
}
void resize(GLuint levels, GLuint layers, GLuint faces,
GLuint dimensions, bool array,
GLsizei width, GLsizei height, GLsizei depth) {
this->numLayers = layers;
this->numFaces = faces;
this->numLevels = levels;
this->numDimensions = dimensions;
this->generateMipmaps = false;
this->isArray = array;
baseWidth = width;
baseHeight = height;
baseDepth = depth;
headerNumLayers = isArray ? numLayers: 0;
headerPixelHeight = numDimensions >= 2 ? height : 0;
headerPixelDepth = numDimensions == 3 ? depth : 0;
}
bool compare(KTX_header* header) {
if (header->glType == glType
&& header->glTypeSize == glTypeSize
&& header->glFormat == glFormat
&& header->glInternalformat == glInternalformat
&& header->glBaseInternalformat == glBaseInternalformat
&& header->pixelWidth == baseWidth
&& header->pixelHeight == headerPixelHeight
&& header->pixelDepth == headerPixelDepth
&& header->numberOfArrayElements == headerNumLayers
&& header->numberOfFaces == numFaces
&& header->numberOfMipLevels == numLevels)
return true;
else
return false;
}
bool compare(KTX_header2* header) {
VkFormat format =
vkGetFormatFromOpenGLInternalFormat(glInternalformat);
// Should find better way to test this. Code we're testing uses the
// same switch to convert format.
if (header->vkFormat == (ktx_uint32_t)format
&& header->pixelWidth == baseWidth
&& header->pixelHeight == headerPixelHeight
&& header->pixelDepth == headerPixelDepth
&& header->layerCount == headerNumLayers
&& header->faceCount == numFaces
&& header->levelCount == numLevels
&& header->supercompressionScheme >= KTX_SS_BEGIN_RANGE
&& header->supercompressionScheme <= KTX_SS_END_RANGE)
return true;
else
return false;
}
bool compare(ktxTexture2* texture) {
VkFormat format =
vkGetFormatFromOpenGLInternalFormat(glInternalformat);
if (texture->vkFormat == (ktx_uint32_t)format
&& texture->baseWidth == baseWidth
&& texture->baseHeight == baseHeight
&& texture->baseDepth == baseDepth
&& texture->numLayers == numLayers
&& texture->numFaces == numFaces
&& texture->numLevels == numLevels
&& texture->supercompressionScheme >= KTX_SS_BEGIN_RANGE
&& texture->supercompressionScheme <= KTX_SS_END_RANGE)
return true;
else
return false;
}
} texinfo;
};