diff options
| author | rdenno <rhysdenno@gmail.com> | 2026-11-02 08:07:36 -0500 |
|---|---|---|
| committer | rdenno <rhysdenno@gmail.com> | 2026-11-02 08:46:35 -0500 |
| commit | 8b8d0fc85f1267dc93d069c5a331c4f24ac3a835 (patch) | |
| tree | c22ccd4be329c83c1b6d909af9707c9540f0ba79 | |
| parent | 9e368c29181cfa0ce23cf61c9b5b654917842dd0 (diff) | |
shader: lower TLD and TLD4 with half-precision results
TLD.SCR and TLD4 with .F16 (bit 79) return their components packed in
pairs: components 0 and 1 in Rd, 2 and 3 in the second destination.
The lowering rejected them ("native texel fetch mode", "native texture
gather mode"), so the compute programs using them never ran.
In Hyrule Warriors: Age of Imprisonment these are compute programs
that run once gameplay starts, and gameplay stayed black (the pause
menu, drawn another way, showed the scene).
Lower the F32 form into R248/R249 and R246/R247, then pack each pair
the way F2FP.PACK_AB is lowered.
Assisted-by: Claude Opus 5.5 (diagnosis, fix and testing)
| -rw-r--r-- | src/shader_recompiler/frontend/ampere/lower_to_maxwell.h | 29 |
1 files changed, 29 insertions, 0 deletions
diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h index 00ff585041..a72c3525ce 100644 --- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h +++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h @@ -88,6 +88,7 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32 result.source_instruction_offsets.push_back(static_cast<u32>(result.instructions.size())); u32 opcode = insn.Bits(0, 12); if (insn.Bits(12, 4) == 15) continue; // @!PT: never executes (compiler padding) + u32 f16_dest = 255, f16_second = 255, f16_components = 0; std::optional<u32> active_link; u32 active_entry = 0; if (main_exited) { @@ -108,6 +109,19 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32 const u32 reg = insn.Bits(bit, 8); if (reg >= (uses_uniform_bank ? high_uniform_base : 246U) && reg < 255) return fail(index, "native program uses lowering scratch registers"); } + // TLD/TLD4 .F16 (bit 79): the components come packed in pairs, (0, 1) in Rd and (2, 3) in + // the second destination. Lowered as the F32 form into R248/R249 and R246/R247, then packed. + if ((opcode == 0xb66 || opcode == 0xb63) && insn.Bits(79, 2) == 1) { + f16_dest = insn.Bits(16, 8); + f16_second = insn.Bits(64, 8); + f16_components = static_cast<u32>(std::popcount(insn.Bits(72, 4))); + if (f16_dest >= 246 || (f16_second != 255 && f16_second >= 246) || insn.Bits(61, 3) == 5 || + (f16_second == 255 && f16_components > 2)) + return fail(index, "native half-precision texture result registers"); + insn.words[2] &= ~(1U << 15); + insn.words[0] = (insn.words[0] & ~0x00ff0000U) | (f16_dest == 255 ? 0x00ff0000U : (248U << 16)); + if (f16_second != 255) insn.words[2] = (insn.words[2] & ~0xffU) | 246U; + } // UR0-UR21 live in R216-R237 and UR22-UR27 in R240-R245 (R239 holds the uniform // 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. @@ -2613,6 +2627,21 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32 return fail(index, "unsupported native opcode"); } if (!operand_error.empty()) return fail(index, operand_error); + if (f16_components != 0) { + // Each pair as F2FP.PACK_AB: the second value in the high half. + const u64 pred = u64{insn.Bits(12, 4)} << 16; + const auto pack = [&](u32 target, u32 low, u32 high) { + result.instructions.push_back(0x5ca8000000000000ULL | pred | 250 | (u64{1} << 8) | + (u64{2} << 10) | (u64{high} << 20)); + result.instructions.push_back(0x5ca8000000000000ULL | pred | 251 | (u64{1} << 8) | + (u64{2} << 10) | (u64{low} << 20)); + result.instructions.push_back(0x5c18000000000000ULL | pred | target | (u64{250} << 8) | + (u64{251} << 20) | (u64{16} << 39)); + }; + if (f16_dest != 255) pack(f16_dest, 248, f16_components > 1 ? 249U : 255U); + if (f16_second != 255 && f16_components > 2) + pack(f16_second, 246, f16_components > 3 ? 247U : 255U); + } if (output_mask) { emit(0x38e8000000070000ULL | 239 | (u64{239} << 8) | (u64{output_mask} << 20)); emit(0x38f0000000070000ULL | (u64{254} << 8) | (u64{output_mask} << 20)); |
