Files
2026-06-14 19:09:18 +01:00

1139 lines
37 KiB
C++

/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2010-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Tests of internal API functions.
*
* @author Mark Callow, github.com/MarkCallow
*/
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
#endif
#include <string.h>
#include "GL/glcorearb.h"
#include "gl_format.h"
#include "ktx.h"
extern "C" {
#include "ktxint.h"
#include "filestream.h"
#include "memstream.h"
}
#include "gtest/gtest.h"
#include "wthelper.h"
#include "ltexceptions.h"
// These are so we can test swizzle_to_rgba. Do not put inside "namespace {"
// as there is no "namespace {" in basis_encode.cpp where the function is
// defined.
enum swizzle_e {
R = 1,
G = 2,
B = 3,
A = 4,
ZERO = 5,
ONE = 6,
};
extern void
swizzle_to_rgba(uint8_t* rgbadst, uint8_t* rgbasrc, uint32_t src_len,
ktx_size_t image_size, swizzle_e swizzle[4]);
namespace {
///////////////////////////////////////////////////////////
// Test fixtures
///////////////////////////////////////////////////////////
//--------------------------------------------
// Fixture for CheckHeaderTest
//--------------------------------------------
class CheckHeader1Test : public ::testing::Test {
protected:
CheckHeader1Test() {
// Done this way rather than by using an initializer here
// so it will compile on VS 2008.
memcpy(testHeader.identifier, ktxId, sizeof(ktxId));
testHeader.endianness = 0x04030201;
testHeader.glType = GL_UNSIGNED_BYTE;
testHeader.glTypeSize = 1;
testHeader.glFormat = GL_RGBA;
testHeader.glInternalformat = GL_RGBA8;
testHeader.glBaseInternalformat = GL_RGBA8;
testHeader.pixelWidth = 16;
testHeader.pixelHeight = 16;
testHeader.pixelDepth = 16;
testHeader.numberOfArrayElements = 0;
testHeader.numberOfFaces = 1;
testHeader.numberOfMipLevels = 5;
testHeader.bytesOfKeyValueData = 0;
}
KTX_header testHeader;
public:
static ktx_uint8_t ktxId[12];
};
ktx_uint8_t CheckHeader1Test::ktxId[12] = {
0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
};
//--------------------------------------------
// Base fixture for WriterTestHelper tests.
//--------------------------------------------
template<typename component_type, ktx_uint32_t num_components,
GLenum internalformat>
class WriterTestHelperTestBase : public ::testing::Test {
public:
WriterTestHelperTestBase() { }
WriterTestHelper<component_type, num_components, internalformat> helper;
ktx_uint32_t numComponents() { return num_components; }
};
class WriterTestHelperRGBA8Test : public WriterTestHelperTestBase<GLubyte, 4, GL_RGBA8> { };
class WriterTestHelperRGB8Test : public WriterTestHelperTestBase<GLubyte, 3, GL_RGB8> { };
typedef WriterTestHelper<GLubyte, 4, GL_RGBA8>::createFlagBits createFlagBits;
//////////////////////////////
// CheckHeaderTest
//////////////////////////////
#if defined(DEBUG)
TEST_F(CheckHeader1Test, AssertsOnNullArguments) {
ASSERT_DEATH_IF_SUPPORTED(ktxCheckHeader1_(0, 0), "Assert*");
}
#endif
TEST_F(CheckHeader1Test, ValidatesIdentifier) {
KTX_supplemental_info suppInfo;
EXPECT_EQ(ktxCheckHeader1_(&testHeader, &suppInfo), KTX_SUCCESS);
testHeader.identifier[9] = 0;
EXPECT_EQ(ktxCheckHeader1_(&testHeader, &suppInfo), KTX_UNKNOWN_FILE_FORMAT);
}
TEST_F(CheckHeader1Test, DisallowsInvalidEndianness) {
KTX_supplemental_info suppInfo;
testHeader.endianness = 0;
EXPECT_EQ(ktxCheckHeader1_(&testHeader, &suppInfo), KTX_FILE_DATA_ERROR);
}
//////////////////////////////
// MemStreamTest
//////////////////////////////
TEST(MemStreamTest, Read) {
ktxStream stream;
const ktx_uint8_t* data = (ktx_uint8_t*)"28 bytes of rubbish to read.";
const size_t size = 28;
char readBuf[size];
ktxMemStream_construct_ro(&stream, data, size);
stream.read(&stream, readBuf, size);
EXPECT_EQ(memcmp(data, readBuf, size), 0);
ktxMemStream_destruct(&stream);
}
TEST(MemStreamTest, Write) {
ktxStream stream;
const ktx_uint8_t* data = (ktx_uint8_t*)"29 bytes of rubbish to write.";
const size_t count = 29;
size_t returnedCount;
ktx_uint8_t* returnedData;
ktxMemStream_construct(&stream, KTX_TRUE);
stream.write(&stream, data, 1, count);
stream.getsize(&stream, &returnedCount);
ktxMemStream_getdata(&stream, &returnedData);
EXPECT_EQ(returnedCount, count);
EXPECT_EQ(memcmp(data, returnedData, count), 0);
ktxMemStream_destruct(&stream);
}
TEST(MemStreamTest, WriteExpand) {
ktxStream stream;
const ktx_uint8_t* data = (ktx_uint8_t*)"29 bytes of rubbish to write.";
const ktx_uint8_t* data2 = (ktx_uint8_t*)" 26 more bytes of rubbish.";
const size_t count = 29;
const size_t count2 = 26;
size_t returnedCount;
ktx_uint8_t* returnedData;
ktxMemStream_construct(&stream, KTX_TRUE);
stream.write(&stream, data, 1, count);
stream.write(&stream, data2, 1, count2);
stream.getsize(&stream, &returnedCount);
EXPECT_EQ(returnedCount, count + count2);
ktxMemStream_getdata(&stream, &returnedData);
EXPECT_EQ(memcmp(data, returnedData, count), 0);
EXPECT_EQ(memcmp(data2, &returnedData[count], count2), 0);
ktxMemStream_destruct(&stream);
}
//////////////////////////////
// WriterTestHelper tests.
//////////////////////////////
TEST_F(WriterTestHelperRGB8Test, Construct2D) {
helper.resize(createFlagBits::eNone, 1, 1, 2, 32, 32, 1);
EXPECT_EQ(helper.images.size(), 1U);
EXPECT_EQ(helper.images[0].size(), 1U);
EXPECT_EQ(helper.images[0][0].size(), 1U);
EXPECT_EQ(helper.images[0][0][0].size(), 32 * 32 * 3U);
EXPECT_EQ(numComponents(), 3U);
}
TEST_F(WriterTestHelperRGB8Test, Construct3D) {
helper.resize(createFlagBits::eNone, 1, 1, 3, 32, 32, 32);
EXPECT_EQ(helper.images.size(), 1U);
EXPECT_EQ(helper.images[0].size(), 1U);
EXPECT_EQ(helper.images[0][0].size(), 32U);
EXPECT_EQ(helper.images[0][0][0].size(), (size_t)(32 * 32 * 3));
EXPECT_EQ(numComponents(), 3U);
}
/////////////////////////////////////
// Base fixture for createDFD tests.
/////////////////////////////////////
// TODO: Move DFD tests to dfdutils when build is redone with CMake.
#include <KHR/khr_df.h>
#define LIBKTX // To make dfd.h not include vulkan/vulkan_core.h.
#include "dfdutils/dfd.h"
// Template for single plane formats.
template<ktx_uint32_t numComponents, ktx_uint32_t bytesPlane>
class createDFDTestBase : public ::testing::Test {
public:
struct sampleType {
uint32_t bitOffset: 16;
uint32_t bitLength: 8;
uint32_t channelType: 8; // Includes qualifiers
uint32_t samplePosition0: 8;
uint32_t samplePosition1: 8;
uint32_t samplePosition2: 8;
uint32_t samplePosition3: 8;
uint32_t lower;
uint32_t upper;
};
createDFDTestBase() {
expected.vendorId = KHR_DF_VENDORID_KHRONOS;
expected.descriptorType = KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT;
expected.versionNumber = KHR_DF_VERSIONNUMBER_1_3;
// sizeof is after template instantiation so BDFD already includes
// numComponents samples.
expected.descriptorBlockSize = sizeof(BDFD);
expected.bytesPlane0 = bytesPlane;
expected.bytesPlane1 = 0;
expected.bytesPlane2 = 0;
expected.bytesPlane3 = 0;
expected.bytesPlane4 = 0;
expected.bytesPlane5 = 0;
expected.bytesPlane6 = 0;
expected.bytesPlane7 = 0;
}
protected:
struct BDFD {
uint32_t vendorId: 17;
uint32_t descriptorType: 15;
uint32_t versionNumber: 16;
uint32_t descriptorBlockSize: 16;
uint32_t model: 8;
uint32_t primaries: 8;
uint32_t transfer: 8;
uint32_t flags: 8;
uint32_t texelBlockDimension0: 8;
uint32_t texelBlockDimension1: 8;
uint32_t texelBlockDimension2: 8;
uint32_t texelBlockDimension3: 8;
uint32_t bytesPlane0: 8;
uint32_t bytesPlane1: 8;
uint32_t bytesPlane2: 8;
uint32_t bytesPlane3: 8;
uint32_t bytesPlane4: 8;
uint32_t bytesPlane5: 8;
uint32_t bytesPlane6: 8;
uint32_t bytesPlane7: 8;
sampleType samples[numComponents];
} expected;
};
template<ktx_uint32_t numComponents, ktx_uint32_t bytesPlane>
class createDFDTestBaseUncomp : public createDFDTestBase<numComponents, bytesPlane> {
using typename createDFDTestBase<numComponents, bytesPlane>::sampleType;
public:
createDFDTestBaseUncomp() : createDFDTestBase<numComponents, bytesPlane>() {
expected.texelBlockDimension0 = 0;
expected.texelBlockDimension1 = 0;
expected.texelBlockDimension2 = 0;
expected.texelBlockDimension3 = 0;
}
void customize(ktx_uint8_t model,
ktx_uint8_t primaries, ktx_uint8_t transfer,
ktx_uint8_t flags,
std::initializer_list<sampleType> samples) {
expected.model = model;
expected.primaries = primaries;
expected.transfer = transfer;
expected.flags = flags;
uint32_t i = 0; // There's got to be some syntax for declaring this in the for
for (auto sample : samples ) {
expected.samples[i] = sample;
if (++i == numComponents)
break;
}
}
protected:
using createDFDTestBase<numComponents, bytesPlane>::expected;
};
template<ktx_uint32_t numComponents, ktx_uint32_t bytesPlane>
class createDFDTestBaseComp : public createDFDTestBase<numComponents, bytesPlane> {
using typename createDFDTestBase<numComponents, bytesPlane>::sampleType;
public:
createDFDTestBaseComp() : createDFDTestBase<numComponents, bytesPlane>() {
expected.texelBlockDimension3 = 0;
}
void customize(ktx_uint8_t model,
ktx_uint8_t primaries, ktx_uint8_t transfer,
ktx_uint8_t flags,
ktx_uint32_t dim0, ktx_uint32_t dim1, ktx_uint32_t dim2,
std::initializer_list<sampleType> samples) {
expected.model = model;
expected.primaries = primaries;
expected.transfer = transfer;
expected.flags = flags;
expected.texelBlockDimension0 = dim0;
expected.texelBlockDimension1 = dim1;
expected.texelBlockDimension2 = dim2;
uint32_t i = 0; // There's got to be some syntax for declaring this in the for
for (auto sample : samples ) {
expected.samples[i] = sample;
if (++i == numComponents)
break;
}
}
void customize(ktx_uint8_t model,
ktx_uint8_t primaries, ktx_uint8_t transfer,
ktx_uint8_t flags, ktx_uint32_t dim0, ktx_uint32_t dim1,
std::initializer_list<sampleType> samples) {
customize(model, primaries, transfer, flags, dim0, dim1, 0, samples);
}
protected:
using createDFDTestBase<numComponents, bytesPlane>::expected;
};
class createDFDUnpackedTest4 : public createDFDTestBaseUncomp<4, 4> { };
class createDFDUnpackedTest3 : public createDFDTestBaseUncomp<3, 3> { };
class createDFDPackedTest3 : public createDFDTestBaseUncomp<3, 2> { };
class createDFDCompressedTest1 : public createDFDTestBaseComp<1, 8> { };
class createDFDCompressedTest2 : public createDFDTestBaseComp<2, 16> { };
class createDFDCompressedTest1x16 : public createDFDTestBaseComp<1, 16> { };
//////////////////////////////
// createDFD tests.
//////////////////////////////
TEST_F(createDFDUnpackedTest4, FormatSRGBA8) {
customize(KHR_DF_MODEL_RGBSDA, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_STRAIGHT,
{
{0, 7, KHR_DF_CHANNEL_RGBSDA_RED, 0, 0, 0, 0, 0, 255},
{8, 7, KHR_DF_CHANNEL_RGBSDA_GREEN, 0, 0, 0, 0, 0, 255},
{16, 7, KHR_DF_CHANNEL_RGBSDA_BLUE, 0, 0, 0, 0, 0, 255},
{24, 7, KHR_DF_CHANNEL_RGBSDA_ALPHA | KHR_DF_SAMPLE_DATATYPE_LINEAR,
0, 0, 0, 0, 0, 255}
}
);
uint32_t* dfd = createDFDUnpacked(KTX_FALSE, 4, 1, KTX_FALSE, s_SRGB);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDUnpackedTest4, FormatSBGRA8) {
customize(KHR_DF_MODEL_RGBSDA, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_STRAIGHT,
{
{0, 7, KHR_DF_CHANNEL_RGBSDA_BLUE, 0, 0, 0, 0, 0, 255},
{8, 7, KHR_DF_CHANNEL_RGBSDA_GREEN, 0, 0, 0, 0, 0, 255},
{16, 7, KHR_DF_CHANNEL_RGBSDA_RED, 0, 0, 0, 0, 0, 255},
{24, 7, KHR_DF_CHANNEL_RGBSDA_ALPHA | KHR_DF_SAMPLE_DATATYPE_LINEAR,
0, 0, 0, 0, 0, 255}
}
);
uint32_t* dfd = createDFDUnpacked(KTX_FALSE, 4, 1, KTX_TRUE, s_SRGB);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDUnpackedTest4, FormatRGBA8) {
customize(KHR_DF_MODEL_RGBSDA, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
{
{0, 7, KHR_DF_CHANNEL_RGBSDA_RED, 0, 0, 0, 0, 0, 255},
{8, 7, KHR_DF_CHANNEL_RGBSDA_GREEN, 0, 0, 0, 0, 0, 255},
{16, 7, KHR_DF_CHANNEL_RGBSDA_BLUE, 0, 0, 0, 0, 0, 255},
{24, 7, KHR_DF_CHANNEL_RGBSDA_ALPHA, 0, 0, 0, 0, 0, 255}
}
);
uint32_t* dfd = createDFDUnpacked(KTX_FALSE, 4, 1, KTX_FALSE, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDUnpackedTest3, FormatSRGB8) {
customize(KHR_DF_MODEL_RGBSDA, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_STRAIGHT,
{
{0, 7, KHR_DF_CHANNEL_RGBSDA_RED, 0, 0, 0, 0, 0, 255},
{8, 7, KHR_DF_CHANNEL_RGBSDA_GREEN, 0, 0, 0, 0, 0, 255},
{16, 7, KHR_DF_CHANNEL_RGBSDA_BLUE, 0, 0, 0, 0, 0, 255},
}
);
uint32_t* dfd = createDFDUnpacked(KTX_FALSE, 3, 1, KTX_FALSE, s_SRGB);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDPackedTest3, FormatRGB565) {
customize(KHR_DF_MODEL_RGBSDA, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
{
{0, 4, KHR_DF_CHANNEL_RGBSDA_BLUE, 0, 0, 0, 0, 0, 31},
{5, 5, KHR_DF_CHANNEL_RGBSDA_GREEN, 0, 0, 0, 0, 0, 63},
{11, 4, KHR_DF_CHANNEL_RGBSDA_RED, 0, 0, 0, 0, 0, 31},
}
);
// In order from LSB.
int bits[] = {5, 6, 5, 0};
int channels[] = {
KHR_DF_CHANNEL_RGBSDA_BLUE,
KHR_DF_CHANNEL_RGBSDA_GREEN,
KHR_DF_CHANNEL_RGBSDA_RED,
0
};
uint32_t* dfd = createDFDPacked(KTX_FALSE, 3, bits, channels, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1, FormatETC1S_R8B8G8) {
customize(KHR_DF_MODEL_ETC1S, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
3, 3,
{
{0, 63, KHR_DF_CHANNEL_ETC1S_RGB , 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ETC1S, 4, 4, 1, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1, FormatETC1S_SR8B8G8) {
customize(KHR_DF_MODEL_ETC1S, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_STRAIGHT,
3, 3,
{
{0, 63, KHR_DF_CHANNEL_ETC1S_RGB, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ETC1S, 4, 4, 1, s_SRGB);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1, FormatETC2_R8B8G8) {
customize(KHR_DF_MODEL_ETC2, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
3, 3,
{
{0, 63, KHR_DF_CHANNEL_ETC2_COLOR, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ETC2_R8G8B8, 4, 4, 1, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest2, FormatETC2_R8G8B8A8) {
customize(KHR_DF_MODEL_ETC2, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
3, 3,
{
{0, 63, KHR_DF_CHANNEL_ETC2_ALPHA, 0, 0, 0, 0, 0, 0xFFFFFFFF},
{64, 63, KHR_DF_CHANNEL_ETC2_COLOR, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ETC2_R8G8B8A8, 4, 4, 1, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1, FormatETC2_SR8B8G8) {
customize(KHR_DF_MODEL_ETC2, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_STRAIGHT,
3, 3,
{
{0, 63, KHR_DF_CHANNEL_ETC2_COLOR, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ETC2_R8G8B8, 4, 4, 1, s_SRGB);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest2, FormatETC2_SR8G8B8A8) {
customize(KHR_DF_MODEL_ETC2, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_STRAIGHT,
3, 3,
{
{0, 63, KHR_DF_CHANNEL_ETC2_ALPHA | KHR_DF_SAMPLE_DATATYPE_LINEAR,
0, 0, 0, 0, 0, 0xFFFFFFFF},
{64, 63, KHR_DF_CHANNEL_ETC2_COLOR, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ETC2_R8G8B8A8, 4, 4, 1, s_SRGB);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1x16, FormatASTC_12x12_SRGB) {
customize(KHR_DF_MODEL_ASTC, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_STRAIGHT,
11, 11,
{
{0, 127, KHR_DF_CHANNEL_ASTC_DATA, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ASTC, 12, 12, 1, s_SRGB);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1x16, FormatASTC_10x5_SRGB) {
customize(KHR_DF_MODEL_ASTC, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_STRAIGHT,
9, 4,
{
{0, 127, KHR_DF_CHANNEL_ASTC_DATA, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ASTC, 10, 5, 1, s_SRGB);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1x16, FormatASTC_5x4) {
customize(KHR_DF_MODEL_ASTC, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
4, 3,
{
{0, 127, KHR_DF_CHANNEL_ASTC_DATA, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ASTC, 5, 4, 1, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1x16, FormatASTC_10x8) {
customize(KHR_DF_MODEL_ASTC, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
9, 7,
{
{0, 127, KHR_DF_CHANNEL_ASTC_DATA, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ASTC, 10, 8, 1, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1x16, FormatASTC_3x3x3) {
customize(KHR_DF_MODEL_ASTC, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
2, 2, 2,
{
{0, 127, KHR_DF_CHANNEL_ASTC_DATA, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_ASTC, 3, 3, 3, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
TEST_F(createDFDCompressedTest1, FormatBC1) {
customize(KHR_DF_MODEL_BC1A, KHR_DF_PRIMARIES_BT709,
KHR_DF_TRANSFER_LINEAR, KHR_DF_FLAG_ALPHA_STRAIGHT,
3, 3,
{
{0, 63, KHR_DF_CHANNEL_BC1A_COLOR, 0, 0, 0, 0, 0, 0xFFFFFFFF},
}
);
uint32_t* dfd = createDFDCompressed(c_BC1_RGB, 4, 4, 1, s_UNORM);
EXPECT_EQ(*dfd, sizeof(expected) + 4U);
EXPECT_EQ(memcmp(&expected, dfd+1, sizeof(expected)), 0);
free(dfd);
}
class DFDVkFormatListTest : public ::testing::Test {
protected:
DFDVkFormatListTest() {}
static constexpr VkFormat formats[] = {
#include "vkformat_list.inl"
};
};
extern "C" const char* vkFormatString(VkFormat format);
TEST_F(DFDVkFormatListTest, ReconstructDFDBytesPlanes) {
uint32_t* dfd;
uint32_t* bdb;
uint32_t origBytesPlane03, origBytesPlane47;
for (uint32_t i = 0; i < sizeof(formats) / sizeof(VkFormat); i++) {
dfd = vk2dfd(formats[i]);
ASSERT_TRUE(dfd != NULL) << "vk2dfd failed to produce DFD for "
<< vkFormatString(formats[i]);
bdb = dfd + 1;
// There are 4 bytesPlane values in one word. Capture all at once.
origBytesPlane03 = bdb[KHR_DF_WORD_BYTESPLANE0];
origBytesPlane47 = bdb[KHR_DF_WORD_BYTESPLANE4];
bdb[KHR_DF_WORD_BYTESPLANE0] = 0U;
bdb[KHR_DF_WORD_BYTESPLANE1] = 0U;
reconstructDFDBytesPlanesFromSamples(dfd);
EXPECT_EQ(origBytesPlane03, bdb[KHR_DF_WORD_BYTESPLANE0]);
EXPECT_EQ(origBytesPlane47, bdb[KHR_DF_WORD_BYTESPLANE4]);
free(dfd);
}
// No need to test reconstruction for UASTC as, colorModel aside, the DFD
// is identical to that for VK_FORMAT_ASTC_4x4_BLOCK_UNORM which has been
// tested above.
// Make ETC1S DFDs and test reconstruction.
for (uint32_t sampleCount = 1; sampleCount < 3; sampleCount++) {
uint32_t bdbSize = KHR_DF_WORD_SAMPLESTART
+ sampleCount * KHR_DF_WORD_SAMPLEWORDS;
bdbSize *= sizeof(uint32_t);
uint32_t dfdSize = bdbSize + 1 * sizeof(uint32_t);
dfd = new uint32_t[dfdSize];
bdb = dfd + 1;
KHR_DFDSETVAL(bdb, VENDORID, KHR_DF_VENDORID_KHRONOS);
KHR_DFDSETVAL(bdb, DESCRIPTORTYPE, KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT);
KHR_DFDSETVAL(bdb, VERSIONNUMBER, KHR_DF_VERSIONNUMBER_LATEST);
KHR_DFDSETVAL(bdb, DESCRIPTORBLOCKSIZE, bdbSize);
KHR_DFDSETVAL(bdb, MODEL, KHR_DF_MODEL_ETC1S);
KHR_DFDSETVAL(bdb, PRIMARIES, KHR_DF_PRIMARIES_BT709);
KHR_DFDSETVAL(bdb, TRANSFER, KHR_DF_TRANSFER_SRGB);
KHR_DFDSETVAL(bdb, FLAGS, 0);
bdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0] =
3 | (3 << KHR_DF_SHIFT_TEXELBLOCKDIMENSION1);
bdb[KHR_DF_WORD_BYTESPLANE0] = 0; /* bytesPlane3..0 = 0 */
//KHR_DFDSETVAL(nbdb, BYTESPLANE0, 8);
//if (sampleCoount == 2)
// KHR_DFDSETVAL(nbdb, BYTESPLANE1, 8);
bdb[KHR_DF_WORD_BYTESPLANE4] = 0; /* bytesPlane7..5 = 0 */
for (uint32_t sample = 0; sample < sampleCount; sample++) {
uint16_t channelId, bitOffset;
if (sample == 0) {
bitOffset = 0;
channelId = KHR_DF_CHANNEL_ETC1S_RGB;
} else {
bitOffset = 64;
channelId = KHR_DF_CHANNEL_ETC1S_AAA;
}
KHR_DFDSETSVAL(bdb, sample, CHANNELID, channelId);
KHR_DFDSETSVAL(bdb, sample, QUALIFIERS, 0);
KHR_DFDSETSVAL(bdb, sample, SAMPLEPOSITION_ALL, 0);
KHR_DFDSETSVAL(bdb, sample, BITOFFSET, bitOffset);
KHR_DFDSETSVAL(bdb, sample, BITLENGTH, 63);
KHR_DFDSETSVAL(bdb, sample, SAMPLELOWER, 0);
KHR_DFDSETSVAL(bdb, sample, SAMPLEUPPER, UINT32_MAX);
}
reconstructDFDBytesPlanesFromSamples(dfd);
EXPECT_EQ(8U, KHR_DFDVAL(bdb, BYTESPLANE0));
if (sampleCount == 2)
EXPECT_EQ(8U, KHR_DFDVAL(bdb, BYTESPLANE1));
else
EXPECT_EQ(0U, KHR_DFDVAL(bdb, BYTESPLANE1));
EXPECT_EQ(0U, KHR_DFDVAL(bdb, BYTESPLANE2));
EXPECT_EQ(0U, KHR_DFDVAL(bdb, BYTESPLANE3));
EXPECT_EQ(0U, bdb[KHR_DF_WORD_BYTESPLANE4]);
delete[] dfd;
}
}
TEST_F(DFDVkFormatListTest, BidirectionalVk2DfDTest) {
for (uint32_t i = 0; i < sizeof(formats) / sizeof(VkFormat); i++) {
uint32_t* dfd = vk2dfd(formats[i]);
ASSERT_TRUE(dfd != NULL) << "vk2dfd failed to produce DFD for "
<< vkFormatString(formats[i]);
VkFormat formatOut = dfd2vk(dfd);
// The SCALED formats are indistinguishable from the INT formats
// and dfd2vk resolves the ambiguity in favor of the format more
// likely to be used as a texture.
//
// The A8B8G8R8_*_PACK32 formats are indistinguishable from the
// R8G8B8A8* formats and dfd2vk returns the more common format.
switch (formats[i]) {
case VK_FORMAT_R8_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R8_UINT);
break;
case VK_FORMAT_R8_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R8_SINT);
break;
case VK_FORMAT_R8G8_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8_UINT);
break;
case VK_FORMAT_R8G8_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8_SINT);
break;
case VK_FORMAT_B8G8R8_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_B8G8R8_UINT);
break;
case VK_FORMAT_B8G8R8_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_B8G8R8_SINT);
break;
case VK_FORMAT_R8G8B8_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8_UINT);
break;
case VK_FORMAT_R8G8B8_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8_SINT);
break;
case VK_FORMAT_R8G8B8A8_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_UINT);
break;
case VK_FORMAT_R8G8B8A8_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_SINT);
break;
case VK_FORMAT_B8G8R8A8_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_B8G8R8A8_UINT);
break;
case VK_FORMAT_B8G8R8A8_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_B8G8R8A8_SINT);
break;
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_UINT);
break;
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_SINT);
break;
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_UINT);
break;
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_SINT);
break;
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_SRGB);
break;
case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_A2R10G10B10_UINT_PACK32);
break;
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_A2R10G10B10_SINT_PACK32);
break;
case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_A2B10G10R10_UINT_PACK32);
break;
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_A2B10G10R10_SINT_PACK32);
break;
case VK_FORMAT_R16_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R16_UINT);
break;
case VK_FORMAT_R16_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R16_SINT);
break;
case VK_FORMAT_R16G16_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R16G16_UINT);
break;
case VK_FORMAT_R16G16_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R16G16_SINT);
break;
case VK_FORMAT_R16G16B16_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R16G16B16_UINT);
break;
case VK_FORMAT_R16G16B16_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R16G16B16_SINT);
break;
case VK_FORMAT_R16G16B16A16_USCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R16G16B16A16_UINT);
break;
case VK_FORMAT_R16G16B16A16_SSCALED:
EXPECT_EQ(formatOut, VK_FORMAT_R16G16B16A16_SINT);
break;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_UNORM);
break;
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
EXPECT_EQ(formatOut, VK_FORMAT_R8G8B8A8_SNORM);
break;
default:
EXPECT_EQ(formatOut, formats[i]);
}
free(dfd);
}
}
//////////////////////////////
// HashListTest Fixture
//////////////////////////////
class HashListTest : public ::testing::Test {
protected:
HashListTest() : writerVal("HashListTest"), orientationVal("ruo") { }
void constructList(bool sort) {
KTX_error_code result;
ktxHashList_Construct(&head);
result = ktxHashList_AddKVPair(&head, KTX_WRITER_KEY,
(ktx_uint32_t)writerVal.length() + 1,
writerVal.c_str());
EXPECT_EQ(result, KTX_SUCCESS);
result = ktxHashList_AddKVPair(&head, KTX_ORIENTATION_KEY,
(ktx_uint32_t)orientationVal.length() + 1,
orientationVal.c_str());
EXPECT_EQ(result, KTX_SUCCESS);
if (sort) {
ktxHashList_Sort(&head);
sorted = true;
}
}
void checkList() {
compareList(head, sorted);
}
void compareList(ktxHashList list, bool isSorted) {
ktxHashListEntry* entry = list;
ktx_uint32_t entryCount = 0;
for (; entry != NULL; entry = ktxHashList_Next(entry)) {
char* key;
ktx_uint8_t* value;
ktx_uint32_t keyLen, valueLen;
entryCount++;
ktxHashListEntry_GetKey(entry, &keyLen, &key);
ktxHashListEntry_GetValue(entry, &valueLen, (void**)&value);
if (isSorted) {
switch (entryCount) {
case 1:
EXPECT_STREQ(key, KTX_ORIENTATION_KEY);
break;
case 2:
EXPECT_STREQ(key, KTX_WRITER_KEY);
break;
default:
break;
}
}
if (strcmp(key, KTX_ORIENTATION_KEY) == 0)
EXPECT_EQ(orientationVal.compare((char*)value), 0);
else if (strcmp(key, KTX_WRITER_KEY) == 0)
EXPECT_EQ(writerVal.compare((char*)value), 0);
else
EXPECT_TRUE(false);
}
EXPECT_EQ(entryCount, 2U);
}
ktxHashList head;
std::string writerVal;
std::string orientationVal;
bool sorted;
};
//////////////////////////////
// HashListTests
//////////////////////////////
TEST_F(HashListTest, ConstructSorted) {
constructList(true);
checkList();
}
TEST_F(HashListTest, ConstructCopy) {
ktxHashList copyHead;
constructList(true);
ktxHashList_ConstructCopy(&copyHead, head);
compareList(copyHead, true);
}
///////////////////////
// Swizzle test fixture
///////////////////////
template<ktx_uint32_t num_components, GLenum internalformat>
class SwizzleTestBase : public ::testing::Test {
public:
SwizzleTestBase() {
std::vector<GLubyte> color;
// Use swizzle enumerator values for easy checking of result.
std::vector<GLubyte> defaultColor = { R, G, B, A };
color.resize(num_components);
for (uint32_t i = 0; i < num_components; i++) {
color[i] = defaultColor[i];
}
helper.resize(createFlagBits::eNone, 1, 1, 2, width, height, 1, &color);
}
void runTest(swizzle_e swizzle[4]) {
ktxTexture2* texture;
ktx_error_code_e result;
helper.texinfo.vkFormat
= vkGetFormatFromOpenGLInternalFormat(helper.texinfo.glInternalformat);
result = ktxTexture2_Create(&helper.texinfo,
KTX_TEXTURE_CREATE_ALLOC_STORAGE,
&texture);
ASSERT_TRUE(result == KTX_SUCCESS);
ASSERT_TRUE(texture != NULL) << "ktxTexture_Create failed: "
<< ktxErrorString(result);
ASSERT_TRUE(texture->pData != NULL) << "Image storage not allocated";
result = helper.copyImagesToTexture(ktxTexture(texture));
ASSERT_TRUE(result == KTX_SUCCESS);
size_t destByteLen = width * height * 4 * sizeof(GLubyte);
size_t srcByteLen = width * height * num_components * sizeof(GLubyte);
typedef GLubyte color[4];
color* dest = (color*)malloc(destByteLen);
memset(dest, 0x7f, destByteLen);
swizzle_to_rgba((uint8_t*)dest,
texture->pData,
num_components,
srcByteLen,
swizzle);
for (uint32_t i = 0; i < width * height; i++) {
for (uint32_t c = 0; c < 4; c++) {
if (swizzle[c] == ZERO)
EXPECT_EQ(dest[i][c], 0);
else if (swizzle[c] == ONE)
EXPECT_EQ(dest[i][c], 255);
else
EXPECT_EQ(swizzle[c], dest[i][c]) << "c = " << c << ", i = " << i;
}
}
}
protected:
WriterTestHelper<GLubyte, num_components, internalformat> helper;
const unsigned int width = 16;
const unsigned int height = 16;
};
class SwizzleToRGBATestR8 : public SwizzleTestBase<1, GL_R8> { };
class SwizzleToRGBATestRG8 : public SwizzleTestBase<2, GL_RG8> { };
class SwizzleToRGBATestRGB8 : public SwizzleTestBase<3, GL_RGB8> { };
class SwizzleToRGBATestRGBA8 : public SwizzleTestBase<4, GL_RGBA8> { };
////////////////////////
// swizzle_to_rgba tests
////////////////////////
TEST_F(SwizzleToRGBATestR8, RRRONE) {
swizzle_e r_to_rgba_mapping[4] = { R, R, R, ONE };
runTest(r_to_rgba_mapping);
}
TEST_F(SwizzleToRGBATestRG8, RRRG) {
swizzle_e r_to_rgba_mapping[4] = { R, R, R, G };
runTest(r_to_rgba_mapping);
}
TEST_F(SwizzleToRGBATestRGB8, RGBONE) {
swizzle_e r_to_rgba_mapping[4] = { R, G, B, ONE };
runTest(r_to_rgba_mapping);
}
TEST_F(SwizzleToRGBATestRGB8, RRRG) {
swizzle_e r_to_rgba_mapping[4] = { R, R, R, G };
runTest(r_to_rgba_mapping);
}
TEST_F(SwizzleToRGBATestRGBA8, RGBA) {
swizzle_e r_to_rgba_mapping[4] = { R, G, B, A };
runTest(r_to_rgba_mapping);
}
TEST_F(SwizzleToRGBATestRGBA8, RRRG) {
swizzle_e r_to_rgba_mapping[4] = { R, R, R, G };
runTest(r_to_rgba_mapping);
}
TEST_F(SwizzleToRGBATestRGBA8, BGRA) {
swizzle_e r_to_rgba_mapping[4] = { B, G, R, A };
runTest(r_to_rgba_mapping);
}
TEST_F(SwizzleToRGBATestRGBA8, BGRZERO) {
swizzle_e r_to_rgba_mapping[4] = { B, G, R, ZERO };
runTest(r_to_rgba_mapping);
}
TEST_F(SwizzleToRGBATestRGBA8, ARGB) {
swizzle_e r_to_rgba_mapping[4] = { A, R, G, B };
runTest(r_to_rgba_mapping);
}
//////////////////////////////
// LoadTest exceptions tests
//////////////////////////////
#define OUT_OF_HOST_MEMORY -1
#define OUT_OF_DEVICE_MEMORY -2
#define FRAGMENTED_POOL -12
#define OUT_OF_POOL_MEMORY -1000069000
TEST(BadVulkanAllocExceptionTest, NoDeviceMemory) {
try {
throw bad_vulkan_alloc(OUT_OF_DEVICE_MEMORY, "no device memory test");
} catch (bad_vulkan_alloc& e) {
EXPECT_EQ(strcmp(e.what(), "Out of device memory for no device memory test."), 0);
}
}
TEST(BadVulkanAllocExceptionTest, NoHostMemory) {
try {
throw bad_vulkan_alloc(OUT_OF_HOST_MEMORY, "no host memory test");
} catch (bad_vulkan_alloc& e) {
EXPECT_EQ(strcmp(e.what(), "Out of host memory for no host memory test."), 0);
}
}
TEST(BadVulkanAllocExceptionTest, NoPoolMemory) {
try {
throw bad_vulkan_alloc(OUT_OF_POOL_MEMORY, "no pool memory test");
} catch (bad_vulkan_alloc& e) {
EXPECT_EQ(strcmp(e.what(), "Out of pool memory for no pool memory test."), 0);
}
}
TEST(BadVulkanAllocExceptionTest, PoolFragmented) {
try {
throw bad_vulkan_alloc(FRAGMENTED_POOL, "fragmented pool memory test");
} catch (bad_vulkan_alloc& e) {
EXPECT_EQ(strcmp(e.what(), "Pool fragmented when allocating for fragmented pool memory test."), 0);
}
}
} // namespace