Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vkformat checks to ktxTexture2_DecodeAstc #967

Merged
merged 10 commits into from
Jan 8, 2025
2 changes: 1 addition & 1 deletion include/ktx.h
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,7 @@ KTX_API KTX_error_code KTX_APIENTRY
ktxTexture2_CompressAstc(ktxTexture2* This, ktx_uint32_t quality);

KTX_API KTX_error_code KTX_APIENTRY
ktxTexture2_DecodeAstc(ktxTexture2* This, ktx_uint32_t vkformat);
ktxTexture2_DecodeAstc(ktxTexture2* This);

/**
* @memberof ktxTexture2
Expand Down
213 changes: 110 additions & 103 deletions lib/astc_codec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ unorm8x4ArrayToImage(const uint8_t *data, uint32_t dim_x, uint32_t dim_y) {

/**
* @memberof ktxTexture
* @internal
* @ingroup writer
* @~English
* @brief Creates default ASTC parameters
Expand All @@ -222,6 +223,7 @@ astcDefaultOptions() {

/**
* @memberof ktxTexture
* @internal
* @ingroup writer
* @~English
* @brief Should be used to get VkFormat from ASTC block enum
Expand Down Expand Up @@ -291,38 +293,121 @@ astcVkFormat(ktx_uint32_t block_size, bool sRGB) {

/**
* @memberof ktxTexture
* @ingroup writer
* @internal
* @ingroup reader
* @~English
* @brief Creates valid ASTC encoder action from string.
* @brief Should be used to get uncompressed version of ASTC VkFormat
*
* @return Valid astc_profile from string
* The decompressed format is calculated from corresponding ASTC format. There are
* only 3 possible options currently supported. RGBA8, SRGBA8 and RGBA32.
*
* @return Uncompressed version of VKFormat for a specific ASTC VkFormat
*/
static astcenc_profile
astcEncoderAction(const ktxAstcParams &params, const uint32_t* bdb) {
inline VkFormat getUncompressedFormat(ktxTexture2* This) noexcept {
uint32_t* BDB = This->pDfd + 1;

ktx_uint32_t transfer = KHR_DFDVAL(bdb, TRANSFER);
if (KHR_DFDSVAL(BDB, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_FLOAT) {
return VK_FORMAT_R32G32B32A32_SFLOAT;
} else {
if (khr_df_transfer_e(KHR_DFDVAL(BDB, TRANSFER) == KHR_DF_TRANSFER_SRGB))
return VK_FORMAT_R8G8B8A8_SRGB;
else
return VK_FORMAT_R8G8B8A8_UNORM;
}
}

if (transfer == KHR_DF_TRANSFER_SRGB &&
params.mode == KTX_PACK_ASTC_ENCODER_MODE_LDR)
/**
* @memberof ktxTexture
* @internal
* @ingroup reader
* @~English
* @brief Should be used to check if an ASTC VkFormat is LDR format or not.
*
* @return true if the VkFormat is an ASTC LDR format.
*/
inline bool isFormatAstcLDR(ktxTexture2* This) noexcept {
return (KHR_DFDSVAL(This->pDfd + 1, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_FLOAT) == 0;
}

/**
* @memberof ktxTexture
* @internal
* @ingroup reader writer
* @~English
* @brief Creates valid ASTC decoder profile from VkFormat
*
* @return Valid astc_profile from VkFormat
*/
static astcenc_profile
astcProfile(bool sRGB, bool ldr) {

if (sRGB && ldr)
return ASTCENC_PRF_LDR_SRGB;
else if (transfer == KHR_DF_TRANSFER_LINEAR) {
if (params.mode == KTX_PACK_ASTC_ENCODER_MODE_LDR)
else if (!sRGB) {
if (ldr)
return ASTCENC_PRF_LDR;
else
return ASTCENC_PRF_HDR;
}
// TODO: Add support for the following
// KTX_PACK_ASTC_ENCODER_ACTION_COMP_HDR_RGB_LDR_ALPHA; currently not supported

assert(ldr && "HDR sRGB profile not supported");

return ASTCENC_PRF_LDR_SRGB;
}

/**
* @memberof ktxTexture
* @internal
* @ingroup writer
MarkCallow marked this conversation as resolved.
Show resolved Hide resolved
* @~English
* @brief Creates valid ASTC encoder profile provided params and bdb
*
* @return Valid astc_profile from params and bdb
*/
static astcenc_profile
astcEncoderProfile(const ktxAstcParams &params, const uint32_t* bdb) {

ktx_uint32_t transfer = KHR_DFDVAL(bdb, TRANSFER);

bool sRGB = transfer == KHR_DF_TRANSFER_SRGB;
bool ldr = params.mode == KTX_PACK_ASTC_ENCODER_MODE_LDR;

if (!sRGB) {
assert(transfer == KHR_DF_TRANSFER_LINEAR && "Unsupported transfer function, only support sRGB and Linear");
}

return astcProfile(sRGB, ldr);
}

/**
* @memberof ktxTexture
* @internal
* @ingroup reader
* @~English
* @brief Creates valid ASTC decoder profile from VkFormat
*
* @return Valid astc_profile from VkFormat
*/
static astcenc_profile
astcDecoderProfile(ktxTexture2 *This) {

uint32_t* BDB = This->pDfd + 1;
ktx_uint32_t transfer = KHR_DFDVAL(BDB, TRANSFER);

bool sRGB = transfer == KHR_DF_TRANSFER_SRGB;
bool ldr = isFormatAstcLDR(This);

return astcProfile(sRGB, ldr);
}

/**
* @memberof ktxTexture
* @internal
* @ingroup writer
* @~English
* @brief Creates valid ASTC encoder swizzle from string.
* @brief Creates valid ASTC encoder swizzle from string.
*
* @return Valid astcenc_swizzle from string
*/
Expand Down Expand Up @@ -391,88 +476,6 @@ astcBlockDimensions(ktx_uint32_t block_size,
}
}


static void
astcBlockDimensions(VkFormat format,
uint32_t &x, uint32_t &y, uint32_t &z) noexcept {
switch (format) {
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: x = 4; y = 4; z = 0; break;
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: x = 4; y = 4; z = 0; break;
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: x = 5; y = 4; z = 0; break;
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: x = 5; y = 4; z = 0; break;
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: x = 5; y = 5; z = 0; break;
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: x = 5; y = 5; z = 0; break;
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: x = 6; y = 5; z = 0; break;
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: x = 6; y = 5; z = 0; break;
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: x = 6; y = 6; z = 0; break;
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: x = 6; y = 6; z = 0; break;
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: x = 8; y = 5; z = 0; break;
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: x = 8; y = 5; z = 0; break;
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: x = 8; y = 6; z = 0; break;
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: x = 8; y = 6; z = 0; break;
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: x = 8; y = 8; z = 0; break;
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: x = 8; y = 8; z = 0; break;
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: x = 10; y = 5; z = 0; break;
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: x = 10; y = 5; z = 0; break;
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: x = 10; y = 6; z = 0; break;
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: x = 10; y = 6; z = 0; break;
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: x = 10; y = 8; z = 0; break;
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: x = 10; y = 8; z = 0; break;
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: x = 10; y = 10; z = 0; break;
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: x = 10; y = 10; z = 0; break;
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: x = 12; y = 10; z = 0; break;
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: x = 12; y = 10; z = 0; break;
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: x = 12; y = 12; z = 0; break;
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: x = 12; y = 12; z = 0; break;
case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK: x = 4; y = 4; z = 0; break;
case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK: x = 5; y = 4; z = 0; break;
case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK: x = 5; y = 5; z = 0; break;
case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK: x = 6; y = 5; z = 0; break;
case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK: x = 6; y = 6; z = 0; break;
case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK: x = 8; y = 5; z = 0; break;
case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK: x = 8; y = 6; z = 0; break;
case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK: x = 8; y = 8; z = 0; break;
case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK: x = 10; y = 5; z = 0; break;
case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK: x = 10; y = 6; z = 0; break;
case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK: x = 10; y = 8; z = 0; break;
case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK: x = 10; y = 10; z = 0; break;
case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK: x = 12; y = 10; z = 0; break;
case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK: x = 12; y = 12; z = 0; break;
case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT: x = 3; y = 3; z = 3; break;
case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT: x = 3; y = 3; z = 3; break;
case VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT: x = 3; y = 3; z = 3; break;
case VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT: x = 4; y = 3; z = 3; break;
case VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT: x = 4; y = 3; z = 3; break;
case VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT: x = 4; y = 3; z = 3; break;
case VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT: x = 4; y = 4; z = 3; break;
case VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT: x = 4; y = 4; z = 3; break;
case VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT: x = 4; y = 4; z = 3; break;
case VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT: x = 4; y = 4; z = 4; break;
case VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT: x = 4; y = 4; z = 4; break;
case VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT: x = 4; y = 4; z = 4; break;
case VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT: x = 5; y = 4; z = 4; break;
case VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT: x = 5; y = 4; z = 4; break;
case VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT: x = 5; y = 4; z = 4; break;
case VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT: x = 5; y = 5; z = 4; break;
case VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT: x = 5; y = 5; z = 4; break;
case VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT: x = 5; y = 5; z = 4; break;
case VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT: x = 5; y = 5; z = 5; break;
case VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT: x = 5; y = 5; z = 5; break;
case VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT: x = 5; y = 5; z = 5; break;
case VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT: x = 6; y = 5; z = 5; break;
case VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT: x = 6; y = 5; z = 5; break;
case VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT: x = 6; y = 5; z = 5; break;
case VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT: x = 6; y = 6; z = 5; break;
case VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT: x = 6; y = 6; z = 5; break;
case VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT: x = 6; y = 6; z = 5; break;
case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT: x = 6; y = 6; z = 6; break;
case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT: x = 6; y = 6; z = 6; break;
case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT: x = 6; y = 6; z = 6; break;
default:
x = 0; y = 0; z = 0;
}
}

static float
astcQuality(ktx_uint32_t quality_level) {
switch (quality_level) {
Expand Down Expand Up @@ -756,7 +759,7 @@ ktxTexture2_CompressAstcEx(ktxTexture2* This, ktxAstcParams* params) {
astcBlockDimensions(params->blockDimension,
block_size_x, block_size_y, block_size_z);
quality = astcQuality(params->qualityLevel);
profile = astcEncoderAction(*params, BDB);
profile = astcEncoderProfile(*params, BDB);
swizzle = astcSwizzle(*params);

if(params->perceptual)
Expand Down Expand Up @@ -958,6 +961,7 @@ struct decompression_workload

/**
* @internal
* @ingroup reader
* @brief Runner callback function for a decompression worker thread.
*
* @param thread_count The number of threads in the worker pool.
Expand All @@ -981,11 +985,14 @@ static void decompression_workload_runner(int thread_count, int thread_id, void*
/**
* @ingroup reader
* @brief Decodes a ktx2 texture object, if it is ASTC encoded.

* The decompressed format is calculated from corresponding ASTC format. There are
* only 3 possible options currently supported. RGBA8, SRGBA8 and RGBA32.
* @note 3d textures are decoded to a multi-slice 3d texture.
*
* Updates @p This with the decoded image.
*
* @param This The texture to decode
* @param vkformat The decoding format to use
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
Expand All @@ -1011,7 +1018,7 @@ static void decompression_workload_runner(int thread_count, int thread_id, void*
* not happen in release package.
*/
KTX_error_code
ktxTexture2_DecodeAstc(ktxTexture2 *This, ktx_uint32_t vkformat) {
ktxTexture2_DecodeAstc(ktxTexture2 *This) {
// Decompress This using astc-decoder
uint32_t* BDB = This->pDfd + 1;
khr_df_model_e colorModel = (khr_df_model_e)KHR_DFDVAL(BDB, MODEL);
Expand All @@ -1034,6 +1041,8 @@ ktxTexture2_DecodeAstc(ktxTexture2 *This, ktx_uint32_t vkformat) {
return KTX_FILE_DATA_ERROR;
}

ktx_uint32_t vkformat = (ktx_uint32_t)getUncompressedFormat(This);

// Create a prototype texture to use for calculating sizes in the target
// format and, as useful side effects, provide us with a properly sized
// data allocation and the DFD for the target format.
Expand Down Expand Up @@ -1077,23 +1086,21 @@ ktxTexture2_DecodeAstc(ktxTexture2 *This, ktx_uint32_t vkformat) {
}

// This is where I do the decompression from "This" to prototype target
astcenc_profile profile{ASTCENC_PRF_LDR_SRGB};
astcenc_swizzle swizzle{ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A};

uint32_t block_size_x{4}; // Get the right blocks from vkformat
uint32_t block_size_y{4};
uint32_t block_size_z{1};
float quality{ASTCENC_PRE_MEDIUM};
uint32_t flags{0}; // TODO: Use normals mode to reconstruct normals params->normalMap ? ASTCENC_FLG_MAP_NORMAL : 0};

astcBlockDimensions((VkFormat)This->vkFormat, block_size_x, block_size_y, block_size_z);
uint32_t block_size_x = KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION0) + 1;
uint32_t block_size_y = KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION1) + 1;
uint32_t block_size_z = KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION2) + 1;

// quality = astcQuality(params->qualityLevel);
// profile = astcEncoderAction(*params, BDB);
// swizzle = astcSwizzle(*params);

// if(params->perceptual) flags |= ASTCENC_FLG_USE_PERCEPTUAL;

astcenc_profile profile = astcDecoderProfile(This);

uint32_t threadCount{1}; // Decompression isn't the bottleneck and only used when checking for psnr and ssim
astcenc_config astc_config;
astcenc_context *astc_context;
Expand Down
2 changes: 1 addition & 1 deletion tests/cts
Submodule cts updated 21 files
+1 −1 clitests/golden/encode/compare/astc_0_psnr_R8G8B8A8_UNORM_2D.out.txt
+11 −11 clitests/golden/encode/compare/astc_0_psnr_R8G8B8A8_UNORM_2D_ARRAY_MIP.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_psnr_R8G8B8A8_UNORM_2D_ZLIB_9.out.txt
+35 −35 clitests/golden/encode/compare/astc_0_psnr_R8G8B8A8_UNORM_CUBE_ARRAY_MIP.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_psnr_R8G8B8_UNORM_2D.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_psnr_R8G8_UNORM_2D.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_psnr_R8_UNORM_2D.out.txt
+2 −2 clitests/golden/encode/compare/astc_0_psnr_ssim_R8G8B8A8_UNORM_2D.out.txt
+24 −24 clitests/golden/encode/compare/astc_0_psnr_ssim_R8G8B8A8_UNORM_2D_ARRAY_MIP.out.txt
+2 −2 clitests/golden/encode/compare/astc_0_psnr_ssim_R8G8B8A8_UNORM_2D_ZLIB_9.out.txt
+72 −72 clitests/golden/encode/compare/astc_0_psnr_ssim_R8G8B8A8_UNORM_CUBE_ARRAY_MIP.out.txt
+2 −2 clitests/golden/encode/compare/astc_0_psnr_ssim_R8G8B8_UNORM_2D.out.txt
+2 −2 clitests/golden/encode/compare/astc_0_psnr_ssim_R8G8_UNORM_2D.out.txt
+2 −2 clitests/golden/encode/compare/astc_0_psnr_ssim_R8_UNORM_2D.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_ssim_R8G8B8A8_UNORM_2D.out.txt
+13 −13 clitests/golden/encode/compare/astc_0_ssim_R8G8B8A8_UNORM_2D_ARRAY_MIP.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_ssim_R8G8B8A8_UNORM_2D_ZLIB_9.out.txt
+37 −37 clitests/golden/encode/compare/astc_0_ssim_R8G8B8A8_UNORM_CUBE_ARRAY_MIP.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_ssim_R8G8B8_UNORM_2D.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_ssim_R8G8_UNORM_2D.out.txt
+1 −1 clitests/golden/encode/compare/astc_0_ssim_R8_UNORM_2D.out.txt
8 changes: 3 additions & 5 deletions tools/ktx/metrics_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,10 @@ class MetricsCalculator {

// Decode the encoded texture to observe the compression losses
const auto* bdfd = texture->pDfd + 1;
if (khr_df_model_e(KHR_DFDVAL(bdfd, MODEL)) == KHR_DF_MODEL_ASTC)
{
ec = ktxTexture2_DecodeAstc(texture, VK_FORMAT_R8G8B8A8_UNORM);
if (khr_df_model_e(KHR_DFDVAL(bdfd, MODEL)) == KHR_DF_MODEL_ASTC) {
ec = ktxTexture2_DecodeAstc(texture);
}
else
{
else {
tSwizzleInfo = determineTranscodeSwizzle(texture, report);
ec = ktxTexture2_TranscodeBasis(texture, KTX_TTF_RGBA32, 0);
}
Expand Down
Loading