diff options
| author | rdenno <rhysdenno@gmail.com> | 2026-11-02 08:42:56 -0500 |
|---|---|---|
| committer | rdenno <rhysdenno@gmail.com> | 2026-11-02 08:46:35 -0500 |
| commit | 7ac108e2571d7cb39585c58994524131dd9d8c22 (patch) | |
| tree | dc696012f06d50f0ffbe0c30fec7e88e6559ca06 /src/shader_recompiler | |
| parent | 8b8d0fc85f1267dc93d069c5a331c4f24ac3a835 (diff) | |
shader: lower TXD with any channel mask
TXD was only lowered with all four channels (and rejected any
instruction without a second destination, as RZ & 1 is set).
Hyrule Warriors: Age of Imprisonment's gradient-sampling programs
fetch one channel into Rd, or three channels with the third in the
second destination, and were rejected ("native gradient texture
mode"), so their draws were skipped.
Pass the channel mask on to Maxwell TXD. The first two enabled
channels go to Rd, Rd+1 and the others to the second destination;
when those aren't consecutive, write to scratch and move them as the
four-channel case already did.
Assisted-by: Claude Opus 5.5 (diagnosis, fix and testing)
Diffstat (limited to 'src/shader_recompiler')
| -rw-r--r-- | src/shader_recompiler/frontend/ampere/lower_to_maxwell.h | 28 |
1 files changed, 17 insertions, 11 deletions
diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h index a72c3525ce..f189f879b8 100644 --- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h +++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h @@ -823,27 +823,33 @@ 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. - if ((insn.words[2] & ~(0xffU | (1U << 26))) != 0x001e0f00 || insn.Bits(59, 2) || + // 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. + const u32 mask = insn.Bits(72, 4); + const u32 channels = static_cast<u32>(std::popcount(mask)); + if ((insn.words[2] & ~(0xffU | 0xf00U | (1U << 26))) != 0x001e0000 || insn.Bits(59, 2) || + mask == 0 || (channels > 2) != (high != 255) || (dimension != 1 && dimension != 5) || binding >= 18 || offset > 0x1fff || - (dest & 1) || dest >= 248 || (high & 1) || (high >= 248 && high != 255) || + (channels > 1 && (dest & 1)) || dest >= 248 || + (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 four consecutive registers: when the second pair is elsewhere (Splatoon - // Raiders: R12/R18, R18/R44), write them to scratch and move each pair into place. - const bool consecutive = high == dest + 2; + // 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) | - (u64{derivatives} << 20) | (u64{dimension == 5 ? 3U : 2U} << 28) | (u64{15} << 31) | + (u64{derivatives} << 20) | (u64{dimension == 5 ? 3U : 2U} << 28) | (u64{mask} << 31) | (u64{offset} << 36) | (u64{insn.Bits(90, 1)} << 49) | (u64{7} << 51)); if (!consecutive) { - for (u32 i = 0; i < 2; ++i) { - emit(0x5c98000000000000ULL | predicate | (dest + i) | (u64{15} << 39) | (u64{248 + i} << 20)); - if (high != 255) - emit(0x5c98000000000000ULL | predicate | (high + i) | (u64{15} << 39) | (u64{250 + i} << 20)); + for (u32 i = 0; i < channels; ++i) { + 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; |
