[iterate-4A] intro-video: correct MULSC/ADDSC/SUBSC operand addressing
The scalar-constant ALU ops (MULSC/ADDSC/SUBSC, opcodes 42-47) address their operands through src3 in a special way, not via src_a/src_b like ordinary scalar ops: temp reg = (src3_swiz & 0x3C) | (scalar_opc & 1), component = src3_swiz & 3 const idx = src3_reg (+256 for the PS constant bank), component = .w The op is then temp <op> const. We were feeding these the plain (src_a.x, src_b.x). For the movie's YUV->RGB luma scale that made the single MULSC0 compute r0.x * r0.x = Yb^2 (a squared luma) instead of r0.x * alu[511].w = Yb * 1.1643 (the limited- range 255/219 luma scale). The squared luma crushed shadows, so dark regions rendered as near-pure chroma -- the intro's dark background read purple/magenta while the bright logo looked roughly right. With correct addressing the luma is linear: the background drops to near black (was a purple ~(37,15,39), now ~(12,0,14)) and the logo reaches full white -- the authentic SQUARE ENIX intro. (The earlier attempt at this addressing failed only because the PS constant bank was still mis-indexed before the +256 fix, so the coefficients read zero -> black.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -472,28 +472,37 @@ impl EmitCtx {
|
||||
}
|
||||
}
|
||||
|
||||
// Scalar pipe. Binary ops use (src_a.x, src_b.x); ps-variants use
|
||||
// src_a.x + running ps. `scl_src_a` mirrors the interpreter's
|
||||
// `scalar_src_is_ps` selector.
|
||||
//
|
||||
// NOTE: the scalar-constant family (MULSC/ADDSC/SUBSC, 42..=47) really
|
||||
// reads a SPECIAL operand pair — a float constant at src3's index
|
||||
// (W-swizzled) and a temp register at a bit-packed index (X-swizzled),
|
||||
// per canary ucode.h:1302 / shader_translator.cc:1419. A first attempt
|
||||
// at that addressing rendered the intro video BLACK (the operands
|
||||
// resolved to zero), so it's reverted here to the plain (src_a.x,
|
||||
// src_b.x) read: the YUV→RGB result is then visible but MAGENTA-tinted
|
||||
// (wrong chroma coefficients). Correct SC-operand addressing is a
|
||||
// follow-up. See project memory STEP 92.
|
||||
let scl_src_a = if alu.scalar_src_is_ps {
|
||||
"ps".to_string()
|
||||
// Scalar pipe. Most scalar ops use (src_a.x, src_b.x); ps-variants use
|
||||
// the running `ps`. The scalar-constant family (MULSC/ADDSC/SUBSC,
|
||||
// 42..=47) is SPECIAL: it reads one temp register and one float
|
||||
// constant, both addressed through src3 (canary ucode.h
|
||||
// `scalar_const_reg_op_src_temp_reg`):
|
||||
// temp reg = (src3_swiz & 0x3C) | (scalar_opc & 1), component = src3_swiz & 3
|
||||
// const idx = src3_reg (+256 for the PS constant bank), component = .w
|
||||
// The op is then temp <op> const. Feeding it the plain (src_a.x,
|
||||
// src_b.x) instead computed e.g. r0.x*r0.x (Yb²) for the YUV→RGB luma
|
||||
// scale — a squared luma that crushes shadows so dark regions render as
|
||||
// near-pure chroma (the intro's dark background read purple). The
|
||||
// earlier attempt at this addressing failed only because the constant
|
||||
// bank was still mis-indexed (pre-STEP-95): the coefficients read zero.
|
||||
let (scl_src_a, scl_src_b) = if (42..=47).contains(&alu.scalar_opcode) {
|
||||
let temp_reg = ((alu.src_c_swiz & 0x3C) | (alu.scalar_opcode & 1)) as u32;
|
||||
let temp_comp = ['x', 'y', 'z', 'w'][(alu.src_c_swiz & 0x3) as usize];
|
||||
let const_idx = alu.src_c as u32 + const_base;
|
||||
(
|
||||
format!("r[{temp_reg}u].{temp_comp}"),
|
||||
format!("xenos_consts.alu[{const_idx}u].w"),
|
||||
)
|
||||
} else if alu.scalar_src_is_ps {
|
||||
("ps".to_string(), format!("{}.x", b))
|
||||
} else {
|
||||
format!("{}.x", a)
|
||||
(format!("{}.x", a), format!("{}.x", b))
|
||||
};
|
||||
let scl_src_b = format!("{}.x", b);
|
||||
let expr = match scalar_expr(alu.scalar_opcode, &scl_src_a, &scl_src_b, "ps") {
|
||||
Some(e) => e,
|
||||
None => return Err(reject::SCL_OP_UNSUPPORTED),
|
||||
None => {
|
||||
return Err(reject::SCL_OP_UNSUPPORTED);
|
||||
}
|
||||
};
|
||||
self.push(&format!("ps = {expr};"));
|
||||
if alu.scalar_write_mask != 0 {
|
||||
|
||||
Reference in New Issue
Block a user