diff options
| author | Jonas <104419015+jojo-chaechae@users.noreply.github.com> | 2026-11-03 05:40:18 +0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-11-03 05:40:18 +0600 |
| commit | efa7c19e59839f0d464c2e9bd41f9ca8f867fef0 (patch) | |
| tree | 5486fd47efcb33fe93cea611b04518f3d662abc6 /src | |
| parent | 0f9760e5ccfd336130f194c2564849052acde591 (diff) | |
| parent | 2786bdf580e41bf0ba6e2a6452db73c4f5b97ed2 (diff) | |
Merge pull request #304 from rdenno/fix/hw-shader-lowering
shader: lower F2I.F16, TLD/TLD4.F16 and partial-mask TXD (Hyrule Warriors black gameplay)
Diffstat (limited to 'src')
| -rw-r--r-- | src/shader_recompiler/frontend/ampere/lower_to_maxwell.h | 53 |
1 files changed, 43 insertions, 10 deletions
diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h index 024ed27ea3..81bd4b9e74 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) { @@ -113,6 +114,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. @@ -899,19 +913,22 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32 // (x, y[, layer]) and the four gradients sit in the same registers for both. const u32 dimension = insn.Bits(61, 3); const u32 high = insn.Bits(64, 8); // destination of the third and fourth channels + // Bit 90 is forwarded below (Maxwell's NODEP); Splatoon Raiders sets it. Bits 72-75 are + // the channel mask: the first two enabled channels go to Rd, Rd+1, the others to the + // second destination (RZ drops them; a third channel alone may be in an odd register). const u32 mask = insn.Bits(72, 4), channels = static_cast<u32>(std::popcount(mask)); - // Bit 90 is forwarded below (Maxwell's NODEP); Splatoon Raiders sets it. if ((insn.words[2] & ~(0xfffU | (1U << 26))) != 0x001e0000 || mask == 0 || insn.Bits(59, 2) || (dimension != 1 && dimension != 5) || binding >= 18 || offset > 0x1fff || - (channels > 1 && (dest & 1)) || dest >= 248 || - (channels > 2 ? high != 255 && ((high & 1) || high >= 248) : high != 255) || + (channels > 1 && (dest & 1)) || dest >= 248 || (channels <= 2 && high != 255) || + (high != 255 && ((channels > 3 && (high & 1)) || high >= 248)) || coordinates >= 248 || derivatives >= 247) return fail(index, "native gradient texture mode"); if (result.required_texture_buffer != ~0U && result.required_texture_buffer != binding) return fail(index, "native texture descriptor buffer conflict"); result.required_texture_buffer = binding; - // Maxwell writes consecutive registers: when the second pair is elsewhere (Splatoon - // Raiders: R12/R18, R18/R44), write them to scratch and move each pair into place. + // Maxwell writes the channels to consecutive registers: when the second destination + // doesn't follow Rd (Splatoon Raiders: R12/R18, R18/R44), write them to scratch and move + // each into place. const bool consecutive = channels <= 2 || high == dest + 2; const u64 target = consecutive ? dest : 248; emit(0xde00000000000000ULL | predicate | target | (u64{coordinates} << 8) | @@ -920,8 +937,8 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32 if (!consecutive) { for (u32 i = 0; i < channels; ++i) { if (i >= 2 && high == 255) break; - emit(0x5c98000000000000ULL | predicate | (i < 2 ? dest + i : high + i - 2) | - (u64{15} << 39) | (u64{248 + i} << 20)); + const u32 to = i < 2 ? static_cast<u32>(dest) + i : high + (i - 2); + emit(0x5c98000000000000ULL | predicate | to | (u64{15} << 39) | (u64{248 + i} << 20)); } } break; @@ -1573,12 +1590,13 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32 } case 0x305: case 0xb05: case 0xd05: { // F2I NTZ, register/constant-buffer/uniform source const u32 mode = insn.words[2] & ~(opcode == 0xd05 ? 0x08000000U : 0U); - const bool half = opcode == 0x305 && mode == 0x0010f000; + // F16 source: bit 72 signs the result as in the other forms, bits 60-61 pick H0/H1. + const bool half = opcode == 0x305 && (mode & ~0x100U) == 0x0010f000; const bool small = mode == 0x00212800; const bool floor = (mode & ~0x10100U) == 0x00207000; const bool nearest = (mode & ~0x10100U) == 0x00203000; if ((!half && !small && !floor && !nearest && (mode & ~0x10100U) != 0x0020f000) || - (opcode != 0xb05 && (insn.words[1] & 0xffffff00U)) || + (opcode != 0xb05 && (insn.words[1] & (half ? 0xefffff00U : 0xffffff00U))) || (opcode == 0xd05 && uniform_register(insn.Bits(32, 8)) == ~0U)) { return fail(index, "native float-to-integer conversion mode"); } @@ -1586,7 +1604,7 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32 u32 source = opcode == 0xb05 ? materialize(2, 0, 252, false, false) : opcode == 0xd05 ? uniform_register(insn.Bits(32, 8)) : insn.Bits(32, 8); if (half) { - extract_half({source, 2, false, false}, 0, 252); + extract_half({source, insn.Bits(60, 1) ? 3U : 2U, false, false}, 0, 252); source = 252; } if (!is_signed) { @@ -2870,6 +2888,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)); |
