diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-30 14:29:58 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 11:09:52 -0500 |
| commit | 01389ee5266fe5e775ef205ca90122d68d4c46c5 (patch) | |
| tree | 01fc51be5f8a0b84daeab6caf0c40ae3e5a823f5 | |
| parent | e39825807109ca5ac6c2213b03a6bcdab8fc3922 (diff) | |
nx2: mirror uniform registers above UR27 below R216
Uniform registers have GPR mirrors only for UR0-UR27 (R216-R237,
R240-R245). Kirby Air Riders' graphics shaders load a 64-bit base into
UR28:UR29 (ULDC.64 UR28, c[0x0][0x510]) and address global loads with it
(4 programs). When a program uses UR28 or above, lower it again with those
registers mirrored just below R216 (UR28 at R(216 - n), n even so pairs
stay adjacent) and that range kept from the program's own registers;
other programs keep the same layout.
| -rw-r--r-- | src/shader_recompiler/frontend/ampere/lower_to_maxwell.h | 25 |
1 files changed, 21 insertions, 4 deletions
diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h index 0d977ded9d..198769709d 100644 --- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h +++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h @@ -34,6 +34,9 @@ struct LoweredProgram { std::vector<u32> source_instruction_offsets; u32 source_size{}; u32 required_texture_buffer = ~0U; + // UR28 and up seen without a mirror: lowering again with this many registers below R216 + // reserved for them (even, so pairs stay adjacent) maps them. + u32 high_uniform_registers{}; u32 error_offset{}; std::string_view error; @@ -43,8 +46,10 @@ struct LoweredProgram { // Lower only operations whose semantics are representable by the existing // Maxwell frontend. These are new instructions, not reinterpretations of the // native bytes. Scheduling/control words are added by the environment adapter. -inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) { +inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32 high_uniforms) { LoweredProgram result; + // UR28 and up (when used) live just below R216: UR28 at R(216 - high_uniforms). + const u32 high_uniform_base = 216 - high_uniforms; struct Relocation { u32 output_index; s64 target; }; std::vector<Relocation> relocations; std::vector<u32> barrier_tokens; @@ -101,15 +106,17 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) { for (u32 bit : {16U, 24U, 32U, 64U}) { if (bit == 32 && insn.Bits(9, 3) != 1 && opcode != 0xb60 && opcode != 0xb66) continue; const u32 reg = insn.Bits(bit, 8); - if (reg >= (uses_uniform_bank ? 216U : 246U) && reg < 255) return fail(index, "native program uses lowering scratch registers"); + if (reg >= (uses_uniform_bank ? high_uniform_base : 246U) && reg < 255) return fail(index, "native program uses lowering scratch registers"); } // UR0-UR21 live in R216-R237 and UR22-UR27 in R240-R245 (R239 holds the uniform - // predicates), so even/odd pairs stay adjacent. Sites that still check reg < 16 rely on - // UR0-UR15 being R216-R231. + // predicates), so even/odd pairs stay adjacent; UR28 and up below R216. Sites that + // still check reg < 16 rely on UR0-UR15 being R216-R231. const auto uniform_register = [&](u32 reg) -> u32 { if (reg < 22) return 216 + reg; if (reg < 28) return 218 + reg; if (reg == 63) return 255; + if (reg < 63 && reg - 28 < high_uniforms) return high_uniform_base + (reg - 28); + if (reg < 63) result.high_uniform_registers = (std::max)(result.high_uniform_registers, (reg - 26) & ~1U); return ~0U; }; bool rewritten_uniform = false; @@ -2513,4 +2520,14 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) { return fail(static_cast<u32>(code.size()), main_exited ? "native program lacks captured library return" : "native program has no unconditional EXIT"); } +inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) { + // Programs using uniform registers above UR27 are lowered again with mirrors for them. + u32 high_uniforms = 0; + for (;;) { + LoweredProgram lowered = LowerToMaxwellWith(code, high_uniforms); + if (lowered || lowered.high_uniform_registers <= high_uniforms) return lowered; + high_uniforms = lowered.high_uniform_registers; + } +} + } // namespace Shader::Ampere |
