From 79443aa104be5de16c367f41f7d77f99d1cee55a Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 21 Aug 2026 05:23:09 +0200 Subject: [PATCH] shader: decode compressed UNORM16 MRT exports --- .../backend/spirv/spirvEmitterFlow.cpp | 18 ++++++++------ .../backend/spirv/spirvEmitterInternal.h | 1 + tests/shaderCfgTests.cpp | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp index 204637e7e..12bb72964 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp @@ -203,10 +203,12 @@ uint32_t EmitAttribute(ValueEmitContext& ctx, uint32_t attr, uint32_t chan) { return bits; } -bool MrtUsesUint(const EmitterState& state, const IR::ExportInfo& exp) { - return state.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt && - exp.index < std::size(state.input_info.pixel->target_output_mode) && - state.input_info.pixel->target_output_mode[exp.index] == 7u; +uint32_t MrtOutputMode(const EmitterState& state, const IR::ExportInfo& exp) { + if (state.stage != ShaderType::Pixel || exp.kind != IR::ExportTargetKind::Mrt || + exp.index >= std::size(state.input_info.pixel->target_output_mode)) { + return 0; + } + return state.input_info.pixel->target_output_mode[exp.index]; } uint32_t ExportRawComponent(ValueEmitContext& ctx, uint32_t vector, uint32_t component) { @@ -220,6 +222,8 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo bool uint_output) { auto& state = ctx.state; if (exp.compr && !uint_output) { + const auto unpack = MrtOutputMode(state, exp) == 5u ? GlslUnpackUnorm2x16 + : GlslUnpackHalf2x16; uint32_t f32[4] = {ConstantF32(state, 0), ConstantF32(state, 0), ConstantF32(state, 0), ConstantF32(state, 0x3f800000u)}; for (uint32_t pair = 0; pair < 2u; pair++) { @@ -229,8 +233,8 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo const auto packed = state.builder.AllocateId(); const auto unpacked = state.builder.AllocateId(); state.builder.AddFunction({OpCompositeExtract, TypeU32(state), packed, data, pair}); - state.builder.AddFunction({OpExtInst, TypeF32Vector(state, 2), unpacked, - GlslStd450(state), GlslUnpackHalf2x16, packed}); + state.builder.AddFunction( + {OpExtInst, TypeF32Vector(state, 2), unpacked, GlslStd450(state), unpack, packed}); for (uint32_t lane = 0; lane < 2u; lane++) { const auto component = pair * 2u + lane; if (((exp.en >> component) & 1u) != 0u) { @@ -332,7 +336,7 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { if (variable == 0) { return; } - const bool uint_output = MrtUsesUint(state, exp); + const bool uint_output = MrtOutputMode(state, exp) == 7u; const auto vector_type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4); auto value = ExportVector(ctx, data, exp, uint_output); if (state.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt && diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h index 6f684ccc3..d6f53eee5 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h @@ -271,6 +271,7 @@ enum : uint32_t { GlslPackSnorm2x16 = 56, GlslPackUnorm2x16 = 57, GlslPackHalf2x16 = 58, + GlslUnpackUnorm2x16 = 61, GlslUnpackHalf2x16 = 62, GlslFindILsb = 73, GlslFindUMsb = 75, diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index ecf97deb5..c51eddee1 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -9851,6 +9851,19 @@ void TestNewShaderRecompilerExpPixelOutputs() { "compressed UINT16 MRT export was incorrectly decoded as FP16"); CheckSpirvBinaryValidates(uint16_result.spirv); + ShaderPixelInputInfo unorm16_info; + unorm16_info.target_output_mode[0] = 5; + options.input_info.pixel = &unorm16_info; + ShaderRecompiler::CompileResult unorm16_result; + Check(ShaderRecompiler::TryRecompile(shader, options, unorm16_result, &error), + error.c_str()); + const auto unorm16_source = DisassembleSpirvBinary(unorm16_result.spirv); + Check(CountSourceOccurrences(unorm16_source, "UnpackUnorm2x16") == 2u, + "compressed UNORM16 MRT export did not unpack two normalized pairs"); + Check(!SpirvContainsExtInst(unorm16_result.spirv, 62), + "compressed UNORM16 MRT export was incorrectly decoded as FP16"); + CheckSpirvBinaryValidates(unorm16_result.spirv); + const uint32_t partial_shader[] = { EncodeExp0(0x00, 0x7), EncodeExp1(0, 1, 2, 3), @@ -9899,6 +9912,17 @@ void TestNewShaderRecompilerExpPixelOutputs() { "OpBitFieldUExtract") == 2u, "compressed UINT16 BA-only export did not read and unpack VSRC1"); CheckSpirvBinaryValidates(compressed_ba_result.spirv); + + unorm16_info.target_output_mode[0] = 5; + options.input_info.pixel = &unorm16_info; + ShaderRecompiler::CompileResult unorm16_ba_result; + Check(ShaderRecompiler::TryRecompile(compressed_ba_shader, options, + unorm16_ba_result, &error), + error.c_str()); + const auto unorm16_ba_source = DisassembleSpirvBinary(unorm16_ba_result.spirv); + Check(CountSourceOccurrences(unorm16_ba_source, "UnpackUnorm2x16") == 1u, + "compressed UNORM16 BA-only export did not unpack VSRC1"); + CheckSpirvBinaryValidates(unorm16_ba_result.spirv); } void TestRenderTargetReverseExportMapping() {