mirror of
https://github.com/KhronosGroup/KTX-Software.git
synced 2026-01-18 17:41:19 +01:00
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:
@@ -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()
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -45,3 +45,4 @@ EXPORTS
|
||||
ktxTexture2_destruct
|
||||
vk2dfd
|
||||
vkFormatString
|
||||
stringToVkFormat
|
||||
|
||||
@@ -45,3 +45,4 @@ EXPORTS
|
||||
ktxTexture2_destruct
|
||||
vk2dfd
|
||||
vkFormatString
|
||||
stringToVkFormat
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Submodule tests/cts updated: 53faa7867d...b881ca6ff4
@@ -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
|
||||
|
||||
118
tests/unittests/test_fragment_uri.cc
Normal file
118
tests/unittests/test_fragment_uri.cc
Normal 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
|
||||
@@ -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 ";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
266
tools/ktx/fragment_uri.h
Normal 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 file’s 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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: {}."
|
||||
|
||||
Reference in New Issue
Block a user