mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
shader: lower HS neighbour LDS reads, control-point output read-back, looped HS reflection and integer partitioning
Co-authored-by: NeySlim <4509984+NeySlim@users.noreply.github.com>
This commit is contained in:
@@ -43885,7 +43885,8 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
|
||||
|
||||
// PPSA25468: CP0 writes patch constants below the 32 KiB offchip ring end
|
||||
// through mul24 addresses that reuse data VGPRs under nested EXEC masks, and
|
||||
// the TES reads them back.
|
||||
// the TES reads them back. As in God of War Ragnarok, the HS reads its next LS
|
||||
// record, loops on a v_cmpx header and CP0 reads back another CP's outputs.
|
||||
void CheckTessellationPatchConstants() {
|
||||
using namespace ShaderRecompiler;
|
||||
using Attribute = IR::TessellationAttribute;
|
||||
@@ -43899,7 +43900,7 @@ void CheckTessellationPatchConstants() {
|
||||
local.push_back(EncodeDs1(0, 2, 20));
|
||||
local.push_back(EncodeSop1(0x20, 0, 6));
|
||||
|
||||
const auto make_control = [&](bool divergent) {
|
||||
const auto make_control = [&](bool divergent, bool carried) {
|
||||
std::vector<u32> control;
|
||||
AppendVop3(&control, 0x365, 0, 193u, InlineU32(0));
|
||||
AppendVop3(&control, 0x366, 0, 193u, Vgpr(0));
|
||||
@@ -43912,7 +43913,10 @@ void CheckTessellationPatchConstants() {
|
||||
control.push_back(0xffu);
|
||||
control.push_back(EncodeVop2(0x0b, 0, 255u, 16)); // v_mul_u32_u24 v0, hs_stride, v16
|
||||
control.push_back(hs_stride);
|
||||
AppendVop3(&control, 0x169, 1, Vgpr(16), 255u); // v_mul_lo_u32 v1, v16, ls_stride
|
||||
control.push_back(EncodeVopc(0xc4, InlineU32(2), 16)); // v_cmp_gt_u32 vcc, 2, v16
|
||||
control.push_back(EncodeVop2(0x25, 1, InlineU32(1), 16)); // v_add_nc_u32 v1, 1, v16
|
||||
control.push_back(EncodeVop2(0x01, 1, InlineU32(0), 1)); // v_cndmask_b32 v1, 0, v1, vcc
|
||||
AppendVop3(&control, 0x169, 1, Vgpr(1), 255u); // v_mul_lo_u32 v1, v1, ls_stride
|
||||
control.push_back(ls_stride);
|
||||
AppendVop3(&control, 0x143, 17, 255u, Vgpr(50), Vgpr(0));
|
||||
control.push_back(3 * hs_stride);
|
||||
@@ -43932,12 +43936,33 @@ void CheckTessellationPatchConstants() {
|
||||
control.push_back(EncodeVop2(0x25, 17, InlineU32(4), 17)); // v_add_nc_u32 v17, 4, v17
|
||||
}
|
||||
control[skip_scale] = EncodeSopp(0x06, control.size() - skip_scale - 1u);
|
||||
control.push_back(EncodeSop1(0x04, 6, 126)); // s_mov_b64 s[6:7], exec
|
||||
control.push_back(EncodeSMovB32(5, InlineU32(0))); // s_mov_b32 s5, 0
|
||||
const auto loop = control.size();
|
||||
control.push_back(EncodeVopc(0xd1, 5, 16)); // v_cmpx_lt_u32 s5, v16
|
||||
const auto exit_loop = control.size();
|
||||
control.push_back(0u); // s_cbranch_execz
|
||||
control.push_back(EncodeMubuf0(0x1c, 24));
|
||||
control.push_back(EncodeMubuf1(12, 2, 17, 2));
|
||||
// Only the back edge shows a loop-carried v17 at the store above.
|
||||
if (carried) {
|
||||
control.push_back(EncodeVop2(0x25, 17, InlineU32(4), 17)); // v_add_nc_u32 v17, 4, v17
|
||||
}
|
||||
control.push_back(EncodeVop2(0x25, 13, InlineU32(1), 13)); // v_add_nc_u32 v13, 1, v13
|
||||
control.push_back(EncodeSop2(0x00, 5, 5, InlineU32(1))); // s_add_u32 s5, s5, 1
|
||||
control.push_back(EncodeSopp(0x02, loop - control.size() - 1u)); // s_branch
|
||||
control[exit_loop] = EncodeSopp(0x08, control.size() - exit_loop - 1u);
|
||||
control.push_back(EncodeSop1(0x04, 126, 6)); // s_mov_b64 exec, s[6:7]
|
||||
control.push_back(EncodeSopp(0x0a)); // s_barrier
|
||||
// CP0 reuses data VGPR v10 for the ring address 0x8000 - (patch + 1) * 0xf0.
|
||||
control.push_back(EncodeVopc(0xd4, InlineU32(1), 16)); // v_cmpx_gt_u32 1, v16
|
||||
const auto skip_patch = control.size();
|
||||
control.push_back(0u); // s_cbranch_execz
|
||||
control.push_back(EncodeVop2(0x0b, 11, 255u, 50)); // v_mul_u32_u24 v11, 3 * hs_stride, v50
|
||||
control.push_back(3 * hs_stride);
|
||||
// buffer_load_dwordx2 v[11:12], v11, s[8:11], s2 offen offset:hs_stride + 16 (CP1)
|
||||
control.push_back(EncodeMubuf0(0x0d, hs_stride + 16));
|
||||
control.push_back(EncodeMubuf1(11, 2, 11, 2));
|
||||
control.push_back(EncodeVop2(0x25, 10, InlineU32(1), 50)); // v_add_nc_u32 v10, 1, v50
|
||||
control.push_back(EncodeVop2(0x0b, 10, 255u, 10)); // v_mul_u32_u24 v10, 0xf0, v10
|
||||
control.push_back(patch_stride);
|
||||
@@ -43959,7 +43984,7 @@ void CheckTessellationPatchConstants() {
|
||||
AppendEnd(&control);
|
||||
return control;
|
||||
};
|
||||
const auto control = make_control(false);
|
||||
const auto control = make_control(false, false);
|
||||
|
||||
std::vector<u32> evaluation;
|
||||
AppendVop3(&evaluation, 0x169, 24, 255u, Vgpr(7));
|
||||
@@ -43982,13 +44007,14 @@ void CheckTessellationPatchConstants() {
|
||||
ShaderTessellationInputInfo tess{.input_control_points = 3,
|
||||
.output_control_points = 3,
|
||||
.domain = 1,
|
||||
.partitioning = 2,
|
||||
.partitioning = 0,
|
||||
.output_topology = 2};
|
||||
AnalyzeTessellationPrograms(local, control, tess);
|
||||
Require(name, "reflected interface",
|
||||
tess.ls_stride == ls_stride && tess.hs_stride == hs_stride &&
|
||||
tess.patch_base == 0x7f10u && tess.patch_size == 28u,
|
||||
"b128 LS records, joined HS paths and CP0 patch stores must reflect the layout");
|
||||
"b128 LS records, selected LS reads, joined and looping HS paths and CP0 patch "
|
||||
"stores must reflect the layout");
|
||||
|
||||
auto ls = CompileTessellationStage(name, local, ShaderType::Local, tess);
|
||||
auto hs = CompileTessellationStage(name, control, ShaderType::TessellationControl, tess);
|
||||
@@ -43999,6 +44025,10 @@ void CheckTessellationPatchConstants() {
|
||||
Require(name, "predicated patch outputs",
|
||||
hs.addresses[Attribute::PatchOutput] == std::set<u32>{0x7f10, 0x7f20, 0x7f24, 0x7f28},
|
||||
"CP0 mul24 addresses under nested EXEC masks must reach patch constants");
|
||||
Require(name, "control point read-back",
|
||||
hs.addresses[Attribute::ControlOutput] == std::set<u32>{hs_stride + 16, hs_stride + 20} &&
|
||||
hs.assembly.find("OpUDiv %uint %uint_96 %uint_80") != std::string::npos,
|
||||
"CP0 must read CP1's outputs by address instead of its own invocation's");
|
||||
Require(name, "patch inputs",
|
||||
ds.addresses[Attribute::PatchInput] == std::set<u32>{0x7f20, 0x7f24} &&
|
||||
ds.addresses[Attribute::EvaluationInput] ==
|
||||
@@ -44008,12 +44038,16 @@ void CheckTessellationPatchConstants() {
|
||||
HasPatchInterface(hs.assembly, "Output", 2, 5) &&
|
||||
HasPatchInterface(ds.assembly, "Input", 2, 5),
|
||||
"HS and TES patch variables must share their Location and type");
|
||||
Require(name, "integer partitioning", ds.assembly.find("SpacingEqual") != std::string::npos,
|
||||
"integer partitioning must map to equal spacing");
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
ExpectFatal("TessellationDivergentJoin", [&] {
|
||||
ShaderTessellationInputInfo info{.input_control_points = 3, .output_control_points = 3};
|
||||
AnalyzeTessellationPrograms(local, make_control(true), info);
|
||||
});
|
||||
for (const bool carried : {false, true}) {
|
||||
ExpectFatal(carried ? "TessellationLoopCarriedAddress" : "TessellationDivergentJoin", [&] {
|
||||
ShaderTessellationInputInfo info{.input_control_points = 3, .output_control_points = 3};
|
||||
AnalyzeTessellationPrograms(local, make_control(!carried, carried), info);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user