aboutsummaryrefslogtreecommitdiff
path: root/src/shader_recompiler
diff options
context:
space:
mode:
authorrdenno <rhysdenno@gmail.com>2026-11-02 08:42:56 -0500
committerrdenno <rhysdenno@gmail.com>2026-11-02 08:46:35 -0500
commit7ac108e2571d7cb39585c58994524131dd9d8c22 (patch)
treedc696012f06d50f0ffbe0c30fec7e88e6559ca06 /src/shader_recompiler
parent8b8d0fc85f1267dc93d069c5a331c4f24ac3a835 (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.h28
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;