From 034b9cab96be5231daf2eb50f694f950d2d2a836 Mon Sep 17 00:00:00 2001 From: Takoroka <137927095+Takoroka@users.noreply.github.com> Date: Wed, 28 Oct 2026 05:14:02 -0400 Subject: [PATCH] nx2: decode ASTC textures with the Switch 2 GOB layout The GPU ASTC decoder unswizzles block-linear data itself and never applied the native (Switch 2) GOB swizzle that the other unswizzle paths use, so ASTC textures came out with 16-byte blocks swapped wherever the image varies. Title 4's UI is ASTC (10x5, 4x4, 8x8): portraits, buttons and the squid cursor had hatched edges and clean interiors. Pass the native tiling flag to the decoder and swap offset bits 5 and 6 as block_linear_unswizzle_2d.comp does. --- src/video_core/host_shaders/astc_decoder.comp | 8 +++++++- src/video_core/renderer_opengl/util_shaders.cpp | 1 + src/video_core/renderer_vulkan/vk_compute_pass.cpp | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp index e059c683ff..01bec11f17 100644 --- a/src/video_core/host_shaders/astc_decoder.comp +++ b/src/video_core/host_shaders/astc_decoder.comp @@ -33,6 +33,7 @@ UNIFORM(3) uint block_size; UNIFORM(4) uint x_shift; UNIFORM(5) uint block_height; UNIFORM(6) uint block_height_mask; +UNIFORM(7) uint native_tiling; END_PUSH_CONSTANTS struct EncodingData { @@ -1404,7 +1405,12 @@ uint SwizzleOffset(uvec2 pos) { void main() { uvec3 pos = gl_GlobalInvocationID; pos.x <<= BYTES_PER_BLOCK_LOG2; - const uint swizzle = SwizzleOffset(pos.xy); + uint swizzle = SwizzleOffset(pos.xy); + if (native_tiling != 0u) { + // Switch 2 GOBs swap offset bits 5 and 6 (see block_linear_unswizzle_2d.comp). + const uint difference = ((swizzle >> 5u) ^ (swizzle >> 6u)) & 1u; + swizzle ^= (difference << 5u) | (difference << 6u); + } const uint block_y = pos.y >> GOB_SIZE_Y_SHIFT; uint offset = 0; diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp index 7ced8c77b4..5ed96a2037 100644 --- a/src/video_core/renderer_opengl/util_shaders.cpp +++ b/src/video_core/renderer_opengl/util_shaders.cpp @@ -96,6 +96,7 @@ void UtilShaders::ASTCDecode(Image& image, const StagingBufferMap& map, glUniform1ui(4, params.x_shift); glUniform1ui(5, params.block_height); glUniform1ui(6, params.block_height_mask); + glUniform1ui(7, params.native_tiling); // ASTC texture data glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset, diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index ab58560170..2eac00103e 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -183,6 +183,7 @@ struct AstcPushConstants { u32 x_shift; u32 block_height; u32 block_height_mask; + u32 native_tiling; }; struct QueriesPrefixScanPushConstants { @@ -583,6 +584,7 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, .x_shift = params.x_shift, .block_height = params.block_height, .block_height_mask = params.block_height_mask, + .native_tiling = params.native_tiling, }; const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);