aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 12:07:37 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 11:08:14 -0500
commitde1929c9346dbeefc92f28ae8cd95e768ece733a (patch)
treee91ddfa3bdc2450cf75185bf3919625b486df64c /src
parent732f91ded38d52b298cf4ef2c45f2db3c63331c9 (diff)
nx2: lower bindless TEX (handle in a register or a uniform register)
Kirby Air Riders samples through texture handles computed at run time: TEX with the handle in a uniform register (op f60) and TEX.B with it in Rb (op 361, bit 59 = .B). Both become Maxwell TEX.B, the handle first among the meta registers, so the texture pass tracks it back to its constant buffer words. f60 keeps the usual operand split; 361 keeps the coordinates together from Ra and is taken for plain samples only.
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/frontend/ampere/lower_to_maxwell.h49
1 files changed, 36 insertions, 13 deletions
diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
index 01c789dd9e..e656d73f81 100644
--- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
+++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
@@ -55,7 +55,7 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) {
bool uses_uniform_bank = false;
for (const auto& instruction : code) {
const u32 op = instruction.Bits(0, 12);
- uses_uniform_bank |= op == 0x3c4 || op == 0x2bd || 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 ||
+ uses_uniform_bank |= op == 0x3c4 || op == 0x2bd || 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 == 0x299 || op == 0xc09 || (op == 0x81c && instruction.Bits(67, 1));
}
const auto address_of = [](u32 index) { return 0x50U + index * 8 + ((index + 1) / 3) * 8; };
@@ -1543,7 +1543,17 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) {
}
break;
}
- case 0xb60: { // TEX, scalar 2D implicit-LOD sample
+ case 0xb60: case 0xf60: case 0x361: { // TEX; 0xf60: handle in a uniform register; 0x361: TEX.B
+ // Bindless forms become Maxwell TEX.B, the handle first among the meta registers (the
+ // texture pass tracks it back to its constant buffer words). 0x361 keeps the
+ // coordinates together from Ra and the handle in Rb; only plain samples are taken.
+ const bool bindless = opcode != 0xb60;
+ const u32 handle = opcode == 0xf60 ? uniform_register(insn.Bits(40, 6)) : insn.Bits(32, 8);
+ if (bindless && (handle == ~0U || handle >= 246 ||
+ (opcode == 0xf60 && insn.Bits(46, 13)) ||
+ (opcode == 0x361 && (insn.Bits(40, 19) || insn.Bits(87, 3) || insn.Bits(76, 1) ||
+ insn.Bits(78, 1)))))
+ return fail(index, "native bindless texture mode");
const u32 mask = insn.Bits(72, 4);
const u32 second_dest = insn.Bits(64, 8);
constexpr std::array<u32, 8> rg_masks{1, 2, 4, 8, 3, 9, 10, 12};
@@ -1578,25 +1588,29 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) {
constexpr std::array<u32, 8> coordinate_count{1, 2, 3, 3, 2, 3, 0, 4};
constexpr std::array<u32, 8> maxwell_type{0, 2, 4, 6, 1, 3, 0, 7};
const bool offsets = insn.Bits(76, 1) != 0;
- if (dimension == 6 || insn.Bits(77, 1) || insn.Bits(81, 3) != 7 || insn.Bits(59, 1) ||
- insn.Bits(40, 14) > 0x1fff || insn.Bits(54, 5) >= 18 || (lod > 3 && lod != 6 && lod != 7) ||
- (depth_compare && mask != 1) || mask == 0)
+ // Bit 59 is .B, set in 0x361's encoding.
+ if (dimension == 6 || insn.Bits(77, 1) || insn.Bits(81, 3) != 7 ||
+ insn.Bits(59, 1) != (opcode == 0x361 ? 1U : 0U) ||
+ (!bindless && (insn.Bits(40, 14) > 0x1fff || insn.Bits(54, 5) >= 18)) ||
+ (lod > 3 && lod != 6 && lod != 7) || (depth_compare && mask != 1) || mask == 0)
return false;
const u32 coordinates = coordinate_count[dimension];
const bool has_lod = lod == 2 || lod == 3 || lod == 6 || lod == 7;
const u32 count = coordinates + (has_lod ? 1 : 0) + (offsets ? 1 : 0) + (depth_compare ? 1 : 0);
const u32 in_a = count <= 4 ? (count + 1) / 2 : 4;
const auto value = [&](u32 i) -> u32 {
- if (i < in_a) return source_a == 255 ? 255U : source_a + i;
+ if (opcode == 0x361 || i < in_a) return source_a == 255 ? 255U : source_a + i;
return source_b == 255 ? 255U : source_b + (i - in_a);
};
- if ((source_a != 255 && source_a + in_a > 246) ||
- (count > in_a && source_b != 255 && source_b + (count - in_a) > 246))
+ if ((source_a != 255 && source_a + (opcode == 0x361 ? count : in_a) > 246) ||
+ (opcode != 0x361 && count > in_a && source_b != 255 && source_b + (count - in_a) > 246))
return false;
- const u32 binding = insn.Bits(54, 5);
- if (result.required_texture_buffer != ~0U && result.required_texture_buffer != binding)
- return false;
- result.required_texture_buffer = binding;
+ if (!bindless) {
+ const u32 binding = insn.Bits(54, 5);
+ if (result.required_texture_buffer != ~0U && result.required_texture_buffer != binding)
+ return false;
+ result.required_texture_buffer = binding;
+ }
const auto copy = [&](u32 target, u32 source) {
emit(0x5c98000000000000ULL | predicate | target | (u64{15} << 39) | (u64{source} << 20));
};
@@ -1604,14 +1618,23 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) {
for (u32 i = 0; i < coordinates; ++i) copy(250 + i, value(i));
u32 meta = 246;
u32 next = coordinates;
+ if (bindless) copy(meta++, handle);
if (has_lod) copy(meta++, value(next++));
if (offsets) copy(meta++, value(next++));
if (depth_compare) copy(meta++, value(next++));
+ if (bindless) {
+ // Maxwell TEX.B: AOFFI at 36 and the LOD mode at 37 instead of 54 and 55.
+ emit(0xde80000000000000ULL | predicate | 250 | (u64{250} << 8) | (u64{246} << 20) |
+ (u64{maxwell_type[dimension]} << 28) | (u64{mask} << 31) | (u64{offsets} << 36) |
+ (u64{lod} << 37) | (u64{insn.Bits(90, 1)} << 49) | (u64{depth_compare} << 50) |
+ (u64{7} << 51));
+ } else {
emit(0xc000000000000000ULL | predicate | 250 | (u64{250} << 8) |
(u64{meta == 246 ? 255U : 246U} << 20) | (u64{maxwell_type[dimension]} << 28) |
(u64{mask} << 31) | (u64{insn.Bits(40, 14)} << 36) |
(u64{insn.Bits(90, 1)} << 49) | (u64{depth_compare} << 50) | (u64{7} << 51) |
(u64{offsets} << 54) | (u64{lod} << 55));
+ }
const u32 components = static_cast<u32>(std::popcount(mask));
if (insn.Bits(79, 1)) {
// FP16 results: (c0, c1) packed into the first destination, (c2, c3) into
@@ -1646,7 +1669,7 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) {
// SM70 scalar coordinates and split RG/BA destinations match TEXS.
// Descriptor offsets are words in both encodings, but only native
// explicitly encodes the constant-buffer binding.
- if (!insn.Bits(60, 1) || (dimension != 1 && dimension != 5) ||
+ if (bindless || !insn.Bits(60, 1) || (dimension != 1 && dimension != 5) ||
swizzle == ~0U || insn.Bits(76, 2) ||
insn.Bits(81, 3) != 7 || (lod > 1 && (lod != 3 || dimension != 1 || depth_compare)) ||
(lod == 3 && ((source_a & 1) || source_a >= 249)) ||