From 9fcd0fb8abd0b5fb12e14d0f31f63bc0755538b6 Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Fri, 3 Apr 2026 18:42:23 +0900 Subject: [PATCH 1/8] Change channelType to channelId to match KDFS. --- tools/imageio/formatdesc.h | 54 ++++++++++++++++----------------- tools/imageio/imageio.h | 8 ++--- tools/ktx/command_compare.cpp | 8 ++--- tools/ktx/command_create.cpp | 18 +++++++++-- tools/ktx/command_extract.cpp | 6 ++-- tools/ktx/format_descriptor.h | 2 +- tools/ktx/formats.h | 14 ++++++--- tools/ktx/validate.cpp | 34 ++++++++++----------- tools/ktx/validate.h | 2 +- tools/ktx/validation_messages.h | 8 ++--- 10 files changed, 85 insertions(+), 69 deletions(-) diff --git a/tools/imageio/formatdesc.h b/tools/imageio/formatdesc.h index 11a4e7c686..07343bb34f 100644 --- a/tools/imageio/formatdesc.h +++ b/tools/imageio/formatdesc.h @@ -170,7 +170,7 @@ struct FormatDescriptor { uint32_t bitOffset: 16; uint32_t bitLength: 8; // uint32_t channelType: 8; - uint32_t channelType: 4; + uint32_t channelId: 4; uint32_t qualifierLinear: 1; uint32_t qualifierExponent: 1; uint32_t qualifierSigned: 1; @@ -199,7 +199,7 @@ struct FormatDescriptor { /// For uncompressed formats. Handle integer data as normalized. For /// unsigned use the full range of the number of bits. For signed set /// sampleUpper and sampleLower so 0 is representable. - sample(uint32_t chanType, + sample(uint32_t chanId, uint32_t bLength, uint32_t offset, khr_df_sample_datatype_qualifiers_e dataType = static_cast(0), @@ -207,18 +207,18 @@ struct FormatDescriptor { khr_df_model_e m = KHR_DF_MODEL_RGBSDA) { bitOffset = offset; bitLength = bLength - 1; - channelType = chanType; - if (channelType == 3 && m != KHR_DF_MODEL_XYZW) { + channelId = chanId; + if (channelId == 3 && m != KHR_DF_MODEL_XYZW) { /// XYZW does not have an alpha chennel. *_ALPHA has the same /// value for all other 4-channel-capable uncompressed models. - channelType = KHR_DF_CHANNEL_RGBSDA_ALPHA; + channelId = KHR_DF_CHANNEL_RGBSDA_ALPHA; } qualifierFloat = (dataType & KHR_DF_SAMPLE_DATATYPE_FLOAT) != 0; qualifierSigned = (dataType & KHR_DF_SAMPLE_DATATYPE_SIGNED) != 0; qualifierExponent = (dataType & KHR_DF_SAMPLE_DATATYPE_EXPONENT) != 0; qualifierLinear = (dataType & KHR_DF_SAMPLE_DATATYPE_LINEAR) != 0; if (tf > KHR_DF_TRANSFER_LINEAR - && channelType == KHR_DF_CHANNEL_RGBSDA_ALPHA) { + && channelId == KHR_DF_CHANNEL_RGBSDA_ALPHA) { qualifierLinear = 1; } @@ -295,7 +295,7 @@ struct FormatDescriptor { /// Data is assumed to be unsigned normalized. @c sampleUpper will be /// set to the max value representable by @a channelBitLength. /// - /// @c channelType will be set to the standard channel types for @a channelCount + /// @c channelId will be set to the standard channel types for @a channelCount /// and @a m. FormatDescriptor(uint32_t channelCount, uint32_t channelBitLength, khr_df_sample_datatype_qualifiers_e dt @@ -313,7 +313,7 @@ struct FormatDescriptor { dt, t, m)); } if (m == KHR_DF_MODEL_YUVSDA && channelCount == 2) { - samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA; + samples[1].channelId = KHR_DF_CHANNEL_YUVSDA_ALPHA; } extended.sameUnitAllChannels = true; } @@ -342,7 +342,7 @@ struct FormatDescriptor { dt, t, m)); } if (m == KHR_DF_MODEL_YUVSDA && channelCount == 2) { - samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA; + samples[1].channelId = KHR_DF_CHANNEL_YUVSDA_ALPHA; } extended.sameUnitAllChannels = true; } @@ -360,7 +360,7 @@ struct FormatDescriptor { /// Each channel has the same basic data type. FormatDescriptor(uint32_t channelCount, std::vector& channelBitLengths, - std::vector& channelTypes, + std::vector& channelIds, khr_df_sample_datatype_qualifiers_e dt = static_cast(0), khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED, @@ -371,16 +371,16 @@ struct FormatDescriptor { extended(channelCount) { if (channelCount > channelBitLengths.size() - || channelCount > channelTypes.size()) { + || channelCount > channelIds.size()) { throw std::runtime_error( - "Not enough channelBits or channelType specfications." + "Not enough channelBits or channelId specfications." ); } uint32_t bitOffset = 0; bool bitLengthsEqual = true; uint32_t firstBitLength = channelBitLengths[0]; for (uint32_t s = 0; s < channelCount; s++) { - samples.push_back(sample(channelTypes[s], channelBitLengths[s], + samples.push_back(sample(channelIds[s], channelBitLengths[s], bitOffset, dt, t, m)); bitOffset += channelBitLengths[s]; if (firstBitLength != channelBitLengths[s]) { @@ -391,7 +391,7 @@ struct FormatDescriptor { extended.sameUnitAllChannels = true; } if (m == KHR_DF_MODEL_YUVSDA && channelCount == 2) { - samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA; + samples[1].channelId = KHR_DF_CHANNEL_YUVSDA_ALPHA; } } @@ -402,7 +402,7 @@ struct FormatDescriptor { /// integer data or normalized data that does not use the full bit range. FormatDescriptor(uint32_t channelCount, std::vector& channelBitLengths, - std::vector& channelTypes, + std::vector& channelIds, std::vector& samplesLower, std::vector& samplesUpper, khr_df_sample_datatype_qualifiers_e dt @@ -415,16 +415,16 @@ struct FormatDescriptor { extended(channelCount) { if (channelCount > channelBitLengths.size() - || channelCount > channelTypes.size()) { + || channelCount > channelIds.size()) { throw std::runtime_error( - "Not enough channelBits or channelType specfications." + "Not enough channelBits or channelId specfications." ); } uint32_t bitOffset = 0; bool bitLengthsEqual = true; uint32_t firstBitLength = channelBitLengths[0]; for (uint32_t s = 0; s < channelCount; s++) { - samples.push_back(sample(channelTypes[s], channelBitLengths[s], + samples.push_back(sample(channelIds[s], channelBitLengths[s], samplesLower[s], samplesUpper[s], bitOffset, dt, t, m)); bitOffset += channelBitLengths[s]; @@ -436,7 +436,7 @@ struct FormatDescriptor { extended.sameUnitAllChannels = true; } if (m == KHR_DF_MODEL_YUVSDA && channelCount == 2) { - samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA; + samples[1].channelId = KHR_DF_CHANNEL_YUVSDA_ALPHA; } } @@ -568,7 +568,7 @@ struct FormatDescriptor { { std::vector::iterator sit = samples.begin(); for (; sit < samples.end(); sit++) { - if (sit->channelType == KHR_DF_CHANNEL_RGBSDA_ALPHA) { + if (sit->channelId == KHR_DF_CHANNEL_RGBSDA_ALPHA) { sit->qualifierLinear = t > KHR_DF_TRANSFER_LINEAR; } } @@ -598,7 +598,7 @@ struct FormatDescriptor { std::vector::const_iterator it = samples.begin(); uint32_t bitLength = 0; for (; it < samples.end(); it++) { - if (it->channelType == static_cast(c)) { + if (it->channelId == static_cast(c)) { bitLength += it->bitLength + 1; } } @@ -631,7 +631,7 @@ struct FormatDescriptor { for (uint32_t i = 0; i < 16; ++i) { uint32_t bitLength = 0; for (const auto& sample : samples) - if (sample.channelType == i) + if (sample.channelId == i) bitLength += sample.bitLength + 1; if (bitLength > maxBitLength) @@ -643,7 +643,7 @@ struct FormatDescriptor { for (uint32_t i = 0; i < 16; ++i) { uint32_t channelBitLength = 0; for (const auto& sample : samples) - if (sample.channelType == i) + if (sample.channelId == i) channelBitLength += sample.bitLength + 1; if (bitLength != channelBitLength) @@ -656,9 +656,9 @@ struct FormatDescriptor { // TODO: Fix for shared exponent case... std::vector::const_iterator it = samples.begin(); for (; it < samples.end(); it++) { - if (it->channelType == static_cast(c)) { + if (it->channelId == static_cast(c)) { return static_cast - (it->channelType & KHR_DF_SAMPLEMASK_QUALIFIERS); + (it->channelId & KHR_DF_SAMPLEMASK_QUALIFIERS); } } throw std::runtime_error("No such channel."); @@ -700,7 +700,7 @@ struct FormatDescriptor { basic.model())); } if (basic.model() == KHR_DF_MODEL_YUVSDA && channelCount == 2) { - samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA; + samples[1].channelId = KHR_DF_CHANNEL_YUVSDA_ALPHA; } extended.channelCount = channelCount; extended.sameUnitAllChannels = true; @@ -744,7 +744,7 @@ struct FormatDescriptor { [[nodiscard]] const sample* find(khr_df_model_channels_e channel) const { for (const auto& sample : samples) - if (sample.channelType == static_cast(channel)) + if (sample.channelId == static_cast(channel)) return &sample; return nullptr; } diff --git a/tools/imageio/imageio.h b/tools/imageio/imageio.h index d89c448941..bcfd3098c4 100644 --- a/tools/imageio/imageio.h +++ b/tools/imageio/imageio.h @@ -146,7 +146,7 @@ class ImageSpec { ImageSpec(uint32_t w, uint32_t h, uint32_t d, uint32_t channelCount, std::vector& channelBitLengths, - std::vector& channelTypes, + std::vector& channelIds, khr_df_sample_datatype_qualifiers_e dt = static_cast(0), khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED, @@ -154,11 +154,11 @@ class ImageSpec { khr_df_model_e m = KHR_DF_MODEL_RGBSDA, khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT) : ImageSpec(w, h, d, Origin(), channelCount, - channelBitLengths, channelTypes, dt, t, p, m, f) { } + channelBitLengths, channelIds, dt, t, p, m, f) { } ImageSpec(uint32_t w, uint32_t h, uint32_t d, Origin&& o, uint32_t channelCount, std::vector& channelBitLengths, - std::vector& channelTypes, + std::vector& channelIds, khr_df_sample_datatype_qualifiers_e dt = static_cast(0), khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED, @@ -166,7 +166,7 @@ class ImageSpec { khr_df_model_e m = KHR_DF_MODEL_RGBSDA, khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT) : formatDesc(channelCount, channelBitLengths, - channelTypes, dt, t, p, m, f), + channelIds, dt, t, p, m, f), imageWidth(w), imageHeight(h), imageDepth(d), imageOrigin(o) { } diff --git a/tools/ktx/command_compare.cpp b/tools/ktx/command_compare.cpp index 57669a779d..013c56dfc6 100644 --- a/tools/ktx/command_compare.cpp +++ b/tools/ktx/command_compare.cpp @@ -1740,12 +1740,12 @@ void CommandCompare::compareDFDBasic(PrintDiff& diff, uint32_t blockIndex, fmt::format("/dataFormatDescriptor/blocks/{}/samples/{}/qualifiers", blockIndex, sampleIndex), qualifierFlags[0], qualifierFlags[1], dfdToStringSampleDatatypeQualifiersBit); - diff << DiffEnum(" Channel Type", - fmt::format("/dataFormatDescriptor/blocks/{}/samples/{}/channelType", blockIndex, sampleIndex), - OPT_BITFIELDS(samples, channelType), + diff << DiffEnum(" Channel Id", + fmt::format("/dataFormatDescriptor/blocks/{}/samples/{}/channelId", blockIndex, sampleIndex), + OPT_BITFIELDS(samples, channelId), [&](auto i) { return dfdToStringChannelId(khr_df_model_e(bdfds[i]->model), - khr_df_model_channels_e(samples[i]->channelType)); + khr_df_model_channels_e(samples[i]->channelId)); }) .outputHexInText(); diff --git a/tools/ktx/command_create.cpp b/tools/ktx/command_create.cpp index c6c627d267..83b1abcaba 100644 --- a/tools/ktx/command_create.cpp +++ b/tools/ktx/command_create.cpp @@ -2545,9 +2545,21 @@ KTXTexture2 CommandCreate::createTexture(const ImageSpec& target) { if (KTX_SUCCESS != ret) fatal(rc::KTX_FAILURE, "Failed to create ktxTexture: libktx error: {}", ktxErrorString(ret)); - KHR_DFDSETVAL(texture->pDfd + 1, PRIMARIES, target.format().primaries()); - KHR_DFDSETVAL(texture->pDfd + 1, TRANSFER, target.format().transfer()); - if(options.premultiplyAlpha) { + uint32_t* bdb = texture->pDfd + 1; + KHR_DFDSETVAL(bdb, PRIMARIES, target.format().primaries()); + KHR_DFDSETVAL(bdb, TRANSFER, target.format().transfer()); + if (!isFormatSRGB(options.vkFormat) && isTransferNonLinear(target.format().transfer())) { + // Handle the case where the transfer function is not the default for the format, + // e.g. not LINEAR for a *_UNORM texture. + for (uint32_t s = 0; s < KHR_DFDSAMPLECOUNT(bdb); s++) { + if (KHR_DFDSVAL(bdb, s, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_ALPHA) { + uint8_t qualifiers = KHR_DFDSVAL(bdb, s, QUALIFIERS); + qualifiers |= KHR_DF_SAMPLE_DATATYPE_LINEAR; + KHR_DFDSETSVAL(bdb, s, QUALIFIERS, qualifiers); + } + } + } + if (options.premultiplyAlpha) { KHR_DFDSETVAL(texture->pDfd+1, FLAGS, KHR_DF_FLAG_ALPHA_PREMULTIPLIED); } diff --git a/tools/ktx/command_extract.cpp b/tools/ktx/command_extract.cpp index 3dc52f6bb1..56c5296203 100644 --- a/tools/ktx/command_extract.cpp +++ b/tools/ktx/command_extract.cpp @@ -594,7 +594,7 @@ void CommandExtract::unpackAndSave422(std::string filepath, bool appendExtension uint32_t vPositionX = 0; for (const auto& sample : format.samples) { - switch (sample.channelType) { + switch (sample.channelId) { case KHR_DF_CHANNEL_YUVSDA_Y: if (y0Bits != 0) { y1Offset = sample.bitOffset; @@ -888,8 +888,8 @@ void CommandExtract::saveEXR(std::string filepath, bool appendExtension, std::vector channels; if (format.model() == KHR_DF_MODEL_RGBSDA) { - const auto addChannel = [&](auto channelType, const char* channelName) { - if (const auto sample = format.find(channelType)) + const auto addChannel = [&](auto channelId, const char* channelName) { + if (const auto sample = format.find(channelId)) channels.push_back({ sample->bitOffset, sample->bitLength + 1u, diff --git a/tools/ktx/format_descriptor.h b/tools/ktx/format_descriptor.h index c1d5ce36e9..67266ccd11 100644 --- a/tools/ktx/format_descriptor.h +++ b/tools/ktx/format_descriptor.h @@ -37,7 +37,7 @@ namespace ktx { auto& sample = samples.emplace_back(); sample.bitOffset = KHR_DFDSVAL(bdfd, i, BITOFFSET); sample.bitLength = KHR_DFDSVAL(bdfd, i, BITLENGTH); - sample.channelType = KHR_DFDSVAL(bdfd, i, CHANNELID); + sample.channelId = KHR_DFDSVAL(bdfd, i, CHANNELID); const auto dataType = KHR_DFDSVAL(bdfd, i, QUALIFIERS); sample.qualifierFloat = (dataType & KHR_DF_SAMPLE_DATATYPE_FLOAT) != 0; sample.qualifierSigned = (dataType & KHR_DF_SAMPLE_DATATYPE_SIGNED) != 0; diff --git a/tools/ktx/formats.h b/tools/ktx/formats.h index 90e928469e..14aba0a495 100644 --- a/tools/ktx/formats.h +++ b/tools/ktx/formats.h @@ -85,9 +85,9 @@ namespace ktx { return str ? std::string(str) : fmt::format("(0x{:02X})", static_cast(transfer)); } -[[nodiscard]] inline std::string toString(khr_df_model_e colorModel, khr_df_model_channels_e channelType) noexcept { - const auto str = dfdToStringChannelId(colorModel, channelType); - return str ? std::string(str) : fmt::format("(0x{:01X})", static_cast(channelType)); +[[nodiscard]] inline std::string toString(khr_df_model_e colorModel, khr_df_model_channels_e channelId) noexcept { + const auto str = dfdToStringChannelId(colorModel, channelId); + return str ? std::string(str) : fmt::format("(0x{:01X})", static_cast(channelId)); } // ------------------------------------------------------------------------------------------------- @@ -133,9 +133,9 @@ namespace ktx { return dfdToStringColorPrimaries(primaries) != nullptr; } -[[nodiscard]] inline bool isChannelTypeValid(khr_df_model_e colorModel, khr_df_model_channels_e channelType) noexcept { +[[nodiscard]] inline bool isChannelIdValid(khr_df_model_e colorModel, khr_df_model_channels_e channelId) noexcept { // dfdToStringChannelId yields a nullptr if the model and channel combination is invalid - return dfdToStringChannelId(colorModel, channelType) != nullptr; + return dfdToStringChannelId(colorModel, channelId) != nullptr; } [[nodiscard]] inline khr_df_model_e getColorModelForBlockCompressedFormat(VkFormat format) noexcept { @@ -263,6 +263,10 @@ namespace ktx { } } +[[nodiscard]] inline bool isTransferNonLinear(khr_df_transfer_e tf) { + return (tf != KHR_DF_TRANSFER_UNSPECIFIED && tf != KHR_DF_TRANSFER_LINEAR); +} + // ------------------------------------------------------------------------------------------------- [[nodiscard]] inline bool isFormatValid(VkFormat format) noexcept { diff --git a/tools/ktx/validate.cpp b/tools/ktx/validate.cpp index 86a18a37d2..112501d53c 100644 --- a/tools/ktx/validate.cpp +++ b/tools/ktx/validate.cpp @@ -933,22 +933,22 @@ void ValidationContext::validateDFDBasic(uint32_t blockIndex, const uint32_t* df case KHR_DF_MODEL_ETC1S: // Supercompression already verified above, only need to check samples. if (samples.size() > 0) { - switch (samples[0].channelType) { + switch (samples[0].channelId) { case KHR_DF_CHANNEL_ETC1S_RGB: - if (samples.size() > 1 && samples[1].channelType != KHR_DF_CHANNEL_ETC1S_AAA) + if (samples.size() > 1 && samples[1].channelId != KHR_DF_CHANNEL_ETC1S_AAA) error(DFD::InvalidChannelGLTFBU, blockIndex, "KHR_DF_MODEL_ETC1S", 2, - toString(KHR_DF_MODEL_ETC1S, khr_df_model_channels_e(samples[1].channelType)), - "KHR_DF_CHANNEL_ETC1S_AAA when sample #0 channelType is KHR_DF_CHANNEL_ETC1S_RGB"); + toString(KHR_DF_MODEL_ETC1S, khr_df_model_channels_e(samples[1].channelId)), + "KHR_DF_CHANNEL_ETC1S_AAA when sample #0 channelId is KHR_DF_CHANNEL_ETC1S_RGB"); break; case KHR_DF_CHANNEL_ETC1S_RRR: - if (samples.size() > 1 && samples[1].channelType != KHR_DF_CHANNEL_ETC1S_GGG) + if (samples.size() > 1 && samples[1].channelId != KHR_DF_CHANNEL_ETC1S_GGG) error(DFD::InvalidChannelGLTFBU, blockIndex, "KHR_DF_MODEL_ETC1S", 2, - toString(KHR_DF_MODEL_ETC1S, khr_df_model_channels_e(samples[1].channelType)), - "KHR_DF_CHANNEL_ETC1S_GGG when sample #0 channelType is KHR_DF_CHANNEL_ETC1S_RRR"); + toString(KHR_DF_MODEL_ETC1S, khr_df_model_channels_e(samples[1].channelId)), + "KHR_DF_CHANNEL_ETC1S_GGG when sample #0 channelId is KHR_DF_CHANNEL_ETC1S_RRR"); break; default: error(DFD::InvalidChannelGLTFBU, blockIndex, "KHR_DF_MODEL_ETC1S", 1, - toString(KHR_DF_MODEL_ETC1S, khr_df_model_channels_e(samples[0].channelType)), + toString(KHR_DF_MODEL_ETC1S, khr_df_model_channels_e(samples[0].channelId)), "KHR_DF_CHANNEL_ETC1S_RGB or KHR_DF_CHANNEL_ETC1S_RRR"); break; } @@ -961,7 +961,7 @@ void ValidationContext::validateDFDBasic(uint32_t blockIndex, const uint32_t* df "KTX_SS_NONE or KTX_SS_ZSTD"); if (samples.size() > 0) { - switch (samples[0].channelType) { + switch (samples[0].channelId) { case KHR_DF_CHANNEL_UASTC_RGB: [[fallthrough]]; case KHR_DF_CHANNEL_UASTC_RGBA: [[fallthrough]]; case KHR_DF_CHANNEL_UASTC_RRR: [[fallthrough]]; @@ -969,7 +969,7 @@ void ValidationContext::validateDFDBasic(uint32_t blockIndex, const uint32_t* df break; default: error(DFD::InvalidChannelGLTFBU, blockIndex, "KHR_DF_MODEL_UASTC", 0, - toString(KHR_DF_MODEL_UASTC, khr_df_model_channels_e(samples[0].channelType)), + toString(KHR_DF_MODEL_UASTC, khr_df_model_channels_e(samples[0].channelId)), "KHR_DF_CHANNEL_UASTC_RGB, KHR_DF_CHANNEL_UASTC_RGBA, KHR_DF_CHANNEL_UASTC_RRR, or KHR_DF_CHANNEL_UASTC_RG"); break; } @@ -990,14 +990,14 @@ void ValidationContext::validateDFDBasic(uint32_t blockIndex, const uint32_t* df if (!isColorPrimariesValid(khr_df_primaries_e(block.primaries))) error(DFD::InvalidColorPrimaries, blockIndex, block.primaries); - // Validate samples[].channelType + // Validate samples[].channelId for (std::size_t i = 0; i < samples.size(); ++i) { const auto& sample = samples[i]; - if (!isChannelTypeValid(khr_df_model_e(block.model), khr_df_model_channels_e(sample.channelType))) + if (!isChannelIdValid(khr_df_model_e(block.model), khr_df_model_channels_e(sample.channelId))) error(DFD::InvalidChannelForModel, blockIndex, i + 1, - toString(khr_df_model_e(block.model), khr_df_model_channels_e(sample.channelType)), + toString(khr_df_model_e(block.model), khr_df_model_channels_e(sample.channelId)), toString(khr_df_model_e(block.model))); } @@ -1032,10 +1032,10 @@ void ValidationContext::validateDFDBasic(uint32_t blockIndex, const uint32_t* df if (parsed.bitLength != expected.bitLength) error(DFD::FormatMismatch, blockIndex, i + 1, "bitLength", parsed.bitLength, expected.bitLength, toString(VkFormat(header.vkFormat))); - if (parsed.channelType != expected.channelType) - error(DFD::FormatMismatch, blockIndex, i + 1, "channelType", - toString(khr_df_model_e(block.model), khr_df_model_channels_e(parsed.channelType)), - toString(expectedColorModel.value_or(KHR_DF_MODEL_UNSPECIFIED), khr_df_model_channels_e(expected.channelType)), + if (parsed.channelId != expected.channelId) + error(DFD::FormatMismatch, blockIndex, i + 1, "channelId", + toString(khr_df_model_e(block.model), khr_df_model_channels_e(parsed.channelId)), + toString(expectedColorModel.value_or(KHR_DF_MODEL_UNSPECIFIED), khr_df_model_channels_e(expected.channelId)), toString(VkFormat(header.vkFormat))); if (parsed.qualifierLinear != expected.qualifierLinear) error(DFD::FormatMismatch, blockIndex, i + 1, "qualifierLinear", parsed.qualifierLinear, diff --git a/tools/ktx/validate.h b/tools/ktx/validate.h index 6d0eda65a1..d57a1162c7 100644 --- a/tools/ktx/validate.h +++ b/tools/ktx/validate.h @@ -62,7 +62,7 @@ static_assert(sizeof(BDFD) == 24); struct SampleType { uint32_t bitOffset: 16; uint32_t bitLength: 8; - uint32_t channelType: 4; + uint32_t channelId: 4; uint32_t qualifierLinear: 1; uint32_t qualifierExponent: 1; uint32_t qualifierSigned: 1; diff --git a/tools/ktx/validation_messages.h b/tools/ktx/validation_messages.h index 002bc87158..53d97d9835 100644 --- a/tools/ktx/validation_messages.h +++ b/tools/ktx/validation_messages.h @@ -462,8 +462,8 @@ struct DFD { // 61xx - Basic Data Format Descriptor Block sample related issues: static constexpr IssueError InvalidChannelForModel{ - 6101, "Invalid sample channelType for colorModel in the basic DFD block.", - "DFD block #{} sample #{} channelType in basic DFD block is {} which is not valid for colorModel {}." + 6101, "Invalid sample channelId for colorModel in the basic DFD block.", + "DFD block #{} sample #{} channelId in basic DFD block is {} which is not valid for colorModel {}." }; static constexpr IssueError InvalidBitOffsetForUASTC{ 6102, "Invalid sample bitOffset for UASTC texture in the basic DFD block.", @@ -516,8 +516,8 @@ struct DFD { "DFD block #{} colorModel is {} while supercompressionScheme is {} but KHR_texture_basisu requires supercompressionScheme {} for this colorModel." }; static constexpr IssueError InvalidChannelGLTFBU{ - 6303, "Invalid sample channelType for colorModel for KHR_texture_basisu compatibility.", - "DFD block #{} colorModel is {} but sample #{} channelType is {} while KHR_texture_basisu requires {}." + 6303, "Invalid sample channelId for colorModel for KHR_texture_basisu compatibility.", + "DFD block #{} colorModel is {} but sample #{} channelId is {} while KHR_texture_basisu requires {}." }; static constexpr IssueError InvalidColorSpaceGLTFBU{ 6304, "Color space information is incompatible with KHR_texture_basisu.", From fadf1d35bf09d776625d34b7e9c2b30e43c9ec72 Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Sat, 4 Apr 2026 18:03:23 +0900 Subject: [PATCH 2/8] Revert linear qualifier related change. --- tools/ktx/command_create.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/tools/ktx/command_create.cpp b/tools/ktx/command_create.cpp index 83b1abcaba..c6c627d267 100644 --- a/tools/ktx/command_create.cpp +++ b/tools/ktx/command_create.cpp @@ -2545,21 +2545,9 @@ KTXTexture2 CommandCreate::createTexture(const ImageSpec& target) { if (KTX_SUCCESS != ret) fatal(rc::KTX_FAILURE, "Failed to create ktxTexture: libktx error: {}", ktxErrorString(ret)); - uint32_t* bdb = texture->pDfd + 1; - KHR_DFDSETVAL(bdb, PRIMARIES, target.format().primaries()); - KHR_DFDSETVAL(bdb, TRANSFER, target.format().transfer()); - if (!isFormatSRGB(options.vkFormat) && isTransferNonLinear(target.format().transfer())) { - // Handle the case where the transfer function is not the default for the format, - // e.g. not LINEAR for a *_UNORM texture. - for (uint32_t s = 0; s < KHR_DFDSAMPLECOUNT(bdb); s++) { - if (KHR_DFDSVAL(bdb, s, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_ALPHA) { - uint8_t qualifiers = KHR_DFDSVAL(bdb, s, QUALIFIERS); - qualifiers |= KHR_DF_SAMPLE_DATATYPE_LINEAR; - KHR_DFDSETSVAL(bdb, s, QUALIFIERS, qualifiers); - } - } - } - if (options.premultiplyAlpha) { + KHR_DFDSETVAL(texture->pDfd + 1, PRIMARIES, target.format().primaries()); + KHR_DFDSETVAL(texture->pDfd + 1, TRANSFER, target.format().transfer()); + if(options.premultiplyAlpha) { KHR_DFDSETVAL(texture->pDfd+1, FLAGS, KHR_DF_FLAG_ALPHA_PREMULTIPLIED); } From 1a1284e276d669aae175be1fbca8bd93cd15addf Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Sat, 4 Apr 2026 18:06:54 +0900 Subject: [PATCH 3/8] Revert linear qualifier related change. --- tools/ktx/formats.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tools/ktx/formats.h b/tools/ktx/formats.h index 14aba0a495..d58d3bad7b 100644 --- a/tools/ktx/formats.h +++ b/tools/ktx/formats.h @@ -263,10 +263,6 @@ namespace ktx { } } -[[nodiscard]] inline bool isTransferNonLinear(khr_df_transfer_e tf) { - return (tf != KHR_DF_TRANSFER_UNSPECIFIED && tf != KHR_DF_TRANSFER_LINEAR); -} - // ------------------------------------------------------------------------------------------------- [[nodiscard]] inline bool isFormatValid(VkFormat format) noexcept { From 2189d05f3e349f6f6cd1ab2e4d32451fd7570a76 Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Sun, 5 Apr 2026 12:31:04 +0900 Subject: [PATCH 4/8] Fix set but not used warning for static global from latest CLang. --- lib/src/glloader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/glloader.c b/lib/src/glloader.c index dbb113ea6b..6dcbeb5000 100644 --- a/lib/src/glloader.c +++ b/lib/src/glloader.c @@ -152,7 +152,7 @@ static GLint contextProfile = 0; * by the current context. */ static GLint sizedFormats = _ALL_SIZED_FORMATS; -static GLboolean supportsSwizzle = GL_TRUE; +static MAYBE_UNUSED GLboolean supportsSwizzle = GL_TRUE; /** * @internal * @~English From 7a39c419863e274d48f6a64e5822a074668ca313 Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Sun, 5 Apr 2026 12:32:12 +0900 Subject: [PATCH 5/8] Change channelType to channelId. --- external/dfdutils/printdfd.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/external/dfdutils/printdfd.c b/external/dfdutils/printdfd.c index 7ae7fa8a8c..bde851b34c 100644 --- a/external/dfdutils/printdfd.c +++ b/external/dfdutils/printdfd.c @@ -821,20 +821,20 @@ void printDFD(uint32_t *DFD, uint32_t dataSize) if (remainingSize < sizeof_BDFD + (sample + 1) * sizeof_BDFDSample) break; // Invalid DFD: Missing or partial basic DFD sample - khr_df_model_channels_e channelType = KHR_DFDSVAL(block, sample, CHANNELID); + khr_df_model_channels_e channelId = KHR_DFDSVAL(block, sample, CHANNELID); printf("Sample %u:\n", sample); khr_df_sample_datatype_qualifiers_e qualifiers = KHR_DFDSVAL(block, sample, QUALIFIERS); printf(" Qualifiers: 0x%X (", qualifiers); printFlagBits(qualifiers, dfdToStringSampleDatatypeQualifiersBit); printf(")\n"); - printf(" Channel Type: 0x%X", channelType); + printf(" Channel Id: 0x%X", channelId); { - const char* str = dfdToStringChannelId(model, channelType); + const char* str = dfdToStringChannelId(model, channelId); if (str) printf(" (%s)\n", str); else - printf(" (%u)\n", channelType); + printf(" (%u)\n", channelId); } printf(" Length: %u bits Offset: %u\n", KHR_DFDSVAL(block, sample, BITLENGTH) + 1, @@ -1008,12 +1008,12 @@ void printDFDJSON(uint32_t* DFD, uint32_t dataSize, uint32_t base_indent, uint32 PRINT_INDENT(4, "],%s", nl) } - khr_df_model_channels_e channelType = KHR_DFDSVAL(block, sample, CHANNELID); - const char* channelStr = dfdToStringChannelId(model, channelType); + khr_df_model_channels_e channelId = KHR_DFDSVAL(block, sample, CHANNELID); + const char* channelStr = dfdToStringChannelId(model, channelId); if (channelStr) - PRINT_INDENT(4, "\"channelType\":%s\"%s\",%s", space, channelStr, nl) + PRINT_INDENT(4, "\"channelId\":%s\"%s\",%s", space, channelStr, nl) else - PRINT_INDENT(4, "\"channelType\":%s%u,%s", space, channelType, nl) + PRINT_INDENT(4, "\"channelId\":%s%u,%s", space, channelId, nl) PRINT_INDENT(4, "\"bitLength\":%s%u,%s", space, KHR_DFDSVAL(block, sample, BITLENGTH), nl) PRINT_INDENT(4, "\"bitOffset\":%s%u,%s", space, KHR_DFDSVAL(block, sample, BITOFFSET), nl) From 5ba0759ba7b9d7bc8d7b65217c04c6865f8bbb48 Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Sun, 5 Apr 2026 12:32:41 +0900 Subject: [PATCH 6/8] Change channelType to channelId and increment version. --- tools/ktx/schema/compare.json | 8 ++++---- tools/ktx/schema/info.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/ktx/schema/compare.json b/tools/ktx/schema/compare.json index 1adf4b8ecb..0dfa29fe7b 100644 --- a/tools/ktx/schema/compare.json +++ b/tools/ktx/schema/compare.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://schema.khronos.org/ktx/compare_v0.json", + "$id": "https://schema.khronos.org/ktx/compare_v1.json", "definitions": { "enum_vkFormat": { "oneOf": [ @@ -471,7 +471,7 @@ } ] }, - "enum_channelType": { + "enum_channelId": { "oneOf": [ { "type": "integer" }, { @@ -1117,11 +1117,11 @@ ] } }, - "^/dataFormatDescriptor/blocks/[0-9]+/samples/[0-9]+/channelType$": { + "^/dataFormatDescriptor/blocks/[0-9]+/samples/[0-9]+/channelId$": { "type": "array", "items": { "oneOf": [ - { "$ref": "#/definitions/enum_channelType" }, + { "$ref": "#/definitions/enum_channelId" }, { "type": "null" } ] } diff --git a/tools/ktx/schema/info.json b/tools/ktx/schema/info.json index e633a2363d..60b42d7b4c 100644 --- a/tools/ktx/schema/info.json +++ b/tools/ktx/schema/info.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://schema.khronos.org/ktx/info_v0.json", + "$id": "https://schema.khronos.org/ktx/info_v1.json", "definitions": { "enum_vkFormat": { "oneOf": [ @@ -471,7 +471,7 @@ } ] }, - "enum_channelType": { + "enum_channelId": { "oneOf": [ { "type": "integer" }, { @@ -772,7 +772,7 @@ "type": "object", "required": [ "qualifiers", - "channelType", + "channelId", "bitLength", "bitOffset", "samplePosition", @@ -786,7 +786,7 @@ "items": { "$ref": "#/definitions/enum_qualifier" }, "uniqueItems": true }, - "channelType": { "$ref": "#/definitions/enum_channelType" }, + "channelId": { "$ref": "#/definitions/enum_channelId" }, "bitLength": { "type": "integer" }, "bitOffset": { "type": "integer" }, "samplePosition": { From 6da7cc04dca5f7774d5fc28195f2f9b1a81f308d Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Mon, 6 Apr 2026 20:43:00 +0900 Subject: [PATCH 7/8] Update schema version written to output. --- tools/ktx/command_compare.cpp | 4 ++-- tools/ktx/command_info.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/ktx/command_compare.cpp b/tools/ktx/command_compare.cpp index 013c56dfc6..70102d5bfb 100644 --- a/tools/ktx/command_compare.cpp +++ b/tools/ktx/command_compare.cpp @@ -820,7 +820,7 @@ Compare two KTX2 files. If any of the specified input files are invalid then comparison is done based on best effort and may be incomplete. - The JSON output formats conform to the https://schema.khronos.org/ktx/compare_v0.json + The JSON output formats conform to the https://schema.khronos.org/ktx/compare_v1.json schema even if the input file is invalid and certain information cannot be parsed or displayed. Additionally, for JSON outputs the KTX file identifier is printed using "\u001A" instead of @@ -1351,7 +1351,7 @@ void CommandCompare::executeCompare() { PrintIndent out{std::cout, baseIndent, indentWidth}; out(0, "{{{}", nl); - out(1, "\"$schema\":{}\"https://schema.khronos.org/ktx/compare_v0.json\",{}", space, nl); + out(1, "\"$schema\":{}\"https://schema.khronos.org/ktx/compare_v1.json\",{}", space, nl); for (std::size_t i = 0; i < inputStreams.size(); ++i) { std::ostringstream messagesOS; diff --git a/tools/ktx/command_info.cpp b/tools/ktx/command_info.cpp index dac6242acf..19194a1426 100644 --- a/tools/ktx/command_info.cpp +++ b/tools/ktx/command_info.cpp @@ -37,7 +37,7 @@ Print information about a KTX2 file. found errors and warnings to stdout. If the specified input file is invalid the information is displayed based on best effort and may be incomplete. - The JSON output formats conform to the https://schema.khronos.org/ktx/info_v0.json + The JSON output formats conform to the https://schema.khronos.org/ktx/info_v1.json schema even if the input file is invalid and certain information cannot be parsed or displayed. Additionally, for JSON outputs the KTX file identifier is printed using "\u001A" instead of @@ -213,7 +213,7 @@ KTX_error_code CommandInfo::printInfoJSON(std::istream& file, bool minified) { PrintIndent out{std::cout, base_indent, indent_width}; out(0, "{{{}", nl); - out(1, "\"$schema\":{}\"https://schema.khronos.org/ktx/info_v0.json\",{}", space, nl); + out(1, "\"$schema\":{}\"https://schema.khronos.org/ktx/info_v1.json\",{}", space, nl); out(1, "\"valid\":{}{},{}", space, validationResult == 0, nl); if (!first) { out(1, "\"messages\":{}[{}", space, nl); From 5e059ea249d87539c0a1087199d022c584bd5f5d Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Mon, 6 Apr 2026 20:44:00 +0900 Subject: [PATCH 8/8] Update reference for channelType to channelId updates. --- tests/cts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cts b/tests/cts index 028019aee3..a6352d9d27 160000 --- a/tests/cts +++ b/tests/cts @@ -1 +1 @@ -Subproject commit 028019aee346bdb2386aefea51a290fb83d90c0a +Subproject commit a6352d9d27205353b703b97123818bb6b266b20e