Skip to content

Fix SGD image desc indexing for 3D UASTC HDR 6x6i textures - #1237

Merged
MarkCallow merged 1 commit into
KhronosGroup:mainfrom
SashaRX:fix/hdr6x6i-3d-sgd-descriptor-index
Aug 7, 2026
Merged

Fix SGD image desc indexing for 3D UASTC HDR 6x6i textures#1237
MarkCallow merged 1 commit into
KhronosGroup:mainfrom
SashaRX:fix/hdr6x6i-3d-sgd-descriptor-index

Conversation

@SashaRX

@SashaRX SashaRX commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Fixes transcoding of 3D textures encoded to UASTC HDR 6x6 intermediate.

Problem

transcodeUastcHDR6x6_intermediate indexes the SGD image description table with

sgdImageDescIndex = level * levelImageCount + image

using the current level's image count. The encoder (basis_encode.cpp) writes the ktxUASTCHDR6x6IntermediateImageDesc entries in level order, level 0 first, with each level contributing numLayers × numFaces × depth(level) descriptions. The two agree only while every level has the same image count. For 3D textures depth halves with each mip level, so the transcoder selects descriptions belonging to other levels; since slice offsets are relative to their own level's data, transcoding fails with KTX_TRANSCODE_FAILED (or could silently read wrong slices when the offsets happen to stay in range).

This reproduces with libktx's own encoder output: encode a 24×24×8 VK_FORMAT_R16G16B16A16_SFLOAT texture with 4 mip levels via ktxTexture2_CompressBasisEx with KTX_BASIS_CODEC_UASTC_HDR_6x6_INTERMEDIATE, then ktxTexture2_TranscodeBasis(..., KTX_TTF_ASTC_HDR_6x6_RGBA, 0) fails. A 2D texture with otherwise identical parameters transcodes fine — presumably why this hasn't been noticed.

Fix

Accumulate per-level first-image indices before the level loop and index with firstImages[level] + image — the same approach transcodeEtc1s already uses with its firstImages table. No behavior change for textures where every level has the same image count (2D, arrays, cubemaps): for those firstImages[level] == level * levelImageCount.

Since firstImages[numLevels] is the image count implied by the texture's dimensions, the descriptor table size is now also validated up front: a table that is not exactly that many descriptions, or not a whole number of them, is rejected with KTX_FILE_DATA_ERROR before any level is processed, instead of surfacing as KTX_TRANSCODE_FAILED per image.

Tests

Adds generated 2D control and 3D multi-mip round-trip regression tests to transcodetests (in-process encode → transcode), with no new binary test resources. The 3D case fails without the fix and passes with it; both verify the post-transcode state (vkFormat, supercompression scheme, image data present).

Found while studying the transcoder for the per-level streaming work discussed in #1224.

@CLAassistant

CLAassistant commented Aug 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@SashaRX
SashaRX force-pushed the fix/hdr6x6i-3d-sgd-descriptor-index branch from c06cc69 to 1737c68 Compare August 5, 2026 14:54
@SashaRX
SashaRX marked this pull request as ready for review August 5, 2026 14:59
@MarkCallow

Copy link
Copy Markdown
Collaborator

Excellent catch. Thanks. As with PR #1236 , please sign the CLA. It should be sufficient to do it in just one PR.

@SashaRX
SashaRX force-pushed the fix/hdr6x6i-3d-sgd-descriptor-index branch from 1737c68 to 9907965 Compare August 6, 2026 11:46

@MarkCallow MarkCallow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't run the workflows until you fix the incorrect for loop.

Comment thread lib/src/basis_transcode.cpp
Comment thread lib/src/basis_transcode.cpp Outdated
@SashaRX
SashaRX force-pushed the fix/hdr6x6i-3d-sgd-descriptor-index branch from 9907965 to 96a144e Compare August 6, 2026 13:17
@SashaRX

SashaRX commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Done — switched the accumulation to the same 1..numLevels loop form transcodeEtc1s uses and applied your comment wording. Both regression tests still pass.

Comment thread lib/src/basis_transcode.cpp Outdated
The encoder writes the ktxUASTCHDR6x6IntermediateImageDesc table in
level order, level 0 first, with each level contributing
numLayers * numFaces * depth(level) image descriptions. The transcoder
indexed it with level * levelImageCount + image using the current
level's image count, which only matches that layout while every level
has the same image count. For 3D textures depth halves with each level,
so wrong descriptions - whose slice offsets are relative to a different
level's data - were used and transcoding failed (or could read wrong
slices). Accumulate per-level first-image indices instead, as
transcodeEtc1s already does with its firstImages table.

Since firstImages[numLevels] is the image count implied by the
texture's dimensions, also validate the descriptor table size up
front: a table that is not exactly that many descriptions, or not a
whole number of them, is rejected with KTX_FILE_DATA_ERROR before any
level is processed.

Encoding a 24x24x8 3D texture with 4 mip levels to UASTC HDR 6x6
intermediate and transcoding it to ASTC_HDR_6x6_RGBA failed with
KTX_TRANSCODE_FAILED before this change and succeeds after it; a 2D
texture with identical parameters passes both before and after. Both
cases are added to transcodetests as generated round-trip tests with
no new binary test resources.

Per review, both cumulative tables use the single-increment loop form
(firstImages[l + 1] = firstImages[l] + ...); the ETC1S transcoder's
existing table is restyled to match.
@SashaRX
SashaRX force-pushed the fix/hdr6x6i-3d-sgd-descriptor-index branch from 96a144e to 51be9de Compare August 6, 2026 13:53
@MarkCallow
MarkCallow merged commit 2355409 into KhronosGroup:main Aug 7, 2026
41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants