aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 14:23:11 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 11:09:49 -0500
commit563ed8e60b0dd053ac8017aafa2f78c78747600b (patch)
tree56804a6221a597bd8abae1bc22d7328f6934ca13
parent6e289d893535b76727876efafd64c23eda486cea (diff)
nx2: lower TLD4 with a uniform handle
TLD4.SCR.G with its handle in a uniform register (Kirby Air Riders; bit 91 set) becomes Maxwell TLD4.B, with the handle first in Rb and the component at bits 38-39.
-rw-r--r--src/shader_recompiler/frontend/ampere/lower_to_maxwell.h22
1 files changed, 17 insertions, 5 deletions
diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
index 641e98c1e4..e1c3095abe 100644
--- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
+++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
@@ -56,7 +56,7 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) {
for (const auto& instruction : code) {
const u32 op = instruction.Bits(0, 12);
uses_uniform_bank |= op == 0x3c4 || op == 0x2bd || op == 0x896 || op == 0xf60 || op == 0xc11 || op == 0xd08 || op == 0xc0c || op == 0x986 || op == 0x9a8 || op == 0xc12 || op == 0xc17 || op == 0xe24 || op == 0xc24 || op == 0xd00 || op == 0xd09 || op == 0x9c3 || op == 0xc45 || op == 0x2a4 || op == 0x891 || op == 0x890 || op == 0x290 || op == 0x28c || op == 0x88c || op == 0x287 || op == 0x887 || op == 0x958 || op == 0xab9 || op == 0xabb || op == 0x899 || op == 0xc20 || op == 0xe21 || op == 0xe23 || op == 0xc02 ||
- op == 0x886 || op == 0x3c2 || op == 0xc82 || op == 0x882 || op == 0x292 || op == 0x892 || op == 0x291 || op == 0x981 || op == 0xc13 || op == 0xc10 || op == 0xc23 || op == 0x526 || op == 0x2be || op == 0x8a4 || op == 0xd05 || (op == 0x984 && instruction.Bits(91, 1)) || op == 0x89c || op == 0x883 || op == 0x2a5 || op == 0xd06 || op == 0xf69 || op == 0xc0b || op == 0x8a5 || op == 0x299 || op == 0xc09 || (op == 0x81c && instruction.Bits(67, 1));
+ op == 0x886 || op == 0x3c2 || op == 0xc82 || op == 0x882 || op == 0x292 || op == 0x892 || op == 0x291 || op == 0x981 || op == 0xc13 || op == 0xc10 || op == 0xc23 || op == 0x526 || op == 0x2be || op == 0x8a4 || op == 0xd05 || (op == 0x984 && instruction.Bits(91, 1)) || op == 0x89c || op == 0x883 || op == 0x2a5 || op == 0xd06 || op == 0xf63 || op == 0xf69 || op == 0xc0b || op == 0x8a5 || op == 0x299 || op == 0xc09 || (op == 0x81c && instruction.Bits(67, 1));
}
const auto address_of = [](u32 index) { return 0x50U + index * 8 + ((index + 1) / 3) * 8; };
const auto fail = [&](u32 index, std::string_view reason) {
@@ -1616,22 +1616,34 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) {
}
break;
}
- case 0xb63: { // TLD4 (gather), 2D without offsets or depth compare; component at bits 87-88.
+ case 0xb63: case 0xf63: { // TLD4 (gather), 2D without offsets or depth compare; component at bits 87-88.
+ // 0xf63: the handle in a uniform register (bit 91), Maxwell TLD4.B with the handle in Rb.
+ const bool bindless = opcode == 0xf63;
+ const u32 handle = bindless ? uniform_register(insn.Bits(40, 6)) : 255;
const u32 mask = insn.Bits(72, 4), second_dest = insn.Bits(64, 8);
const u32 source_a = insn.Bits(24, 8), source_b = insn.Bits(32, 8);
const u32 binding = insn.Bits(54, 5);
if (insn.Bits(61, 3) != 1 || insn.Bits(76, 4) || insn.Bits(81, 3) != 7 || insn.Bits(59, 1) ||
- insn.Bits(40, 14) > 0x1fff || binding >= 18 || insn.Bits(89, 1) || mask == 0 ||
- (result.required_texture_buffer != ~0U && result.required_texture_buffer != binding))
+ insn.Bits(89, 1) || mask == 0 ||
+ (bindless ? (handle >= 246 || insn.Bits(46, 13) || !insn.Bits(91, 1))
+ : (insn.Bits(40, 14) > 0x1fff || binding >= 18 ||
+ (result.required_texture_buffer != ~0U && result.required_texture_buffer != binding))))
return fail(index, "native texture gather mode");
- result.required_texture_buffer = binding;
+ if (!bindless) result.required_texture_buffer = binding;
// Two coordinates: x in Ra, y in Rb (the coordinate list splits evenly across them).
emit(0x5c98000000000000ULL | predicate | 250 | (u64{15} << 39) | (u64{source_a} << 20));
emit(0x5c98000000000000ULL | predicate | 251 | (u64{15} << 39) |
(u64{source_b == 255 ? 255U : source_b} << 20));
+ if (bindless) {
+ emit(0x5c98000000000000ULL | predicate | 246 | (u64{15} << 39) | (u64{handle} << 20));
+ emit(0xdec0000000000000ULL | predicate | 250 | (u64{250} << 8) | (u64{246} << 20) |
+ (u64{2} << 28) | (u64{mask} << 31) | (u64{insn.Bits(87, 2)} << 38) |
+ (u64{insn.Bits(90, 1)} << 49) | (u64{7} << 51));
+ } else {
emit(0xc800000000000000ULL | predicate | 250 | (u64{250} << 8) | (u64{255} << 20) |
(u64{2} << 28) | (u64{mask} << 31) | (u64{insn.Bits(40, 14)} << 36) |
(u64{insn.Bits(90, 1)} << 49) | (u64{7} << 51) | (u64{insn.Bits(87, 2)} << 56));
+ }
const u32 components = static_cast<u32>(std::popcount(mask));
const bool split = second_dest != 255;
for (u32 i = 0; i < components; ++i) {