aboutsummaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler/frontend/ampere/lower_to_maxwell.h')
-rw-r--r--src/shader_recompiler/frontend/ampere/lower_to_maxwell.h53
1 files changed, 43 insertions, 10 deletions
diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
index 024ed27ea3..81bd4b9e74 100644
--- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
+++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h
@@ -88,6 +88,7 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32
result.source_instruction_offsets.push_back(static_cast<u32>(result.instructions.size()));
u32 opcode = insn.Bits(0, 12);
if (insn.Bits(12, 4) == 15) continue; // @!PT: never executes (compiler padding)
+ u32 f16_dest = 255, f16_second = 255, f16_components = 0;
std::optional<u32> active_link;
u32 active_entry = 0;
if (main_exited) {
@@ -113,6 +114,19 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32
const u32 reg = insn.Bits(bit, 8);
if (reg >= (uses_uniform_bank ? high_uniform_base : 246U) && reg < 255) return fail(index, "native program uses lowering scratch registers");
}
+ // TLD/TLD4 .F16 (bit 79): the components come packed in pairs, (0, 1) in Rd and (2, 3) in
+ // the second destination. Lowered as the F32 form into R248/R249 and R246/R247, then packed.
+ if ((opcode == 0xb66 || opcode == 0xb63) && insn.Bits(79, 2) == 1) {
+ f16_dest = insn.Bits(16, 8);
+ f16_second = insn.Bits(64, 8);
+ f16_components = static_cast<u32>(std::popcount(insn.Bits(72, 4)));
+ if (f16_dest >= 246 || (f16_second != 255 && f16_second >= 246) || insn.Bits(61, 3) == 5 ||
+ (f16_second == 255 && f16_components > 2))
+ return fail(index, "native half-precision texture result registers");
+ insn.words[2] &= ~(1U << 15);
+ insn.words[0] = (insn.words[0] & ~0x00ff0000U) | (f16_dest == 255 ? 0x00ff0000U : (248U << 16));
+ if (f16_second != 255) insn.words[2] = (insn.words[2] & ~0xffU) | 246U;
+ }
// UR0-UR21 live in R216-R237 and UR22-UR27 in R240-R245 (R239 holds the uniform
// predicates), so even/odd pairs stay adjacent; UR28 and up below R216. Sites that
// still check reg < 16 rely on UR0-UR15 being R216-R231.
@@ -899,19 +913,22 @@ 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. Bits 72-75 are
+ // the channel mask: the first two enabled channels go to Rd, Rd+1, the others to the
+ // second destination (RZ drops them; a third channel alone may be in an odd register).
const u32 mask = insn.Bits(72, 4), channels = static_cast<u32>(std::popcount(mask));
- // Bit 90 is forwarded below (Maxwell's NODEP); Splatoon Raiders sets it.
if ((insn.words[2] & ~(0xfffU | (1U << 26))) != 0x001e0000 || mask == 0 || insn.Bits(59, 2) ||
(dimension != 1 && dimension != 5) || binding >= 18 || offset > 0x1fff ||
- (channels > 1 && (dest & 1)) || dest >= 248 ||
- (channels > 2 ? high != 255 && ((high & 1) || high >= 248) : high != 255) ||
+ (channels > 1 && (dest & 1)) || dest >= 248 || (channels <= 2 && high != 255) ||
+ (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 consecutive registers: when the second pair is elsewhere (Splatoon
- // Raiders: R12/R18, R18/R44), write them to scratch and move each pair into place.
+ // 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) |
@@ -920,8 +937,8 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32
if (!consecutive) {
for (u32 i = 0; i < channels; ++i) {
if (i >= 2 && high == 255) break;
- emit(0x5c98000000000000ULL | predicate | (i < 2 ? dest + i : high + i - 2) |
- (u64{15} << 39) | (u64{248 + i} << 20));
+ 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;
@@ -1573,12 +1590,13 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32
}
case 0x305: case 0xb05: case 0xd05: { // F2I NTZ, register/constant-buffer/uniform source
const u32 mode = insn.words[2] & ~(opcode == 0xd05 ? 0x08000000U : 0U);
- const bool half = opcode == 0x305 && mode == 0x0010f000;
+ // F16 source: bit 72 signs the result as in the other forms, bits 60-61 pick H0/H1.
+ const bool half = opcode == 0x305 && (mode & ~0x100U) == 0x0010f000;
const bool small = mode == 0x00212800;
const bool floor = (mode & ~0x10100U) == 0x00207000;
const bool nearest = (mode & ~0x10100U) == 0x00203000;
if ((!half && !small && !floor && !nearest && (mode & ~0x10100U) != 0x0020f000) ||
- (opcode != 0xb05 && (insn.words[1] & 0xffffff00U)) ||
+ (opcode != 0xb05 && (insn.words[1] & (half ? 0xefffff00U : 0xffffff00U))) ||
(opcode == 0xd05 && uniform_register(insn.Bits(32, 8)) == ~0U)) {
return fail(index, "native float-to-integer conversion mode");
}
@@ -1586,7 +1604,7 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32
u32 source = opcode == 0xb05 ? materialize(2, 0, 252, false, false)
: opcode == 0xd05 ? uniform_register(insn.Bits(32, 8)) : insn.Bits(32, 8);
if (half) {
- extract_half({source, 2, false, false}, 0, 252);
+ extract_half({source, insn.Bits(60, 1) ? 3U : 2U, false, false}, 0, 252);
source = 252;
}
if (!is_signed) {
@@ -2870,6 +2888,21 @@ inline LoweredProgram LowerToMaxwellWith(std::span<const Instruction> code, u32
return fail(index, "unsupported native opcode");
}
if (!operand_error.empty()) return fail(index, operand_error);
+ if (f16_components != 0) {
+ // Each pair as F2FP.PACK_AB: the second value in the high half.
+ const u64 pred = u64{insn.Bits(12, 4)} << 16;
+ const auto pack = [&](u32 target, u32 low, u32 high) {
+ result.instructions.push_back(0x5ca8000000000000ULL | pred | 250 | (u64{1} << 8) |
+ (u64{2} << 10) | (u64{high} << 20));
+ result.instructions.push_back(0x5ca8000000000000ULL | pred | 251 | (u64{1} << 8) |
+ (u64{2} << 10) | (u64{low} << 20));
+ result.instructions.push_back(0x5c18000000000000ULL | pred | target | (u64{250} << 8) |
+ (u64{251} << 20) | (u64{16} << 39));
+ };
+ if (f16_dest != 255) pack(f16_dest, 248, f16_components > 1 ? 249U : 255U);
+ if (f16_second != 255 && f16_components > 2)
+ pack(f16_second, 246, f16_components > 3 ? 247U : 255U);
+ }
if (output_mask) {
emit(0x38e8000000070000ULL | 239 | (u64{239} << 8) | (u64{output_mask} << 20));
emit(0x38f0000000070000ULL | (u64{254} << 8) | (u64{output_mask} << 20));