diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp index 3c088f05c..f77b4a3d0 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp @@ -262,8 +262,12 @@ 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 ? GLSLstd450UnpackUnorm2x16 : GLSLstd450UnpackHalf2x16; + auto unpack = GLSLstd450UnpackHalf2x16; + switch (MrtOutputMode(state, exp)) { + case 5: unpack = GLSLstd450UnpackUnorm2x16; break; + case 6: unpack = GLSLstd450UnpackSnorm2x16; break; + default: break; + } uint32_t f32[4] = {ConstantF32(state, 0), ConstantF32(state, 0), ConstantF32(state, 0), ConstantF32(state, 0x3f800000u)}; for (uint32_t pair = 0; pair < 2u; pair++) { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index e2e2af234..bd0a1d726 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -16470,6 +16470,35 @@ public: } } + static const auto snorm_pixel = [] { + std::vector code; + AppendVMovLiteral(&code, 0, 0xc0008000u); + AppendVMovLiteral(&code, 1, 0x7fff4000u); + code.insert(code.end(), {EncodeExp0(0, 0xf, true, true), EncodeExp1(0, 1, 0, 0)}); + AppendEnd(&code); + return code; + }(); + const auto snorm_address = reinterpret_cast(snorm_pixel.data()); + ShaderMapUserData(snorm_address, + {.type = Prospero::ShaderBinaryType::kPs, + .code_size_bytes = static_cast(snorm_pixel.size() * sizeof(u32))}); + shaders.SetPsShaderBase(snorm_address); + registers.SetShaderMask(0xf000); + registers.SetTargetOutputMode(0, 6); + registers.SetTargetOutputMode(1, 0); + RenderExecutorTestAccess::DrawAuto( + executor, scheduler.Current(), {.vertex_count = 3, .instance_count = 1}); + const auto snorm_pixels = ReadCachedTexel(name, context, sparse_color.image_id, + {}, {extent, extent, 1}); + constexpr std::array snorm_expected{-1, -16384.0f / 32767, + 16384.0f / 32767, 1}; + for (size_t component = 0; component < snorm_pixels.size(); component++) { + Require(name, "compressed SNORM16 color export", + std::abs(std::bit_cast(snorm_pixels[component]) - + snorm_expected[component % 4]) < 0.00001f, + "SNORM16 export lost its sign, normalization or negative endpoint clamp"); + } + registers.SetTargetOutputMode(0, 4); // A fourth VS invocation reads beyond the descriptor instead of reconstructing the corner. const ShaderBufferResource rect_buffer{{