shader: fix spilled mesh shader data loads

This commit is contained in:
nmzik
2026-09-06 16:54:01 +04:00
parent 9c6f6fa624
commit ced73c56cc
2 changed files with 40 additions and 3 deletions
@@ -3,7 +3,7 @@
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
uint32_t EmitShaderDataDwordLoad(EmitterState& state, uint32_t dword_index) { uint32_t EmitShaderDataDwordLoad(EmitterState& state, uint32_t dword_index) {
if (state.push_constant_variable != 0) { if (state.program.bindings.UsesPushData()) {
dword_index += state.program.bindings.push_data_start_dword; dword_index += state.program.bindings.push_data_start_dword;
const auto pointer = state.builder.AllocateId(); const auto pointer = state.builder.AllocateId();
const auto value = state.builder.AllocateId(); const auto value = state.builder.AllocateId();
+39 -2
View File
@@ -9151,6 +9151,7 @@ void TestFusedShaderHandoffPreservesRegisters() {
} }
void TestMeshExportStorage() { void TestMeshExportStorage() {
using ShaderRecompiler::IR::PushData;
const uint32_t front[] = { const uint32_t front[] = {
EncodeSMovB32(12, 255), 0x1003u, EncodeSMovB32(12, 255), 0x1003u,
EncodeSop1(0x20, 0, 6), // merged-stage handoff EncodeSop1(0x20, 0, 6), // merged-stage handoff
@@ -9158,6 +9159,8 @@ void TestMeshExportStorage() {
const uint32_t back[] = { const uint32_t back[] = {
EncodeSMovB32(124, 12), EncodeSopp(0x10, 9), // GS allocation EncodeSMovB32(124, 12), EncodeSopp(0x10, 9), // GS allocation
EncodeVop1(0x01, 0, 128), EncodeVop1(0x01, 0, 128),
EncodeVop1(0x01, 9, 13), // retain user s13
EncodeMubuf0(0x1c), EncodeMubuf1(9, 2, 5), // store using s[8:11] and vertex ID
EncodeDs0(0x0d), EncodeDs1(0, 0, 0), // retain guest LDS EncodeDs0(0x0d), EncodeDs1(0, 0, 0), // retain guest LDS
EncodeExp0(0x0c, 0xf, false), EncodeExp1(0, 0, 0, 0), EncodeExp0(0x0c, 0xf, false), EncodeExp1(0, 0, 0, 0),
EncodeExp0(0x20, 0xf, false), EncodeExp1(0, 0, 0, 0), EncodeExp0(0x20, 0xf, false), EncodeExp1(0, 0, 0, 0),
@@ -9177,14 +9180,48 @@ void TestMeshExportStorage() {
mesh.vertices_per_group = 64; mesh.vertices_per_group = 64;
mesh.max_vertices = 192; mesh.max_vertices = 192;
mesh.max_primitives = 176; mesh.max_primitives = 176;
std::array<uint32_t, 14> user_data{};
user_data[8] = 0x10000008u;
user_data[9] = 4u << 16u;
user_data[10] = 64u;
user_data[11] = 0x00027000u;
user_data[13] = 0x3f800000u;
ShaderRecompiler::CompileOptions options{}; ShaderRecompiler::CompileOptions options{};
options.stage = ShaderType::Mesh; options.stage = ShaderType::Mesh;
options.input_info.vertex = &input; options.input_info.vertex = &input;
options.back_code = back; options.back_code = back;
for (const uint32_t subgroup_size : {32u, 64u}) { options.user_data = user_data;
for (const auto [subgroup_size, push_data_start] :
{std::pair{32u, PushData::MeshDrawDwordCount},
std::pair{64u, PushData::MeshDrawDwordCount},
std::pair{32u, PushData::DwordCount},
std::pair{64u, PushData::DwordCount}}) {
mesh.host_subgroup_size = subgroup_size; mesh.host_subgroup_size = subgroup_size;
const auto result = RecompileForTest(front, options); const auto result =
RecompileForTest(front, options, nullptr, nullptr, push_data_start);
CheckSpirvBinaryValidates(result.spirv); CheckSpirvBinaryValidates(result.spirv);
const auto &layout = result.program.bindings;
Check(layout.UsesPushData() == (push_data_start == PushData::MeshDrawDwordCount) &&
layout.memory_offset_count == 1,
"mesh shader did not retain its user-data placement and buffer offset");
const auto reg = std::ranges::find(layout.user_data_registers, 13u);
Check(reg != layout.user_data_registers.end(), "mesh shader lost user s13");
const auto source = DisassembleSpirvBinary(result.spirv);
for (const auto dword :
{static_cast<uint32_t>(reg - layout.user_data_registers.begin()),
layout.memory_offset_dword}) {
const auto operand =
std::string(layout.UsesPushData() ? "vsharp" : "shader_data") +
" %uint_0 %uint_" +
std::to_string(dword + (layout.UsesPushData() ? push_data_start : 0u));
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", operand.c_str()),
"mesh user SGPR or buffer offset loaded from the wrong storage");
}
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain",
"vsharp %uint_0 %uint_0") &&
!SpirvSourceHasInstructionUsing(source, "OpAccessChain",
"vsharp %uint_0 %uint_4294967295"),
"mesh draw prefix was lost or spilled shader data wrapped into push constants");
const auto &binary = result.spirv; const auto &binary = result.spirv;
std::vector<uint32_t> sizes(binary[3]), constants(binary[3]); std::vector<uint32_t> sizes(binary[3]), constants(binary[3]);
uint32_t shared_bytes = 0, private_bytes = 0; uint32_t shared_bytes = 0, private_bytes = 0;