Use the correct pixel shader wave size

This commit is contained in:
nmzik
2026-09-14 13:38:10 +04:00
parent 586cbd4a0d
commit 968cf3c03f
4 changed files with 104 additions and 5 deletions
@@ -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<InputInfo, ShaderComputeInputInfo>) {
} else {
options.wave_size = input_info.wave_size;
}
auto translated = ShaderRecompiler::TranslateProgram(params.code, options);
+5 -2
View File
@@ -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<uint32_t>
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);
+1
View File
@@ -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;
+97 -2
View File
@@ -940,6 +940,7 @@ void EnsureConfigInitialized() {
Config::Load(options);
subsystems.Initialize<Log::Lifecycle>();
subsystems.Initialize<Libs::LibKernel::Memory::Lifecycle>();
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<u32> 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<u32> 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<uint64_t>(native_vertex.data());
const auto pixel_address = reinterpret_cast<uint64_t>(native_pixel.data());
ShaderMapUserData(vertex_address,
{.type = Prospero::ShaderBinaryType::kGs,
.user_data = &native_user_data,
.code_size_bytes = static_cast<uint32_t>(native_vertex.size() * sizeof(u32))});
ShaderMapUserData(pixel_address,
{.type = Prospero::ShaderBinaryType::kPs,
.code_size_bytes = static_cast<uint32_t>(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<Prospero::ColorComponentMapping, 8> export_mapping{};
std::array<ShaderVertexInputInfo, 3> native_vertex_info{};
std::array<uint64_t, 2> wave_ids{};
std::array<std::vector<u32>, 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<uint32_t>(wave.width == 32);
std::vector<u32> 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();