From 968cf3c03ffb90ec83f8e493604170325e253564 Mon Sep 17 00:00:00 2001 From: nmzik Date: Mon, 14 Sep 2026 13:38:10 +0400 Subject: [PATCH] Use the correct pixel shader wave size --- .../renderer/pipeline/pipelineCache.cpp | 2 +- src/graphics/shader/shader.cpp | 7 +- src/graphics/shader/shader.h | 1 + tests/ShaderRecompilerComputeTests.cpp | 99 ++++++++++++++++++- 4 files changed, 104 insertions(+), 5 deletions(-) diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index 07cabafe8..23502f401 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -351,7 +351,7 @@ struct PipelineCache::ProgramCache { options.user_data_base = 0; options.wave_size = stage == ShaderType::Mesh ? input_info.mesh.wave_size : 64u; } - } else if constexpr (std::is_same_v) { + } else { options.wave_size = input_info.wave_size; } auto translated = ShaderRecompiler::TranslateProgram(params.code, options); diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 520a06a43..712522baf 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -573,9 +573,11 @@ static void ShaderGetStaticInputInfoPS( ps_info = {}; ps_info.scratch_size_dwords = data.scratch_size_dwords; - // SPI_PS_IN_CONTROL.NUM_INTERP occupies bits 5:0. Keep the remaining control - // flags in the hardware state and extract only the input count here. + // SPI_PS_IN_CONTROL: NUM_INTERP occupies bits 5:0 and PS_W32_EN is bit 15. ps_info.input_num = sh.ps_in_control & 0x3fu; + if ((sh.ps_in_control & 0x8000u) != 0) { + ps_info.wave_size = 32; + } EXIT_NOT_IMPLEMENTED(ps_info.input_num > std::size(ps_info.interpolator_settings)); ps_info.ps_system_input_base = ShaderCalcPsSystemInputBase(sh); const uint32_t active_inputs = sh.ps_input_ena & sh.ps_input_addr; @@ -701,6 +703,7 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector key.clear(); key.push_back(info.scratch_size_dwords); key.push_back(info.input_num); + key.push_back(info.wave_size); key.push_back(info.ps_system_input_base); key.push_back(info.custom_interpolation_mask); key.push_back(info.ps_perspective_center_vgpr); diff --git a/src/graphics/shader/shader.h b/src/graphics/shader/shader.h index cac8a3deb..ace2dfaff 100644 --- a/src/graphics/shader/shader.h +++ b/src/graphics/shader/shader.h @@ -153,6 +153,7 @@ struct ShaderComputeInputInfo: ShaderWorkgroupInputInfo { struct ShaderPixelInputInfo { uint32_t interpolator_settings[32] = {0}; uint32_t input_num = 0; + uint32_t wave_size = 64; uint32_t ps_system_input_base = 0; uint32_t custom_interpolation_mask = 0; uint32_t ps_perspective_center_vgpr = UINT32_MAX; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 545ce09bf..c20a4c5a2 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -940,6 +940,7 @@ void EnsureConfigInitialized() { Config::Load(options); subsystems.Initialize(); subsystems.Initialize(); + ShaderInit(); config_initialized = true; } } @@ -12182,8 +12183,8 @@ public: AppendEnd(&test.fragment_code); auto fragment = CompileFragmentCase(test); const auto vertex_spirv = TestSpv::MakePassthroughVertexSpirv(false); - const ShaderProgram vertex_shader{1, CreateShaderModule(name, vertex_spirv)}; - const ShaderProgram pixel_shader{2, CreateShaderModule(name, fragment.spirv)}; + ShaderProgram vertex_shader{1, CreateShaderModule(name, vertex_spirv)}; + ShaderProgram pixel_shader{2, CreateShaderModule(name, fragment.spirv)}; ShaderRecompiler::IR::CompiledShaderInfo vertex_program{}; vertex_program.stage = ShaderType::Vertex; vertex_program.info.vertex_fetch_components[0] = 2; @@ -12497,6 +12498,100 @@ public: check_stencil("masked compare fails", false, 0xb0, 0xb0); DestroyBuffer(&stencil_readback); + + // SPI_PS_IN_CONTROL must select the native wave width through the actual + // program cache. In wave32 a low-word compare preserves scalar VCC_HI. + static const auto native_vertex = [] { + std::vector code; + AppendVMovLiteral(&code, 1, 0xbf800000u); + AppendVMovLiteral(&code, 2, 0x40400000u); + code.push_back(EncodeVopc(0xc2, InlineU32(1), 5)); + code.push_back(EncodeVop2(0x01, 3, Vgpr(1), 2)); + code.push_back(EncodeVopc(0xc2, InlineU32(2), 5)); + code.push_back(EncodeVop2(0x01, 4, Vgpr(1), 2)); + AppendVMovU32(&code, 0, 0); + AppendVMovLiteral(&code, 6, 0x3f800000u); + code.push_back(EncodeExp0(0x0c, 0xf)); + code.push_back(EncodeExp1(3, 4, 0, 6)); + for (u32 parameter = 0; parameter < 8; parameter++) { + code.push_back(EncodeExp0(0x20 + parameter, 0xf)); + code.push_back(EncodeExp1(0, 0, 0, 0)); + } + AppendEnd(&code); + return code; + }(); + static const auto native_pixel = [] { + std::vector code; + AppendVMovU32(&code, 0, 0); + AppendSMovLiteral(&code, 107, 0x3e800000u); + code.push_back(EncodeVopc(0xc2, InlineU32(1), 0)); + code.push_back(EncodeVop1(0x01, 1, 107)); + AppendVMovLiteral(&code, 2, 0x3f800000u); + code.push_back(EncodeExp0(0x00, 0xf)); + code.push_back(EncodeExp1(1, 1, 1, 2)); + AppendEnd(&code); + return code; + }(); + static ShaderUserData native_user_data{}; + const auto vertex_address = reinterpret_cast(native_vertex.data()); + const auto pixel_address = reinterpret_cast(native_pixel.data()); + ShaderMapUserData(vertex_address, + {.type = Prospero::ShaderBinaryType::kGs, + .user_data = &native_user_data, + .code_size_bytes = static_cast(native_vertex.size() * sizeof(u32))}); + ShaderMapUserData(pixel_address, + {.type = Prospero::ShaderBinaryType::kPs, + .code_size_bytes = static_cast(native_pixel.size() * sizeof(u32))}); + HW::VertexShaderInfo native_vertex_regs{}; + native_vertex_regs.es_regs.data_addr = vertex_address; + HW::PixelShaderInfo native_pixel_regs{}; + native_pixel_regs.ps_regs.data_addr = pixel_address; + const auto owned_vertex_shader = vertex_shader; + const auto owned_pixel_shader = pixel_shader; + depth.stencil_test_enable = false; + registers.SetViewportTransformControl(0x300); + user_config.SetPrimitiveType(Prospero::PrimitiveType::kTriList); + std::array export_mapping{}; + std::array native_vertex_info{}; + std::array wave_ids{}; + std::array, 2> wave_keys; + struct PixelWaveCase { uint32_t control, width, color; }; + for (const auto wave : {PixelWaveCase{0x0008, 64, 0}, + PixelWaveCase{0x8008, 32, 0x3e800000}, + PixelWaveCase{0x0008, 64, 0}}) { + registers.SetPsInControl(wave.control); + const auto programs = context.GetPipelineCache().GetGraphicsPrograms( + native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(), + registers, user_config, export_mapping, true, native_vertex_info, pixel); + Require(name, "pixel wave metadata propagation", + pixel.input_num == 8 && pixel.stage.program->wave_size == wave.width, + "SPI_PS_IN_CONTROL width did not reach the compiled pixel program"); + const auto index = static_cast(wave.width == 32); + std::vector key; + BuildStageStaticKey(pixel, key); + if (wave_ids[index] != 0) { + Require(name, "pixel wave cache reuse", + programs.pixel.id == wave_ids[index] && key == wave_keys[index], + "restoring wave64 did not reuse its original program"); + } + wave_ids[index] = programs.pixel.id; + wave_keys[index] = std::move(key); + vertex_shader = programs.vertex[0]; + pixel_shader = programs.pixel; + vertex = native_vertex_info[0]; + draw(pipeline(true, 2, 2)); + const auto wave_pixels = read_color(); + for (size_t component = 0; component < wave_pixels.size(); component++) { + const auto expected = component % 4 == 3 ? 0x3f800000u : wave.color; + Require(name, "pixel wave VCC_HI readback", wave_pixels[component] == expected, + "pixel comparison clobbered VCC_HI in wave32 or retained it in wave64"); + } + } + Require(name, "pixel wave cache distinction", + wave_ids[0] != wave_ids[1] && wave_keys[0] != wave_keys[1], + "wave32 and wave64 pixel programs shared a cache key"); + vertex_shader = owned_vertex_shader; + pixel_shader = owned_pixel_shader; } resources.UnmapMemory(depth_address, allocation_size); scheduler.Finish();