diff --git a/src/xenia/gpu/shader_interpreter.cc b/src/xenia/gpu/shader_interpreter.cc index ec7cf9a02..c96c2d838 100644 --- a/src/xenia/gpu/shader_interpreter.cc +++ b/src/xenia/gpu/shader_interpreter.cc @@ -620,7 +620,8 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) { bool scalar_src_absolute = false; switch (scalar_opcode_info.operand_count) { case 1: { - // r#/c#.w or r#/c#.wx. + // r#/c#.w, or r#/c#.wx unless the paired vector opcode has three + // operands, in which case r#/c#.wz. const float* scalar_src_ptr; uint32_t scalar_src_register = instr.src_reg(3); std::array scalar_src_float_constant; @@ -640,9 +641,11 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) { scalar_operand_component_count = scalar_opcode_info.single_operand_is_two_component ? 2 : 1; for (uint32_t i = 0; i < scalar_operand_component_count; ++i) { + uint32_t source_component = + i == 0 ? 3 : (vector_opcode_info.GetOperandCount() == 3 ? 2 : 0); scalar_operands[i] = scalar_src_ptr[ucode::AluInstruction::GetSwizzledComponentIndex( - scalar_src_swizzle, (3 + i) & 3)]; + scalar_src_swizzle, source_component)]; } } break; case 2: { diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index a7eb4e6ff..24213af87 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -1213,7 +1213,8 @@ uint32_t ParsedTextureFetchInstruction::GetNonZeroResultComponents() const { static void ParseAluInstructionOperand(const AluInstruction& op, uint32_t i, uint32_t swizzle_component_count, - InstructionOperand& out_op) { + InstructionOperand& out_op, + uint32_t scalar_second_component = 0) { out_op.is_negated = op.src_negate(i); uint32_t reg = op.src_reg(i); if (op.src_is_temp(i)) { @@ -1247,9 +1248,10 @@ static void ParseAluInstructionOperand(const AluInstruction& op, uint32_t i, // Scalar `a` (W). out_op.components[0] = GetSwizzledAluSourceComponent(swizzle, 3); } else if (swizzle_component_count == 2) { - // Scalar left-hand `a` (W) and right-hand `b` (X). + // Scalar left-hand `a` (W) and right-hand `b` (X or Z). out_op.components[0] = GetSwizzledAluSourceComponent(swizzle, 3); - out_op.components[1] = GetSwizzledAluSourceComponent(swizzle, 0); + out_op.components[1] = + GetSwizzledAluSourceComponent(swizzle, scalar_second_component); } else if (swizzle_component_count == 3) { assert_always(); } else if (swizzle_component_count == 4) { @@ -1421,9 +1423,13 @@ void ParseAluInstruction(const AluInstruction& op, instr.scalar_operand_count = scalar_opcode_info.operand_count; if (instr.scalar_operand_count) { if (instr.scalar_operand_count == 1) { + // The scalar operation shares source 3 with the vector operation. If the + // vector operation uses source 3, the scalar `b` component is Z; + // otherwise it is X. ParseAluInstructionOperand( op, 3, scalar_opcode_info.single_operand_is_two_component ? 2 : 1, - instr.scalar_operands[0]); + instr.scalar_operands[0], + vector_opcode_info.GetOperandCount() == 3 ? 2 : 0); } else { // Constant and temporary register. diff --git a/src/xenia/gpu/ucode.h b/src/xenia/gpu/ucode.h index 5ae943f62..1dd073c5a 100644 --- a/src/xenia/gpu/ucode.h +++ b/src/xenia/gpu/ucode.h @@ -942,10 +942,13 @@ static_assert_size(FetchInstruction, sizeof(uint32_t) * 3); // - All temporary registers are vec4s. // - Most scalar ALU operations work with one or two components of the source // register or the float constant passed as the third operand of the whole -// co-issued ALU operation, denoted by `a` (the left-hand operand) and `b` -// (the right-hand operand). +// ALU instruction, denoted by `a` (the left-hand operand) and `b` (the +// right-hand operand). // `a` is the [(3 + src3_swizzle[6:7]) & 3] component (W - alpha). -// `b` is the [(0 + src3_swizzle[0:1]) & 3] component (X - red). +// For single-source two-component scalar operations, `b` is the +// [(0 + src3_swizzle[0:1]) & 3] component (X - red), or the +// [(2 + src3_swizzle[4:5]) & 3] component (Z - blue) if the vector operation +// uses source 3. // - mulsc, addsc, subsc scalar ALU operations accept two operands - a float // constant with the `a` (W) swizzle (addressed by the third operand index and // addressing mode) being the left-hand operand, and a temporary register with @@ -1330,11 +1333,12 @@ enum class AluScalarOpcode : uint32_t { struct AluScalarOpcodeInfo { const char* name; // 0 - no operands. - // 1 - one single-component (W) or two-component (WX) r# or c#. + // 1 - one single-component (W) or two-component (WX, or WZ if the vector + // operation uses source 3) r# or c#. // 2 - c#.w and r#.x. uint32_t operand_count; - // If operand_count is 1, whether both W and X of the operand are used rather - // than only W. + // If operand_count is 1, whether two components of the operand are used + // rather than only W. bool single_operand_is_two_component; // Note that all scalar instructions except for retain_prev modify the // previous scalar register, so they must be executed even if they don't write