Implement the extended scope and further improvements for ktxtools (#722)

- tools: Implement stdout support
- tools: Implement stdin support
- tools: Implement 4:2:2 support
- tools: Implement support for 10X6 and 10X4 formats
- tools: Implement support for B10G11R11_UFLOAT and E5B9G9R9_UFLOAT formats
- tools: Complete support for Depth-Stencil formats
- tools: Improvements, cleanup and bug fixes
- extract: Implement fragment URI support
- extract: Implement 4:2:2 sub-sampling
- validate: Fix and extend padding byte validation checks
- cts: Add support for stdin/stdout test cases (including binary IO)
- cts: Add tests to cover new features and capabilities
- cts: Extend existing tests to improve coverage
- cts: Test runner now deletes matching output files (this enables easy packaging of mismatching files)
- cts: Added a new cli arg to the runner script: --keep-matching-outputs to prevent the deletion of matching output files
- dfdUtils: Implement 4:2:2 support
- dfdUtils: Implement support for 10X6 and 10X4 formats
- imageio: Fix stdin support
- ktx: Add stringToVkFormat to mkvkformatfiles
- ktx: Implement 3D BC support (ASTC 3D Blocks)
- ktx: Implement 4:2:2 support
- ktx: Complete support for Depth-Stencil formats
- ktx: Improve interpretDFD
- ktx: Improvements, cleanup and bug fixes
- cmake: Add CMake generator expression for output directory on Mac
This commit is contained in:
Császár Mátyás
2023-07-01 02:41:53 +02:00
committed by GitHub
parent ba28b4128f
commit 2189c54e3e
39 changed files with 2765 additions and 1333 deletions

View File

@@ -359,7 +359,7 @@ elseif(APPLE)
# Set a common RUNTIME_OUTPUT_DIR for all targets, so that
# INSTALL RPATH is functional in build directory as well.
# BUILD_WITH_INSTALL_RPATH is necessary for working code signing.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${KTX_BUILD_DIR}/$<CONFIG>)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${KTX_BUILD_DIR}/$<CONFIG>>)
endif()
endif()

View File

@@ -15,7 +15,9 @@
* Author: Andrew Garrard
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <KHR/khr_df.h>
#include "dfd.h"
@@ -228,6 +230,8 @@ uint32_t *createDFDUnpacked(int bigEndian, int numChannels, int bytes,
* @param bits[] An array of length numChannels.
* Each entry is the number of bits composing the channel, in
* order starting at bit 0 of the packed type.
* @param paddings[] An array of length numChannels.
* Each entry is the number of padding bits after each channel.
* @param channels[] An array of length numChannels.
* Each entry enumerates the channel type: 0 = red, 1 = green,
* 2 = blue, 15 = alpha, in order starting at bit 0 of the
@@ -239,9 +243,9 @@ uint32_t *createDFDUnpacked(int bigEndian, int numChannels, int bytes,
* @return A data format descriptor in malloc'd data. The caller is responsible
* for freeing the descriptor.
**/
uint32_t *createDFDPacked(int bigEndian, int numChannels,
int bits[], int channels[],
enum VkSuffix suffix)
uint32_t *createDFDPackedPadded(int bigEndian, int numChannels,
int bits[], int paddings[], int channels[],
enum VkSuffix suffix)
{
uint32_t *DFD = 0;
if (numChannels == 6) {
@@ -287,7 +291,7 @@ uint32_t *createDFDPacked(int bigEndian, int numChannels,
int sampleCounter;
for (channelCounter = 0; channelCounter < numChannels; ++channelCounter) {
beChannelStart[channelCounter] = totalBits;
totalBits += bits[channelCounter];
totalBits += bits[channelCounter] + paddings[channelCounter];
}
BEMask = (totalBits - 1) & 0x18;
for (channelCounter = 0; channelCounter < numChannels; ++channelCounter) {
@@ -297,7 +301,7 @@ uint32_t *createDFDPacked(int bigEndian, int numChannels,
bitChannel[((bitOffset + bits[channelCounter] - 1) & ~7) ^ BEMask] = channelCounter;
numSamples++;
}
bitOffset += bits[channelCounter];
bitOffset += bits[channelCounter] + paddings[channelCounter];
}
DFD = writeHeader(numSamples, totalBits >> 3, suffix, i_COLOR);
@@ -339,7 +343,7 @@ uint32_t *createDFDPacked(int bigEndian, int numChannels,
int totalBits = 0;
int bitOffset = 0;
for (sampleCounter = 0; sampleCounter < numChannels; ++sampleCounter) {
totalBits += bits[sampleCounter];
totalBits += bits[sampleCounter] + paddings[sampleCounter];
}
/* One sample per channel */
@@ -348,12 +352,98 @@ uint32_t *createDFDPacked(int bigEndian, int numChannels,
writeSample(DFD, sampleCounter, channels[sampleCounter],
bits[sampleCounter], bitOffset,
1, 1, suffix);
bitOffset += bits[sampleCounter];
bitOffset += bits[sampleCounter] + paddings[sampleCounter];
}
}
return DFD;
}
/**
* @~English
* @brief Create a Data Format Descriptor for a packed format.
*
* @param bigEndian Big-endian flag: Set to 1 for big-endian byte ordering and
* 0 for little-endian byte ordering.
* @param numChannels The number of color channels.
* @param bits[] An array of length numChannels.
* Each entry is the number of bits composing the channel, in
* order starting at bit 0 of the packed type.
* @param channels[] An array of length numChannels.
* Each entry enumerates the channel type: 0 = red, 1 = green,
* 2 = blue, 15 = alpha, in order starting at bit 0 of the
* packed type. These values match channel IDs for RGBSDA in
* the Khronos Data Format header. To simplify iteration
* through channels, channel id 3 is a synonym for alpha.
* @param suffix Indicates the format suffix for the type.
*
* @return A data format descriptor in malloc'd data. The caller is responsible
* for freeing the descriptor.
**/
uint32_t *createDFDPacked(int bigEndian, int numChannels,
int bits[], int channels[],
enum VkSuffix suffix) {
assert(numChannels <= 6);
int paddings[] = {0, 0, 0, 0, 0, 0};
return createDFDPackedPadded(bigEndian, numChannels, bits, paddings, channels, suffix);
}
uint32_t *createDFD422(int bigEndian, int numSamples,
int bits[], int paddings[], int channels[],
int position_xs[], int position_ys[],
enum VkSuffix suffix) {
assert(!bigEndian); (void) bigEndian;
assert(suffix == s_UNORM); (void) suffix;
int totalBits = 0;
for (int i = 0; i < numSamples; ++i)
totalBits += bits[i] + paddings[i];
assert(totalBits % 8 == 0);
uint32_t BDFDSize = sizeof(uint32_t) * (KHR_DF_WORD_SAMPLESTART + numSamples * KHR_DF_WORD_SAMPLEWORDS);
uint32_t DFDSize = sizeof(uint32_t) + BDFDSize;
uint32_t *DFD = (uint32_t *) malloc(DFDSize);
memset(DFD, 0, DFDSize);
DFD[0] = DFDSize;
uint32_t *BDFD = DFD + 1;
KHR_DFDSETVAL(BDFD, VENDORID, KHR_DF_VENDORID_KHRONOS);
KHR_DFDSETVAL(BDFD, DESCRIPTORTYPE, KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT);
KHR_DFDSETVAL(BDFD, VERSIONNUMBER, KHR_DF_VERSIONNUMBER_LATEST);
KHR_DFDSETVAL(BDFD, DESCRIPTORBLOCKSIZE, BDFDSize);
KHR_DFDSETVAL(BDFD, MODEL, KHR_DF_MODEL_YUVSDA);
KHR_DFDSETVAL(BDFD, PRIMARIES, KHR_DF_PRIMARIES_UNSPECIFIED);
KHR_DFDSETVAL(BDFD, TRANSFER, KHR_DF_TRANSFER_LINEAR);
KHR_DFDSETVAL(BDFD, FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT);
KHR_DFDSETVAL(BDFD, TEXELBLOCKDIMENSION0, 2 - 1); // 422 contains 2 x 1 blocks
KHR_DFDSETVAL(BDFD, TEXELBLOCKDIMENSION1, 1 - 1);
KHR_DFDSETVAL(BDFD, TEXELBLOCKDIMENSION2, 1 - 1);
KHR_DFDSETVAL(BDFD, TEXELBLOCKDIMENSION3, 1 - 1);
KHR_DFDSETVAL(BDFD, BYTESPLANE0, totalBits / 8);
KHR_DFDSETVAL(BDFD, BYTESPLANE1, 0);
KHR_DFDSETVAL(BDFD, BYTESPLANE2, 0);
KHR_DFDSETVAL(BDFD, BYTESPLANE3, 0);
KHR_DFDSETVAL(BDFD, BYTESPLANE4, 0);
KHR_DFDSETVAL(BDFD, BYTESPLANE5, 0);
KHR_DFDSETVAL(BDFD, BYTESPLANE6, 0);
KHR_DFDSETVAL(BDFD, BYTESPLANE7, 0);
int bitOffset = 0;
for (int i = 0; i < numSamples; ++i) {
KHR_DFDSETSVAL(BDFD, i, BITOFFSET, bitOffset);
KHR_DFDSETSVAL(BDFD, i, BITLENGTH, bits[i] - 1);
KHR_DFDSETSVAL(BDFD, i, CHANNELID, channels[i]);
KHR_DFDSETSVAL(BDFD, i, QUALIFIERS, 0); // None of: FLOAT, SIGNED, EXPONENT, LINEAR
KHR_DFDSETSVAL(BDFD, i, SAMPLEPOSITION0, position_xs[i]);
KHR_DFDSETSVAL(BDFD, i, SAMPLEPOSITION1, position_ys[i]);
KHR_DFDSETSVAL(BDFD, i, SAMPLEPOSITION2, 0);
KHR_DFDSETSVAL(BDFD, i, SAMPLEPOSITION3, 0);
KHR_DFDSETSVAL(BDFD, i, SAMPLELOWER, 0);
KHR_DFDSETSVAL(BDFD, i, SAMPLEUPPER, (1u << bits[i]) - 1u);
bitOffset += bits[i] + paddings[i];
}
return DFD;
}
static khr_df_model_e compModelMapping[] = {
KHR_DF_MODEL_BC1A, /*!< BC1, aka DXT1, no alpha. */
KHR_DF_MODEL_BC1A, /*!< BC1, aka DXT1, punch-through alpha. */

View File

@@ -73,11 +73,22 @@ uint32_t* vk2dfd(enum VkFormat format);
uint32_t *createDFDUnpacked(int bigEndian, int numChannels, int bytes,
int redBlueSwap, enum VkSuffix suffix);
/* Create a Data Format Descriptor for a packed padded format. */
uint32_t *createDFDPackedPadded(int bigEndian, int numChannels,
int bits[], int paddings[], int channels[],
enum VkSuffix suffix);
/* Create a Data Format Descriptor for a packed format. */
uint32_t *createDFDPacked(int bigEndian, int numChannels,
int bits[], int channels[],
enum VkSuffix suffix);
/* Create a Data Format Descriptor for a 4:2:2 format. */
uint32_t *createDFD422(int bigEndian, int numChannels,
int bits[], int paddings[], int channels[],
int position_xs[], int position_ys[],
enum VkSuffix suffix);
/* Create a Data Format Descriptor for a compressed format. */
uint32_t *createDFDCompressed(enum VkCompScheme compScheme,
int bwidth, int bheight, int bdepth,
@@ -91,13 +102,15 @@ uint32_t *createDFDDepthStencil(int depthBits,
/** @brief Result of interpreting the data format descriptor. */
enum InterpretDFDResult {
i_LITTLE_ENDIAN_FORMAT_BIT = 0, /*!< Confirmed little-endian (default for 8bpc). */
i_BIG_ENDIAN_FORMAT_BIT = 1, /*!< Confirmed big-endian. */
i_PACKED_FORMAT_BIT = 2, /*!< Packed format. */
i_SRGB_FORMAT_BIT = 4, /*!< sRGB transfer function. */
i_NORMALIZED_FORMAT_BIT = 8, /*!< Normalized (UNORM or SNORM). */
i_SIGNED_FORMAT_BIT = 16, /*!< Format is signed. */
i_FLOAT_FORMAT_BIT = 32, /*!< Format is floating point. */
i_UNSUPPORTED_ERROR_BIT = 64, /*!< Format not successfully interpreted. */
i_BIG_ENDIAN_FORMAT_BIT = 1u << 0u, /*!< Confirmed big-endian. */
i_PACKED_FORMAT_BIT = 1u << 1u, /*!< Packed format. */
i_SRGB_FORMAT_BIT = 1u << 2u, /*!< sRGB transfer function. */
i_NORMALIZED_FORMAT_BIT = 1u << 3u, /*!< Normalized (UNORM or SNORM). */
i_SIGNED_FORMAT_BIT = 1u << 4u, /*!< Format is signed. */
i_FLOAT_FORMAT_BIT = 1u << 5u, /*!< Format is floating point. */
i_COMPRESSED_FORMAT_BIT = 1u << 6u, /*!< Format is block compressed (422). */
i_YUVSDA_FORMAT_BIT = 1u << 7u, /*!< Color model is YUVSDA. */
i_UNSUPPORTED_ERROR_BIT = 1u << 8u, /*!< Format not successfully interpreted. */
/** "NONTRIVIAL_ENDIANNESS" means not big-endian, not little-endian
* (a channel has bits that are not consecutive in either order). **/
i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS = i_UNSUPPORTED_ERROR_BIT,
@@ -110,7 +123,9 @@ enum InterpretDFDResult {
i_UNSUPPORTED_CHANNEL_TYPES = i_UNSUPPORTED_ERROR_BIT + 3,
/** Only channels with the same flags are supported
* (e.g. we don't support float red with integer green). */
i_UNSUPPORTED_MIXED_CHANNELS = i_UNSUPPORTED_ERROR_BIT + 4
i_UNSUPPORTED_MIXED_CHANNELS = i_UNSUPPORTED_ERROR_BIT + 4,
/** Only 2x1 block is supported for YUVSDA model. */
i_UNSUPPORTED_BLOCK_DIMENSIONS = i_UNSUPPORTED_ERROR_BIT + 5,
};
/** @brief Interpretation of a channel from the data format descriptor. */

View File

@@ -17,6 +17,14 @@
#include <KHR/khr_df.h>
#include "dfd.h"
static uint32_t bit_ceil(uint32_t x) {
x -= 1;
for (uint32_t i = 0; i < sizeof(x) * 8; ++i)
if (1u << i > x)
return 1u << i;
return 0;
}
/**
* @~English
* @brief Interpret a Data Format Descriptor for a simple format.
@@ -54,14 +62,15 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
const uint32_t *BDFDB = DFD+1;
uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB);
if (numSamples == 0)
return i_UNSUPPORTED_CHANNEL_TYPES;
uint32_t sampleCounter;
int determinedEndianness = 0;
int determinedNormalizedness = 0;
int determinedSignedness = 0;
int determinedFloatness = 0;
enum InterpretDFDResult result = 0; /* Build this up incrementally. */
bool isDepthStencil = false;
/* Clear these so following code doesn't get confused. */
R->offset = R->size = 0;
G->offset = G->size = 0;
@@ -76,85 +85,6 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
if ((BDFDB[KHR_DF_WORD_BYTESPLANE0] & ~KHR_DF_MASK_BYTESPLANE0)
|| BDFDB[KHR_DF_WORD_BYTESPLANE4]) return i_UNSUPPORTED_MULTIPLE_PLANES;
/* Only support the RGB color model. */
/* We could expand this to allow "UNSPECIFIED" as well. */
if (KHR_DFDVAL(BDFDB, MODEL) != KHR_DF_MODEL_RGBSDA) return i_UNSUPPORTED_CHANNEL_TYPES;
/* We only pay attention to sRGB. */
if (KHR_DFDVAL(BDFDB, TRANSFER) == KHR_DF_TRANSFER_SRGB) result |= i_SRGB_FORMAT_BIT;
/* We only support samples at coordinate 0,0,0,0. */
/* (We could confirm this from texel_block_dimensions in 1.2, but */
/* the interpretation might change in later versions.) */
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEPOSITION_ALL))
return i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS;
}
/* Set flags and check for consistency. */
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
/* Note: We're ignoring 9995, which is weird and worth special-casing */
/* rather than trying to generalise to all float formats. */
if (!determinedFloatness) {
if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
& KHR_DF_SAMPLE_DATATYPE_FLOAT) {
result |= i_FLOAT_FORMAT_BIT;
}
determinedFloatness = 1;
} else {
/* Check whether we disagree with our predetermined floatness. */
/* Note that this could justifiably happen with (say) D24S8. */
if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
& KHR_DF_SAMPLE_DATATYPE_FLOAT) {
if (!(result & i_FLOAT_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
} else {
if ((result & i_FLOAT_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
}
}
if (!determinedSignedness) {
if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
& KHR_DF_SAMPLE_DATATYPE_SIGNED) {
result |= i_SIGNED_FORMAT_BIT;
}
determinedSignedness = 1;
} else {
/* Check whether we disagree with our predetermined signedness. */
if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
& KHR_DF_SAMPLE_DATATYPE_SIGNED) {
if (!(result & i_SIGNED_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
} else {
if ((result & i_SIGNED_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
}
}
/* We define "unnormalized" as "sample_upper = 1". */
/* We don't check whether any non-1 normalization value is correct */
/* (i.e. set to the maximum bit value, and check min value) on */
/* the assumption that we're looking at a format which *came* from */
/* an API we can support. */
if (!determinedNormalizedness) {
/* The ambiguity here is if the bottom bit is a single-bit value, */
/* as in RGBA 5:5:5:1, so we defer the decision if the channel only has one bit. */
if (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) > 0) {
if ((result & i_FLOAT_FORMAT_BIT)) {
if (*(float *)(void *)&BDFDB[KHR_DF_WORD_SAMPLESTART +
KHR_DF_WORD_SAMPLEWORDS * sampleCounter +
KHR_DF_SAMPLEWORD_SAMPLEUPPER] != 1.0f) {
result |= i_NORMALIZED_FORMAT_BIT;
}
} else {
if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEUPPER) != 1U) {
result |= i_NORMALIZED_FORMAT_BIT;
}
}
determinedNormalizedness = 1;
}
}
/* Note: We don't check for inconsistent normalization, because */
/* channels composed of multiple samples will have 0 in the */
/* lower/upper range. */
/* This heuristic should handle 64-bit integers, too. */
}
/* If this is a packed format, we work out our offsets differently. */
/* We assume a packed format has channels that aren't byte-aligned. */
/* If we have a format in which every channel is byte-aligned *and* packed, */
@@ -162,196 +92,315 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
/* version in this case, and if hardware has to pack it and swizzle, */
/* that's up to the hardware to special-case. */
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
if (KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) & 0x7U) {
uint32_t offset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET);
uint32_t length = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1;
if ((offset & 0x7U) || ((offset + length) & 0x7U)) {
result |= i_PACKED_FORMAT_BIT;
/* Once we're packed, we're packed, no need to keep checking. */
break;
}
}
/* Remember: the canonical ordering of samples is to start with */
/* the lowest bit of the channel/location which touches bit 0 of */
/* the data, when the latter is concatenated in little-endian order, */
/* and then progress until all the bits of that channel/location */
/* have been processed. Multiple channels sharing the same source */
/* bits are processed in channel ID order. (I should clarify this */
/* for partially-shared data, but it doesn't really matter so long */
/* as everything is consecutive, except to make things canonical.) */
/* Note: For standard formats we could determine big/little-endianness */
/* simply from whether the first sample starts in bit 0; technically */
/* it's possible to have a format with unaligned channels wherein the */
/* first channel starts at bit 0 and is one byte, yet other channels */
/* take more bytes or aren't aligned (e.g. D24S8), but this should be */
/* irrelevant for the formats that we support. */
if ((result & i_PACKED_FORMAT_BIT)) {
/* A packed format. */
uint32_t currentChannel = ~0U; /* Don't start matched. */
uint32_t currentBitOffset = 0;
uint32_t currentByteOffset = 0;
uint32_t currentBitLength = 0;
*wordBytes = (BDFDB[KHR_DF_WORD_BYTESPLANE0] & 0xFFU);
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
uint32_t sampleBitOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET);
uint32_t sampleByteOffset = sampleBitOffset >> 3U;
/* The sample bitLength field stores the bit length - 1. */
uint32_t sampleBitLength = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1;
uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
InterpretedDFDChannel *sampleChannelPtr;
switch (sampleChannel) {
case KHR_DF_CHANNEL_RGBSDA_RED:
sampleChannelPtr = R;
// Check data types.
bool hasSigned = false;
bool hasFloat = false;
bool hasNormalized = false;
// Note: We're ignoring 9995, which is weird and worth special-casing
// rather than trying to generalise to all float formats.
for (uint32_t i = 0; i < numSamples; ++i) {
const bool isSigned = (KHR_DFDSVAL(BDFDB, i, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED) != 0;
const bool isFloat = (KHR_DFDSVAL(BDFDB, i, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_FLOAT) != 0;
// We define "unnormalized" as "sample_upper = 1" or "sample_upper = 1.0f".
// We don't check whether any non-1 normalization value is correct
// (i.e. set to the maximum bit value, and check min value) on
// the assumption that we're looking at a format which *came* from
// an API we can support.
const bool isNormalized = isFloat ?
*(float*) (void*) &BDFDB[KHR_DF_WORD_SAMPLESTART +
KHR_DF_WORD_SAMPLEWORDS * sampleCounter +
KHR_DF_SAMPLEWORD_SAMPLEUPPER] != 1.0f :
KHR_DFDSVAL(BDFDB, i, SAMPLEUPPER) != 1U;
hasSigned |= isSigned;
hasFloat |= isFloat;
// By our definition the normalizedness of a single bit channel (like in RGBA 5:5:5:1)
// is ambiguous. Ignore these during normalized checks.
if (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) > 0)
hasNormalized |= isNormalized;
}
result |= hasSigned ? i_SIGNED_FORMAT_BIT : 0;
result |= hasFloat ? i_FLOAT_FORMAT_BIT : 0;
result |= hasNormalized ? i_NORMALIZED_FORMAT_BIT : 0;
// Checks based on color model
if (KHR_DFDVAL(BDFDB, MODEL) == KHR_DF_MODEL_YUVSDA) {
result |= i_NORMALIZED_FORMAT_BIT;
result |= i_COMPRESSED_FORMAT_BIT;
result |= i_YUVSDA_FORMAT_BIT;
for (uint32_t i = 0; i < numSamples; ++i) {
switch (KHR_DFDSVAL(BDFDB, i, CHANNELID)) {
case KHR_DF_CHANNEL_YUVSDA_Y:
case KHR_DF_CHANNEL_YUVSDA_U:
case KHR_DF_CHANNEL_YUVSDA_V:
case KHR_DF_CHANNEL_YUVSDA_A:
break;
case KHR_DF_CHANNEL_RGBSDA_GREEN:
sampleChannelPtr = G;
break;
case KHR_DF_CHANNEL_RGBSDA_BLUE:
sampleChannelPtr = B;
break;
case KHR_DF_CHANNEL_RGBSDA_DEPTH:
sampleChannelPtr = R;
break;
case KHR_DF_CHANNEL_RGBSDA_STENCIL:
sampleChannelPtr = G;
break;
case KHR_DF_CHANNEL_RGBSDA_ALPHA:
sampleChannelPtr = A;
case KHR_DF_CHANNEL_YUVSDA_DEPTH:
case KHR_DF_CHANNEL_YUVSDA_STENCIL:
isDepthStencil = true;
break;
default:
return i_UNSUPPORTED_CHANNEL_TYPES;
}
if (sampleChannel == currentChannel) {
/* Continuation of the same channel. */
/* Since a big (>32-bit) channel isn't "packed", */
/* this should only happen in big-endian, or if */
/* we have a wacky format that we won't support. */
if (sampleByteOffset == currentByteOffset - 1U && /* One byte earlier */
((currentBitOffset + currentBitLength) & 7U) == 0 && /* Already at the end of a byte */
(sampleBitOffset & 7U) == 0) { /* Start at the beginning of the byte */
/* All is good, continue big-endian. */
/* N.B. We shouldn't be here if we decided we were little-endian, */
/* so we don't bother to check that disagreement. */
result |= i_BIG_ENDIAN_FORMAT_BIT;
determinedEndianness = 1;
} else {
/* Oh dear. */
/* We could be little-endian, but not with any standard format. */
/* More likely we've got something weird that we can't support. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* Remember where we are. */
currentBitOffset = sampleBitOffset;
currentByteOffset = sampleByteOffset;
currentBitLength = sampleBitLength;
/* Accumulate the bit length. */
sampleChannelPtr->size += sampleBitLength;
} else {
/* Everything is new. Hopefully. */
currentChannel = sampleChannel;
currentBitOffset = sampleBitOffset;
currentByteOffset = sampleByteOffset;
currentBitLength = sampleBitLength;
if (sampleChannelPtr->size) {
/* Uh-oh, we've seen this channel before. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* For now, record the bit offset in little-endian terms, */
/* because we may not know to reverse it yet. */
sampleChannelPtr->offset = sampleBitOffset;
sampleChannelPtr->size = sampleBitLength;
}
// Determine wordBytes
uint32_t largestSampleSize = 0;
for (uint32_t i = 0; i < numSamples; ++i) {
uint32_t length = KHR_DFDSVAL(BDFDB, i, BITLENGTH) + 1;
if (largestSampleSize < length)
largestSampleSize = length;
}
*wordBytes = ((result & i_PACKED_FORMAT_BIT) ? 4 : 1) * bit_ceil(largestSampleSize) / 8;
} else if (KHR_DFDVAL(BDFDB, MODEL) == KHR_DF_MODEL_RGBSDA) {
/* We only pay attention to sRGB. */
if (KHR_DFDVAL(BDFDB, TRANSFER) == KHR_DF_TRANSFER_SRGB) result |= i_SRGB_FORMAT_BIT;
/* We only support samples at coordinate 0,0,0,0. */
/* (We could confirm this from texel_block_dimensions in 1.2, but */
/* the interpretation might change in later versions.) */
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEPOSITION_ALL))
return i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS;
}
/* For Depth/Stencil formats mixed channels are allowed */
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
switch (KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID)) {
case KHR_DF_CHANNEL_RGBSDA_DEPTH:
case KHR_DF_CHANNEL_RGBSDA_STENCIL:
isDepthStencil = true;
break;
default:
break;
}
}
if ((result & i_BIG_ENDIAN_FORMAT_BIT)) {
/* Our bit offsets to bit 0 of each channel are in little-endian terms. */
/* We need to do a byte swap to work out where they should be. */
/* We assume, for sanity, that byte sizes are a power of two for this. */
uint32_t offsetMask = (*wordBytes - 1U) << 3U;
R->offset ^= offsetMask;
G->offset ^= offsetMask;
B->offset ^= offsetMask;
A->offset ^= offsetMask;
// Check for mixed channels
if (!isDepthStencil) {
for (uint32_t i = 0; i < numSamples; ++i) {
const bool isSigned = (KHR_DFDSVAL(BDFDB, i, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED) != 0;
const bool isFloat = (KHR_DFDSVAL(BDFDB, i, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_FLOAT) != 0;
if (isSigned != hasSigned)
return i_UNSUPPORTED_MIXED_CHANNELS;
if (isFloat != hasFloat)
return i_UNSUPPORTED_MIXED_CHANNELS;
// Note: We don't check for inconsistent normalization, because
// channels composed of multiple samples will have 0 in the
// lower/upper range. Single bit channels are also ambiguous.
// This heuristic should handle 64-bit integers, too.
}
}
/* Remember: the canonical ordering of samples is to start with */
/* the lowest bit of the channel/location which touches bit 0 of */
/* the data, when the latter is concatenated in little-endian order, */
/* and then progress until all the bits of that channel/location */
/* have been processed. Multiple channels sharing the same source */
/* bits are processed in channel ID order. (I should clarify this */
/* for partially-shared data, but it doesn't really matter so long */
/* as everything is consecutive, except to make things canonical.) */
/* Note: For standard formats we could determine big/little-endianness */
/* simply from whether the first sample starts in bit 0; technically */
/* it's possible to have a format with unaligned channels wherein the */
/* first channel starts at bit 0 and is one byte, yet other channels */
/* take more bytes or aren't aligned (e.g. D24S8), but this should be */
/* irrelevant for the formats that we support. */
if ((result & i_PACKED_FORMAT_BIT)) {
/* A packed format. */
uint32_t currentChannel = ~0U; /* Don't start matched. */
uint32_t currentBitOffset = 0;
uint32_t currentByteOffset = 0;
uint32_t currentBitLength = 0;
*wordBytes = (BDFDB[KHR_DF_WORD_BYTESPLANE0] & 0xFFU);
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
uint32_t sampleBitOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET);
uint32_t sampleByteOffset = sampleBitOffset >> 3U;
/* The sample bitLength field stores the bit length - 1. */
uint32_t sampleBitLength = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1;
uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
InterpretedDFDChannel *sampleChannelPtr;
switch (sampleChannel) {
case KHR_DF_CHANNEL_RGBSDA_RED:
sampleChannelPtr = R;
break;
case KHR_DF_CHANNEL_RGBSDA_GREEN:
sampleChannelPtr = G;
break;
case KHR_DF_CHANNEL_RGBSDA_BLUE:
sampleChannelPtr = B;
break;
case KHR_DF_CHANNEL_RGBSDA_DEPTH:
sampleChannelPtr = R;
break;
case KHR_DF_CHANNEL_RGBSDA_STENCIL:
sampleChannelPtr = G;
break;
case KHR_DF_CHANNEL_RGBSDA_ALPHA:
sampleChannelPtr = A;
break;
default:
return i_UNSUPPORTED_CHANNEL_TYPES;
}
if (sampleChannel == currentChannel) {
/* Continuation of the same channel. */
/* Since a big (>32-bit) channel isn't "packed", */
/* this should only happen in big-endian, or if */
/* we have a wacky format that we won't support. */
if (sampleByteOffset == currentByteOffset - 1U && /* One byte earlier */
((currentBitOffset + currentBitLength) & 7U) == 0 && /* Already at the end of a byte */
(sampleBitOffset & 7U) == 0) { /* Start at the beginning of the byte */
/* All is good, continue big-endian. */
/* N.B. We shouldn't be here if we decided we were little-endian, */
/* so we don't bother to check that disagreement. */
result |= i_BIG_ENDIAN_FORMAT_BIT;
determinedEndianness = 1;
} else {
/* Oh dear. */
/* We could be little-endian, but not with any standard format. */
/* More likely we've got something weird that we can't support. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* Remember where we are. */
currentBitOffset = sampleBitOffset;
currentByteOffset = sampleByteOffset;
currentBitLength = sampleBitLength;
/* Accumulate the bit length. */
sampleChannelPtr->size += sampleBitLength;
} else {
/* Everything is new. Hopefully. */
currentChannel = sampleChannel;
currentBitOffset = sampleBitOffset;
currentByteOffset = sampleByteOffset;
currentBitLength = sampleBitLength;
if (sampleChannelPtr->size) {
/* Uh-oh, we've seen this channel before. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* For now, record the bit offset in little-endian terms, */
/* because we may not know to reverse it yet. */
sampleChannelPtr->offset = sampleBitOffset;
sampleChannelPtr->size = sampleBitLength;
}
}
if ((result & i_BIG_ENDIAN_FORMAT_BIT)) {
/* Our bit offsets to bit 0 of each channel are in little-endian terms. */
/* We need to do a byte swap to work out where they should be. */
/* We assume, for sanity, that byte sizes are a power of two for this. */
uint32_t offsetMask = (*wordBytes - 1U) << 3U;
R->offset ^= offsetMask;
G->offset ^= offsetMask;
B->offset ^= offsetMask;
A->offset ^= offsetMask;
}
} else {
/* Not a packed format. */
/* Everything is byte-aligned. */
/* Question is whether there multiple samples per channel. */
uint32_t currentChannel = ~0U; /* Don't start matched. */
uint32_t currentByteOffset = 0;
uint32_t currentByteLength = 0;
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
uint32_t sampleByteOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) >> 3U;
uint32_t sampleByteLength = (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1) >> 3U;
uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
InterpretedDFDChannel *sampleChannelPtr;
switch (sampleChannel) {
case KHR_DF_CHANNEL_RGBSDA_RED:
sampleChannelPtr = R;
break;
case KHR_DF_CHANNEL_RGBSDA_GREEN:
sampleChannelPtr = G;
break;
case KHR_DF_CHANNEL_RGBSDA_BLUE:
sampleChannelPtr = B;
break;
case KHR_DF_CHANNEL_RGBSDA_DEPTH:
sampleChannelPtr = R;
break;
case KHR_DF_CHANNEL_RGBSDA_STENCIL:
sampleChannelPtr = G;
break;
case KHR_DF_CHANNEL_RGBSDA_ALPHA:
sampleChannelPtr = A;
break;
default:
return i_UNSUPPORTED_CHANNEL_TYPES;
}
if (sampleChannel == currentChannel) {
/* Continuation of the same channel. */
/* Either big-endian, or little-endian with a very large channel. */
if (sampleByteOffset == currentByteOffset - 1) { /* One byte earlier */
if (determinedEndianness && !(result & i_BIG_ENDIAN_FORMAT_BIT)) {
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* All is good, continue big-endian. */
result |= i_BIG_ENDIAN_FORMAT_BIT;
determinedEndianness = 1;
/* Update the start */
sampleChannelPtr->offset = sampleByteOffset;
} else if (sampleByteOffset == currentByteOffset + currentByteLength) {
if (determinedEndianness && (result & i_BIG_ENDIAN_FORMAT_BIT)) {
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* All is good, continue little-endian. */
determinedEndianness = 1;
} else {
/* Oh dear. */
/* We could be little-endian, but not with any standard format. */
/* More likely we've got something weird that we can't support. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* Remember where we are. */
currentByteOffset = sampleByteOffset;
currentByteLength = sampleByteLength;
/* Accumulate the byte length. */
sampleChannelPtr->size += sampleByteLength;
/* Assume these are all the same. */
*wordBytes = sampleChannelPtr->size;
} else {
/* Everything is new. Hopefully. */
currentChannel = sampleChannel;
currentByteOffset = sampleByteOffset;
currentByteLength = sampleByteLength;
if (sampleChannelPtr->size) {
/* Uh-oh, we've seen this channel before. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* For now, record the byte offset in little-endian terms, */
/* because we may not know to reverse it yet. */
sampleChannelPtr->offset = sampleByteOffset;
sampleChannelPtr->size = sampleByteLength;
/* Assume these are all the same. */
*wordBytes = sampleByteLength;
}
}
}
} else {
/* Not a packed format. */
/* Everything is byte-aligned. */
/* Question is whether there multiple samples per channel. */
uint32_t currentChannel = ~0U; /* Don't start matched. */
uint32_t currentByteOffset = 0;
uint32_t currentByteLength = 0;
for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
uint32_t sampleByteOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) >> 3U;
uint32_t sampleByteLength = (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1) >> 3U;
uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
InterpretedDFDChannel *sampleChannelPtr;
switch (sampleChannel) {
case KHR_DF_CHANNEL_RGBSDA_RED:
sampleChannelPtr = R;
break;
case KHR_DF_CHANNEL_RGBSDA_GREEN:
sampleChannelPtr = G;
break;
case KHR_DF_CHANNEL_RGBSDA_BLUE:
sampleChannelPtr = B;
break;
case KHR_DF_CHANNEL_RGBSDA_DEPTH:
sampleChannelPtr = R;
break;
case KHR_DF_CHANNEL_RGBSDA_STENCIL:
sampleChannelPtr = G;
break;
case KHR_DF_CHANNEL_RGBSDA_ALPHA:
sampleChannelPtr = A;
break;
default:
return i_UNSUPPORTED_CHANNEL_TYPES;
}
if (sampleChannel == currentChannel) {
/* Continuation of the same channel. */
/* Either big-endian, or little-endian with a very large channel. */
if (sampleByteOffset == currentByteOffset - 1) { /* One byte earlier */
if (determinedEndianness && !(result & i_BIG_ENDIAN_FORMAT_BIT)) {
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* All is good, continue big-endian. */
result |= i_BIG_ENDIAN_FORMAT_BIT;
determinedEndianness = 1;
/* Update the start */
sampleChannelPtr->offset = sampleByteOffset;
} else if (sampleByteOffset == currentByteOffset + currentByteLength) {
if (determinedEndianness && (result & i_BIG_ENDIAN_FORMAT_BIT)) {
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* All is good, continue little-endian. */
determinedEndianness = 1;
} else {
/* Oh dear. */
/* We could be little-endian, but not with any standard format. */
/* More likely we've got something weird that we can't support. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* Remember where we are. */
currentByteOffset = sampleByteOffset;
currentByteLength = sampleByteLength;
/* Accumulate the byte length. */
sampleChannelPtr->size += sampleByteLength;
/* Assume these are all the same. */
*wordBytes = sampleChannelPtr->size;
} else {
/* Everything is new. Hopefully. */
currentChannel = sampleChannel;
currentByteOffset = sampleByteOffset;
currentByteLength = sampleByteLength;
if (sampleChannelPtr->size) {
/* Uh-oh, we've seen this channel before. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
/* For now, record the byte offset in little-endian terms, */
/* because we may not know to reverse it yet. */
sampleChannelPtr->offset = sampleByteOffset;
sampleChannelPtr->size = sampleByteLength;
/* Assume these are all the same. */
*wordBytes = sampleByteLength;
}
}
return i_UNSUPPORTED_CHANNEL_TYPES;
}
if (isDepthStencil) {
/* For Depth/Stencil formats wordBytes is determined by the required alignment of */
/* the larger channel. */
uint32_t largerSize = R->size > G->size ? R->size : G->size;
*wordBytes = bit_ceil(largerSize);
}
return result;
}

View File

@@ -25,18 +25,98 @@ print "/* SPDX-", "License-Identifier: Apache-2.0 */\n\n";
print "/***************************** Do not edit. *****************************\n";
print " Automatically generated by makevk2dfd.pl.\n";
print " *************************************************************************/\n\n";
print "/* Vulkan combined depth & stencil formats are not included here\n";
print " * because they do not exist outside a Vulkan device.\n";
print " */\n";
# Loop over each line of input
while ($line = <>) {
# Match any format that starts with a channel description (some number of R, G, B, A or a number)
# In PERL, "=~" performs a regex operation on the left argument
# m/<regex>/ matches the regular expression
if ($line =~ m/VK_FORMAT_[RGBAE0-9]+_/) {
# VK_FORMAT_ is a simple 422 format
if ($line =~ m/(VK_FORMAT_[RGBX0-9]+_422_UNORM(_4PACK16)?)/) {
$format = $1; # Extract the format identifier from the rest of the line
if (exists($foundFormats{$format})) { next }
$foundFormats{$format} = 1;
# VK_FORMAT_G8B8G8R8_422_UNORM,
# VK_FORMAT_B8G8R8G8_422_UNORM,
# VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16,
# VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16,
# VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16,
# VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16,
# VK_FORMAT_G16B16G16R16_422_UNORM,
# VK_FORMAT_B16G16R16G16_422_UNORM,
$format =~ m/VK_FORMAT_([RGBX0-9]+)_422_UNORM(_4PACK16)?/;
$formatChannels = $1;
$channels = "";
$bits = "";
$paddings = "";
$position_xs = "";
$position_ys = "";
$last_green = 1;
while ($formatChannels =~ m/([RGBX0-9]*)([RGB])([0-9]+)X?([0-9]+)?/) {
# Processing from right to left for ease of regex expression
$channel = $2;
$bit = $3;
$padding = $4;
if ($channels ne "") {
$channels = ", " . $channels;
$bits = ", " . $bits;
$paddings = ", " . $paddings;
$position_xs = ", " . $position_xs;
$position_ys = ", " . $position_ys;
}
# The default 4:2:2 positioning is currently cosited-even.
# Block size is 2x1: Sample positions coded as 1/128 on X and 1/256 on Y
# Cosited-even:
# Position Sample
# X Y X Y
# Y0: 0.5, 0.5 -> 64, 128
# U : 0.5, 0.5 -> 64, 128
# Y1: 1.5, 0.5 -> 192, 128
# V : 0.5, 0.5 -> 64, 128
# Midpoint:
# Position Sample
# X Y X Y
# Y0: 0.5, 0.5 -> 64, 128
# U : 1.0, 0.5 -> 128, 128
# Y1: 1.5, 0.5 -> 192, 128
# V : 1.0, 0.5 -> 128, 128
if ($channel eq 'R') {
$channels = "2" . $channels; # 2 = Cr / V
$position_xs = "64" . $position_xs;
$position_ys = "128" . $position_ys;
} elsif ($channel eq 'G') {
$channels = "0" . $channels; # 0 = Y
$position_xs = ($last_green ? "192" : "64") . $position_xs;
$position_ys = "128" . $position_ys;
$last_green = 0;
} elsif ($channel eq 'B') {
$channels = "1" . $channels; # 1 = Cb / U
$position_xs = "64" . $position_xs;
$position_ys = "128" . $position_ys;
}
$bits = $bit . $bits;
if ($padding ne "") { $paddings = $padding . $paddings; }
else { $paddings = "0" . $paddings; }
$formatChannels = $1;
}
print "case $format: {\n";
print " int channels[] = {$channels}; int bits[] = {$bits}; int paddings[] = {$paddings};\n";
print " int position_xs[] = {$position_xs}; int position_ys[] = {$position_ys};\n";
print " return createDFD422($bigEndian, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);\n}\n";
} elsif ($line =~ m/VK_FORMAT_[RGBAE0-9]+_/) {
# Set $format to the enum identifier
($line =~ m/(VK_FORMAT[A-Z0-9_]+)/);
@@ -176,6 +256,62 @@ while ($line = <>) {
}
}
# VK_FORMAT_ is a packed HDR formats with padding
# R10X6_UNORM_PACK16
# R10X6G10X6_UNORM_2PACK16
# R10X6G10X6B10X6A10X6_UNORM_4PACK16
# R12X4_UNORM_PACK16
# R12X4G12X4_UNORM_2PACK16
# R12X4G12X4B12X4A12X4_UNORM_4PACK16
} elsif ($line =~ m/(VK_FORMAT_R10X6_UNORM_PACK16)/) {
$format = $1; # Extract the format identifier from the rest of the line
if (!exists($foundFormats{$format})) {
$foundFormats{$format} = 1;
print "case $format: {\n";
print " int channels[] = {0}; int bits[] = {10}; int paddings[] = {6};\n";
print " return createDFDPackedPadded($bigEndian, 1, bits, paddings, channels, s_UNORM);\n}\n"
}
} elsif ($line =~ m/(VK_FORMAT_R10X6G10X6_UNORM_2PACK16)/) {
$format = $1; # Extract the format identifier from the rest of the line
if (!exists($foundFormats{$format})) {
$foundFormats{$format} = 1;
print "case $format: {\n";
print " int channels[] = {0, 1}; int bits[] = {10, 10}; int paddings[] = {6, 6};\n";
print " return createDFDPackedPadded($bigEndian, 2, bits, paddings, channels, s_UNORM);\n}\n"
}
} elsif ($line =~ m/(VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)/) {
$format = $1; # Extract the format identifier from the rest of the line
if (!exists($foundFormats{$format})) {
$foundFormats{$format} = 1;
print "case $format: {\n";
print " int channels[] = {0, 1, 2, 3}; int bits[] = {10, 10, 10, 10}; int paddings[] = {6, 6, 6, 6};\n";
print " return createDFDPackedPadded($bigEndian, 4, bits, paddings, channels, s_UNORM);\n}\n"
}
} elsif ($line =~ m/(VK_FORMAT_R12X4_UNORM_PACK16)/) {
$format = $1; # Extract the format identifier from the rest of the line
if (!exists($foundFormats{$format})) {
$foundFormats{$format} = 1;
print "case $format: {\n";
print " int channels[] = {0}; int bits[] = {12}; int paddings[] = {4};\n";
print " return createDFDPackedPadded($bigEndian, 1, bits, paddings, channels, s_UNORM);\n}\n"
}
} elsif ($line =~ m/(VK_FORMAT_R12X4G12X4_UNORM_2PACK16)/) {
$format = $1; # Extract the format identifier from the rest of the line
if (!exists($foundFormats{$format})) {
$foundFormats{$format} = 1;
print "case $format: {\n";
print " int channels[] = {0, 1}; int bits[] = {12, 12}; int paddings[] = {4, 4};\n";
print " return createDFDPackedPadded($bigEndian, 2, bits, paddings, channels, s_UNORM);\n}\n"
}
} elsif ($line =~ m/(VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16)/) {
$format = $1; # Extract the format identifier from the rest of the line
if (!exists($foundFormats{$format})) {
$foundFormats{$format} = 1;
print "case $format: {\n";
print " int channels[] = {0, 1, 2, 3}; int bits[] = {12, 12, 12, 12}; int paddings[] = {4, 4, 4, 4};\n";
print " return createDFDPackedPadded($bigEndian, 4, bits, paddings, channels, s_UNORM);\n}\n"
}
# If we weren't VK_FORMAT_ plus a channel, we might be a compressed
# format, that ends "_BLOCK"
} elsif ($line =~ m/(VK_FORMAT_[A-Z0-9x_]+_BLOCK(_[A-Z]+)?)/) {
@@ -285,6 +421,37 @@ while ($line = <>) {
$foundFormats{$format} = 1;
print "case $format: return createDFDDepthStencil(24,0,4);\n";
}
} elsif ($line =~ m/(VK_FORMAT_D32_SFLOAT_S8_UINT)/) {
# Extract the format identifier from the rest of the line
$format = $1;
if (!exists($foundFormats{$format})) {
# Add the format we've processed to our "done" hash
$foundFormats{$format} = 1;
print "case $format: return createDFDDepthStencil(32,8,8);\n";
}
} elsif ($line =~ m/(VK_FORMAT_D16_UNORM_S8_UINT)/) {
# Extract the format identifier from the rest of the line
$format = $1;
if (!exists($foundFormats{$format})) {
# Add the format we've processed to our "done" hash
$foundFormats{$format} = 1;
print "case $format: return createDFDDepthStencil(16,8,4);\n";
}
} elsif ($line =~ m/(VK_FORMAT_D24_UNORM_S8_UINT)/) {
# Extract the format identifier from the rest of the line
$format = $1;
if (!exists($foundFormats{$format})) {
# Add the format we've processed to our "done" hash
$foundFormats{$format} = 1;
print "case $format: return createDFDDepthStencil(24,8,4);\n";
}
} elsif ($line =~ m/(VK_FORMAT_D32_SFLOAT)/) {
# Extract the format identifier from the rest of the line

View File

@@ -805,9 +805,9 @@ void printDFD(uint32_t *DFD, uint32_t dataSize)
KHR_DFDSVAL(block, sample, SAMPLEUPPER));
}
} else if (vendorID == KHR_DF_VENDORID_KHRONOS && descriptorType == KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_DIMENSIONS) {
// TODO Tools P5: Implement DFD print for ADDITIONAL_DIMENSIONS
// TODO: Implement DFD print for ADDITIONAL_DIMENSIONS
} else if (vendorID == KHR_DF_VENDORID_KHRONOS && descriptorType == KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_PLANES) {
// TODO Tools P5: Implement DFD print for ADDITIONAL_PLANES
// TODO: Implement DFD print for ADDITIONAL_PLANES
} else {
printf("Unknown block\n");
}
@@ -994,15 +994,15 @@ void printDFDJSON(uint32_t* DFD, uint32_t dataSize, uint32_t base_indent, uint32
} else if (vendorID == KHR_DF_VENDORID_KHRONOS && descriptorType == KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_DIMENSIONS) {
printf("%s", nl);
// printf(",%s", nl); // If there is extra member printed
// TODO Tools P5: Implement DFD print for ADDITIONAL_DIMENSIONS
// TODO: Implement DFD print for ADDITIONAL_DIMENSIONS
} else if (vendorID == KHR_DF_VENDORID_KHRONOS && descriptorType == KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_PLANES) {
printf("%s", nl);
// printf(",%s", nl); // If there is extra member printed
// TODO Tools P5: Implement DFD print for ADDITIONAL_PLANES
// TODO: Implement DFD print for ADDITIONAL_PLANES
} else {
printf("%s", nl);
// printf(",%s", nl); // If there is extra member printed
// TODO Tools P5: What to do with unknown blocks for json?
// TODO: What to do with unknown blocks for json?
// Unknown block data in binary?
}

View File

@@ -5,9 +5,6 @@
Automatically generated by makevk2dfd.pl.
*************************************************************************/
/* Vulkan combined depth & stencil formats are not included here
* because they do not exist outside a Vulkan device.
*/
case VK_FORMAT_R4G4_UNORM_PACK8: {
int channels[] = {1,0}; int bits[] = {4,4};
return createDFDPacked(0, 2, bits, channels, s_UNORM);
@@ -222,6 +219,9 @@ case VK_FORMAT_D16_UNORM: return createDFDDepthStencil(16,0,2);
case VK_FORMAT_X8_D24_UNORM_PACK32: return createDFDDepthStencil(24,0,4);
case VK_FORMAT_D32_SFLOAT: return createDFDDepthStencil(32,0,4);
case VK_FORMAT_S8_UINT: return createDFDDepthStencil(0,8,1);
case VK_FORMAT_D16_UNORM_S8_UINT: return createDFDDepthStencil(16,8,4);
case VK_FORMAT_D24_UNORM_S8_UINT: return createDFDDepthStencil(24,8,4);
case VK_FORMAT_D32_SFLOAT_S8_UINT: return createDFDDepthStencil(32,8,8);
case VK_FORMAT_BC1_RGB_UNORM_BLOCK: return createDFDCompressed(c_BC1_RGB, 4, 4, 1, s_UNORM);
case VK_FORMAT_BC1_RGB_SRGB_BLOCK: return createDFDCompressed(c_BC1_RGB, 4, 4, 1, s_SRGB);
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: return createDFDCompressed(c_BC1_RGBA, 4, 4, 1, s_UNORM);
@@ -276,6 +276,70 @@ case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 12, 10
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 12, 10, 1, s_SRGB);
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 12, 12, 1, s_UNORM);
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 12, 12, 1, s_SRGB);
case VK_FORMAT_G8B8G8R8_422_UNORM: {
int channels[] = {0, 1, 0, 2}; int bits[] = {8, 8, 8, 8}; int paddings[] = {0, 0, 0, 0};
int position_xs[] = {64, 64, 192, 64}; int position_ys[] = {128, 128, 128, 128};
return createDFD422(0, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);
}
case VK_FORMAT_B8G8R8G8_422_UNORM: {
int channels[] = {1, 0, 2, 0}; int bits[] = {8, 8, 8, 8}; int paddings[] = {0, 0, 0, 0};
int position_xs[] = {64, 64, 64, 192}; int position_ys[] = {128, 128, 128, 128};
return createDFD422(0, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);
}
case VK_FORMAT_R10X6_UNORM_PACK16: {
int channels[] = {0}; int bits[] = {10}; int paddings[] = {6};
return createDFDPackedPadded(0, 1, bits, paddings, channels, s_UNORM);
}
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16: {
int channels[] = {0, 1}; int bits[] = {10, 10}; int paddings[] = {6, 6};
return createDFDPackedPadded(0, 2, bits, paddings, channels, s_UNORM);
}
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: {
int channels[] = {0, 1, 2, 3}; int bits[] = {10, 10, 10, 10}; int paddings[] = {6, 6, 6, 6};
return createDFDPackedPadded(0, 4, bits, paddings, channels, s_UNORM);
}
case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: {
int channels[] = {0, 1, 0, 2}; int bits[] = {10, 10, 10, 10}; int paddings[] = {6, 6, 6, 6};
int position_xs[] = {64, 64, 192, 64}; int position_ys[] = {128, 128, 128, 128};
return createDFD422(0, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);
}
case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: {
int channels[] = {1, 0, 2, 0}; int bits[] = {10, 10, 10, 10}; int paddings[] = {6, 6, 6, 6};
int position_xs[] = {64, 64, 64, 192}; int position_ys[] = {128, 128, 128, 128};
return createDFD422(0, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);
}
case VK_FORMAT_R12X4_UNORM_PACK16: {
int channels[] = {0}; int bits[] = {12}; int paddings[] = {4};
return createDFDPackedPadded(0, 1, bits, paddings, channels, s_UNORM);
}
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16: {
int channels[] = {0, 1}; int bits[] = {12, 12}; int paddings[] = {4, 4};
return createDFDPackedPadded(0, 2, bits, paddings, channels, s_UNORM);
}
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: {
int channels[] = {0, 1, 2, 3}; int bits[] = {12, 12, 12, 12}; int paddings[] = {4, 4, 4, 4};
return createDFDPackedPadded(0, 4, bits, paddings, channels, s_UNORM);
}
case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: {
int channels[] = {0, 1, 0, 2}; int bits[] = {12, 12, 12, 12}; int paddings[] = {4, 4, 4, 4};
int position_xs[] = {64, 64, 192, 64}; int position_ys[] = {128, 128, 128, 128};
return createDFD422(0, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);
}
case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: {
int channels[] = {1, 0, 2, 0}; int bits[] = {12, 12, 12, 12}; int paddings[] = {4, 4, 4, 4};
int position_xs[] = {64, 64, 64, 192}; int position_ys[] = {128, 128, 128, 128};
return createDFD422(0, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);
}
case VK_FORMAT_G16B16G16R16_422_UNORM: {
int channels[] = {0, 1, 0, 2}; int bits[] = {16, 16, 16, 16}; int paddings[] = {0, 0, 0, 0};
int position_xs[] = {64, 64, 192, 64}; int position_ys[] = {128, 128, 128, 128};
return createDFD422(0, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);
}
case VK_FORMAT_B16G16R16G16_422_UNORM: {
int channels[] = {1, 0, 2, 0}; int bits[] = {16, 16, 16, 16}; int paddings[] = {0, 0, 0, 0};
int position_xs[] = {64, 64, 64, 192}; int position_ys[] = {128, 128, 128, 128};
return createDFD422(0, 4, bits, paddings, channels, position_xs, position_ys, s_UNORM);
}
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 8, 4, 1, s_UNORM);
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 4, 4, 1, s_UNORM);
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 8, 4, 1, s_UNORM);

View File

@@ -27,6 +27,7 @@ typedef enum ktxFormatSizeFlagBits {
KTX_FORMAT_SIZE_PALETTIZED_BIT = 0x00000004,
KTX_FORMAT_SIZE_DEPTH_BIT = 0x00000008,
KTX_FORMAT_SIZE_STENCIL_BIT = 0x00000010,
KTX_FORMAT_SIZE_YUVSDA_BIT = 0x00000020,
} ktxFormatSizeFlagBits;
typedef ktx_uint32_t ktxFormatSizeFlags;

View File

@@ -45,3 +45,4 @@ EXPORTS
ktxTexture2_destruct
vk2dfd
vkFormatString
stringToVkFormat

View File

@@ -45,3 +45,4 @@ EXPORTS
ktxTexture2_destruct
vk2dfd
vkFormatString
stringToVkFormat

View File

@@ -98,15 +98,21 @@ $2 == "VK_HEADER_VERSION" {
}
# Extract valid formats with values > VK_FORMAT_END_RANGE.
/VK_FORMAT_[^F].* = 1000/ && ! /PLANE/ && ! /422/ && !/420/ {
/VK_FORMAT_[^F].* = 1000/ && ! /PLANE/ && !/420/ {
valid = valid " case " $1 ":\n";
}
function removePrefix(string, prefix) {
sub("^" prefix, "", string)
return string
}
# Extract VK_FORMAT token names. [^F] avoids the VK_FORMAT_FEATURE* tokens.
/ VK_FORMAT_[^F]/ {
# Avoid values defined as existing values and avoid the MAX_ENUM value.
if ($3 !~ /VK_FORMAT_.*/ && $1 !~ /.*MAX_ENUM/) {
switch_body = switch_body " case " $1 ":\n return \"" $1 "\";\n"
switch_body_vk2str = switch_body_vk2str " case " $1 ":\n return \"" $1 "\";\n"
switch_body_str2vk = switch_body_str2vk " if (ktx_strcasecmp(str, \"" removePrefix($1, "VK_FORMAT_") "\") == 0)\n return " $1 ";\n"
}
}
@@ -142,14 +148,54 @@ END {
# vkformat_str.c
prelude = "\n";
prelude = prelude "#include <stdint.h>\n\n";
prelude = prelude "#include \"vkformat_enum.h\"\n\n";
prelude = prelude "#include <stdint.h>\n";
prelude = prelude "#include <ctype.h>\n";
prelude = prelude "\n";
prelude = prelude "#include \"vkformat_enum.h\"\n";
prelude = prelude "\n";
prelude = prelude "const char*\nvkFormatString(VkFormat format)\n{\n";
prelude = prelude " switch (format) {\n";
postscript = " default:\n return \"VK_UNKNOWN_FORMAT\";\n";
postscript = postscript " }\n";
postscript = postscript "}\n";
write_source_file(prelude switch_body postscript, format_strings);
begin_str2vk = "\n"
begin_str2vk = begin_str2vk "static int ktx_strcasecmp(const char* s1, const char* s2) {\n"
begin_str2vk = begin_str2vk " const unsigned char* us1 = (const unsigned char*) s1;\n"
begin_str2vk = begin_str2vk " const unsigned char* us2 = (const unsigned char*) s2;\n"
begin_str2vk = begin_str2vk "\n"
begin_str2vk = begin_str2vk " while (tolower(*us1) == tolower(*us2)) {\n"
begin_str2vk = begin_str2vk " if (*us1 == '\\0')\n"
begin_str2vk = begin_str2vk " return 0;\n"
begin_str2vk = begin_str2vk " ++us1;\n"
begin_str2vk = begin_str2vk " ++us2;\n"
begin_str2vk = begin_str2vk " }\n"
begin_str2vk = begin_str2vk " return tolower(*us1) - tolower(*us2);\n"
begin_str2vk = begin_str2vk "}\n"
begin_str2vk = begin_str2vk "\n"
begin_str2vk = begin_str2vk "static int ktx_strncasecmp(const char* s1, const char* s2, int length) {\n"
begin_str2vk = begin_str2vk " const unsigned char* us1 = (const unsigned char*) s1;\n"
begin_str2vk = begin_str2vk " const unsigned char* us2 = (const unsigned char*) s2;\n"
begin_str2vk = begin_str2vk "\n"
begin_str2vk = begin_str2vk " while (length > 0 && tolower(*us1) == tolower(*us2)) {\n"
begin_str2vk = begin_str2vk " if (*us1 == '\\0')\n"
begin_str2vk = begin_str2vk " return 0;\n"
begin_str2vk = begin_str2vk " ++us1;\n"
begin_str2vk = begin_str2vk " ++us2;\n"
begin_str2vk = begin_str2vk " --length;\n"
begin_str2vk = begin_str2vk " }\n"
begin_str2vk = begin_str2vk " return tolower(*us1) - tolower(*us2);\n"
begin_str2vk = begin_str2vk "}\n"
begin_str2vk = begin_str2vk "\n"
begin_str2vk = begin_str2vk "/// Parses a VkFormat. VK_FORMAT_ prefix is optional. Case insensitive.\n"
begin_str2vk = begin_str2vk "VkFormat\n"
begin_str2vk = begin_str2vk "stringToVkFormat(const char* str)\n"
begin_str2vk = begin_str2vk "{\n"
begin_str2vk = begin_str2vk " if (ktx_strncasecmp(str, \"VK_FORMAT_\", sizeof(\"VK_FORMAT_\") - 1) == 0)\n"
begin_str2vk = begin_str2vk " str += sizeof(\"VK_FORMAT_\") - 1;\n"
begin_str2vk = begin_str2vk "\n"
end_str2vk = " return VK_FORMAT_UNDEFINED;\n";
end_str2vk = end_str2vk "}"
write_source_file(prelude switch_body_vk2str postscript begin_str2vk switch_body_str2vk end_str2vk, format_strings);
}
# vim:ai:ts=4:sts=4:sw=2:expandtab:textwidth=70

View File

@@ -567,7 +567,7 @@ ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level,
blockCount.y
= (ktx_uint32_t)ceilf(levelHeight / prtctd->_formatSize.blockHeight);
blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);
blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y);
blockCount.y = MAX(prtctd->_formatSize.minBlocksY, blockCount.y);
blockSizeInBytes = prtctd->_formatSize.blockSizeInBits / 8;
@@ -726,8 +726,10 @@ ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level,
ktx_size_t imageSize, layerSize;
assert (This != NULL);
assert (prtctd->_formatSize.blockDepth != 0);
blockCountZ = MAX(1, (This->baseDepth / prtctd->_formatSize.blockDepth) >> level);
blockCountZ = ((This->baseDepth >> level) + prtctd->_formatSize.blockDepth - 1) / prtctd->_formatSize.blockDepth;
blockCountZ = MAX(1, blockCountZ);
imageSize = ktxTexture_calcImageSize(This, level, fv);
layerSize = imageSize * blockCountZ;
if (fv == KTX_FORMAT_VERSION_ONE && KTX_GL_UNPACK_ALIGNMENT != 4) {

View File

@@ -294,6 +294,10 @@ ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)
return false;
if (result & i_PACKED_FORMAT_BIT)
This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
if (result & i_COMPRESSED_FORMAT_BIT)
This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;
if (result & i_YUVSDA_FORMAT_BIT)
This->flags |= KTX_FORMAT_SIZE_YUVSDA_BIT;
}
}
if (This->blockSizeInBits == 0) {
@@ -326,20 +330,7 @@ ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)
static uint32_t*
ktxVk2dfd(ktx_uint32_t vkFormat)
{
switch(vkFormat) {
case VK_FORMAT_D16_UNORM_S8_UINT:
// 2 16-bit words. D16 in the first. S8 in the 8 LSBs of the second.
return createDFDDepthStencil(16, 8, 4);
case VK_FORMAT_D24_UNORM_S8_UINT:
// 1 32-bit word. D24 in the MSBs. S8 in the LSBs.
return createDFDDepthStencil(24, 8, 4);
case VK_FORMAT_D32_SFLOAT_S8_UINT:
// 2 32-bit words. D32 float in the first word. S8 in LSBs of the
// second.
return createDFDDepthStencil(32, 8, 8);
default:
return vk2dfd(vkFormat);
}
return vk2dfd(vkFormat);
}
/**
@@ -438,7 +429,7 @@ ktxTexture2_construct(ktxTexture2* This, ktxTextureCreateInfo* createInfo,
// Ideally we'd set all these things in ktxFormatSize_initFromDfd
// but This->_protected is not allocated until ktxTexture_construct;
if (This->isCompressed) {
if (This->isCompressed && (formatSize.flags & KTX_FORMAT_SIZE_YUVSDA_BIT) == 0) {
This->_protected->_typeSize = 1;
} else if (formatSize.flags & (KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT)) {
switch (createInfo->vkFormat) {

View File

@@ -82,12 +82,20 @@ isValidFormat(VkFormat format)
if ((uint32_t) format <= VK_FORMAT_MAX_STANDARD_ENUM)
return true;
else switch(format) {
case VK_FORMAT_G8B8G8R8_422_UNORM:
case VK_FORMAT_B8G8R8G8_422_UNORM:
case VK_FORMAT_R10X6_UNORM_PACK16:
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
case VK_FORMAT_R12X4_UNORM_PACK16:
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
case VK_FORMAT_G16B16G16R16_422_UNORM:
case VK_FORMAT_B16G16R16G16_422_UNORM:
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:

View File

@@ -11,6 +11,7 @@
#include <stdint.h>
#include <ctype.h>
#include "vkformat_enum.h"
@@ -569,3 +570,585 @@ vkFormatString(VkFormat format)
}
}
static int ktx_strcasecmp(const char* s1, const char* s2) {
const unsigned char* us1 = (const unsigned char*) s1;
const unsigned char* us2 = (const unsigned char*) s2;
while (tolower(*us1) == tolower(*us2)) {
if (*us1 == '\0')
return 0;
++us1;
++us2;
}
return tolower(*us1) - tolower(*us2);
}
static int ktx_strncasecmp(const char* s1, const char* s2, int length) {
const unsigned char* us1 = (const unsigned char*) s1;
const unsigned char* us2 = (const unsigned char*) s2;
while (length > 0 && tolower(*us1) == tolower(*us2)) {
if (*us1 == '\0')
return 0;
++us1;
++us2;
--length;
}
return tolower(*us1) - tolower(*us2);
}
/// Parses a VkFormat. VK_FORMAT_ prefix is optional. Case insensitive.
VkFormat
stringToVkFormat(const char* str)
{
if (ktx_strncasecmp(str, "VK_FORMAT_", sizeof("VK_FORMAT_") - 1) == 0)
str += sizeof("VK_FORMAT_") - 1;
if (ktx_strcasecmp(str, "UNDEFINED") == 0)
return VK_FORMAT_UNDEFINED;
if (ktx_strcasecmp(str, "R4G4_UNORM_PACK8") == 0)
return VK_FORMAT_R4G4_UNORM_PACK8;
if (ktx_strcasecmp(str, "R4G4B4A4_UNORM_PACK16") == 0)
return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
if (ktx_strcasecmp(str, "B4G4R4A4_UNORM_PACK16") == 0)
return VK_FORMAT_B4G4R4A4_UNORM_PACK16;
if (ktx_strcasecmp(str, "R5G6B5_UNORM_PACK16") == 0)
return VK_FORMAT_R5G6B5_UNORM_PACK16;
if (ktx_strcasecmp(str, "B5G6R5_UNORM_PACK16") == 0)
return VK_FORMAT_B5G6R5_UNORM_PACK16;
if (ktx_strcasecmp(str, "R5G5B5A1_UNORM_PACK16") == 0)
return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
if (ktx_strcasecmp(str, "B5G5R5A1_UNORM_PACK16") == 0)
return VK_FORMAT_B5G5R5A1_UNORM_PACK16;
if (ktx_strcasecmp(str, "A1R5G5B5_UNORM_PACK16") == 0)
return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
if (ktx_strcasecmp(str, "R8_UNORM") == 0)
return VK_FORMAT_R8_UNORM;
if (ktx_strcasecmp(str, "R8_SNORM") == 0)
return VK_FORMAT_R8_SNORM;
if (ktx_strcasecmp(str, "R8_USCALED") == 0)
return VK_FORMAT_R8_USCALED;
if (ktx_strcasecmp(str, "R8_SSCALED") == 0)
return VK_FORMAT_R8_SSCALED;
if (ktx_strcasecmp(str, "R8_UINT") == 0)
return VK_FORMAT_R8_UINT;
if (ktx_strcasecmp(str, "R8_SINT") == 0)
return VK_FORMAT_R8_SINT;
if (ktx_strcasecmp(str, "R8_SRGB") == 0)
return VK_FORMAT_R8_SRGB;
if (ktx_strcasecmp(str, "R8G8_UNORM") == 0)
return VK_FORMAT_R8G8_UNORM;
if (ktx_strcasecmp(str, "R8G8_SNORM") == 0)
return VK_FORMAT_R8G8_SNORM;
if (ktx_strcasecmp(str, "R8G8_USCALED") == 0)
return VK_FORMAT_R8G8_USCALED;
if (ktx_strcasecmp(str, "R8G8_SSCALED") == 0)
return VK_FORMAT_R8G8_SSCALED;
if (ktx_strcasecmp(str, "R8G8_UINT") == 0)
return VK_FORMAT_R8G8_UINT;
if (ktx_strcasecmp(str, "R8G8_SINT") == 0)
return VK_FORMAT_R8G8_SINT;
if (ktx_strcasecmp(str, "R8G8_SRGB") == 0)
return VK_FORMAT_R8G8_SRGB;
if (ktx_strcasecmp(str, "R8G8B8_UNORM") == 0)
return VK_FORMAT_R8G8B8_UNORM;
if (ktx_strcasecmp(str, "R8G8B8_SNORM") == 0)
return VK_FORMAT_R8G8B8_SNORM;
if (ktx_strcasecmp(str, "R8G8B8_USCALED") == 0)
return VK_FORMAT_R8G8B8_USCALED;
if (ktx_strcasecmp(str, "R8G8B8_SSCALED") == 0)
return VK_FORMAT_R8G8B8_SSCALED;
if (ktx_strcasecmp(str, "R8G8B8_UINT") == 0)
return VK_FORMAT_R8G8B8_UINT;
if (ktx_strcasecmp(str, "R8G8B8_SINT") == 0)
return VK_FORMAT_R8G8B8_SINT;
if (ktx_strcasecmp(str, "R8G8B8_SRGB") == 0)
return VK_FORMAT_R8G8B8_SRGB;
if (ktx_strcasecmp(str, "B8G8R8_UNORM") == 0)
return VK_FORMAT_B8G8R8_UNORM;
if (ktx_strcasecmp(str, "B8G8R8_SNORM") == 0)
return VK_FORMAT_B8G8R8_SNORM;
if (ktx_strcasecmp(str, "B8G8R8_USCALED") == 0)
return VK_FORMAT_B8G8R8_USCALED;
if (ktx_strcasecmp(str, "B8G8R8_SSCALED") == 0)
return VK_FORMAT_B8G8R8_SSCALED;
if (ktx_strcasecmp(str, "B8G8R8_UINT") == 0)
return VK_FORMAT_B8G8R8_UINT;
if (ktx_strcasecmp(str, "B8G8R8_SINT") == 0)
return VK_FORMAT_B8G8R8_SINT;
if (ktx_strcasecmp(str, "B8G8R8_SRGB") == 0)
return VK_FORMAT_B8G8R8_SRGB;
if (ktx_strcasecmp(str, "R8G8B8A8_UNORM") == 0)
return VK_FORMAT_R8G8B8A8_UNORM;
if (ktx_strcasecmp(str, "R8G8B8A8_SNORM") == 0)
return VK_FORMAT_R8G8B8A8_SNORM;
if (ktx_strcasecmp(str, "R8G8B8A8_USCALED") == 0)
return VK_FORMAT_R8G8B8A8_USCALED;
if (ktx_strcasecmp(str, "R8G8B8A8_SSCALED") == 0)
return VK_FORMAT_R8G8B8A8_SSCALED;
if (ktx_strcasecmp(str, "R8G8B8A8_UINT") == 0)
return VK_FORMAT_R8G8B8A8_UINT;
if (ktx_strcasecmp(str, "R8G8B8A8_SINT") == 0)
return VK_FORMAT_R8G8B8A8_SINT;
if (ktx_strcasecmp(str, "R8G8B8A8_SRGB") == 0)
return VK_FORMAT_R8G8B8A8_SRGB;
if (ktx_strcasecmp(str, "B8G8R8A8_UNORM") == 0)
return VK_FORMAT_B8G8R8A8_UNORM;
if (ktx_strcasecmp(str, "B8G8R8A8_SNORM") == 0)
return VK_FORMAT_B8G8R8A8_SNORM;
if (ktx_strcasecmp(str, "B8G8R8A8_USCALED") == 0)
return VK_FORMAT_B8G8R8A8_USCALED;
if (ktx_strcasecmp(str, "B8G8R8A8_SSCALED") == 0)
return VK_FORMAT_B8G8R8A8_SSCALED;
if (ktx_strcasecmp(str, "B8G8R8A8_UINT") == 0)
return VK_FORMAT_B8G8R8A8_UINT;
if (ktx_strcasecmp(str, "B8G8R8A8_SINT") == 0)
return VK_FORMAT_B8G8R8A8_SINT;
if (ktx_strcasecmp(str, "B8G8R8A8_SRGB") == 0)
return VK_FORMAT_B8G8R8A8_SRGB;
if (ktx_strcasecmp(str, "A8B8G8R8_UNORM_PACK32") == 0)
return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
if (ktx_strcasecmp(str, "A8B8G8R8_SNORM_PACK32") == 0)
return VK_FORMAT_A8B8G8R8_SNORM_PACK32;
if (ktx_strcasecmp(str, "A8B8G8R8_USCALED_PACK32") == 0)
return VK_FORMAT_A8B8G8R8_USCALED_PACK32;
if (ktx_strcasecmp(str, "A8B8G8R8_SSCALED_PACK32") == 0)
return VK_FORMAT_A8B8G8R8_SSCALED_PACK32;
if (ktx_strcasecmp(str, "A8B8G8R8_UINT_PACK32") == 0)
return VK_FORMAT_A8B8G8R8_UINT_PACK32;
if (ktx_strcasecmp(str, "A8B8G8R8_SINT_PACK32") == 0)
return VK_FORMAT_A8B8G8R8_SINT_PACK32;
if (ktx_strcasecmp(str, "A8B8G8R8_SRGB_PACK32") == 0)
return VK_FORMAT_A8B8G8R8_SRGB_PACK32;
if (ktx_strcasecmp(str, "A2R10G10B10_UNORM_PACK32") == 0)
return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
if (ktx_strcasecmp(str, "A2R10G10B10_SNORM_PACK32") == 0)
return VK_FORMAT_A2R10G10B10_SNORM_PACK32;
if (ktx_strcasecmp(str, "A2R10G10B10_USCALED_PACK32") == 0)
return VK_FORMAT_A2R10G10B10_USCALED_PACK32;
if (ktx_strcasecmp(str, "A2R10G10B10_SSCALED_PACK32") == 0)
return VK_FORMAT_A2R10G10B10_SSCALED_PACK32;
if (ktx_strcasecmp(str, "A2R10G10B10_UINT_PACK32") == 0)
return VK_FORMAT_A2R10G10B10_UINT_PACK32;
if (ktx_strcasecmp(str, "A2R10G10B10_SINT_PACK32") == 0)
return VK_FORMAT_A2R10G10B10_SINT_PACK32;
if (ktx_strcasecmp(str, "A2B10G10R10_UNORM_PACK32") == 0)
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
if (ktx_strcasecmp(str, "A2B10G10R10_SNORM_PACK32") == 0)
return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
if (ktx_strcasecmp(str, "A2B10G10R10_USCALED_PACK32") == 0)
return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
if (ktx_strcasecmp(str, "A2B10G10R10_SSCALED_PACK32") == 0)
return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
if (ktx_strcasecmp(str, "A2B10G10R10_UINT_PACK32") == 0)
return VK_FORMAT_A2B10G10R10_UINT_PACK32;
if (ktx_strcasecmp(str, "A2B10G10R10_SINT_PACK32") == 0)
return VK_FORMAT_A2B10G10R10_SINT_PACK32;
if (ktx_strcasecmp(str, "R16_UNORM") == 0)
return VK_FORMAT_R16_UNORM;
if (ktx_strcasecmp(str, "R16_SNORM") == 0)
return VK_FORMAT_R16_SNORM;
if (ktx_strcasecmp(str, "R16_USCALED") == 0)
return VK_FORMAT_R16_USCALED;
if (ktx_strcasecmp(str, "R16_SSCALED") == 0)
return VK_FORMAT_R16_SSCALED;
if (ktx_strcasecmp(str, "R16_UINT") == 0)
return VK_FORMAT_R16_UINT;
if (ktx_strcasecmp(str, "R16_SINT") == 0)
return VK_FORMAT_R16_SINT;
if (ktx_strcasecmp(str, "R16_SFLOAT") == 0)
return VK_FORMAT_R16_SFLOAT;
if (ktx_strcasecmp(str, "R16G16_UNORM") == 0)
return VK_FORMAT_R16G16_UNORM;
if (ktx_strcasecmp(str, "R16G16_SNORM") == 0)
return VK_FORMAT_R16G16_SNORM;
if (ktx_strcasecmp(str, "R16G16_USCALED") == 0)
return VK_FORMAT_R16G16_USCALED;
if (ktx_strcasecmp(str, "R16G16_SSCALED") == 0)
return VK_FORMAT_R16G16_SSCALED;
if (ktx_strcasecmp(str, "R16G16_UINT") == 0)
return VK_FORMAT_R16G16_UINT;
if (ktx_strcasecmp(str, "R16G16_SINT") == 0)
return VK_FORMAT_R16G16_SINT;
if (ktx_strcasecmp(str, "R16G16_SFLOAT") == 0)
return VK_FORMAT_R16G16_SFLOAT;
if (ktx_strcasecmp(str, "R16G16B16_UNORM") == 0)
return VK_FORMAT_R16G16B16_UNORM;
if (ktx_strcasecmp(str, "R16G16B16_SNORM") == 0)
return VK_FORMAT_R16G16B16_SNORM;
if (ktx_strcasecmp(str, "R16G16B16_USCALED") == 0)
return VK_FORMAT_R16G16B16_USCALED;
if (ktx_strcasecmp(str, "R16G16B16_SSCALED") == 0)
return VK_FORMAT_R16G16B16_SSCALED;
if (ktx_strcasecmp(str, "R16G16B16_UINT") == 0)
return VK_FORMAT_R16G16B16_UINT;
if (ktx_strcasecmp(str, "R16G16B16_SINT") == 0)
return VK_FORMAT_R16G16B16_SINT;
if (ktx_strcasecmp(str, "R16G16B16_SFLOAT") == 0)
return VK_FORMAT_R16G16B16_SFLOAT;
if (ktx_strcasecmp(str, "R16G16B16A16_UNORM") == 0)
return VK_FORMAT_R16G16B16A16_UNORM;
if (ktx_strcasecmp(str, "R16G16B16A16_SNORM") == 0)
return VK_FORMAT_R16G16B16A16_SNORM;
if (ktx_strcasecmp(str, "R16G16B16A16_USCALED") == 0)
return VK_FORMAT_R16G16B16A16_USCALED;
if (ktx_strcasecmp(str, "R16G16B16A16_SSCALED") == 0)
return VK_FORMAT_R16G16B16A16_SSCALED;
if (ktx_strcasecmp(str, "R16G16B16A16_UINT") == 0)
return VK_FORMAT_R16G16B16A16_UINT;
if (ktx_strcasecmp(str, "R16G16B16A16_SINT") == 0)
return VK_FORMAT_R16G16B16A16_SINT;
if (ktx_strcasecmp(str, "R16G16B16A16_SFLOAT") == 0)
return VK_FORMAT_R16G16B16A16_SFLOAT;
if (ktx_strcasecmp(str, "R32_UINT") == 0)
return VK_FORMAT_R32_UINT;
if (ktx_strcasecmp(str, "R32_SINT") == 0)
return VK_FORMAT_R32_SINT;
if (ktx_strcasecmp(str, "R32_SFLOAT") == 0)
return VK_FORMAT_R32_SFLOAT;
if (ktx_strcasecmp(str, "R32G32_UINT") == 0)
return VK_FORMAT_R32G32_UINT;
if (ktx_strcasecmp(str, "R32G32_SINT") == 0)
return VK_FORMAT_R32G32_SINT;
if (ktx_strcasecmp(str, "R32G32_SFLOAT") == 0)
return VK_FORMAT_R32G32_SFLOAT;
if (ktx_strcasecmp(str, "R32G32B32_UINT") == 0)
return VK_FORMAT_R32G32B32_UINT;
if (ktx_strcasecmp(str, "R32G32B32_SINT") == 0)
return VK_FORMAT_R32G32B32_SINT;
if (ktx_strcasecmp(str, "R32G32B32_SFLOAT") == 0)
return VK_FORMAT_R32G32B32_SFLOAT;
if (ktx_strcasecmp(str, "R32G32B32A32_UINT") == 0)
return VK_FORMAT_R32G32B32A32_UINT;
if (ktx_strcasecmp(str, "R32G32B32A32_SINT") == 0)
return VK_FORMAT_R32G32B32A32_SINT;
if (ktx_strcasecmp(str, "R32G32B32A32_SFLOAT") == 0)
return VK_FORMAT_R32G32B32A32_SFLOAT;
if (ktx_strcasecmp(str, "R64_UINT") == 0)
return VK_FORMAT_R64_UINT;
if (ktx_strcasecmp(str, "R64_SINT") == 0)
return VK_FORMAT_R64_SINT;
if (ktx_strcasecmp(str, "R64_SFLOAT") == 0)
return VK_FORMAT_R64_SFLOAT;
if (ktx_strcasecmp(str, "R64G64_UINT") == 0)
return VK_FORMAT_R64G64_UINT;
if (ktx_strcasecmp(str, "R64G64_SINT") == 0)
return VK_FORMAT_R64G64_SINT;
if (ktx_strcasecmp(str, "R64G64_SFLOAT") == 0)
return VK_FORMAT_R64G64_SFLOAT;
if (ktx_strcasecmp(str, "R64G64B64_UINT") == 0)
return VK_FORMAT_R64G64B64_UINT;
if (ktx_strcasecmp(str, "R64G64B64_SINT") == 0)
return VK_FORMAT_R64G64B64_SINT;
if (ktx_strcasecmp(str, "R64G64B64_SFLOAT") == 0)
return VK_FORMAT_R64G64B64_SFLOAT;
if (ktx_strcasecmp(str, "R64G64B64A64_UINT") == 0)
return VK_FORMAT_R64G64B64A64_UINT;
if (ktx_strcasecmp(str, "R64G64B64A64_SINT") == 0)
return VK_FORMAT_R64G64B64A64_SINT;
if (ktx_strcasecmp(str, "R64G64B64A64_SFLOAT") == 0)
return VK_FORMAT_R64G64B64A64_SFLOAT;
if (ktx_strcasecmp(str, "B10G11R11_UFLOAT_PACK32") == 0)
return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
if (ktx_strcasecmp(str, "E5B9G9R9_UFLOAT_PACK32") == 0)
return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32;
if (ktx_strcasecmp(str, "D16_UNORM") == 0)
return VK_FORMAT_D16_UNORM;
if (ktx_strcasecmp(str, "X8_D24_UNORM_PACK32") == 0)
return VK_FORMAT_X8_D24_UNORM_PACK32;
if (ktx_strcasecmp(str, "D32_SFLOAT") == 0)
return VK_FORMAT_D32_SFLOAT;
if (ktx_strcasecmp(str, "S8_UINT") == 0)
return VK_FORMAT_S8_UINT;
if (ktx_strcasecmp(str, "D16_UNORM_S8_UINT") == 0)
return VK_FORMAT_D16_UNORM_S8_UINT;
if (ktx_strcasecmp(str, "D24_UNORM_S8_UINT") == 0)
return VK_FORMAT_D24_UNORM_S8_UINT;
if (ktx_strcasecmp(str, "D32_SFLOAT_S8_UINT") == 0)
return VK_FORMAT_D32_SFLOAT_S8_UINT;
if (ktx_strcasecmp(str, "BC1_RGB_UNORM_BLOCK") == 0)
return VK_FORMAT_BC1_RGB_UNORM_BLOCK;
if (ktx_strcasecmp(str, "BC1_RGB_SRGB_BLOCK") == 0)
return VK_FORMAT_BC1_RGB_SRGB_BLOCK;
if (ktx_strcasecmp(str, "BC1_RGBA_UNORM_BLOCK") == 0)
return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
if (ktx_strcasecmp(str, "BC1_RGBA_SRGB_BLOCK") == 0)
return VK_FORMAT_BC1_RGBA_SRGB_BLOCK;
if (ktx_strcasecmp(str, "BC2_UNORM_BLOCK") == 0)
return VK_FORMAT_BC2_UNORM_BLOCK;
if (ktx_strcasecmp(str, "BC2_SRGB_BLOCK") == 0)
return VK_FORMAT_BC2_SRGB_BLOCK;
if (ktx_strcasecmp(str, "BC3_UNORM_BLOCK") == 0)
return VK_FORMAT_BC3_UNORM_BLOCK;
if (ktx_strcasecmp(str, "BC3_SRGB_BLOCK") == 0)
return VK_FORMAT_BC3_SRGB_BLOCK;
if (ktx_strcasecmp(str, "BC4_UNORM_BLOCK") == 0)
return VK_FORMAT_BC4_UNORM_BLOCK;
if (ktx_strcasecmp(str, "BC4_SNORM_BLOCK") == 0)
return VK_FORMAT_BC4_SNORM_BLOCK;
if (ktx_strcasecmp(str, "BC5_UNORM_BLOCK") == 0)
return VK_FORMAT_BC5_UNORM_BLOCK;
if (ktx_strcasecmp(str, "BC5_SNORM_BLOCK") == 0)
return VK_FORMAT_BC5_SNORM_BLOCK;
if (ktx_strcasecmp(str, "BC6H_UFLOAT_BLOCK") == 0)
return VK_FORMAT_BC6H_UFLOAT_BLOCK;
if (ktx_strcasecmp(str, "BC6H_SFLOAT_BLOCK") == 0)
return VK_FORMAT_BC6H_SFLOAT_BLOCK;
if (ktx_strcasecmp(str, "BC7_UNORM_BLOCK") == 0)
return VK_FORMAT_BC7_UNORM_BLOCK;
if (ktx_strcasecmp(str, "BC7_SRGB_BLOCK") == 0)
return VK_FORMAT_BC7_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ETC2_R8G8B8_UNORM_BLOCK") == 0)
return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ETC2_R8G8B8_SRGB_BLOCK") == 0)
return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ETC2_R8G8B8A1_UNORM_BLOCK") == 0)
return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ETC2_R8G8B8A1_SRGB_BLOCK") == 0)
return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ETC2_R8G8B8A8_UNORM_BLOCK") == 0)
return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ETC2_R8G8B8A8_SRGB_BLOCK") == 0)
return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;
if (ktx_strcasecmp(str, "EAC_R11_UNORM_BLOCK") == 0)
return VK_FORMAT_EAC_R11_UNORM_BLOCK;
if (ktx_strcasecmp(str, "EAC_R11_SNORM_BLOCK") == 0)
return VK_FORMAT_EAC_R11_SNORM_BLOCK;
if (ktx_strcasecmp(str, "EAC_R11G11_UNORM_BLOCK") == 0)
return VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
if (ktx_strcasecmp(str, "EAC_R11G11_SNORM_BLOCK") == 0)
return VK_FORMAT_EAC_R11G11_SNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_4x4_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_4x4_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_4x4_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_5x4_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_5x4_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_5x4_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_5x5_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_5x5_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_5x5_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_6x5_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_6x5_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_6x5_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_6x6_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_6x6_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_6x6_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_8x5_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_8x5_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_8x5_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_8x6_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_8x6_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_8x6_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_8x8_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_8x8_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_8x8_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_10x5_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_10x5_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_10x5_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_10x6_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_10x6_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_10x6_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_10x8_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_10x8_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_10x8_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_10x10_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_10x10_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_10x10_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_12x10_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_12x10_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_12x10_SRGB_BLOCK;
if (ktx_strcasecmp(str, "ASTC_12x12_UNORM_BLOCK") == 0)
return VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
if (ktx_strcasecmp(str, "ASTC_12x12_SRGB_BLOCK") == 0)
return VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
if (ktx_strcasecmp(str, "G8B8G8R8_422_UNORM") == 0)
return VK_FORMAT_G8B8G8R8_422_UNORM;
if (ktx_strcasecmp(str, "B8G8R8G8_422_UNORM") == 0)
return VK_FORMAT_B8G8R8G8_422_UNORM;
if (ktx_strcasecmp(str, "G8_B8_R8_3PLANE_420_UNORM") == 0)
return VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
if (ktx_strcasecmp(str, "G8_B8R8_2PLANE_420_UNORM") == 0)
return VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
if (ktx_strcasecmp(str, "G8_B8_R8_3PLANE_422_UNORM") == 0)
return VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM;
if (ktx_strcasecmp(str, "G8_B8R8_2PLANE_422_UNORM") == 0)
return VK_FORMAT_G8_B8R8_2PLANE_422_UNORM;
if (ktx_strcasecmp(str, "G8_B8_R8_3PLANE_444_UNORM") == 0)
return VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM;
if (ktx_strcasecmp(str, "R10X6_UNORM_PACK16") == 0)
return VK_FORMAT_R10X6_UNORM_PACK16;
if (ktx_strcasecmp(str, "R10X6G10X6_UNORM_2PACK16") == 0)
return VK_FORMAT_R10X6G10X6_UNORM_2PACK16;
if (ktx_strcasecmp(str, "R10X6G10X6B10X6A10X6_UNORM_4PACK16") == 0)
return VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16;
if (ktx_strcasecmp(str, "G10X6B10X6G10X6R10X6_422_UNORM_4PACK16") == 0)
return VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16;
if (ktx_strcasecmp(str, "B10X6G10X6R10X6G10X6_422_UNORM_4PACK16") == 0)
return VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16;
if (ktx_strcasecmp(str, "G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16") == 0)
return VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16") == 0)
return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16") == 0)
return VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16") == 0)
return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16") == 0)
return VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16;
if (ktx_strcasecmp(str, "R12X4_UNORM_PACK16") == 0)
return VK_FORMAT_R12X4_UNORM_PACK16;
if (ktx_strcasecmp(str, "R12X4G12X4_UNORM_2PACK16") == 0)
return VK_FORMAT_R12X4G12X4_UNORM_2PACK16;
if (ktx_strcasecmp(str, "R12X4G12X4B12X4A12X4_UNORM_4PACK16") == 0)
return VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16;
if (ktx_strcasecmp(str, "G12X4B12X4G12X4R12X4_422_UNORM_4PACK16") == 0)
return VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16;
if (ktx_strcasecmp(str, "B12X4G12X4R12X4G12X4_422_UNORM_4PACK16") == 0)
return VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16;
if (ktx_strcasecmp(str, "G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16") == 0)
return VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16") == 0)
return VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16") == 0)
return VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16") == 0)
return VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16") == 0)
return VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16;
if (ktx_strcasecmp(str, "G16B16G16R16_422_UNORM") == 0)
return VK_FORMAT_G16B16G16R16_422_UNORM;
if (ktx_strcasecmp(str, "B16G16R16G16_422_UNORM") == 0)
return VK_FORMAT_B16G16R16G16_422_UNORM;
if (ktx_strcasecmp(str, "G16_B16_R16_3PLANE_420_UNORM") == 0)
return VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM;
if (ktx_strcasecmp(str, "G16_B16R16_2PLANE_420_UNORM") == 0)
return VK_FORMAT_G16_B16R16_2PLANE_420_UNORM;
if (ktx_strcasecmp(str, "G16_B16_R16_3PLANE_422_UNORM") == 0)
return VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM;
if (ktx_strcasecmp(str, "G16_B16R16_2PLANE_422_UNORM") == 0)
return VK_FORMAT_G16_B16R16_2PLANE_422_UNORM;
if (ktx_strcasecmp(str, "G16_B16_R16_3PLANE_444_UNORM") == 0)
return VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
if (ktx_strcasecmp(str, "PVRTC1_2BPP_UNORM_BLOCK_IMG") == 0)
return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;
if (ktx_strcasecmp(str, "PVRTC1_4BPP_UNORM_BLOCK_IMG") == 0)
return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
if (ktx_strcasecmp(str, "PVRTC2_2BPP_UNORM_BLOCK_IMG") == 0)
return VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG;
if (ktx_strcasecmp(str, "PVRTC2_4BPP_UNORM_BLOCK_IMG") == 0)
return VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;
if (ktx_strcasecmp(str, "PVRTC1_2BPP_SRGB_BLOCK_IMG") == 0)
return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
if (ktx_strcasecmp(str, "PVRTC1_4BPP_SRGB_BLOCK_IMG") == 0)
return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;
if (ktx_strcasecmp(str, "PVRTC2_2BPP_SRGB_BLOCK_IMG") == 0)
return VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG;
if (ktx_strcasecmp(str, "PVRTC2_4BPP_SRGB_BLOCK_IMG") == 0)
return VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG;
if (ktx_strcasecmp(str, "ASTC_4x4_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x4_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x5_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x5_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x6_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_8x5_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_8x6_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_8x8_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_10x5_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_10x6_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_10x8_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_10x10_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_12x10_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_12x12_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_3x3x3_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_3x3x3_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_3x3x3_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x3x3_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x3x3_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x3x3_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x4x3_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x4x3_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x4x3_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x4x4_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x4x4_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_4x4x4_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x4x4_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x4x4_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x4x4_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x5x4_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x5x4_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x5x4_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x5x5_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x5x5_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_5x5x5_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x5x5_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x5x5_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x5x5_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x6x5_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x6x5_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x6x5_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x6x6_UNORM_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x6x6_SRGB_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT;
if (ktx_strcasecmp(str, "ASTC_6x6x6_SFLOAT_BLOCK_EXT") == 0)
return VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT;
if (ktx_strcasecmp(str, "A4R4G4B4_UNORM_PACK16_EXT") == 0)
return VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT;
if (ktx_strcasecmp(str, "A4B4G4R4_UNORM_PACK16_EXT") == 0)
return VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT;
return VK_FORMAT_UNDEFINED;
}

View File

@@ -31,8 +31,9 @@ add_subdirectory(transcodetests)
add_subdirectory(streamtests)
add_executable( unittests
unittests/unittests.cc
unittests/image_unittests.cc
unittests/test_fragment_uri.cc
unittests/unittests.cc
unittests/wthelper.h
tests.cmake
)
@@ -52,9 +53,17 @@ target_link_libraries(
unittests
gtest
ktx
fmt::fmt
${CMAKE_THREAD_LIBS_INIT}
)
set_target_properties(
unittests
PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
)
add_executable( texturetests
texturetests/texturetests.cc
unittests/wthelper.h

View File

@@ -0,0 +1,118 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2010-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
#include <ktx/fragment_uri.h>
#include "gtest/gtest.h"
namespace {
// -------------------------------------------------------------------------------------------------
class FragmentURITest : public ::testing::Test {
protected:
FragmentURITest() {}
};
TEST_F(FragmentURITest, ParseWholeRange) {
EXPECT_EQ(ktx::parseFragmentURI("").mip, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("").facial, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("").stratal, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("m").mip, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("m").stratal, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("m").facial, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("a").mip, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("a").stratal, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("a").facial, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("f").mip, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("f").stratal, ktx::SelectorRange{});
EXPECT_EQ(ktx::parseFragmentURI("f").facial, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("m&a").mip, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("m&a").stratal, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("a&f").stratal, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("a&f").facial, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("f&m").mip, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("f&m").facial, ktx::SelectorRange(ktx::all));
}
TEST_F(FragmentURITest, ParseRangeEmpty) {
EXPECT_EQ(ktx::parseFragmentURI("m=").mip, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("a=").stratal, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("f=").facial, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("m=,").mip, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("a=,").stratal, ktx::SelectorRange(ktx::all));
EXPECT_EQ(ktx::parseFragmentURI("f=,").facial, ktx::SelectorRange(ktx::all));
}
TEST_F(FragmentURITest, ParseRangeBegin) {
EXPECT_EQ(ktx::parseFragmentURI("m=0").mip, ktx::SelectorRange(0, ktx::RangeEnd));
EXPECT_EQ(ktx::parseFragmentURI("a=0").stratal, ktx::SelectorRange(0, ktx::RangeEnd));
EXPECT_EQ(ktx::parseFragmentURI("f=0").facial, ktx::SelectorRange(0, ktx::RangeEnd));
EXPECT_EQ(ktx::parseFragmentURI("m=1").mip, ktx::SelectorRange(1, ktx::RangeEnd));
EXPECT_EQ(ktx::parseFragmentURI("a=1").stratal, ktx::SelectorRange(1, ktx::RangeEnd));
EXPECT_EQ(ktx::parseFragmentURI("f=1").facial, ktx::SelectorRange(1, ktx::RangeEnd));
}
TEST_F(FragmentURITest, ParseRangeEnd) {
EXPECT_EQ(ktx::parseFragmentURI("m=,0").mip, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("a=,0").stratal, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("f=,0").facial, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("m=,1").mip, ktx::SelectorRange(0, 2));
EXPECT_EQ(ktx::parseFragmentURI("a=,1").stratal, ktx::SelectorRange(0, 2));
EXPECT_EQ(ktx::parseFragmentURI("f=,1").facial, ktx::SelectorRange(0, 2));
}
TEST_F(FragmentURITest, ParseRangeBeginEnd) {
EXPECT_EQ(ktx::parseFragmentURI("m=0,0").mip, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("a=0,0").stratal, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("f=0,0").facial, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("m=0,1").mip, ktx::SelectorRange(0, 2));
EXPECT_EQ(ktx::parseFragmentURI("a=0,1").stratal, ktx::SelectorRange(0, 2));
EXPECT_EQ(ktx::parseFragmentURI("f=0,1").facial, ktx::SelectorRange(0, 2));
EXPECT_EQ(ktx::parseFragmentURI("m=1,3").mip, ktx::SelectorRange(1, 4));
EXPECT_EQ(ktx::parseFragmentURI("a=1,3").stratal, ktx::SelectorRange(1, 4));
EXPECT_EQ(ktx::parseFragmentURI("f=1,3").facial, ktx::SelectorRange(1, 4));
}
TEST_F(FragmentURITest, ParseMultipleRange) {
EXPECT_EQ(ktx::parseFragmentURI("m=0,0&a=1,1").mip, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("m=0,0&a=1,1").stratal, ktx::SelectorRange(1, 2));
EXPECT_EQ(ktx::parseFragmentURI("a=0,0&f=1,1").stratal, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("a=0,0&f=1,1").facial, ktx::SelectorRange(1, 2));
EXPECT_EQ(ktx::parseFragmentURI("f=0,0&m=1,1").facial, ktx::SelectorRange(0, 1));
EXPECT_EQ(ktx::parseFragmentURI("f=0,0&m=1,1").mip, ktx::SelectorRange(1, 2));
}
TEST_F(FragmentURITest, ParseMultiRange) {
EXPECT_EQ(fmt::format("{}", ktx::parseFragmentURI("m=10,15&m=20,").mip), "10..15,20..last");
EXPECT_EQ(fmt::format("{}", ktx::parseFragmentURI("m=0,0&m=1,1&m=10,15&m=20,").mip), "0,1,10..15,20..last");
}
TEST_F(FragmentURITest, Validate) {
EXPECT_TRUE(ktx::parseFragmentURI("m=0,0&a=1,1").validate(1, 2, 1));
EXPECT_FALSE(ktx::parseFragmentURI("m=0,0&a=1,1").validate(1, 1, 1));
EXPECT_FALSE(ktx::parseFragmentURI("m=0,0&a=1,1").validate(0, 0, 0));
}
TEST_F(FragmentURITest, SelectorRangeToString) {
EXPECT_EQ(fmt::format("{}", ktx::SelectorRange{0, 0}), "none");
EXPECT_EQ(fmt::format("{}", ktx::SelectorRange{0, 1}), "0");
EXPECT_EQ(fmt::format("{}", ktx::SelectorRange{10, 11}), "10");
EXPECT_EQ(fmt::format("{}", ktx::SelectorRange{0, 2}), "0..1");
EXPECT_EQ(fmt::format("{}", ktx::SelectorRange{10, 12}), "10..11");
EXPECT_EQ(fmt::format("{}", ktx::SelectorRange{0, ktx::RangeEnd}), "all");
}
} // namespace

View File

@@ -712,6 +712,16 @@ struct FormatDescriptor {
return nullptr;
}
void removeLastChannel() {
const auto numChannels = static_cast<uint32_t>(samples.size());
assert(numChannels > 1);
assert(basic.bytesPlane0 % numChannels == 0);
samples.pop_back();
basic.bytesPlane0 = basic.bytesPlane0 / numChannels * (numChannels - 1u);
if (extended.channelCount != 0)
--extended.channelCount;
}
friend std::ostream& operator<< (std::ostream& o, khr_df_sample_datatype_qualifiers_e q) {
if (q & KHR_DF_SAMPLE_DATATYPE_SIGNED)
o << " signed ";

View File

@@ -83,7 +83,7 @@ ImageInput::open(const _tstring& filename,
buffer =
std::unique_ptr<std::stringstream>(new std::stringstream);
*buffer << std::cin.rdbuf();
buffer->seekg(0, buffer->beg);
buffer->seekg(0, std::ios::beg);
}
fn = &sn;
}

View File

@@ -313,6 +313,7 @@ class ImageInput {
open(std::move(ifs), newspec);
} catch (...) {
ifs = std::move(getFile());
ifs.clear();
ifs.seekg(0);
throw;
}
@@ -321,6 +322,7 @@ class ImageInput {
open(std::move(bufferIn), newspec);
} catch (...) {
bufferIn = std::move(getBuffer());
bufferIn->clear();
bufferIn.get()->seekg(0);
throw;
}
@@ -328,6 +330,7 @@ class ImageInput {
try {
open(std::cin, newspec);
} catch (...) {
std::cin.clear();
std::cin.seekg(0);
throw;
}

View File

@@ -283,7 +283,7 @@ PngInput::readHeader()
}
}
// TODO Tools P5: Lift bit_ceil function into a common header where both ktx tools and imageio can access it
// TODO: Lift bit_ceil function into a common header where both ktx tools and imageio can access it
// C++20 - std::bit_ceil
template <typename T>
[[nodiscard]] static constexpr inline T bit_ceil(T x) noexcept {
@@ -294,7 +294,7 @@ template <typename T>
return 0;
}
// TODO Tools P5: Lift convertUNORM function into a common header where both ktx tools and imageio can access it
// TODO: Lift convertUNORM function into a common header where both ktx tools and imageio can access it
[[nodiscard]] constexpr inline uint32_t convertUNORM(uint32_t value, uint32_t sourceBits, uint32_t targetBits) noexcept {
assert(sourceBits != 0);
assert(targetBits != 0);
@@ -381,7 +381,7 @@ PngInput::readImage(void* bufferOut, size_t bufferOutByteCount,
throw std::runtime_error(fmt::format(
"PNG decode error: {}.", lodepng_error_text(lodepngError)));
// TODO Tools P5: Detect endianness
// TODO: Detect endianness
// if constexpr (std::endian::native == std::endian::little)
if (requestBits == 16) {
// LodePNG loads 16 bit channels in big endian order

View File

@@ -17,6 +17,7 @@ add_executable(ktxtools
encode_utils.h
format_descriptor.h
formats.h
fragment_uri.h
ktx_main.cpp
metrics_utils.h
transcode_utils.cpp
@@ -43,7 +44,14 @@ PRIVATE
.
$<TARGET_PROPERTY:imageio,INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:ktx,INCLUDE_DIRECTORIES>
)
target_include_directories(
ktxtools
SYSTEM
PRIVATE
${PROJECT_SOURCE_DIR}/lib
${PROJECT_SOURCE_DIR}/other_include
)
target_link_libraries(

View File

@@ -5,6 +5,9 @@
#include "command.h"
#include "version.h"
#include "ktx.h"
#include "sbufstream.h"
#include <filesystem>
#include <iostream>
#include <fmt/ostream.h>
@@ -43,4 +46,101 @@ std::string version(bool testrun) {
// -------------------------------------------------------------------------------------------------
InputStream::InputStream(const std::string& filepath, Reporter& report) :
filepath(filepath) {
if (filepath == "-") {
#if defined(_WIN32)
// Set "stdin" to binary mode
const auto setmodeResult = _setmode(_fileno(stdin), _O_BINARY);
if (setmodeResult == -1)
report.fatal(rc::IO_FAILURE, "Failed to set stdin mode to binary: {}.", setmodeResult);
// #else
// std::freopen(nullptr, "rb", stdin);
#endif
// Read everything from stdin into memory to enable random access
stdinBuffer << std::cin.rdbuf();
activeStream = &stdinBuffer;
} else {
file.open(filepath, std::ios::binary | std::ios::in);
if (!file)
report.fatal(rc::IO_FAILURE, "Could not open input file \"{}\": {}.", filepath, errnoMessage());
activeStream = &file;
}
}
// -------------------------------------------------------------------------------------------------
OutputStream::OutputStream(const std::string& filepath, Reporter& report) :
filepath(filepath) {
if (filepath == "-") {
#if defined(_WIN32)
// Set "stdout" to binary mode
const auto setmodeResult = _setmode(_fileno(stdout), _O_BINARY);
if (setmodeResult == -1)
report.fatal(rc::IO_FAILURE, "Failed to set stdout mode to binary: {}.", setmodeResult);
// #else
// std::freopen(nullptr, "wb", stdout);
#endif
file = stdout;
} else {
file = std::fopen(filepath.c_str(), "wb");
if (!file)
report.fatal(rc::IO_FAILURE, "Could not open output file \"{}\": {}.", filepath, errnoMessage());
}
// TODO: Investigate and resolve the portability issue with the C++ streams. The issue will most likely
// be in StreambufStream's position reporting and seeking. Currently a fallback is implemented in C above.
// if (filepath == "-") {
// #if defined(_WIN32)
// // Set "stdout" to binary mode
// const auto setmodeResult = _setmode(_fileno(stdout), _O_BINARY);
// if (setmodeResult == -1)
// report.fatal(rc::IO_FAILURE, "Failed to set stdout mode to binary: {}", setmodeResult);
// // #else
// // std::freopen(nullptr, "wb", stdout);
// #endif
// activeStream = &std::cout;
// } else {
// file.open(filepath, std::ios::binary | std::ios::out);
// if (!file)
// report.fatal(rc::IO_FAILURE, "Could not open output file \"{}\": {}", filepath, errnoMessage());
// activeStream = &file;
// }
}
OutputStream::~OutputStream() {
if (file != stdout)
std::fclose(file);
}
void OutputStream::write(const char* data, std::size_t size, Reporter& report) {
const auto written = std::fwrite(data, 1, size, file);
if (written != size)
report.fatal(rc::IO_FAILURE, "Failed to write output file \"{}\": {}.", fmtOutFile(filepath), errnoMessage());
}
void OutputStream::writeKTX2(ktxTexture* texture, Reporter& report) {
const auto ret = ktxTexture_WriteToStdioStream(texture, file);
if (KTX_SUCCESS != ret) {
if (file != stdout)
std::filesystem::remove(filepath);
report.fatal(rc::IO_FAILURE, "Failed to write KTX file \"{}\": KTX error: {}.", filepath, ktxErrorString(ret));
}
// TODO: Investigate and resolve the portability issue with the C++ streams. The issue will most likely
// be in StreambufStream's position reporting and seeking. Currently a fallback is implemented in C above.
// StreambufStream<std::streambuf*> stream(activeStream->rdbuf(), std::ios::in | std::ios::binary);
// const auto ret = ktxTexture_WriteToStream(texture, stream.stream());
//
// if (KTX_SUCCESS != ret) {
// if (activeStream != &std::cout)
// std::filesystem::remove(filepath);
// report.fatal(rc::IO_FAILURE, "Failed to write KTX file \"{}\": {}.", fmtOutFile(filepath), ktxErrorString(ret));
// }
}
// -------------------------------------------------------------------------------------------------
} // namespace ktx

View File

@@ -232,8 +232,8 @@ struct OptionsSingleIn {
void init(cxxopts::Options& opts) {
opts.add_options()
("s,stdin", "Use stdin as the input file")
("i,input-file", "The input file", cxxopts::value<std::string>(), "filepath");
("stdin", "Use stdin as the input file. (Using a single dash '-' as the input file has the same effect)")
("i,input-file", "The input file. Using a single dash '-' as the input file will use stdin.", cxxopts::value<std::string>(), "filepath");
opts.parse_positional("input-file");
opts.positional_help("<input-file>");
}
@@ -244,11 +244,9 @@ struct OptionsSingleIn {
if (args.count("stdin") + args.count("input-file") > 1)
report.fatal_usage("Conflicting options: Only one can be specified from <input-file> and --stdin.");
if (args.count("stdin")) {
// TODO: Tools P4: Add support for stdin (To support '-' alias argv has to be scanned as cxxopts has no direct support for it)
report.fatal(rc::NOT_IMPLEMENTED, "stdin support is not yet implemented.");
if (args.count("stdin"))
inputFilepath = "-";
} else
else
inputFilepath = args["input-file"].as<std::string>();
}
};
@@ -259,9 +257,10 @@ struct OptionsSingleInSingleOut {
void init(cxxopts::Options& opts) {
opts.add_options()
("s,stdin", "Use stdin as the input file")
("i,input-file", "The input file", cxxopts::value<std::string>(), "filepath")
("o,output-file", "The output file", cxxopts::value<std::string>(), "filepath");
("stdin", "Use stdin as the input file. (Using a single dash '-' as the input file has the same effect)")
("stdout", "Use stdout as the output file. (Using a single dash '-' as the output file has the same effect)")
("i,input-file", "The input file. Using a single dash '-' as the input file will use stdin.", cxxopts::value<std::string>(), "filepath")
("o,output-file", "The output file. Using a single dash '-' as the output file will use stdout.", cxxopts::value<std::string>(), "filepath");
opts.parse_positional("input-file", "output-file");
opts.positional_help("<input-file> <output-file>");
}
@@ -272,14 +271,20 @@ struct OptionsSingleInSingleOut {
if (args.count("stdin") + args.count("input-file") > 1)
report.fatal_usage("Conflicting options: Only one can be specified from <input-file> and --stdin.");
if (args.count("stdin")) {
// TODO: Tools P4: Add support for stdin (To support '-' alias argv has to be scanned as cxxopts has no direct support for it)
report.fatal(rc::NOT_IMPLEMENTED, "stdin support is not yet implemented.");
if (args.count("stdout") + args.count("output-file") == 0)
report.fatal_usage("Missing output file. Either <output-file> or --stdout must be specified.");
if (args.count("stdout") + args.count("output-file") > 1)
report.fatal_usage("Conflicting options: Only one can be specified from <output-file> and --stdout.");
if (args.count("stdin"))
inputFilepath = "-";
} else
else
inputFilepath = args["input-file"].as<std::string>();
outputFilepath = args["output-file"].as<std::string>();
if (args.count("stdout"))
outputFilepath = "-";
else
outputFilepath = args["output-file"].as<std::string>();
}
};
@@ -289,23 +294,35 @@ struct OptionsMultiInSingleOut {
void init(cxxopts::Options& opts) {
opts.add_options()
("files", "Input/output files. Last file specified will be used as output", cxxopts::value<std::vector<std::string>>(), "<filepath>");
("stdin", "Use stdin as the first input file. (Using a single dash '-' as the first input file has the same effect)")
("stdout", "Use stdout as the output file. (Using a single dash '-' as the output file has the same effect)")
("files", "Input/output files. Last file specified will be used as output."
" Using a single dash '-' as an input or output file will use stdin/stdout.", cxxopts::value<std::vector<std::string>>(), "<filepath>");
opts.parse_positional("files");
opts.positional_help("<input-file...> <output-file>");
}
void process(cxxopts::Options&, cxxopts::ParseResult& args, Reporter& report) {
std::vector<std::string> files;
if (args.count("files"))
files = args["files"].as<std::vector<std::string>>();
if (args.count("stdin"))
files.emplace_back("-");
if (args.count("files")) {
const auto& argFiles = args["files"].as<std::vector<std::string>>();
files.insert(files.end(), argFiles.begin(), argFiles.end());
}
if (args.count("stdout"))
files.emplace_back("-");
if (files.size() < 1)
report.fatal_usage("Input and output files must be specified.");
if (files.size() < 2)
report.fatal_usage("Output file must be specified.");
report.fatal_usage("{} file must be specified.", args.count("stdout") == 0 ? "Output" : "Input");
outputFilepath = std::move(files.back());
files.pop_back();
inputFilepaths = std::move(files);
if (std::count(inputFilepaths.begin(), inputFilepaths.end(), "-") > 1)
report.fatal_usage("'-' or --stdin as input file was specified more than once.");
}
};
@@ -324,4 +341,40 @@ struct Combine : Args... {
}
};
/// Helper to handle stdin and fstream uniformly
class InputStream {
std::string filepath;
std::istream* activeStream = nullptr;
std::ifstream file; // Unused for stdin/stdout
std::stringstream stdinBuffer;
public:
InputStream(const std::string& filepath, Reporter& report);
/*explicit(false)*/ operator std::istream&() {
return *activeStream;
}
std::istream* operator->() {
return activeStream;
}
std::istream& operator*() {
return *activeStream;
}
};
/// Helper to handle stdout and fstream uniformly
class OutputStream {
std::string filepath;
FILE* file;
// std::ostream* activeStream = nullptr;
// std::ofstream file; // Unused for stdin/stdout
public:
OutputStream(const std::string& filepath, Reporter& report);
~OutputStream();
void writeKTX2(ktxTexture* texture, Reporter& report);
void write(const char* data, std::size_t size, Reporter& report);
};
} // namespace ktx

View File

@@ -594,7 +594,9 @@ Create a KTX2 file from various input files.
@section ktxtools_create_description DESCRIPTION
@b ktx @b create can create, encode and supercompress a KTX2 file from the
input images specified as the @e input-file... arguments and save it as the
@e output-file. The last positional argument is treated as the output-file.
@e output-file. The last positional argument is treated as the @e output-file.
If the @e input-file is '-' the file will be read from the stdin.
If the @e output-path is '-' the output file will be written to the stdout.
Each @e input-file must be a valid EXR (.exr), PNG (.png) or Raw (.raw) file.
PNG files with luminance (L) or luminance + alpha (LA) data will be converted
@@ -822,13 +824,13 @@ void CommandCreate::processOptions(cxxopts::Options& opts, cxxopts::ParseResult&
numLayers = options.layers.value_or(1);
numFaces = options.cubemap ? 6 : 1;
numBaseDepths = options.depth.value_or(1u);
// baseDepth is determined by the --depth option. As the loaded images are
// 2D "z_slice_of_blocks" their depth is always 1 and not relevant for any kind of deduction
const auto blockSizeZ = isFormat3DBlockCompressed(options.vkFormat) ?
createFormatDescriptor(options.vkFormat, *this).basic.texelBlockDimension2 + 1u : 1u;
uint32_t expectedInputImages = 0;
for (uint32_t i = 0; i < (options.mipmapGenerate ? 1 : numLevels); ++i)
// If --generate-mipmap is set the input only contains the base level images
expectedInputImages += numLayers * numFaces * std::max(numBaseDepths >> i, 1u);
expectedInputImages += numLayers * numFaces * ceil_div(std::max(numBaseDepths >> i, 1u), blockSizeZ);
if (options.inputFilepaths.size() != expectedInputImages) {
fatal_usage("Too {} input image for {} level{}, {} layer, {} face and {} depth. Provided {} but expected {}.",
options.inputFilepaths.size() > expectedInputImages ? "many" : "few",
@@ -954,12 +956,10 @@ void CommandCreate::foreachImage(const FormatDescriptor& format, F&& func) {
auto inputFileIt = options.inputFilepaths.begin();
for (uint32_t levelIndex = 0; levelIndex < (options.mipmapGenerate ? 1 : numLevels); ++levelIndex) {
// TODO: Tools P5: 3D BC formats currently discard the last partial z block slice
// This should be: ceil_div instead of div
const auto numimageDepth = std::max(numBaseDepths >> levelIndex, 1u) / (format.basic.texelBlockDimension2 + 1);
const auto numDepthSlices = ceil_div(std::max(numBaseDepths >> levelIndex, 1u), format.basic.texelBlockDimension2 + 1u);
for (uint32_t layerIndex = 0; layerIndex < numLayers; ++layerIndex) {
for (uint32_t faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
for (uint32_t depthSliceIndex = 0; depthSliceIndex < numimageDepth; ++depthSliceIndex) {
for (uint32_t depthSliceIndex = 0; depthSliceIndex < numDepthSlices; ++depthSliceIndex) {
assert(inputFileIt != options.inputFilepaths.end() && "Internal error"); // inputFilepaths size was already validated during arg parsing
func(*inputFileIt++, levelIndex, layerIndex, faceIndex, depthSliceIndex);
}
@@ -971,18 +971,20 @@ void CommandCreate::foreachImage(const FormatDescriptor& format, F&& func) {
std::string CommandCreate::readRawFile(const std::filesystem::path& filepath) {
std::string result;
std::ifstream file(filepath, std::ios::binary | std::ios::in | std::ios::ate);
if (!file)
fatal(rc::IO_FAILURE, "Failed to open file \"{}\": {}.", filepath.generic_string(), errnoMessage());
InputStream inputStream(filepath.string(), *this);
const auto size = file.tellg();
file.seekg(0);
if (file.fail())
inputStream->seekg(0, std::ios::end);
if (inputStream->fail())
fatal(rc::IO_FAILURE, "Failed to seek file \"{}\": {}.", filepath.generic_string(), errnoMessage());
const auto size = inputStream->tellg();
inputStream->seekg(0);
if (inputStream->fail())
fatal(rc::IO_FAILURE, "Failed to seek file \"{}\": {}.", filepath.generic_string(), errnoMessage());
result.resize(size);
file.read(result.data(), size);
if (file.fail())
inputStream->read(result.data(), size);
if (inputStream->fail())
fatal(rc::IO_FAILURE, "Failed to read file \"{}\": {}.", filepath.generic_string(), errnoMessage());
return result;
@@ -1017,7 +1019,7 @@ void CommandCreate::executeCreate() {
if (options.cubemap && target.width() != target.height())
fatal(rc::INVALID_FILE, "--cubemap specified but the input image \"{}\" with size {}x{} is not square.",
inputFilepath, target.width(), target.height());
fmtInFile(inputFilepath), target.width(), target.height());
texture = createTexture(target);
}
@@ -1027,7 +1029,7 @@ void CommandCreate::executeCreate() {
const auto expectedFileSize = ktxTexture_GetImageSize(texture, levelIndex);
if (rawData.size() != expectedFileSize)
fatal(rc::INVALID_FILE, "Raw input file \"{}\" with {} bytes for level {} does not match the expected size of {} bytes.",
inputFilepath, rawData.size(), levelIndex, expectedFileSize);
fmtInFile(inputFilepath), rawData.size(), levelIndex, expectedFileSize);
const auto ret = ktxTexture_SetImageFromMemory(
texture,
@@ -1050,17 +1052,17 @@ void CommandCreate::executeCreate() {
if (options.cubemap && target.width() != target.height())
fatal(rc::INVALID_FILE, "--cubemap specified but the input image \"{}\" with size {}x{} is not square.",
inputFilepath, target.width(), target.height());
fmtInFile(inputFilepath), target.width(), target.height());
if (options._1d && target.height() != 1)
fatal(rc::INVALID_FILE, "For --1d textures the input image height must be 1, but for \"{}\" it was {}.",
inputFilepath, target.height());
fmtInFile(inputFilepath), target.height());
const auto maxDimension = std::max(target.width(), std::max(target.height(), numBaseDepths));
const auto maxLevels = log2(maxDimension) + 1;
if (options.levels.value_or(1) > maxLevels)
fatal_usage("Requested {} levels is too many. With input image \"{}\" sized {}x{} and depth {} the texture can only have {} levels at most.",
options.levels.value_or(1), inputFilepath, target.width(), target.height(), numBaseDepths, maxLevels);
options.levels.value_or(1), fmtInFile(inputFilepath), target.width(), target.height(), numBaseDepths, maxLevels);
if (options.astc)
selectASTCMode(inputImageFile->spec().format().largestChannelBitLength());
@@ -1077,7 +1079,7 @@ void CommandCreate::executeCreate() {
if (inputImageFile->spec().width() != imageWidth || inputImageFile->spec().height() != imageHeight)
fatal(rc::INVALID_FILE, "Input image \"{}\" with size {}x{} does not match expected size {}x{} for level {}.",
inputFilepath, inputImageFile->spec().width(), inputImageFile->spec().height(), imageWidth, imageHeight, levelIndex);
fmtInFile(inputFilepath), inputImageFile->spec().width(), inputImageFile->spec().height(), imageWidth, imageHeight, levelIndex);
auto image = loadInputImage(*inputImageFile);
@@ -1091,12 +1093,12 @@ void CommandCreate::executeCreate() {
fatal(rc::INVALID_FILE,
"Input file \"{}\" would need color conversion as input and output primaries are different. "
"Use --assign-primaries and do not use --convert-primaries to avoid unwanted color conversions.",
inputFilepath);
fmtInFile(inputFilepath));
if (options.warnOnColorConversions)
warning("Input file \"{}\" is color converted as input and output primaries are different. "
"Use --assign-primaries and do not use --convert-primaries to avoid unwanted color conversions.",
inputFilepath);
fmtInFile(inputFilepath));
// Transform OETF with primary transform
image->transformColorSpace(*colorSpaceInfo.srcTransferFunction, *colorSpaceInfo.dstTransferFunction, &primaryTransform);
@@ -1105,12 +1107,12 @@ void CommandCreate::executeCreate() {
fatal(rc::INVALID_FILE,
"Input file \"{}\" would need color conversion as input and output transfer functions are different. "
"Use --assign-oetf and do not use --convert-oetf to avoid unwanted color conversions.",
inputFilepath);
fmtInFile(inputFilepath));
if (options.warnOnColorConversions)
warning("Input file \"{}\" is color converted as input and output transfer functions are different. "
"Use --assign-oetf and do not use --convert-oetf to avoid unwanted color conversions.",
inputFilepath);
fmtInFile(inputFilepath));
// Transform OETF without primary transform
image->transformColorSpace(*colorSpaceInfo.srcTransferFunction, *colorSpaceInfo.dstTransferFunction);
@@ -1161,25 +1163,9 @@ void CommandCreate::executeCreate() {
// Save output file
if (std::filesystem::path(options.outputFilepath).has_parent_path())
std::filesystem::create_directories(std::filesystem::path(options.outputFilepath).parent_path());
FILE* f = _tfopen(options.outputFilepath.c_str(), "wb");
if (!f)
fatal(rc::IO_FAILURE, "Could not open output file \"{}\": {}.", options.outputFilepath, errnoMessage());
// #if defined(_WIN32)
// if (f == stdout) {
// /* Set "stdout" to have binary mode */
// (void) _setmode(_fileno(stdout), _O_BINARY);
// }
// #endif
const auto ret = ktxTexture_WriteToStdioStream(texture, f);
fclose(f);
if (KTX_SUCCESS != ret) {
if (f != stdout)
std::filesystem::remove(options.outputFilepath);
fatal(rc::IO_FAILURE, "Failed to write KTX file \"{}\": KTX error: {}", options.outputFilepath, ktxErrorString(ret));
}
OutputStream outputFile(options.outputFilepath, *this);
outputFile.writeKTX2(texture, *this);
}
// -------------------------------------------------------------------------------------------------
@@ -1371,6 +1357,14 @@ std::vector<uint8_t> convertSFLOAT(const std::unique_ptr<Image>& image, std::str
return image->getSFloat(componentCount, bits);
}
std::vector<uint8_t> convertB10G11R11(const std::unique_ptr<Image>& image) {
return image->getB10G11R11();
}
std::vector<uint8_t> convertE5B9G9R9(const std::unique_ptr<Image>& image) {
return image->getE5B9G9R9();
}
template <typename T>
std::vector<uint8_t> convertUINT(const std::unique_ptr<Image>& image, std::string_view swizzle = "") {
using ComponentT = typename T::Color::value_type;
@@ -1776,10 +1770,10 @@ std::vector<uint8_t> CommandCreate::convert(const std::unique_ptr<Image>& image,
// The same EXR pixel types as for the decoding must be enforced.
// Extra channels must be dropped.
// case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
// TODO: Tools P4: Create B10G11R11_UFLOAT_PACK32
// case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
// TODO: Tools P4: Create E5B9G9R9_UFLOAT_PACK32
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
return convertB10G11R11(image);
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
return convertE5B9G9R9(image);
// Input data must be rounded to the target precision.
@@ -1837,7 +1831,7 @@ KTXTexture2 CommandCreate::createTexture(const ImageSpec& target) {
// BT709 is the default for DFDs.
if (target.format().primaries() != KHR_DF_PRIMARIES_BT709)
KHR_DFDSETVAL(((ktxTexture2*) texture)->pDfd + 1, PRIMARIES, target.format().primaries());
KHR_DFDSETVAL(texture->pDfd + 1, PRIMARIES, target.format().primaries());
return texture;
}
@@ -2110,7 +2104,6 @@ void CommandCreate::checkSpecsMatch(const ImageInput& currentFile, const ImageSp
const FormatDescriptor& firstFormat = firstSpec.format();
const FormatDescriptor& currentFormat = currentFile.spec().format();
// TODO: Tools P5: Question: Should we allow these with warnings? Spec says fatal, but if a conversion is possible this would just stop valid usecases
if (currentFormat.transfer() != firstFormat.transfer()) {
fatal(rc::INVALID_FILE, "Input image \"{}\" has different transfer function ({}) than preceding image(s) ({}).",
currentFile.filename(), toString(currentFormat.transfer()), toString(firstFormat.transfer()));

View File

@@ -39,6 +39,8 @@ Encode a KTX2 file.
@section ktxtools_encode_description DESCRIPTION
@b ktx @b encode can encode the KTX file specified as the @e input-file argument,
optionally supercompress the result, and save it as the @e output-file.
If the @e input-file is '-' the file will be read from the stdin.
If the @e output-path is '-' the output file will be written to the stdout.
The input file must be R8, RG8, RGB8 or RGBA8 (or their sRGB variant).
If the input file is invalid the first encountered validation error is displayed
to the stderr and the command exits with the relevant non-zero status code.
@@ -141,11 +143,11 @@ void CommandEncode::processOptions(cxxopts::Options& opts, cxxopts::ParseResult&
}
void CommandEncode::executeEncode() {
std::ifstream file(options.inputFilepath, std::ios::in | std::ios::binary);
validateToolInput(file, options.inputFilepath, *this);
InputStream inputStream(options.inputFilepath, *this);
validateToolInput(inputStream, fmtInFile(options.inputFilepath), *this);
KTXTexture2 texture{nullptr};
StreambufStream<std::streambuf*> ktx2Stream{file.rdbuf(), std::ios::in | std::ios::binary};
StreambufStream<std::streambuf*> ktx2Stream{inputStream->rdbuf(), std::ios::in | std::ios::binary};
auto ret = ktxTexture2_CreateFromStream(ktx2Stream.stream(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, texture.pHandle());
if (ret != KTX_SUCCESS)
fatal(rc::INVALID_FILE, "Failed to create KTX2 texture: {}", ktxErrorString(ret));
@@ -171,6 +173,9 @@ void CommandEncode::executeEncode() {
break;
}
// Convert 1D textures to 2D (we could consider 1D as an invalid input)
texture->numDimensions = std::max(2u, texture->numDimensions);
// Modify KTXwriter metadata
const auto writer = fmt::format("{} {}", commandName, version(options.testrun));
ktxHashList_DeleteKVPair(&texture->kvDataHead, KTX_WRITER_KEY);
@@ -206,19 +211,9 @@ void CommandEncode::executeEncode() {
// Save output file
if (std::filesystem::path(options.outputFilepath).has_parent_path())
std::filesystem::create_directories(std::filesystem::path(options.outputFilepath).parent_path());
FILE* f = _tfopen(options.outputFilepath.c_str(), "wb");
if (!f)
fatal(rc::IO_FAILURE, "Could not open output file \"{}\": ", options.outputFilepath, errnoMessage());
ret = ktxTexture_WriteToStdioStream(texture, f);
fclose(f);
if (KTX_SUCCESS != ret) {
if (f != stdout)
std::filesystem::remove(options.outputFilepath);
fatal(rc::IO_FAILURE, "Failed to write KTX file \"{}\": KTX error: {}",
options.outputFilepath, ktxErrorString(ret));
}
OutputStream outputFile(options.outputFilepath, *this);
outputFile.writeKTX2(texture, *this);
}
} // namespace ktx

File diff suppressed because it is too large Load Diff

View File

@@ -32,6 +32,7 @@ Print information about a KTX2 file.
@section ktxtools_info_description DESCRIPTION
@b ktx @b info prints information about the KTX2 file specified as the @e input-file argument.
If the @e input-file is '-' the file will be read from the stdin.
The command implicitly calls @ref ktxtools_validate "validate" and prints any found errors
and warnings to stdout.
If the specified input file is invalid the information is displayed based on best effort and
@@ -106,21 +107,19 @@ void CommandInfo::processOptions(cxxopts::Options& opts, cxxopts::ParseResult& a
}
void CommandInfo::executeInfo() {
std::ifstream file(options.inputFilepath, std::ios::binary | std::ios::in);
if (!file)
fatal(rc::IO_FAILURE, "Could not open input file \"{}\": {}", options.inputFilepath, errnoMessage());
InputStream inputStream(options.inputFilepath, *this);
KTX_error_code result;
switch (options.format) {
case OutputFormat::text:
result = printInfoText(file);
result = printInfoText(inputStream);
break;
case OutputFormat::json:
result = printInfoJSON(file, false);
result = printInfoJSON(inputStream, false);
break;
case OutputFormat::json_mini:
result = printInfoJSON(file, true);
result = printInfoJSON(inputStream, true);
break;
default:
assert(false && "Internal error");
@@ -128,12 +127,12 @@ void CommandInfo::executeInfo() {
}
if (result != KTX_SUCCESS)
fatal(rc::INVALID_FILE, "Failed to process KTX2 file \"{}\": {}", options.inputFilepath, ktxErrorString(result));
fatal(rc::INVALID_FILE, "Failed to process KTX2 file \"{}\": {}", fmtInFile(options.inputFilepath), ktxErrorString(result));
}
KTX_error_code CommandInfo::printInfoText(std::istream& file) {
std::ostringstream messagesOS;
const auto validationResult = validateIOStream(file, options.inputFilepath, false, false, [&](const ValidationReport& issue) {
const auto validationResult = validateIOStream(file, fmtInFile(options.inputFilepath), false, false, [&](const ValidationReport& issue) {
fmt::print(messagesOS, "{}-{:04}: {}\n", toString(issue.type), issue.id, issue.message);
fmt::print(messagesOS, " {}\n", issue.details);
});
@@ -167,7 +166,7 @@ KTX_error_code CommandInfo::printInfoJSON(std::istream& file, bool minified) {
PrintIndent pi{messagesOS, base_indent, indent_width};
bool first = true;
const auto validationResult = validateIOStream(file, options.inputFilepath, false, false, [&](const ValidationReport& issue) {
const auto validationResult = validateIOStream(file, fmtInFile(options.inputFilepath), false, false, [&](const ValidationReport& issue) {
if (!std::exchange(first, false)) {
pi(2, "}},{}", nl);
}

View File

@@ -39,6 +39,8 @@ Transcode a KTX2 file.
@section ktxtools_transcode_description DESCRIPTION
@b ktx @b transcode can transcode the KTX file specified as the @e input-file argument,
optionally supercompress the result, and save it as the @e output-file.
If the @e input-file is '-' the file will be read from the stdin.
If the @e output-path is '-' the output file will be written to the stdout.
The input file must be transcodable (it must be either BasisLZ supercompressed or has UASTC
color model in the DFD).
If the input file is invalid the first encountered validation error is displayed
@@ -135,11 +137,11 @@ void CommandTranscode::processOptions(cxxopts::Options& opts, cxxopts::ParseResu
}
void CommandTranscode::executeTranscode() {
std::ifstream file(options.inputFilepath, std::ios::in | std::ios::binary);
validateToolInput(file, options.inputFilepath, *this);
InputStream inputStream(options.inputFilepath, *this);
validateToolInput(inputStream, fmtInFile(options.inputFilepath), *this);
KTXTexture2 texture{nullptr};
StreambufStream<std::streambuf*> ktx2Stream{file.rdbuf(), std::ios::in | std::ios::binary};
StreambufStream<std::streambuf*> ktx2Stream{inputStream->rdbuf(), std::ios::in | std::ios::binary};
auto ret = ktxTexture2_CreateFromStream(ktx2Stream.stream(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, texture.pHandle());
if (ret != KTX_SUCCESS)
fatal(rc::INVALID_FILE, "Failed to create KTX2 texture: {}", ktxErrorString(ret));
@@ -147,148 +149,33 @@ void CommandTranscode::executeTranscode() {
if (!ktxTexture2_NeedsTranscoding(texture))
fatal(rc::INVALID_FILE, "KTX file is not transcodable.");
options.validateTextureTranscode(texture, *this);
ret = ktxTexture2_TranscodeBasis(texture, options.transcodeTarget.value(), 0);
if (ret != KTX_SUCCESS)
fatal(rc::INVALID_FILE, "Failed to transcode KTX2 texture: {}", ktxErrorString(ret));
// Need to perform format conversion and swizzling if needed
bool needFormatConversion = false;
bool needSwizzle = false;
if (options.transcodeSwizzleComponents != 0) {
if (options.transcodeSwizzleComponents == 4) {
if (options.transcodeSwizzle != "rgba") {
needSwizzle = true;
}
} else {
needFormatConversion = true;
needSwizzle = true;
}
}
KTXTexture2 convertedTexture{nullptr};
if (needFormatConversion) {
ktxTextureCreateInfo createInfo;
std::memset(&createInfo, 0, sizeof(createInfo));
const bool srgb = (texture->vkFormat == VK_FORMAT_R8G8B8A8_SRGB);
switch (options.transcodeSwizzleComponents) {
case 1:
createInfo.vkFormat = srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
break;
case 2:
createInfo.vkFormat = srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM;
break;
case 3:
createInfo.vkFormat = srgb ? VK_FORMAT_R8G8B8_SRGB : VK_FORMAT_R8G8B8_UNORM;
break;
default:
assert(false);
}
createInfo.baseWidth = texture->baseWidth;
createInfo.baseHeight = texture->baseHeight;
createInfo.baseDepth = texture->baseDepth;
createInfo.generateMipmaps = texture->generateMipmaps;
createInfo.isArray = texture->isArray;
createInfo.numDimensions = texture->numDimensions;
createInfo.numFaces = texture->numFaces;
createInfo.numLayers = texture->numLayers;
createInfo.numLevels = texture->numLevels;
createInfo.pDfd = nullptr;
ret = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, convertedTexture.pHandle());
if (KTX_SUCCESS != ret)
fatal(rc::IO_FAILURE, "Failed to create output texture: {}", ktxErrorString(ret));
}
KTXTexture2& outputTexture = (convertedTexture.handle() != nullptr) ? convertedTexture : texture;
if (needFormatConversion || needSwizzle) {
for (uint32_t levelIndex = 0; levelIndex < texture->numLevels; ++levelIndex) {
const auto imageWidth = std::max(1u, texture->baseWidth >> levelIndex);
const auto imageHeight = std::max(1u, texture->baseHeight >> levelIndex);
const auto imageDepth = std::max(1u, texture->baseDepth >> levelIndex);
for (uint32_t faceIndex = 0; faceIndex < texture->numFaces; ++faceIndex) {
for (uint32_t layerIndex = 0; layerIndex < texture->numLayers; ++layerIndex) {
for (uint32_t depthIndex = 0; depthIndex < imageDepth; ++depthIndex) {
ktx_size_t srcImageOffset;
ktxTexture_GetImageOffset(texture, levelIndex, layerIndex, faceIndex + depthIndex, &srcImageOffset);
ktx_size_t dstImageOffset;
ktxTexture_GetImageOffset(outputTexture, levelIndex, layerIndex, faceIndex + depthIndex, &dstImageOffset);
auto srcImageData = texture->pData + srcImageOffset;
auto dstImageData = outputTexture->pData + dstImageOffset;
rgba8image srcImage(imageWidth, imageHeight, reinterpret_cast<rgba8color*>(srcImageData));
switch (options.transcodeSwizzleComponents) {
case 1: {
r8image dstImage(imageWidth, imageHeight, reinterpret_cast<r8color*>(dstImageData));
srcImage.copyToR(dstImage, options.transcodeSwizzle);
break;
}
case 2: {
rg8image dstImage(imageWidth, imageHeight, reinterpret_cast<rg8color*>(dstImageData));
srcImage.copyToRG(dstImage, options.transcodeSwizzle);
break;
}
case 3: {
rgb8image dstImage(imageWidth, imageHeight, reinterpret_cast<rgb8color*>(dstImageData));
srcImage.copyToRGB(dstImage, options.transcodeSwizzle);
break;
}
case 4: {
// Swizzle in-place
assert(srcImageData == dstImageData);
srcImage.swizzle(options.transcodeSwizzle);
break;
}
default:
assert(false);
}
}
}
}
}
}
texture = transcode(std::move(texture), options, *this);
if (options.zstd) {
ret = ktxTexture2_DeflateZstd(outputTexture, *options.zstd);
ret = ktxTexture2_DeflateZstd(texture, *options.zstd);
if (ret != KTX_SUCCESS)
fatal(rc::KTX_FAILURE, "Zstd deflation failed. KTX Error: {}", ktxErrorString(ret));
}
if (options.zlib) {
ret = ktxTexture2_DeflateZLIB(outputTexture, *options.zlib);
ret = ktxTexture2_DeflateZLIB(texture, *options.zlib);
if (ret != KTX_SUCCESS)
fatal(rc::KTX_FAILURE, "ZLIB deflation failed. KTX Error: {}", ktxErrorString(ret));
}
// Modify KTXwriter metadata
const auto writer = fmt::format("{} {}", commandName, version(options.testrun));
ktxHashList_DeleteKVPair(&outputTexture->kvDataHead, KTX_WRITER_KEY);
ktxHashList_AddKVPair(&outputTexture->kvDataHead, KTX_WRITER_KEY,
ktxHashList_DeleteKVPair(&texture->kvDataHead, KTX_WRITER_KEY);
ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY,
static_cast<uint32_t>(writer.size() + 1), // +1 to include the \0
writer.c_str());
// Save output file
if (std::filesystem::path(options.outputFilepath).has_parent_path())
std::filesystem::create_directories(std::filesystem::path(options.outputFilepath).parent_path());
FILE* f = _tfopen(options.outputFilepath.c_str(), "wb");
if (!f)
fatal(rc::IO_FAILURE, "Could not open output file \"{}\": ", options.outputFilepath, errnoMessage());
ret = ktxTexture_WriteToStdioStream(outputTexture, f);
fclose(f);
if (KTX_SUCCESS != ret) {
if (f != stdout)
std::filesystem::remove(options.outputFilepath);
fatal(rc::IO_FAILURE, "Failed to write KTX file \"{}\": KTX error: {}",
options.outputFilepath, ktxErrorString(ret));
}
OutputStream outputFile(options.outputFilepath, *this);
outputFile.writeKTX2(texture, *this);
}
} // namespace ktx

View File

@@ -28,6 +28,7 @@ Validate a KTX2 file.
@section ktxtools_validate_description DESCRIPTION
@b ktx @b validate validates the Khronos texture format version 2 (KTX2) file specified
as the @e input-file argument. It prints any found errors and warnings to stdout.
If the @e input-file is '-' the file will be read from the stdin.
The validation rules and checks are based on the official specification:
KTX File Format Specification - https://registry.khronos.org/KTX/specs/2.0/ktxspec.v2.html
@@ -116,11 +117,14 @@ void CommandValidate::processOptions(cxxopts::Options& opts, cxxopts::ParseResul
}
void CommandValidate::executeValidate() {
InputStream inputStream(options.inputFilepath, *this);
switch (options.format) {
case OutputFormat::text: {
std::ostringstream messagesOS;
const auto validationResult = validateNamedFile(
options.inputFilepath,
const auto validationResult = validateIOStream(
inputStream,
fmtInFile(options.inputFilepath),
options.warningsAsErrors,
options.GLTFBasisU,
[&](const ValidationReport& issue) {
@@ -150,8 +154,9 @@ void CommandValidate::executeValidate() {
PrintIndent pi{messagesOS, base_indent, indent_width};
bool first = true;
const auto validationResult = validateNamedFile(
options.inputFilepath,
const auto validationResult = validateIOStream(
inputStream,
fmtInFile(options.inputFilepath),
options.warningsAsErrors,
options.GLTFBasisU,
[&](const ValidationReport& issue) {

View File

@@ -9,6 +9,8 @@
#include "ktx.h"
#define LIBKTX // To stop dfdutils including vulkan_core.h.
#include "dfdutils/dfd.h"
#include <string>
#include <string_view>
#include "utility.h"
@@ -20,6 +22,7 @@ extern "C" {
bool isProhibitedFormat(VkFormat format);
bool isValidFormat(VkFormat format);
const char* vkFormatString(VkFormat format);
VkFormat stringToVkFormat(const char* str);
}
// -------------------------------------------------------------------------------------------------
@@ -89,291 +92,11 @@ namespace ktx {
// -------------------------------------------------------------------------------------------------
/// Parses a VkFormat. VK_FORMAT_ prefix is optional. Case insensitive.
[[nodiscard]] inline std::optional<VkFormat> parseVkFormat(std::string str) noexcept {
to_upper_inplace(str);
if (starts_with(str, "VK_FORMAT_"))
str.erase(0, sizeof("VK_FORMAT_") - 1);
static const std::unordered_map<std::string, VkFormat> lookupTable{
{ "R4G4_UNORM_PACK8", VK_FORMAT_R4G4_UNORM_PACK8 },
{ "R4G4B4A4_UNORM_PACK16", VK_FORMAT_R4G4B4A4_UNORM_PACK16 },
{ "B4G4R4A4_UNORM_PACK16", VK_FORMAT_B4G4R4A4_UNORM_PACK16 },
{ "R5G6B5_UNORM_PACK16", VK_FORMAT_R5G6B5_UNORM_PACK16 },
{ "B5G6R5_UNORM_PACK16", VK_FORMAT_B5G6R5_UNORM_PACK16 },
{ "R5G5B5A1_UNORM_PACK16", VK_FORMAT_R5G5B5A1_UNORM_PACK16 },
{ "B5G5R5A1_UNORM_PACK16", VK_FORMAT_B5G5R5A1_UNORM_PACK16 },
{ "A1R5G5B5_UNORM_PACK16", VK_FORMAT_A1R5G5B5_UNORM_PACK16 },
{ "R8_UNORM", VK_FORMAT_R8_UNORM },
{ "R8_SNORM", VK_FORMAT_R8_SNORM },
{ "R8_USCALED", VK_FORMAT_R8_USCALED },
{ "R8_SSCALED", VK_FORMAT_R8_SSCALED },
{ "R8_UINT", VK_FORMAT_R8_UINT },
{ "R8_SINT", VK_FORMAT_R8_SINT },
{ "R8_SRGB", VK_FORMAT_R8_SRGB },
{ "R8G8_UNORM", VK_FORMAT_R8G8_UNORM },
{ "R8G8_SNORM", VK_FORMAT_R8G8_SNORM },
{ "R8G8_USCALED", VK_FORMAT_R8G8_USCALED },
{ "R8G8_SSCALED", VK_FORMAT_R8G8_SSCALED },
{ "R8G8_UINT", VK_FORMAT_R8G8_UINT },
{ "R8G8_SINT", VK_FORMAT_R8G8_SINT },
{ "R8G8_SRGB", VK_FORMAT_R8G8_SRGB },
{ "R8G8B8_UNORM", VK_FORMAT_R8G8B8_UNORM },
{ "R8G8B8_SNORM", VK_FORMAT_R8G8B8_SNORM },
{ "R8G8B8_USCALED", VK_FORMAT_R8G8B8_USCALED },
{ "R8G8B8_SSCALED", VK_FORMAT_R8G8B8_SSCALED },
{ "R8G8B8_UINT", VK_FORMAT_R8G8B8_UINT },
{ "R8G8B8_SINT", VK_FORMAT_R8G8B8_SINT },
{ "R8G8B8_SRGB", VK_FORMAT_R8G8B8_SRGB },
{ "B8G8R8_UNORM", VK_FORMAT_B8G8R8_UNORM },
{ "B8G8R8_SNORM", VK_FORMAT_B8G8R8_SNORM },
{ "B8G8R8_USCALED", VK_FORMAT_B8G8R8_USCALED },
{ "B8G8R8_SSCALED", VK_FORMAT_B8G8R8_SSCALED },
{ "B8G8R8_UINT", VK_FORMAT_B8G8R8_UINT },
{ "B8G8R8_SINT", VK_FORMAT_B8G8R8_SINT },
{ "B8G8R8_SRGB", VK_FORMAT_B8G8R8_SRGB },
{ "R8G8B8A8_UNORM", VK_FORMAT_R8G8B8A8_UNORM },
{ "R8G8B8A8_SNORM", VK_FORMAT_R8G8B8A8_SNORM },
{ "R8G8B8A8_USCALED", VK_FORMAT_R8G8B8A8_USCALED },
{ "R8G8B8A8_SSCALED", VK_FORMAT_R8G8B8A8_SSCALED },
{ "R8G8B8A8_UINT", VK_FORMAT_R8G8B8A8_UINT },
{ "R8G8B8A8_SINT", VK_FORMAT_R8G8B8A8_SINT },
{ "R8G8B8A8_SRGB", VK_FORMAT_R8G8B8A8_SRGB },
{ "B8G8R8A8_UNORM", VK_FORMAT_B8G8R8A8_UNORM },
{ "B8G8R8A8_SNORM", VK_FORMAT_B8G8R8A8_SNORM },
{ "B8G8R8A8_USCALED", VK_FORMAT_B8G8R8A8_USCALED },
{ "B8G8R8A8_SSCALED", VK_FORMAT_B8G8R8A8_SSCALED },
{ "B8G8R8A8_UINT", VK_FORMAT_B8G8R8A8_UINT },
{ "B8G8R8A8_SINT", VK_FORMAT_B8G8R8A8_SINT },
{ "B8G8R8A8_SRGB", VK_FORMAT_B8G8R8A8_SRGB },
{ "A8B8G8R8_UNORM_PACK32", VK_FORMAT_A8B8G8R8_UNORM_PACK32 },
{ "A8B8G8R8_SNORM_PACK32", VK_FORMAT_A8B8G8R8_SNORM_PACK32 },
{ "A8B8G8R8_USCALED_PACK32", VK_FORMAT_A8B8G8R8_USCALED_PACK32 },
{ "A8B8G8R8_SSCALED_PACK32", VK_FORMAT_A8B8G8R8_SSCALED_PACK32 },
{ "A8B8G8R8_UINT_PACK32", VK_FORMAT_A8B8G8R8_UINT_PACK32 },
{ "A8B8G8R8_SINT_PACK32", VK_FORMAT_A8B8G8R8_SINT_PACK32 },
{ "A8B8G8R8_SRGB_PACK32", VK_FORMAT_A8B8G8R8_SRGB_PACK32 },
{ "A2R10G10B10_UNORM_PACK32", VK_FORMAT_A2R10G10B10_UNORM_PACK32 },
{ "A2R10G10B10_SNORM_PACK32", VK_FORMAT_A2R10G10B10_SNORM_PACK32 },
{ "A2R10G10B10_USCALED_PACK32", VK_FORMAT_A2R10G10B10_USCALED_PACK32 },
{ "A2R10G10B10_SSCALED_PACK32", VK_FORMAT_A2R10G10B10_SSCALED_PACK32 },
{ "A2R10G10B10_UINT_PACK32", VK_FORMAT_A2R10G10B10_UINT_PACK32 },
{ "A2R10G10B10_SINT_PACK32", VK_FORMAT_A2R10G10B10_SINT_PACK32 },
{ "A2B10G10R10_UNORM_PACK32", VK_FORMAT_A2B10G10R10_UNORM_PACK32 },
{ "A2B10G10R10_SNORM_PACK32", VK_FORMAT_A2B10G10R10_SNORM_PACK32 },
{ "A2B10G10R10_USCALED_PACK32", VK_FORMAT_A2B10G10R10_USCALED_PACK32 },
{ "A2B10G10R10_SSCALED_PACK32", VK_FORMAT_A2B10G10R10_SSCALED_PACK32 },
{ "A2B10G10R10_UINT_PACK32", VK_FORMAT_A2B10G10R10_UINT_PACK32 },
{ "A2B10G10R10_SINT_PACK32", VK_FORMAT_A2B10G10R10_SINT_PACK32 },
{ "R16_UNORM", VK_FORMAT_R16_UNORM },
{ "R16_SNORM", VK_FORMAT_R16_SNORM },
{ "R16_USCALED", VK_FORMAT_R16_USCALED },
{ "R16_SSCALED", VK_FORMAT_R16_SSCALED },
{ "R16_UINT", VK_FORMAT_R16_UINT },
{ "R16_SINT", VK_FORMAT_R16_SINT },
{ "R16_SFLOAT", VK_FORMAT_R16_SFLOAT },
{ "R16G16_UNORM", VK_FORMAT_R16G16_UNORM },
{ "R16G16_SNORM", VK_FORMAT_R16G16_SNORM },
{ "R16G16_USCALED", VK_FORMAT_R16G16_USCALED },
{ "R16G16_SSCALED", VK_FORMAT_R16G16_SSCALED },
{ "R16G16_UINT", VK_FORMAT_R16G16_UINT },
{ "R16G16_SINT", VK_FORMAT_R16G16_SINT },
{ "R16G16_SFLOAT", VK_FORMAT_R16G16_SFLOAT },
{ "R16G16B16_UNORM", VK_FORMAT_R16G16B16_UNORM },
{ "R16G16B16_SNORM", VK_FORMAT_R16G16B16_SNORM },
{ "R16G16B16_USCALED", VK_FORMAT_R16G16B16_USCALED },
{ "R16G16B16_SSCALED", VK_FORMAT_R16G16B16_SSCALED },
{ "R16G16B16_UINT", VK_FORMAT_R16G16B16_UINT },
{ "R16G16B16_SINT", VK_FORMAT_R16G16B16_SINT },
{ "R16G16B16_SFLOAT", VK_FORMAT_R16G16B16_SFLOAT },
{ "R16G16B16A16_UNORM", VK_FORMAT_R16G16B16A16_UNORM },
{ "R16G16B16A16_SNORM", VK_FORMAT_R16G16B16A16_SNORM },
{ "R16G16B16A16_USCALED", VK_FORMAT_R16G16B16A16_USCALED },
{ "R16G16B16A16_SSCALED", VK_FORMAT_R16G16B16A16_SSCALED },
{ "R16G16B16A16_UINT", VK_FORMAT_R16G16B16A16_UINT },
{ "R16G16B16A16_SINT", VK_FORMAT_R16G16B16A16_SINT },
{ "R16G16B16A16_SFLOAT", VK_FORMAT_R16G16B16A16_SFLOAT },
{ "R32_UINT", VK_FORMAT_R32_UINT },
{ "R32_SINT", VK_FORMAT_R32_SINT },
{ "R32_SFLOAT", VK_FORMAT_R32_SFLOAT },
{ "R32G32_UINT", VK_FORMAT_R32G32_UINT },
{ "R32G32_SINT", VK_FORMAT_R32G32_SINT },
{ "R32G32_SFLOAT", VK_FORMAT_R32G32_SFLOAT },
{ "R32G32B32_UINT", VK_FORMAT_R32G32B32_UINT },
{ "R32G32B32_SINT", VK_FORMAT_R32G32B32_SINT },
{ "R32G32B32_SFLOAT", VK_FORMAT_R32G32B32_SFLOAT },
{ "R32G32B32A32_UINT", VK_FORMAT_R32G32B32A32_UINT },
{ "R32G32B32A32_SINT", VK_FORMAT_R32G32B32A32_SINT },
{ "R32G32B32A32_SFLOAT", VK_FORMAT_R32G32B32A32_SFLOAT },
{ "R64_UINT", VK_FORMAT_R64_UINT },
{ "R64_SINT", VK_FORMAT_R64_SINT },
{ "R64_SFLOAT", VK_FORMAT_R64_SFLOAT },
{ "R64G64_UINT", VK_FORMAT_R64G64_UINT },
{ "R64G64_SINT", VK_FORMAT_R64G64_SINT },
{ "R64G64_SFLOAT", VK_FORMAT_R64G64_SFLOAT },
{ "R64G64B64_UINT", VK_FORMAT_R64G64B64_UINT },
{ "R64G64B64_SINT", VK_FORMAT_R64G64B64_SINT },
{ "R64G64B64_SFLOAT", VK_FORMAT_R64G64B64_SFLOAT },
{ "R64G64B64A64_UINT", VK_FORMAT_R64G64B64A64_UINT },
{ "R64G64B64A64_SINT", VK_FORMAT_R64G64B64A64_SINT },
{ "R64G64B64A64_SFLOAT", VK_FORMAT_R64G64B64A64_SFLOAT },
{ "B10G11R11_UFLOAT_PACK32", VK_FORMAT_B10G11R11_UFLOAT_PACK32 },
{ "E5B9G9R9_UFLOAT_PACK32", VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 },
{ "D16_UNORM", VK_FORMAT_D16_UNORM },
{ "X8_D24_UNORM_PACK32", VK_FORMAT_X8_D24_UNORM_PACK32 },
{ "D32_SFLOAT", VK_FORMAT_D32_SFLOAT },
{ "S8_UINT", VK_FORMAT_S8_UINT },
{ "D16_UNORM_S8_UINT", VK_FORMAT_D16_UNORM_S8_UINT },
{ "D24_UNORM_S8_UINT", VK_FORMAT_D24_UNORM_S8_UINT },
{ "D32_SFLOAT_S8_UINT", VK_FORMAT_D32_SFLOAT_S8_UINT },
{ "BC1_RGB_UNORM_BLOCK", VK_FORMAT_BC1_RGB_UNORM_BLOCK },
{ "BC1_RGB_SRGB_BLOCK", VK_FORMAT_BC1_RGB_SRGB_BLOCK },
{ "BC1_RGBA_UNORM_BLOCK", VK_FORMAT_BC1_RGBA_UNORM_BLOCK },
{ "BC1_RGBA_SRGB_BLOCK", VK_FORMAT_BC1_RGBA_SRGB_BLOCK },
{ "BC2_UNORM_BLOCK", VK_FORMAT_BC2_UNORM_BLOCK },
{ "BC2_SRGB_BLOCK", VK_FORMAT_BC2_SRGB_BLOCK },
{ "BC3_UNORM_BLOCK", VK_FORMAT_BC3_UNORM_BLOCK },
{ "BC3_SRGB_BLOCK", VK_FORMAT_BC3_SRGB_BLOCK },
{ "BC4_UNORM_BLOCK", VK_FORMAT_BC4_UNORM_BLOCK },
{ "BC4_SNORM_BLOCK", VK_FORMAT_BC4_SNORM_BLOCK },
{ "BC5_UNORM_BLOCK", VK_FORMAT_BC5_UNORM_BLOCK },
{ "BC5_SNORM_BLOCK", VK_FORMAT_BC5_SNORM_BLOCK },
{ "BC6H_UFLOAT_BLOCK", VK_FORMAT_BC6H_UFLOAT_BLOCK },
{ "BC6H_SFLOAT_BLOCK", VK_FORMAT_BC6H_SFLOAT_BLOCK },
{ "BC7_UNORM_BLOCK", VK_FORMAT_BC7_UNORM_BLOCK },
{ "BC7_SRGB_BLOCK", VK_FORMAT_BC7_SRGB_BLOCK },
{ "ETC2_R8G8B8_UNORM_BLOCK", VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK },
{ "ETC2_R8G8B8_SRGB_BLOCK", VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK },
{ "ETC2_R8G8B8A1_UNORM_BLOCK", VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK },
{ "ETC2_R8G8B8A1_SRGB_BLOCK", VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK },
{ "ETC2_R8G8B8A8_UNORM_BLOCK", VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK },
{ "ETC2_R8G8B8A8_SRGB_BLOCK", VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK },
{ "EAC_R11_UNORM_BLOCK", VK_FORMAT_EAC_R11_UNORM_BLOCK },
{ "EAC_R11_SNORM_BLOCK", VK_FORMAT_EAC_R11_SNORM_BLOCK },
{ "EAC_R11G11_UNORM_BLOCK", VK_FORMAT_EAC_R11G11_UNORM_BLOCK },
{ "EAC_R11G11_SNORM_BLOCK", VK_FORMAT_EAC_R11G11_SNORM_BLOCK },
{ "ASTC_4X4_UNORM_BLOCK", VK_FORMAT_ASTC_4x4_UNORM_BLOCK },
{ "ASTC_4X4_SRGB_BLOCK", VK_FORMAT_ASTC_4x4_SRGB_BLOCK },
{ "ASTC_5X4_UNORM_BLOCK", VK_FORMAT_ASTC_5x4_UNORM_BLOCK },
{ "ASTC_5X4_SRGB_BLOCK", VK_FORMAT_ASTC_5x4_SRGB_BLOCK },
{ "ASTC_5X5_UNORM_BLOCK", VK_FORMAT_ASTC_5x5_UNORM_BLOCK },
{ "ASTC_5X5_SRGB_BLOCK", VK_FORMAT_ASTC_5x5_SRGB_BLOCK },
{ "ASTC_6X5_UNORM_BLOCK", VK_FORMAT_ASTC_6x5_UNORM_BLOCK },
{ "ASTC_6X5_SRGB_BLOCK", VK_FORMAT_ASTC_6x5_SRGB_BLOCK },
{ "ASTC_6X6_UNORM_BLOCK", VK_FORMAT_ASTC_6x6_UNORM_BLOCK },
{ "ASTC_6X6_SRGB_BLOCK", VK_FORMAT_ASTC_6x6_SRGB_BLOCK },
{ "ASTC_8X5_UNORM_BLOCK", VK_FORMAT_ASTC_8x5_UNORM_BLOCK },
{ "ASTC_8X5_SRGB_BLOCK", VK_FORMAT_ASTC_8x5_SRGB_BLOCK },
{ "ASTC_8X6_UNORM_BLOCK", VK_FORMAT_ASTC_8x6_UNORM_BLOCK },
{ "ASTC_8X6_SRGB_BLOCK", VK_FORMAT_ASTC_8x6_SRGB_BLOCK },
{ "ASTC_8X8_UNORM_BLOCK", VK_FORMAT_ASTC_8x8_UNORM_BLOCK },
{ "ASTC_8X8_SRGB_BLOCK", VK_FORMAT_ASTC_8x8_SRGB_BLOCK },
{ "ASTC_10X5_UNORM_BLOCK", VK_FORMAT_ASTC_10x5_UNORM_BLOCK },
{ "ASTC_10X5_SRGB_BLOCK", VK_FORMAT_ASTC_10x5_SRGB_BLOCK },
{ "ASTC_10X6_UNORM_BLOCK", VK_FORMAT_ASTC_10x6_UNORM_BLOCK },
{ "ASTC_10X6_SRGB_BLOCK", VK_FORMAT_ASTC_10x6_SRGB_BLOCK },
{ "ASTC_10X8_UNORM_BLOCK", VK_FORMAT_ASTC_10x8_UNORM_BLOCK },
{ "ASTC_10X8_SRGB_BLOCK", VK_FORMAT_ASTC_10x8_SRGB_BLOCK },
{ "ASTC_10X10_UNORM_BLOCK", VK_FORMAT_ASTC_10x10_UNORM_BLOCK },
{ "ASTC_10X10_SRGB_BLOCK", VK_FORMAT_ASTC_10x10_SRGB_BLOCK },
{ "ASTC_12X10_UNORM_BLOCK", VK_FORMAT_ASTC_12x10_UNORM_BLOCK },
{ "ASTC_12X10_SRGB_BLOCK", VK_FORMAT_ASTC_12x10_SRGB_BLOCK },
{ "ASTC_12X12_UNORM_BLOCK", VK_FORMAT_ASTC_12x12_UNORM_BLOCK },
{ "ASTC_12X12_SRGB_BLOCK", VK_FORMAT_ASTC_12x12_SRGB_BLOCK },
{ "G8B8G8R8_422_UNORM", VK_FORMAT_G8B8G8R8_422_UNORM },
{ "B8G8R8G8_422_UNORM", VK_FORMAT_B8G8R8G8_422_UNORM },
{ "G8_B8_R8_3PLANE_420_UNORM", VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM },
{ "G8_B8R8_2PLANE_420_UNORM", VK_FORMAT_G8_B8R8_2PLANE_420_UNORM },
{ "G8_B8_R8_3PLANE_422_UNORM", VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM },
{ "G8_B8R8_2PLANE_422_UNORM", VK_FORMAT_G8_B8R8_2PLANE_422_UNORM },
{ "G8_B8_R8_3PLANE_444_UNORM", VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM },
{ "R10X6_UNORM_PACK16", VK_FORMAT_R10X6_UNORM_PACK16 },
{ "R10X6G10X6_UNORM_2PACK16", VK_FORMAT_R10X6G10X6_UNORM_2PACK16 },
{ "R10X6G10X6B10X6A10X6_UNORM_4PACK16", VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 },
{ "G10X6B10X6G10X6R10X6_422_UNORM_4PACK16", VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 },
{ "B10X6G10X6R10X6G10X6_422_UNORM_4PACK16", VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 },
{ "G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16", VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 },
{ "G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16", VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 },
{ "G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16", VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 },
{ "G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16", VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 },
{ "G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16", VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 },
{ "R12X4_UNORM_PACK16", VK_FORMAT_R12X4_UNORM_PACK16 },
{ "R12X4G12X4_UNORM_2PACK16", VK_FORMAT_R12X4G12X4_UNORM_2PACK16 },
{ "R12X4G12X4B12X4A12X4_UNORM_4PACK16", VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 },
{ "G12X4B12X4G12X4R12X4_422_UNORM_4PACK16", VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 },
{ "B12X4G12X4R12X4G12X4_422_UNORM_4PACK16", VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 },
{ "G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16", VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 },
{ "G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16", VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 },
{ "G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16", VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 },
{ "G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16", VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 },
{ "G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16", VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 },
{ "G16B16G16R16_422_UNORM", VK_FORMAT_G16B16G16R16_422_UNORM },
{ "B16G16R16G16_422_UNORM", VK_FORMAT_B16G16R16G16_422_UNORM },
{ "G16_B16_R16_3PLANE_420_UNORM", VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM },
{ "G16_B16R16_2PLANE_420_UNORM", VK_FORMAT_G16_B16R16_2PLANE_420_UNORM },
{ "G16_B16_R16_3PLANE_422_UNORM", VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM },
{ "G16_B16R16_2PLANE_422_UNORM", VK_FORMAT_G16_B16R16_2PLANE_422_UNORM },
{ "G16_B16_R16_3PLANE_444_UNORM", VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM },
{ "PVRTC1_2BPP_UNORM_BLOCK_IMG", VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG },
{ "PVRTC1_4BPP_UNORM_BLOCK_IMG", VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG },
{ "PVRTC2_2BPP_UNORM_BLOCK_IMG", VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG },
{ "PVRTC2_4BPP_UNORM_BLOCK_IMG", VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG },
{ "PVRTC1_2BPP_SRGB_BLOCK_IMG", VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG },
{ "PVRTC1_4BPP_SRGB_BLOCK_IMG", VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG },
{ "PVRTC2_2BPP_SRGB_BLOCK_IMG", VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG },
{ "PVRTC2_4BPP_SRGB_BLOCK_IMG", VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG },
{ "ASTC_4X4_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT },
{ "ASTC_5X4_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT },
{ "ASTC_5X5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT },
{ "ASTC_6X5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT },
{ "ASTC_6X6_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT },
{ "ASTC_8X5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT },
{ "ASTC_8X6_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT },
{ "ASTC_8X8_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT },
{ "ASTC_10X5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT },
{ "ASTC_10X6_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT },
{ "ASTC_10X8_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT },
{ "ASTC_10X10_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT },
{ "ASTC_12X10_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT },
{ "ASTC_12X12_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT },
{ "ASTC_3X3X3_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT },
{ "ASTC_3X3X3_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT },
{ "ASTC_3X3X3_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT },
{ "ASTC_4X3X3_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT },
{ "ASTC_4X3X3_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT },
{ "ASTC_4X3X3_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT },
{ "ASTC_4X4X3_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT },
{ "ASTC_4X4X3_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT },
{ "ASTC_4X4X3_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT },
{ "ASTC_4X4X4_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT },
{ "ASTC_4X4X4_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT },
{ "ASTC_4X4X4_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT },
{ "ASTC_5X4X4_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT },
{ "ASTC_5X4X4_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT },
{ "ASTC_5X4X4_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT },
{ "ASTC_5X5X4_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT },
{ "ASTC_5X5X4_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT },
{ "ASTC_5X5X4_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT },
{ "ASTC_5X5X5_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT },
{ "ASTC_5X5X5_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT },
{ "ASTC_5X5X5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT },
{ "ASTC_6X5X5_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT },
{ "ASTC_6X5X5_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT },
{ "ASTC_6X5X5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT },
{ "ASTC_6X6X5_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT },
{ "ASTC_6X6X5_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT },
{ "ASTC_6X6X5_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT },
{ "ASTC_6X6X6_UNORM_BLOCK_EXT", VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT },
{ "ASTC_6X6X6_SRGB_BLOCK_EXT", VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT },
{ "ASTC_6X6X6_SFLOAT_BLOCK_EXT", VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT },
{ "A4R4G4B4_UNORM_PACK16_EXT", VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT },
{ "A4B4G4R4_UNORM_PACK16_EXT", VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT },
};
const auto it = lookupTable.find(str);
if (it == lookupTable.end())
[[nodiscard]] inline std::optional<VkFormat> parseVkFormat(const std::string& str) noexcept {
const auto vkFormat = stringToVkFormat(str.c_str());
if (vkFormat == VK_FORMAT_UNDEFINED)
return std::nullopt;
return it->second;
return vkFormat;
}
// -------------------------------------------------------------------------------------------------
@@ -545,6 +268,11 @@ namespace ktx {
return isValidFormat(format) && format >= 0;
}
[[nodiscard]] inline bool isFormatKnown(VkFormat format) noexcept {
const std::string_view str = vkFormatString(format);
return str != "VK_UNKNOWN_FORMAT";
}
[[nodiscard]] constexpr inline bool isFormatStencil(VkFormat format) noexcept {
switch (format) {
// Stencil only formats:

266
tools/ktx/fragment_uri.h Normal file
View File

@@ -0,0 +1,266 @@
// Copyright 2022-2023 The Khronos Group Inc.
// Copyright 2022-2023 RasterGrid Kft.
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <fmt/format.h>
#include <limits>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
// -------------------------------------------------------------------------------------------------
namespace ktx {
struct All_t {};
static constexpr All_t all{};
using RangeIndex = uint32_t;
static constexpr RangeIndex RangeEnd = std::numeric_limits<RangeIndex>::max();
class SelectorRange {
public:
// Half open range [begin, end)
struct HalfRange {
RangeIndex begin = 0;
RangeIndex end = RangeEnd;
[[nodiscard]] friend constexpr bool operator==(const HalfRange& lhs, const HalfRange& rhs) {
return lhs.begin == rhs.begin && lhs.end == rhs.end;
}
};
private:
std::vector<HalfRange> ranges;
public:
SelectorRange(RangeIndex begin, RangeIndex end) {
ranges.emplace_back(HalfRange{begin, end});
}
SelectorRange() = default;
explicit SelectorRange(All_t) : SelectorRange(0, RangeEnd) {};
explicit SelectorRange(RangeIndex index) : SelectorRange(index, index + 1u) {};
[[nodiscard]] friend bool operator==(const SelectorRange& lhs, const SelectorRange& rhs) {
return lhs.ranges == rhs.ranges;
}
[[nodiscard]] bool is_empty() const {
if (ranges.empty())
return true;
for (const auto& range : ranges)
if (range.begin != range.end)
return false;
return true;
}
[[nodiscard]] bool is_single() const {
if (ranges.empty())
return false;
const auto index = ranges[0].begin;
for (const auto& range : ranges)
if (range.begin != index || range.end != index + 1)
return false;
return true;
}
[[nodiscard]] bool is_multi() const {
return !is_single();
}
void clear() {
ranges.clear();
}
[[nodiscard]] bool is_undefined() const {
return ranges.empty();
}
void add(HalfRange range) {
ranges.emplace_back(range);
}
[[nodiscard]] bool contains(RangeIndex index) const {
for (const auto& range : ranges)
if (range.begin <= index && index < range.end)
return true;
return false;
}
[[nodiscard]] bool validate(RangeIndex last) const {
for (const auto& range : ranges) {
if (range.begin > last)
return false;
if (range.end > last && range.end != RangeEnd)
return false;
}
return true;
}
[[nodiscard]] RangeIndex last() const {
RangeIndex last = 0;
for (const auto& range : ranges)
if (range.begin != range.end && range.end > last + 1)
last = range.end - 1;
return last;
}
// Only used for fmt print
[[nodiscard]] const std::vector<HalfRange>& _ranges() const {
return ranges;
}
SelectorRange& operator=(All_t) & {
ranges.clear();
ranges.emplace_back(HalfRange{0, RangeEnd});
return *this;
}
SelectorRange& operator=(RangeIndex index) & {
ranges.clear();
ranges.emplace_back(HalfRange{index, index + 1});
return *this;
}
friend bool operator==(const SelectorRange& var, All_t) {
return var == SelectorRange{all};
}
friend bool operator==(All_t, const SelectorRange& var) {
return var == all;
}
friend bool operator!=(const SelectorRange& var, All_t) {
return !(var == all);
}
friend bool operator!=(All_t, const SelectorRange& var) {
return !(var == all);
}
friend bool operator==(const SelectorRange& var, RangeIndex index) {
return var == SelectorRange{index};
}
friend bool operator==(RangeIndex index, const SelectorRange& var) {
return var == index;
}
};
} // namespace ktx
namespace fmt {
template<> struct formatter<ktx::SelectorRange> : fmt::formatter<uint32_t> {
template <typename FormatContext>
auto format(const ktx::SelectorRange& var, FormatContext& ctx) const -> decltype(ctx.out()) {
if (var == ktx::all)
return formatter<std::string_view>{}.format("all", ctx);
else if (var.is_empty())
return formatter<std::string_view>{}.format("none", ctx);
else {
bool first = true;
auto out = ctx.out();
for (const auto& range : var._ranges()) {
if (!first) {
out = fmt::format_to(out, ",");
ctx.advance_to(out);
}
first = false;
if (range.begin + 1 == range.end) {
out = fmt::format_to(out, "{}", range.begin);
ctx.advance_to(out);
} else if (range.end == ktx::RangeEnd) {
out = fmt::format_to(out, "{}..last", range.begin);
ctx.advance_to(out);
} else {
out = fmt::format_to(out, "{}..{}", range.begin, range.end - 1);
ctx.advance_to(out);
}
}
return out;
}
}
};
} // namespace fmt
namespace ktx {
/// https://registry.khronos.org/KTX/specs/2.0/ktx-frag.html
///
/// KTX fragments support addressing the KTX files payload along 5 dimensions
/// mip - This dimension denotes a range of mip levels in the KTX file.
/// stratal - This dimension denotes a range of array layers when the KTX file contains an array texture.
/// temporal - This dimension denotes a specific time range in a KTX file containing KTXanimData metadata. Since a frame is an array layer, this is an alternate way of selecting in the stratal dimension.
/// facial - This dimension denotes a range of faces when the KTX file contains a cube map.
/// spatial - xyzwhd This dimension denotes a range of pixels in the KTX file such as "a volume with size (100,100,1) with its origin at (10,10,0).
struct FragmentURI {
SelectorRange mip;
SelectorRange stratal;
// SelectorTemporal temporal; // Temporal selector is outside the current scope
SelectorRange facial;
// SelectorSpatial spatial; // Spatial selector is outside the current scope
bool validate(uint32_t numLevels, uint32_t numLayers, uint32_t numFaces) {
return mip.validate(numLevels) &&
stratal.validate(numLayers) &&
facial.validate(numFaces);
}
};
[[nodiscard]] inline SelectorRange::HalfRange parseHalfRange(std::string_view key, std::string_view str) {
const auto indexComma = str.find(',');
const auto strBegin = indexComma == std::string_view::npos ? str : str.substr(0, indexComma);
const auto strEnd = indexComma == std::string_view::npos ? "" : str.substr(indexComma + 1);
try {
const auto begin = strBegin.empty() ? 0u : static_cast<uint32_t>(std::stoi(std::string(strBegin)));
const auto end = strEnd.empty() ? RangeEnd : static_cast<uint32_t>(std::stoi(std::string(strEnd))) + 1u;
return {begin, end};
} catch (const std::exception& ex) {
throw std::invalid_argument(fmt::format("Invalid key-value \"{}={}\": {}", key, str, ex.what()));
}
}
[[nodiscard]] inline FragmentURI parseFragmentURI(std::string_view str) {
// Name and value components are separated by an equal sign (=),
// while multiple name-value pairs are separated by an ampersand (&).
FragmentURI result;
result.mip.clear();
result.stratal.clear();
result.facial.clear();
while (true) {
const auto indexAmp = str.find('&');
const auto strKeyValue = indexAmp == std::string_view::npos ? str : str.substr(0, indexAmp);
const auto indexEqual = strKeyValue.find('=');
const auto strKey = indexEqual == std::string_view::npos ? strKeyValue : strKeyValue.substr(0, indexEqual);
const auto strValue = indexEqual == std::string_view::npos ? "" : strKeyValue.substr(indexEqual + 1);
if (strKey == "m" || strKey == "%6D")
result.mip.add(parseHalfRange(strKey, strValue));
else if (strKey == "a" || strKey == "%61")
result.stratal.add(parseHalfRange(strKey, strValue));
else if (strKey == "t" || strKey == "%74")
throw std::invalid_argument(fmt::format("Temporal selector (t) is not yet supported."));
else if (strKey == "f")
result.facial.add(parseHalfRange(strKey, strValue));
else if (strKey == "xyzwhd")
throw std::invalid_argument(fmt::format("Spatial selector (xyzwhd) is not yet supported."));
else if (!strKey.empty())
throw std::invalid_argument(fmt::format("Unknown key \"{}\"", strKey));
if (indexAmp != std::string_view::npos)
str.remove_prefix(indexAmp + 1);
else
break;
}
return result;
}
} // namespace ktx

View File

@@ -23,6 +23,7 @@
#include <vector>
#include <KHR/khr_df.h>
#include <fmt/format.h>
#include <glm/gtc/packing.hpp>
#include "utility.h"
#include "unused.h"
@@ -699,6 +700,10 @@ class Image {
uint32_t c2, uint32_t c2Pad, uint32_t c3, uint32_t c3Pad) const = 0;
/// Should only be used if the stored image data is SFloat convertable
virtual std::vector<uint8_t> getSFloat(uint32_t numChannels, uint32_t targetBits) const = 0;
/// Should only be used if the stored image data is UFloat convertable
virtual std::vector<uint8_t> getB10G11R11() const = 0;
/// Should only be used if the stored image data is UFloat convertable
virtual std::vector<uint8_t> getE5B9G9R9() const = 0;
/// Should only be used if the stored image data is UINT convertable
virtual std::vector<uint8_t> getUINT(uint32_t numChannels, uint32_t targetBits) const = 0;
/// Should only be used if the stored image data is SINT convertable
@@ -924,6 +929,50 @@ class ImageT : public Image {
return data;
}
virtual std::vector<uint8_t> getB10G11R11() const override {
assert(3 <= componentCount);
assert(std::is_floating_point_v<componentType>);
std::vector<uint8_t> data(height * width * 4);
for (uint32_t y = 0; y < height; ++y) {
for (uint32_t x = 0; x < width; ++x) {
auto* target = data.data() + (y * height + x) * 4;
const auto pixel = (*this)(x, y);
const auto r = pixel[0];
const auto g = pixel[1];
const auto b = pixel[2];
const auto outValue = glm::packF2x11_1x10(glm::vec3(r, g, b));
std::memcpy(target, &outValue, sizeof(outValue));
}
}
return data;
}
virtual std::vector<uint8_t> getE5B9G9R9() const override {
assert(3 <= componentCount);
assert(std::is_floating_point_v<componentType>);
std::vector<uint8_t> data(height * width * 4);
for (uint32_t y = 0; y < height; ++y) {
for (uint32_t x = 0; x < width; ++x) {
auto* target = data.data() + (y * height + x) * 4;
const auto pixel = (*this)(x, y);
const auto r = pixel[0];
const auto g = pixel[1];
const auto b = pixel[2];
const auto outValue = glm::packF3x9_E1x5(glm::vec3(r, g, b));
std::memcpy(target, &outValue, sizeof(outValue));
}
}
return data;
}
virtual std::vector<uint8_t> getUINT(uint32_t numChannels, uint32_t targetBits) const override {
assert(numChannels <= componentCount);
assert(targetBits == 8 || targetBits == 16 || targetBits == 32 || targetBits == 64);

View File

@@ -21,7 +21,7 @@ std::optional<khr_df_model_channels_e> getChannelType(const KTXTexture2& texture
TranscodeSwizzleInfo determineTranscodeSwizzle(const KTXTexture2& texture, Reporter& report) {
TranscodeSwizzleInfo result;
const auto* bdfd = (texture->pDfd + 1);
const auto* bdfd = texture->pDfd + 1;
const auto sample0 = getChannelType(texture, 0);
const auto sample1 = getChannelType(texture, 1);

View File

@@ -5,8 +5,9 @@
#pragma once
#include "command.h"
#include "utility.h"
#include "formats.h"
#include "image.hpp"
#include "utility.h"
#include "dfdutils/dfd.h"
#include <KHR/khr_df.h>
@@ -37,14 +38,9 @@ struct OptionsTranscodeTarget {
void init(cxxopts::Options&) {}
void process(cxxopts::Options&, cxxopts::ParseResult& args, Reporter& report) {
const char* argName = nullptr;
if (TRANSCODE_CMD) {
// "transcode" command - optional "target" argument
argName = "target";
} else {
// "extract" command - optional "transcode" argument
argName = "transcode";
}
// "transcode" command - optional "target" argument
// "extract" command - optional "transcode" argument
const auto argName = TRANSCODE_CMD ? "target" : "transcode";
static const std::unordered_map<std::string, std::pair<ktx_transcode_fmt_e, uint32_t>> targets{
{"etc-rgb", {KTX_TTF_ETC1_RGB, 0}},
@@ -87,4 +83,116 @@ struct OptionsTranscodeTarget {
}
};
template <bool TRANSCODE_CMD>
KTXTexture2 transcode(KTXTexture2&& texture, OptionsTranscodeTarget<TRANSCODE_CMD>& options, Reporter& report) {
options.validateTextureTranscode(texture, report);
auto ret = ktxTexture2_TranscodeBasis(texture, options.transcodeTarget.value(), 0);
if (ret != KTX_SUCCESS)
report.fatal(rc::INVALID_FILE, "Failed to transcode KTX2 texture: {}", ktxErrorString(ret));
// Need to perform format conversion and swizzling if needed
bool needFormatConversion = false;
bool needSwizzle = false;
if (options.transcodeSwizzleComponents != 0) {
if (options.transcodeSwizzleComponents == 4) {
if (options.transcodeSwizzle != "rgba") {
needSwizzle = true;
}
} else {
needFormatConversion = true;
needSwizzle = true;
}
}
KTXTexture2 convertedTexture{nullptr};
if (needFormatConversion) {
ktxTextureCreateInfo createInfo;
std::memset(&createInfo, 0, sizeof(createInfo));
const bool srgb = (texture->vkFormat == VK_FORMAT_R8G8B8A8_SRGB);
switch (options.transcodeSwizzleComponents) {
case 1:
createInfo.vkFormat = srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
break;
case 2:
createInfo.vkFormat = srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM;
break;
case 3:
createInfo.vkFormat = srgb ? VK_FORMAT_R8G8B8_SRGB : VK_FORMAT_R8G8B8_UNORM;
break;
default:
assert(false);
}
createInfo.baseWidth = texture->baseWidth;
createInfo.baseHeight = texture->baseHeight;
createInfo.baseDepth = texture->baseDepth;
createInfo.generateMipmaps = texture->generateMipmaps;
createInfo.isArray = texture->isArray;
createInfo.numDimensions = texture->numDimensions;
createInfo.numFaces = texture->numFaces;
createInfo.numLayers = texture->numLayers;
createInfo.numLevels = texture->numLevels;
createInfo.pDfd = nullptr;
ret = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, convertedTexture.pHandle());
if (KTX_SUCCESS != ret)
report.fatal(rc::IO_FAILURE, "Failed to create output texture: {}", ktxErrorString(ret));
}
KTXTexture2& outputTexture = (convertedTexture.handle() != nullptr) ? convertedTexture : texture;
if (needFormatConversion || needSwizzle) {
for (uint32_t levelIndex = 0; levelIndex < texture->numLevels; ++levelIndex) {
const auto imageWidth = std::max(1u, texture->baseWidth >> levelIndex);
const auto imageHeight = std::max(1u, texture->baseHeight >> levelIndex);
const auto imageDepth = std::max(1u, texture->baseDepth >> levelIndex);
for (uint32_t faceIndex = 0; faceIndex < texture->numFaces; ++faceIndex) {
for (uint32_t layerIndex = 0; layerIndex < texture->numLayers; ++layerIndex) {
for (uint32_t depthIndex = 0; depthIndex < imageDepth; ++depthIndex) {
ktx_size_t srcImageOffset;
ktxTexture_GetImageOffset(texture, levelIndex, layerIndex, faceIndex + depthIndex, &srcImageOffset);
ktx_size_t dstImageOffset;
ktxTexture_GetImageOffset(outputTexture, levelIndex, layerIndex, faceIndex + depthIndex, &dstImageOffset);
auto srcImageData = texture->pData + srcImageOffset;
auto dstImageData = outputTexture->pData + dstImageOffset;
rgba8image srcImage(imageWidth, imageHeight, reinterpret_cast<rgba8color*>(srcImageData));
switch (options.transcodeSwizzleComponents) {
case 1: {
r8image dstImage(imageWidth, imageHeight, reinterpret_cast<r8color*>(dstImageData));
srcImage.copyToR(dstImage, options.transcodeSwizzle);
break;
}
case 2: {
rg8image dstImage(imageWidth, imageHeight, reinterpret_cast<rg8color*>(dstImageData));
srcImage.copyToRG(dstImage, options.transcodeSwizzle);
break;
}
case 3: {
rgb8image dstImage(imageWidth, imageHeight, reinterpret_cast<rgb8color*>(dstImageData));
srcImage.copyToRGB(dstImage, options.transcodeSwizzle);
break;
}
case 4: {
// Swizzle in-place
assert(srcImageData == dstImageData);
srcImage.swizzle(options.transcodeSwizzle);
break;
}
default:
assert(false);
}
}
}
}
}
}
return std::move(outputTexture);
}
} // namespace ktx

View File

@@ -8,6 +8,14 @@
#include "ktx.h"
#include <fmt/ostream.h>
#include <fmt/printf.h>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4201)
#endif
#include <glm/gtc/packing.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include <algorithm>
#include <array>
#include <functional>
@@ -19,7 +27,7 @@
namespace ktx {
// TODO: Tools P5: Detect endianness
// TODO: Detect endianness
// C++20: std::endian::native == std::endian::little
constexpr bool is_big_endian = false;
@@ -65,7 +73,7 @@ template <typename T>
template <typename T>
[[nodiscard]] constexpr inline T bit_ceil(T x) noexcept {
x -= 1;
for (uint32_t i = 1; i < sizeof(x) * 8; ++i)
for (uint32_t i = 0; i < sizeof(x) * 8; ++i)
if (1u << i > x)
return 1u << i;
return 0;
@@ -187,6 +195,14 @@ inline void replace_all_inplace(std::string& string, std::string_view search, st
return string;
}
// -------------------------------------------------------------------------------------------------
/// Remaps the value from [from_lo..from_hi] to [to_lo..to_hi] with extrapolation
template <typename T>
[[nodiscard]] constexpr inline T remap(T value, T from_lo, T from_hi, T to_lo, T to_hi) noexcept {
return to_lo + (value - from_lo) * (to_hi - to_lo) / (from_hi - from_lo);
}
// --- Half utilities ------------------------------------------------------------------------------
// Based on https://gist.github.com/rygorous/eb3a019b99fdaa9c3064
@@ -291,6 +307,17 @@ template <typename T>
return bit_cast<T>(target);
}
[[nodiscard]] inline uint32_t covertFloatToUNORM(float value, uint32_t numBits) {
assert(numBits > 0 && numBits <= 32);
if (std::isnan(value))
return 0;
if (value < 0.f)
return 0;
if (value > 1.f)
return (1u << numBits) - 1u;
return static_cast<uint32_t>(value * static_cast<float>((1u << numBits) - 1u) + 0.5f);
}
[[nodiscard]] inline float covertSFloatToFloat(uint32_t rawBits, uint32_t numBits) {
assert(numBits == 16 || numBits == 32);
if (numBits == 16)
@@ -300,11 +327,11 @@ template <typename T>
return 0;
}
[[nodiscard]] inline float covertUFloatToFloat(uint32_t rawBits, uint32_t numBits) {
assert(numBits == 10 || numBits == 11 || numBits == 14);
// TODO: Tools P4: covertUFloatToFloat for 10, 11 and "14"
(void) rawBits;
(void) numBits;
assert(false && "Not yet implemented");
assert(numBits == 10 || numBits == 11);
if (numBits == 10)
return glm::detail::packed10bitToFloat(rawBits);
else if (numBits == 11)
return glm::detail::packed11bitToFloat(rawBits);
return 0;
}
[[nodiscard]] inline float covertSIntToFloat(uint32_t rawBits, uint32_t numBits) {
@@ -318,6 +345,18 @@ template <typename T>
assert(numBits > 0 && numBits <= 32); (void) numBits;
return static_cast<float>(rawBits);
}
[[nodiscard]] inline float covertSNORMToFloat(uint32_t rawBits, uint32_t numBits) {
assert(numBits > 0 && numBits <= 32);
(void) rawBits;
(void) numBits;
assert(false && "Not yet implemented");
return 0;
}
[[nodiscard]] inline float covertUNORMToFloat(uint32_t rawBits, uint32_t numBits) {
assert(numBits > 0 && numBits <= 32);
const auto upper = static_cast<float>((1u << numBits) - 1u);
return static_cast<float>(rawBits) / upper;
}
[[nodiscard]] inline uint32_t covertSFloatToUInt(uint32_t rawBits, uint32_t numBits) {
assert(numBits == 16 || numBits == 32);
if (numBits == 16)
@@ -532,6 +571,16 @@ template <typename Iterator>
// -------------------------------------------------------------------------------------------------
[[nodiscard]] inline std::string fmtInFile(std::string_view filepath) {
return filepath == "-" ? std::string("stdin") : std::string(filepath);
}
[[nodiscard]] inline std::string fmtOutFile(std::string_view filepath) {
return filepath == "-" ? std::string("stdout") : std::string(filepath);
}
// -------------------------------------------------------------------------------------------------
struct PrintIndent {
std::ostream& os;
int indentBase = 0;

View File

@@ -209,11 +209,11 @@ private:
private:
template <typename... Args>
void validatePaddingZeros(const void* ptr, const void* bufferEnd, std::size_t alignment, const IssueError& issue, Args&&... args) {
void validateAlignmentPaddingZeros(const void* ptr, const void* bufferEnd, std::size_t alignment, const IssueError& issue, Args&&... args) {
const auto* begin = static_cast<const char*>(ptr);
const auto* end = std::min(bufferEnd, align(ptr, alignment));
for (auto it = begin; it != end; ++it)
for (auto it = begin; it < end; ++it)
if (*it != 0)
error(issue, static_cast<uint8_t>(*it), std::forward<Args>(args)...);
}
@@ -229,6 +229,7 @@ private:
void validateDFD();
void validateKVD();
void validateSGD();
void validatePaddings();
void validateCreateAndTranscode();
void validateDFDBasic(uint32_t blockIndex, const uint32_t* dfd, const BDFD& block, const std::vector<SampleType>& samples);
@@ -444,15 +445,10 @@ int ValidationContext::validate(bool doCreateAndTranscodeChecks) {
call(&ValidationContext::validateLevelIndex, "Level Index"); // Must come after the DFD parsed
call(&ValidationContext::validateKVD, "KVD");
call(&ValidationContext::validateSGD, "SGD");
call(&ValidationContext::validatePaddings, "padding");
if (doCreateAndTranscodeChecks)
call(&ValidationContext::validateCreateAndTranscode, "Create and Transcode");
// TODO: Tools P3: Verify validation of padding zeros between levelIndex and DFD
// TODO: Tools P3: Verify validation of padding zeros between DFD and KVD
// TODO: Tools P3: Verify validation of padding zeros between KVD and SGD
// TODO: Tools P3: Verify validation of padding zeros between SGD and image levels
// TODO: Tools P3: Verify validation of padding zeros between image levels
return returnCode;
}
@@ -476,7 +472,7 @@ void ValidationContext::validateHeader() {
error(HeaderData::InvalidFormat, toString(vkFormat));
if (VK_FORMAT_MAX_STANDARD_ENUM < vkFormat && vkFormat < 1000001000)
error(HeaderData::InvalidFormat, toString(vkFormat));
if (1000001000 <= vkFormat)
if (1000001000 <= vkFormat && !isFormatKnown(vkFormat))
warning(HeaderData::UnknownFormat, toString(vkFormat));
}
@@ -796,8 +792,6 @@ void ValidationContext::validateLevelIndex() {
lastByteOffset = level.byteOffset;
lastByteLength = level.byteLength;
// dataSizeFromLevelIndex += align(level.byteLength, requiredLevelAlignment);
}
}
@@ -805,25 +799,7 @@ void ValidationContext::calculateExpectedDFD(VkFormat format) {
if (format == VK_FORMAT_UNDEFINED || !isFormatValid(format) || isProhibitedFormat(format))
return;
std::unique_ptr<uint32_t, decltype(&free)> dfd{ nullptr, &free };
switch (header.vkFormat) {
case VK_FORMAT_D16_UNORM_S8_UINT:
// 2 16-bit words. D16 in the first. S8 in the 8 LSBs of the second.
dfd = std::unique_ptr<uint32_t, decltype(&free)>(createDFDDepthStencil(16, 8, 4), &free);
break;
case VK_FORMAT_D24_UNORM_S8_UINT:
// 1 32-bit word. D24 in the MSBs. S8 in the LSBs.
dfd = std::unique_ptr<uint32_t, decltype(&free)>(createDFDDepthStencil(24, 8, 4), &free);
break;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
// 2 32-bit words. D32 float in the first word. S8 in LSBs of the second.
dfd = std::unique_ptr<uint32_t, decltype(&free)>(createDFDDepthStencil(32, 8, 8), &free);
break;
default:
dfd = std::unique_ptr<uint32_t, decltype(&free)>(vk2dfd(format), &free);
}
const auto dfd = std::unique_ptr<uint32_t, decltype(&free)>(vk2dfd(format), &free);
if (dfd == nullptr) {
error(Validator::CreateExpectedDFDFailure, toString(format));
return;
@@ -858,30 +834,8 @@ void ValidationContext::calculateExpectedDFD(VkFormat format) {
result = interpretDFD(dfd.get(), &r, &g, &b, &a, &componentByteLength);
// TODO: Tools P5: Add interpretDFD support for depth, stencil and packed exponent formats
// Workaround for missing interpretDFD support for depth/stencil formats
switch (format) {
case VK_FORMAT_S8_UINT:
componentByteLength = 1;
break;
case VK_FORMAT_D16_UNORM: [[fallthrough]];
case VK_FORMAT_D16_UNORM_S8_UINT:
componentByteLength = 2;
break;
case VK_FORMAT_X8_D24_UNORM_PACK32: [[fallthrough]];
case VK_FORMAT_D24_UNORM_S8_UINT: [[fallthrough]];
case VK_FORMAT_D32_SFLOAT: [[fallthrough]];
case VK_FORMAT_D32_SFLOAT_S8_UINT:
componentByteLength = 4;
break;
default:
break;
}
// Reset the "false" positive error in interpretDFD with VK_FORMAT_D32_SFLOAT_S8_UINT
if (header.vkFormat == VK_FORMAT_D32_SFLOAT_S8_UINT && result == i_UNSUPPORTED_MIXED_CHANNELS)
result = InterpretDFDResult(0);
// Reset the "false" positive error in interpretDFD with VK_FORMAT_E5B9G9R9_UFLOAT_PACK32
// Reset the false positive error in interpretDFD with VK_FORMAT_E5B9G9R9_UFLOAT_PACK32
// interpretDFD by design doesn't support this format
if (header.vkFormat == VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 && result == i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS)
result = InterpretDFDResult(0);
@@ -972,10 +926,10 @@ void ValidationContext::validateDFD() {
}
} else if (blockHeader.vendorId == KHR_DF_VENDORID_KHRONOS && blockHeader.descriptorType == KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_DIMENSIONS) {
// TODO: Tools P5: Implement DFD validation for ADDITIONAL_DIMENSIONS
// TODO: Implement DFD validation for ADDITIONAL_DIMENSIONS
} else if (blockHeader.vendorId == KHR_DF_VENDORID_KHRONOS && blockHeader.descriptorType == KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_PLANES) {
// TODO: Tools P5: Implement DFD validation for ADDITIONAL_PLANES
// TODO: Implement DFD validation for ADDITIONAL_PLANES
} else {
warning(DFD::UnknownDFDBlock,
@@ -988,8 +942,6 @@ void ValidationContext::validateDFD() {
ptrDFDIt += std::max(blockHeader.descriptorBlockSize, 8u);
}
// validatePaddingZeros(ptrDFDIt, ptrDFDEnd, 4, Metadata::PaddingNotZero, "after the last DFD block");
if (!foundBDFD)
error(DFD::MissingBDFD);
}
@@ -1150,7 +1102,8 @@ void ValidationContext::validateDFDBasic(uint32_t blockIndex, const uint32_t* df
if (parsed.qualifierFloat != expected.qualifierFloat)
error(DFD::FormatMismatch, blockIndex, i + 1, "qualifierFloat", parsed.qualifierFloat,
expected.qualifierFloat, toString(VkFormat(header.vkFormat)));
if (parsed.samplePosition0 != expected.samplePosition0)
// For 4:2:2 formats the X sample positions can vary
if (!isFormat422(VkFormat(header.vkFormat)) && parsed.samplePosition0 != expected.samplePosition0)
error(DFD::FormatMismatch, blockIndex, i + 1, "samplePosition0", parsed.samplePosition0,
expected.samplePosition0, toString(VkFormat(header.vkFormat)));
if (parsed.samplePosition1 != expected.samplePosition1)
@@ -1198,9 +1151,6 @@ void ValidationContext::validateDFDBasic(uint32_t blockIndex, const uint32_t* df
result = interpretDFD(dfd, &r, &g, &b, &a, &componentByteLength);
// Reset the "false" positive error in interpretDFD with VK_FORMAT_D32_SFLOAT_S8_UINT
if (header.vkFormat == VK_FORMAT_D32_SFLOAT_S8_UINT && result == i_UNSUPPORTED_MIXED_CHANNELS)
result = InterpretDFDResult(0);
// Reset the "false" positive error in interpretDFD with VK_FORMAT_E5B9G9R9_UFLOAT_PACK32
if (header.vkFormat == VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 && result == i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS)
result = InterpretDFDResult(0);
@@ -1208,10 +1158,7 @@ void ValidationContext::validateDFDBasic(uint32_t blockIndex, const uint32_t* df
if (result >= i_UNSUPPORTED_ERROR_BIT) {
switch (result) {
case i_UNSUPPORTED_CHANNEL_TYPES:
// TODO: Tools P4: We intentionally ignore this error from interpretDFD().
// We already check channel types elsewhere and interpretDFD() currently doesn't
// handle anything else but the RGBSDA color model which is insufficient to handle
// cases like the allowed 422 YCbCr formats.
// We already checked channel types elsewhere with more detailed error message
break;
case i_UNSUPPORTED_MULTIPLE_PLANES:
error(DFD::MultiplaneFormatsNotSupported, blockIndex,
@@ -1472,7 +1419,7 @@ void ValidationContext::validateKVD() {
// Finish entry
ptrEntry += sizeof(uint32_t) + sizeKeyValuePair;
validatePaddingZeros(ptrEntry, ptrKVDEnd, 4, Metadata::PaddingNotZero, "after a Key-Value entry");
validateAlignmentPaddingZeros(ptrEntry, ptrKVDEnd, 4, Metadata::PaddingNotZero, "after a Key-Value entry");
ptrEntry = align(ptrEntry, 4);
}
@@ -1481,7 +1428,7 @@ void ValidationContext::validateKVD() {
error(Metadata::SizesDontAddUp, ptrEntry - ptrKVD, kvdByteLength);
if (header.supercompressionGlobalData.byteLength != 0)
validatePaddingZeros(ptrEntry, ptrKVDEnd, 8, Metadata::PaddingNotZero, "between KVD and SGD");
validateAlignmentPaddingZeros(ptrEntry, ptrKVDEnd, 8, Metadata::PaddingNotZero, "after the last KVD entry");
if (!is_sorted(entries, std::less<>{}, &KeyValueEntry::key)) {
error(Metadata::OutOfOrder);
@@ -1802,27 +1749,46 @@ void ValidationContext::validateSGD() {
error(SGD::BLZENoAnimationSequencesPFrame);
}
// =================================================================================================
// TODO: Tools P2: validate DataSize
//
// void ktxValidator::validateDataSize(validationContext& ctx) {
// // Expects to be called after validateSgd so current file offset is at
// // the start of the data.
// uint64_t dataSizeInFile;
// off_t dataStart = (off_t) (ctx.inp->tellg());
//
// ctx.inp->seekg(0, ios_base::end);
// if (ctx.inp->fail())
// addIssue(logger::eFatal, IOError.FileSeekEndFailure, strerror(errno));
// off_t dataEnd = (off_t) (ctx.inp->tellg());
// if (dataEnd < 0)
// addIssue(logger::eFatal, IOError.FileTellFailure, strerror(errno));
// dataSizeInFile = dataEnd - dataStart;
// if (dataSizeInFile != ctx.dataSizeFromLevelIndex)
// addIssue(logger::eError, FileError.IncorrectDataSize);
// }
//
// =================================================================================================
void ValidationContext::validatePaddings() {
const auto levelIndexOffset = sizeof(KTX_header2);
const auto levelIndexSize = sizeof(ktxLevelIndexEntry) * numLevels;
const auto dfdByteOffset = header.dataFormatDescriptor.byteOffset;
const auto dfdByteLength = header.dataFormatDescriptor.byteLength;
const auto kvdByteOffset = header.keyValueData.byteOffset;
const auto kvdByteLength = header.keyValueData.byteLength;
const auto sgdByteOffset = header.supercompressionGlobalData.byteOffset;
const auto sgdByteLength = header.supercompressionGlobalData.byteLength;
size_t position = levelIndexOffset + levelIndexSize;
const auto check = [&](size_t offset, size_t size, std::string name) {
if (offset == 0 || size == 0)
return; // Block is missing, skip
if (offset < position) {
position = std::max(position, offset + size);
return; // Just ignore invalid block placements regarding padding checks
}
const auto paddingSize = offset - position;
const auto buffer = std::make_unique<uint8_t[]>(paddingSize);
read(position, buffer.get(), paddingSize, "the padding before " + name);
for (size_t i = 0; i < paddingSize; ++i)
if (buffer[i] != 0) {
error(Metadata::PaddingNotZero, buffer[i], fmt::format("before {} at offset {}", name, position + i));
break; // Only report the first non-zero byte per padding, no need to spam
}
position = offset + size;
};
check(dfdByteOffset, dfdByteLength, "DFD");
check(kvdByteOffset, kvdByteLength, "KVD");
check(sgdByteOffset, sgdByteLength, "SGD");
size_t i = levelIndices.size() - 1;
for (auto it = levelIndices.rbegin(); it != levelIndices.rend(); ++it)
check(it->byteOffset, it->byteLength, "image level " + std::to_string(i--));
}
void ValidationContext::validateCreateAndTranscode() {
KTXTexture2 texture{nullptr};

View File

@@ -106,6 +106,8 @@ struct FileError {
};
struct HeaderData {
// 30xx - KTX Header related issues
static constexpr IssueError ProhibitedFormat{
3001, "Prohibited VkFormat.",
"VkFormat {} is prohibited in a KTX2 file."
@@ -246,6 +248,8 @@ struct HeaderData {
};
struct LevelIndex {
// 40xx - Level index related issues
static constexpr IssueError IncorrectIndexOrder{
4001, "Invalid Level Index. Indices must be sorted from the largest level to the smallest level.",
"Indexes for level {} with byteLength {} and level {} with byteLength {} are incorrectly ordered."
@@ -292,6 +296,8 @@ struct LevelIndex {
};
struct Validator {
// 50xx - Validator or KTX Library related issues
static constexpr IssueError CreateExpectedDFDFailure{
5001, "Failed to create expected DFD for the given VkFormat.",
"Failed to create expected DFD for the given VkFormat {}."
@@ -782,6 +788,7 @@ struct SGD {
};
struct System {
// 90xx - Generic System issues
static constexpr IssueError OutOfMemory{
9001, "System ran out of memory during a validation step.",
"An allocation failed during {} validation: {}."