mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: initialize NGG vertex count from active native invocations
This commit is contained in:
@@ -1163,9 +1163,11 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
}
|
||||
entry_ir.SetExec(initial_exec);
|
||||
const auto initial_mask = entry_ir.Emit(IR::ValueOpcode::Ballot, {initial_exec});
|
||||
entry_ir.SetExecLo(entry_ir.CompositeExtract(initial_mask, 0));
|
||||
entry_ir.SetExecHi(options.wave_size == 64u ? entry_ir.CompositeExtract(initial_mask, 1)
|
||||
: IR::U32(IR::Value(0u)));
|
||||
const auto initial_lo = entry_ir.CompositeExtract(initial_mask, 0);
|
||||
const auto initial_hi = options.wave_size == 64u ? entry_ir.CompositeExtract(initial_mask, 1)
|
||||
: IR::U32(IR::Value(0u));
|
||||
entry_ir.SetExecLo(initial_lo);
|
||||
entry_ir.SetExecHi(initial_hi);
|
||||
if (options.stage == ShaderType::Compute) {
|
||||
const auto* cs = options.input_info.compute;
|
||||
const auto thread_ids =
|
||||
@@ -1381,11 +1383,16 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
}
|
||||
} else if (options.stage == ShaderType::Vertex) {
|
||||
// Vulkan owns primitive assembly; each vertex subgroup is one NGG wave.
|
||||
// Keep its full lane extent: mbcnt(-1) uses lane ordinals, not active counts.
|
||||
// ESVertCount describes input vertices, independently of lane ordinals.
|
||||
auto vertex_count = IR::U32(entry_ir.Emit(IR::ValueOpcode::BitCount32, {initial_lo}));
|
||||
if (options.wave_size == 64u) {
|
||||
vertex_count = entry_ir.IAdd(vertex_count, IR::U32(entry_ir.Emit(
|
||||
IR::ValueOpcode::BitCount32, {initial_hi})));
|
||||
}
|
||||
entry_ir.SetScalarReg(static_cast<IR::ScalarReg>(2),
|
||||
IR::U32(IR::Value(options.wave_size << 12u)));
|
||||
entry_ir.SetScalarReg(static_cast<IR::ScalarReg>(3),
|
||||
IR::U32(IR::Value((1u << 28u) | options.wave_size)));
|
||||
entry_ir.BitwiseOr(vertex_count, IR::U32(IR::Value(1u << 28u))));
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(5),
|
||||
builtin(IR::StageInputKind::VertexIndex));
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(8),
|
||||
|
||||
@@ -16439,6 +16439,60 @@ public:
|
||||
wave_ids[0] != wave_ids[1] && wave_keys[0] != wave_keys[1],
|
||||
"wave32 and wave64 pixel programs shared a cache key");
|
||||
|
||||
static const auto waterfall_vertex = [&] {
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 9, 0);
|
||||
// d3e1ae284c4dea3b reconstructs EXEC twice from packed NGG s3.
|
||||
code.insert(code.end(), {0xbefe04c1u, 0x906a8803u, 0x81ea6a80u,
|
||||
0x90fe6ac1u, 0x81ea0380u, 0x90fe6ac1u,
|
||||
EncodeSop1(0x04, 10, 126)});
|
||||
const auto loop = code.size();
|
||||
code.push_back(EncodeSop1(0x14, 4, 10));
|
||||
AppendVop3(&code, 0x360, 6, Vgpr(5), 4);
|
||||
code.push_back(EncodeVopc(0xc2, 6, 5));
|
||||
code.push_back(EncodeSop1(0x24, 18, 106));
|
||||
AppendVMovLiteral(&code, 9, 0x3f800000u);
|
||||
code.push_back(EncodeSop2(0x15, 10, 10, 106));
|
||||
code.push_back(EncodeSop1(0x04, 126, 18));
|
||||
code.push_back(EncodeSopp(0x05, static_cast<int16_t>(loop - code.size() - 1u)));
|
||||
const auto base = code.size();
|
||||
code.insert(code.end(), native_vertex.begin(), native_vertex.end());
|
||||
const auto position = std::find(code.begin() + base, code.end(), EncodeExp0(0x0c, 0xf));
|
||||
// Unprocessed vertices keep W=0, making waterfall progress observable.
|
||||
*(position + 1) = EncodeExp1(3, 4, 0, 9);
|
||||
return code;
|
||||
}();
|
||||
native_vertex_regs.es_regs.data_addr = reinterpret_cast<uint64_t>(waterfall_vertex.data());
|
||||
ShaderMapUserData(native_vertex_regs.es_regs.data_addr,
|
||||
{.type = Prospero::ShaderBinaryType::kGs, .user_data = &native_user_data,
|
||||
.code_size_bytes = static_cast<u32>(waterfall_vertex.size() * sizeof(u32))});
|
||||
registers.SetPsInControl(0x8008);
|
||||
const auto saved_stages = registers.GetShaderStages();
|
||||
for (const u32 wave : {32u, 64u}) {
|
||||
registers.SetShaderStages((saved_stages & ~0x00400000u) |
|
||||
(wave == 32u ? 0x00400000u : 0u));
|
||||
const auto programs = context.GetPipelineCache().GetGraphicsPrograms(
|
||||
native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(),
|
||||
registers, user_config, export_mapping, true, native_vertex_info, pixel);
|
||||
vertex_shader = programs.vertex[0];
|
||||
pixel_shader = programs.pixel;
|
||||
vertex = native_vertex_info[0];
|
||||
Require(name, "native NGG waterfall wave",
|
||||
vertex.stage.program->stage == ShaderType::Vertex &&
|
||||
vertex.stage.program->wave_size == wave,
|
||||
"NGG waterfall bypassed the native vertex path or lost its guest wave width");
|
||||
for (const u32 count : {3u, 6u, 33u}) {
|
||||
draw(pipeline(true, 2, 2), count);
|
||||
const auto pixels = read_color();
|
||||
for (size_t component = 0; component < pixels.size(); ++component)
|
||||
Require(name, "NGG partial-wave waterfall output",
|
||||
pixels[component] == (component % 4 == 3 ? 0x3f800000u : 0x3e800000u),
|
||||
"native vertex waterfall did not terminate with every input vertex processed");
|
||||
}
|
||||
}
|
||||
registers.SetShaderStages(saved_stages);
|
||||
native_vertex_regs.es_regs.data_addr = vertex_address;
|
||||
|
||||
// Reversed RGBA targets must use logical Sa for RGB, with a separate
|
||||
// zero or unit source factor for separate alpha attenuation or accumulation.
|
||||
static const auto blend_pixel = [] {
|
||||
|
||||
Reference in New Issue
Block a user