From 6f38217ed3f2bc3de96c2ae65f60330530b59778 Mon Sep 17 00:00:00 2001 From: Takoroka <137927095+Takoroka@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:21:26 -0400 Subject: [PATCH] nx2: lower what title 4 needs after its intro - 64-bit adds: IADD3/UIADD3 with a carry-out predicate (unsigned compare of the sum) and IADD3.X/UIADD3.X with a carry-in predicate (SEL + add); uniform predicates go through R239 like UISETP. Used by a compute pass the game runs thousands of times while loading the next area. - TMML (0xb69) LOD query, 2D: 8.8 fixed-point results as on Maxwell. - Vertex stores to a[0x5c] (an Ampere per-primitive hint, likely shading rate) are dropped; Eden had no attribute for it and refused the pipelines. All 864 shaders from a 5-minute scripted session past the intro now lower, except the opt-in tensor-core upscaler (bilinear stand-in). --- .../frontend/ampere/lower_to_maxwell.h | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h index 2bcf4a49ce..5c8c2822c5 100644 --- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h +++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h @@ -105,9 +105,11 @@ inline LoweredProgram LowerToMaxwell(std::span code) { if (reg == 63) return 255; return ~0U; }; + bool rewritten_uniform = false; if (opcode == 0x2a4 || opcode == 0x891 || opcode == 0x890 || opcode == 0x290 || opcode == 0x292 || opcode == 0x892 || opcode == 0x291) { if (insn.Bits(12, 4) != 7) return fail(index, "native uniform arithmetic predication"); + rewritten_uniform = true; const u32 dst = uniform_register(insn.Bits(16, 8)); const u32 a = uniform_register(insn.Bits(24, 8)); const u32 c = uniform_register(insn.Bits(64, 8)); @@ -1233,6 +1235,33 @@ inline LoweredProgram LowerToMaxwell(std::span code) { return fail(index, "native dependency barrier mode"); } break; + case 0xb69: { // TMML (LOD query), 2D. Results are 8.8 fixed point, as on Maxwell. + 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(80, 10) || insn.Bits(59, 1) || + insn.Bits(40, 14) > 0x1fff || binding >= 18 || mask == 0 || source_a >= 246 || + (result.required_texture_buffer != ~0U && result.required_texture_buffer != binding)) + return fail(index, "native texture LOD query mode"); + result.required_texture_buffer = binding; + // Coordinates: (Ra, Ra+1) when Rb is RZ, else (Ra, Rb). + emit(0x5c98000000000000ULL | predicate | 250 | (u64{15} << 39) | (u64{source_a} << 20)); + emit(0x5c98000000000000ULL | predicate | 251 | (u64{15} << 39) | + (u64{source_b == 255 ? source_a + 1 : source_b} << 20)); + emit(0xdf58000000000000ULL | 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)); + const u32 components = static_cast(std::popcount(mask)); + const bool split = second_dest != 255; + for (u32 i = 0; i < components; ++i) { + u32 target = 255; + if (!split || i < 2) target = dest == 255 ? 255U : static_cast(dest) + i; + else target = second_dest + (i - 2); + if (target != 255) + emit(0x5c98000000000000ULL | predicate | target | (u64{15} << 39) | (u64{250 + i} << 20)); + } + break; + } case 0xb63: { // TLD4 (gather), 2D without offsets or depth compare; component at bits 87-88. 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); @@ -1417,6 +1446,9 @@ inline LoweredProgram LowerToMaxwell(std::span code) { if (address % 4 || address + components * 4 > 0x400 || insn.Bits(24, 8) != 255) { return fail(index, "native attribute address or indexing"); } + // a[0x5c] has no Maxwell meaning (Ampere uses it for a per-primitive hint such as the + // shading rate). Nothing reads it back; dropping the store keeps full-rate shading. + if (opcode == 0x322 && address == 0x5c && components == 1) break; u32 value_reg = opcode == 0x321 ? static_cast(dest) : insn.Bits(32, 8); if (value_reg == 255) { if (opcode == 0x321) break; // load into RZ: nothing to do @@ -1715,6 +1747,53 @@ inline LoweredProgram LowerToMaxwell(std::span code) { break; } case 0x210: case 0x810: case 0xa10: case 0xc10: { // IADD3 with zero third source and no carry outputs + // 64-bit adds: IADD3 with a carry-out predicate (bits 81-83), then IADD3.X (bit 74) with + // a carry-in predicate (bits 87-89, negate 90). Uniform forms keep UPn in R239 bit 6-n. + if ((opcode == 0x210 || opcode == 0x810) && insn.Bits(64, 8) == 255 && !insn.Bits(73, 1) && + !insn.Bits(75, 2) && insn.Bits(84, 3) == 7 && insn.Bits(77, 4) == 15 && !insn.Bits(72, 1) && + (insn.Bits(81, 3) != 7 || insn.Bits(74, 1))) { + const bool carry_in = insn.Bits(74, 1) != 0; + const u32 carry_out = insn.Bits(81, 3); + const u32 a = insn.Bits(24, 8); + if ((carry_in && carry_out != 7) || (opcode == 0x210 && (insn.words[1] & ~0xffU)) || + (rewritten_uniform && carry_out != 7 && carry_out > 1) || + (rewritten_uniform && carry_in && insn.Bits(87, 3) > 1 && insn.Bits(87, 3) != 7)) + return fail(index, "native integer carry mode"); + if (opcode == 0x810) { + emit(0x1c00000000000000ULL | predicate | 250 | (u64{a} << 8) | (u64{insn.words[1]} << 20)); + } else { + emit(0x5c10000000000000ULL | predicate | 250 | (u64{a} << 8) | (u64{insn.Bits(32, 8)} << 20)); + } + const auto save = [&](u32 mask) { emit(0x38e8000000070000ULL | 253 | (u64{255} << 8) | (u64{mask} << 20)); }; + const auto restore = [&](u32 mask) { emit(0x38f0000000070000ULL | (u64{253} << 8) | (u64{mask} << 20)); }; + if (!carry_in) { + // carry = (a + b) < a, unsigned + const u32 target = rewritten_uniform ? 6 - carry_out : carry_out; + const u32 mask = 1U << target; + if (rewritten_uniform) save(mask); + emit(0x5b60000000000000ULL | predicate | 7 | (u64{target} << 3) | (u64{250} << 8) | + (u64{a} << 20) | (u64{7} << 39) | (u64{1} << 49)); + if (rewritten_uniform) { + emit(0x38e8000000070000ULL | 239 | (u64{239} << 8) | (u64{mask} << 20)); + restore(mask); + } + emit(base(0x5c98000000000000ULL) | (u64{15} << 39) | (u64{250} << 20)); + break; + } + const u32 condition = insn.Bits(87, 3); + const u32 source = rewritten_uniform && condition != 7 ? 6 - condition : condition; + const u32 mask = 1U << source; + mov_imm(252, 1); + if (rewritten_uniform && condition != 7) { + save(mask); + emit(0x38f0000000070000ULL | (u64{239} << 8) | (u64{mask} << 20)); + } + emit(0x5ca0000000000000ULL | predicate | 251 | (u64{252} << 8) | (u64{255} << 20) | + (u64{source} << 39) | (u64{insn.Bits(90, 1)} << 42)); + if (rewritten_uniform && condition != 7) restore(mask); + emit(base(0x5c10000000000000ULL) | (u64{250} << 8) | (u64{251} << 20)); + break; + } if (opcode == 0xa10 && insn.Bits(81, 3) != 7) { if (insn.Bits(64, 8) != 255 || insn.Bits(73, 4) || insn.Bits(84, 7) != 0x7f || insn.Bits(77, 4) != 15 || insn.Bits(63, 1) || insn.Bits(72, 1))