Files
Xenia-Canary/src/xenia/gpu/dxbc_shader_translator_alu.cc
2021-01-04 16:15:50 +03:00

1085 lines
46 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/dxbc_shader_translator.h"
#include <cfloat>
#include <cmath>
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
namespace xe {
namespace gpu {
using namespace ucode;
void DxbcShaderTranslator::ProcessVectorAluOperation(
const ParsedAluInstruction& instr, uint32_t& result_swizzle,
bool& predicate_written) {
result_swizzle = dxbc::Src::kXYZW;
predicate_written = false;
uint32_t used_result_components =
instr.vector_and_constant_result.GetUsedResultComponents();
if (!used_result_components &&
!AluVectorOpHasSideEffects(instr.vector_opcode)) {
return;
}
// Load operands.
// A small shortcut, operands of cube are the same, but swizzled.
uint32_t operand_count;
if (instr.vector_opcode == AluVectorOpcode::kCube) {
operand_count = 1;
} else {
operand_count = instr.vector_operand_count;
}
uint32_t operand_needed_components[3];
for (uint32_t i = 0; i < operand_count; ++i) {
operand_needed_components[i] = ucode::GetAluVectorOpNeededSourceComponents(
instr.vector_opcode, i + 1, used_result_components);
}
// .zzxy - don't need duplicated Z.
if (instr.vector_opcode == AluVectorOpcode::kCube) {
operand_needed_components[0] &= 0b1101;
}
dxbc::Src operands[3]{dxbc::Src::LF(0.0f), dxbc::Src::LF(0.0f),
dxbc::Src::LF(0.0f)};
uint32_t operand_temps = 0;
for (uint32_t i = 0; i < operand_count; ++i) {
bool operand_temp_pushed = false;
operands[i] =
LoadOperand(instr.vector_operands[i], operand_needed_components[i],
operand_temp_pushed);
operand_temps += uint32_t(operand_temp_pushed);
}
// Don't return without PopSystemTemp(operand_temps) from now on!
dxbc::Dest per_component_dest(
dxbc::Dest::R(system_temp_result_, used_result_components));
switch (instr.vector_opcode) {
case AluVectorOpcode::kAdd:
a_.OpAdd(per_component_dest, operands[0], operands[1]);
break;
case AluVectorOpcode::kMul:
case AluVectorOpcode::kMad: {
// Not using DXBC mad to prevent fused multiply-add (mul followed by add
// may be optimized into non-fused mad by the driver in the identical
// operands case also).
a_.OpMul(per_component_dest, operands[0], operands[1]);
uint32_t multiplicands_different =
used_result_components &
~instr.vector_operands[0].GetIdenticalComponents(
instr.vector_operands[1]);
if (multiplicands_different) {
// Shader Model 3: +-0 or denormal * anything = +0.
uint32_t is_zero_temp = PushSystemTemp();
a_.OpMin(dxbc::Dest::R(is_zero_temp, multiplicands_different),
operands[0].Abs(), operands[1].Abs());
// min isn't required to flush denormals, eq is.
a_.OpEq(dxbc::Dest::R(is_zero_temp, multiplicands_different),
dxbc::Src::R(is_zero_temp), dxbc::Src::LF(0.0f));
// Not replacing true `0 + term` with movc of the term because +0 + -0
// should result in +0, not -0.
a_.OpMovC(dxbc::Dest::R(system_temp_result_, multiplicands_different),
dxbc::Src::R(is_zero_temp), dxbc::Src::LF(0.0f),
dxbc::Src::R(system_temp_result_));
// Release is_zero_temp.
PopSystemTemp();
}
if (instr.vector_opcode == AluVectorOpcode::kMad) {
a_.OpAdd(per_component_dest, dxbc::Src::R(system_temp_result_),
operands[2]);
}
} break;
case AluVectorOpcode::kMax:
case AluVectorOpcode::kMin: {
// max is commonly used as mov.
uint32_t identical = instr.vector_operands[0].GetIdenticalComponents(
instr.vector_operands[1]) &
used_result_components;
uint32_t different = used_result_components & ~identical;
if (different) {
// Shader Model 3 NaN behavior (a op b ? a : b, not fmax/fmin).
if (instr.vector_opcode == AluVectorOpcode::kMin) {
a_.OpLT(dxbc::Dest::R(system_temp_result_, different), operands[0],
operands[1]);
} else {
a_.OpGE(dxbc::Dest::R(system_temp_result_, different), operands[0],
operands[1]);
}
a_.OpMovC(dxbc::Dest::R(system_temp_result_, different),
dxbc::Src::R(system_temp_result_), operands[0], operands[1]);
}
if (identical) {
a_.OpMov(dxbc::Dest::R(system_temp_result_, identical), operands[0]);
}
} break;
case AluVectorOpcode::kSeq:
a_.OpEq(per_component_dest, operands[0], operands[1]);
a_.OpAnd(per_component_dest, dxbc::Src::R(system_temp_result_),
dxbc::Src::LF(1.0f));
break;
case AluVectorOpcode::kSgt:
a_.OpLT(per_component_dest, operands[1], operands[0]);
a_.OpAnd(per_component_dest, dxbc::Src::R(system_temp_result_),
dxbc::Src::LF(1.0f));
break;
case AluVectorOpcode::kSge:
a_.OpGE(per_component_dest, operands[0], operands[1]);
a_.OpAnd(per_component_dest, dxbc::Src::R(system_temp_result_),
dxbc::Src::LF(1.0f));
break;
case AluVectorOpcode::kSne:
a_.OpNE(per_component_dest, operands[0], operands[1]);
a_.OpAnd(per_component_dest, dxbc::Src::R(system_temp_result_),
dxbc::Src::LF(1.0f));
break;
case AluVectorOpcode::kFrc:
a_.OpFrc(per_component_dest, operands[0]);
break;
case AluVectorOpcode::kTrunc:
a_.OpRoundZ(per_component_dest, operands[0]);
break;
case AluVectorOpcode::kFloor:
a_.OpRoundNI(per_component_dest, operands[0]);
break;
case AluVectorOpcode::kCndEq:
a_.OpEq(per_component_dest, operands[0], dxbc::Src::LF(0.0f));
a_.OpMovC(per_component_dest, dxbc::Src::R(system_temp_result_),
operands[1], operands[2]);
break;
case AluVectorOpcode::kCndGe:
a_.OpGE(per_component_dest, operands[0], dxbc::Src::LF(0.0f));
a_.OpMovC(per_component_dest, dxbc::Src::R(system_temp_result_),
operands[1], operands[2]);
break;
case AluVectorOpcode::kCndGt:
a_.OpLT(per_component_dest, dxbc::Src::LF(0.0f), operands[0]);
a_.OpMovC(per_component_dest, dxbc::Src::R(system_temp_result_),
operands[1], operands[2]);
break;
case AluVectorOpcode::kDp4:
case AluVectorOpcode::kDp3:
case AluVectorOpcode::kDp2Add: {
uint32_t component_count;
if (instr.vector_opcode == AluVectorOpcode::kDp2Add) {
component_count = 2;
} else if (instr.vector_opcode == AluVectorOpcode::kDp3) {
component_count = 3;
} else {
component_count = 4;
}
result_swizzle = dxbc::Src::kXXXX;
uint32_t different = uint32_t((1 << component_count) - 1) &
~instr.vector_operands[0].GetIdenticalComponents(
instr.vector_operands[1]);
for (uint32_t i = 0; i < component_count; ++i) {
a_.OpMul(dxbc::Dest::R(system_temp_result_, i ? 0b0010 : 0b0001),
operands[0].SelectFromSwizzled(i),
operands[1].SelectFromSwizzled(i));
if ((different & (1 << i)) != 0) {
// Shader Model 3: +-0 or denormal * anything = +0 (also not replacing
// true `0 + term` with movc of the term because +0 + -0 should result
// in +0, not -0).
a_.OpMin(dxbc::Dest::R(system_temp_result_, 0b0100),
operands[0].SelectFromSwizzled(i).Abs(),
operands[1].SelectFromSwizzled(i).Abs());
a_.OpEq(dxbc::Dest::R(system_temp_result_, 0b0100),
dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f));
a_.OpMovC(dxbc::Dest::R(system_temp_result_, i ? 0b0010 : 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f),
dxbc::Src::R(system_temp_result_,
i ? dxbc::Src::kYYYY : dxbc::Src::kXXXX));
}
if (i) {
// Not using DXBC dp# to avoid fused multiply-add, PC GPUs are scalar
// as of 2020 anyway, and not using mad for the same reason (mul
// followed by add may be optimized into non-fused mad by the driver
// in the identical operands case also).
a_.OpAdd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY));
}
}
if (component_count == 2) {
a_.OpAdd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
operands[2].SelectFromSwizzled(0));
}
} break;
case AluVectorOpcode::kCube: {
// operands[0] is .z_xy.
// Result is T coordinate, S coordinate, 2 * major axis, face ID.
constexpr uint32_t kCubeX = 2, kCubeY = 3, kCubeZ = 0;
dxbc::Src cube_x_src(operands[0].SelectFromSwizzled(kCubeX));
dxbc::Src cube_y_src(operands[0].SelectFromSwizzled(kCubeY));
dxbc::Src cube_z_src(operands[0].SelectFromSwizzled(kCubeZ));
// result.xy = bool2(abs(z) >= abs(x), abs(z) >= abs(y))
a_.OpGE(dxbc::Dest::R(system_temp_result_, 0b0011), cube_z_src.Abs(),
operands[0].SwizzleSwizzled(kCubeX | (kCubeY << 2)).Abs());
// result.x = abs(z) >= abs(x) && abs(z) >= abs(y)
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY));
dxbc::Dest tc_dest(dxbc::Dest::R(system_temp_result_, 0b0001));
dxbc::Dest sc_dest(dxbc::Dest::R(system_temp_result_, 0b0010));
dxbc::Dest ma_dest(dxbc::Dest::R(system_temp_result_, 0b0100));
dxbc::Dest id_dest(dxbc::Dest::R(system_temp_result_, 0b1000));
a_.OpIf(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
{
// Z is the major axis.
// z < 0 needed for SC and ID, but the last to use is ID.
uint32_t ma_neg_component = (used_result_components & 0b1000) ? 3 : 1;
if (used_result_components & 0b1010) {
a_.OpLT(dxbc::Dest::R(system_temp_result_, 1 << ma_neg_component),
cube_z_src, dxbc::Src::LF(0.0f));
}
if (used_result_components & 0b0001) {
a_.OpMov(tc_dest, -cube_y_src);
}
if (used_result_components & 0b0010) {
a_.OpMovC(sc_dest,
dxbc::Src::R(system_temp_result_).Select(ma_neg_component),
-cube_x_src, cube_x_src);
}
if (used_result_components & 0b0100) {
a_.OpMul(ma_dest, dxbc::Src::LF(2.0f), cube_z_src);
}
if (used_result_components & 0b1000) {
a_.OpMovC(id_dest,
dxbc::Src::R(system_temp_result_).Select(ma_neg_component),
dxbc::Src::LF(5.0f), dxbc::Src::LF(4.0f));
}
}
a_.OpElse();
{
// result.x = abs(y) >= abs(x)
a_.OpGE(dxbc::Dest::R(system_temp_result_, 0b0001), cube_y_src.Abs(),
cube_x_src.Abs());
a_.OpIf(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
{
// Y is the major axis.
// y < 0 needed for TC and ID, but the last to use is ID.
uint32_t ma_neg_component = (used_result_components & 0b1000) ? 3 : 0;
if (used_result_components & 0b1001) {
a_.OpLT(dxbc::Dest::R(system_temp_result_, 1 << ma_neg_component),
cube_y_src, dxbc::Src::LF(0.0f));
}
if (used_result_components & 0b0001) {
a_.OpMovC(
tc_dest,
dxbc::Src::R(system_temp_result_).Select(ma_neg_component),
-cube_z_src, cube_z_src);
}
if (used_result_components & 0b0010) {
a_.OpMov(sc_dest, cube_x_src);
}
if (used_result_components & 0b0100) {
a_.OpMul(ma_dest, dxbc::Src::LF(2.0f), cube_y_src);
}
if (used_result_components & 0b1000) {
a_.OpMovC(
id_dest,
dxbc::Src::R(system_temp_result_).Select(ma_neg_component),
dxbc::Src::LF(3.0f), dxbc::Src::LF(2.0f));
}
}
a_.OpElse();
{
// X is the major axis.
// x < 0 needed for SC and ID, but the last to use is ID.
uint32_t ma_neg_component = (used_result_components & 0b1000) ? 3 : 1;
if (used_result_components & 0b1010) {
a_.OpLT(dxbc::Dest::R(system_temp_result_, 1 << ma_neg_component),
cube_x_src, dxbc::Src::LF(0.0f));
}
if (used_result_components & 0b0001) {
a_.OpMov(tc_dest, -cube_y_src);
}
if (used_result_components & 0b0010) {
a_.OpMovC(
sc_dest,
dxbc::Src::R(system_temp_result_).Select(ma_neg_component),
cube_z_src, -cube_z_src);
}
if (used_result_components & 0b0100) {
a_.OpMul(ma_dest, dxbc::Src::LF(2.0f), cube_x_src);
}
if (used_result_components & 0b1000) {
a_.OpAnd(id_dest,
dxbc::Src::R(system_temp_result_).Select(ma_neg_component),
dxbc::Src::LF(1.0f));
}
}
a_.OpEndIf();
}
a_.OpEndIf();
} break;
case AluVectorOpcode::kMax4: {
result_swizzle = dxbc::Src::kXXXX;
// Find max of all different components of the first operand.
// FIXME(Triang3l): Not caring about NaN because no info about the
// correct order, just using SM4 max here, which replaces them with the
// non-NaN component (however, there's one nice thing about it is that it
// may be compiled into max3 + max on GCN).
uint32_t remaining_components = 0;
for (uint32_t i = 0; i < 4; ++i) {
remaining_components |= 1 << ((operands[0].swizzle_ >> (i * 2)) & 3);
}
uint32_t unique_component_0;
xe::bit_scan_forward(remaining_components, &unique_component_0);
remaining_components &= ~uint32_t(1 << unique_component_0);
if (remaining_components) {
uint32_t unique_component_1;
xe::bit_scan_forward(remaining_components, &unique_component_1);
remaining_components &= ~uint32_t(1 << unique_component_1);
a_.OpMax(dxbc::Dest::R(system_temp_result_, 0b0001),
operands[0].Select(unique_component_0),
operands[0].Select(unique_component_1));
while (remaining_components) {
uint32_t unique_component;
xe::bit_scan_forward(remaining_components, &unique_component);
remaining_components &= ~uint32_t(1 << unique_component);
a_.OpMax(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
operands[0].Select(unique_component));
}
} else {
a_.OpMov(dxbc::Dest::R(system_temp_result_, 0b0001),
operands[0].Select(unique_component_0));
}
} break;
case AluVectorOpcode::kSetpEqPush:
predicate_written = true;
result_swizzle = dxbc::Src::kXXXX;
// result.xy = src0.xw == 0.0 (x only if needed).
a_.OpEq(dxbc::Dest::R(system_temp_result_,
used_result_components ? 0b0011 : 0b0010),
operands[0].SwizzleSwizzled(0b1100), dxbc::Src::LF(0.0f));
// result.zw = src1.xw == 0.0 (z only if needed).
a_.OpEq(dxbc::Dest::R(system_temp_result_,
used_result_components ? 0b1100 : 0b1000),
operands[1].SwizzleSwizzled(0b11000000), dxbc::Src::LF(0.0f));
// p0 = src0.w == 0.0 && src1.w == 0.0
a_.OpAnd(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY),
dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW));
if (used_result_components) {
// result = (src0.x == 0.0 && src1.x == 0.0) ? 0.0 : src0.x + 1.0
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ));
// If the condition is true, 1 will be added to make it 0.
a_.OpMovC(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(-1.0f), operands[0].SelectFromSwizzled(0));
a_.OpAdd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(1.0f));
}
break;
case AluVectorOpcode::kSetpNePush:
predicate_written = true;
result_swizzle = dxbc::Src::kXXXX;
// result.xy = src0.xw == 0.0 (x only if needed).
a_.OpEq(dxbc::Dest::R(system_temp_result_,
used_result_components ? 0b0011 : 0b0010),
operands[0].SwizzleSwizzled(0b1100), dxbc::Src::LF(0.0f));
// result.zw = src1.xw != 0.0 (z only if needed).
a_.OpNE(dxbc::Dest::R(system_temp_result_,
used_result_components ? 0b1100 : 0b1000),
operands[1].SwizzleSwizzled(0b11000000), dxbc::Src::LF(0.0f));
// p0 = src0.w == 0.0 && src1.w != 0.0
a_.OpAnd(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY),
dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW));
if (used_result_components) {
// result = (src0.x == 0.0 && src1.x != 0.0) ? 0.0 : src0.x + 1.0
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ));
// If the condition is true, 1 will be added to make it 0.
a_.OpMovC(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(-1.0f), operands[0].SelectFromSwizzled(0));
a_.OpAdd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(1.0f));
}
break;
case AluVectorOpcode::kSetpGtPush:
predicate_written = true;
result_swizzle = dxbc::Src::kXXXX;
// result.xy = src0.xw == 0.0 (x only if needed).
a_.OpEq(dxbc::Dest::R(system_temp_result_,
used_result_components ? 0b0011 : 0b0010),
operands[0].SwizzleSwizzled(0b1100), dxbc::Src::LF(0.0f));
// result.zw = src1.xw > 0.0 (z only if needed).
a_.OpLT(dxbc::Dest::R(system_temp_result_,
used_result_components ? 0b1100 : 0b1000),
dxbc::Src::LF(0.0f), operands[1].SwizzleSwizzled(0b11000000));
// p0 = src0.w == 0.0 && src1.w > 0.0
a_.OpAnd(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY),
dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW));
if (used_result_components) {
// result = (src0.x == 0.0 && src1.x > 0.0) ? 0.0 : src0.x + 1.0
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ));
// If the condition is true, 1 will be added to make it 0.
a_.OpMovC(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(-1.0f), operands[0].SelectFromSwizzled(0));
a_.OpAdd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(1.0f));
}
break;
case AluVectorOpcode::kSetpGePush:
predicate_written = true;
result_swizzle = dxbc::Src::kXXXX;
// result.xy = src0.xw == 0.0 (x only if needed).
a_.OpEq(dxbc::Dest::R(system_temp_result_,
used_result_components ? 0b0011 : 0b0010),
operands[0].SwizzleSwizzled(0b1100), dxbc::Src::LF(0.0f));
// result.zw = src1.xw >= 0.0 (z only if needed).
a_.OpGE(dxbc::Dest::R(system_temp_result_,
used_result_components ? 0b1100 : 0b1000),
operands[1].SwizzleSwizzled(0b11000000), dxbc::Src::LF(0.0f));
// p0 = src0.w == 0.0 && src1.w >= 0.0
a_.OpAnd(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY),
dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW));
if (used_result_components) {
// result = (src0.x == 0.0 && src1.x >= 0.0) ? 0.0 : src0.x + 1.0
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ));
// If the condition is true, 1 will be added to make it 0.
a_.OpMovC(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(-1.0f), operands[0].SelectFromSwizzled(0));
a_.OpAdd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(1.0f));
}
break;
case AluVectorOpcode::kKillEq:
result_swizzle = dxbc::Src::kXXXX;
a_.OpEq(dxbc::Dest::R(system_temp_result_), operands[0], operands[1]);
a_.OpOr(dxbc::Dest::R(system_temp_result_, 0b0011),
dxbc::Src::R(system_temp_result_, 0b0100),
dxbc::Src::R(system_temp_result_, 0b1110));
a_.OpOr(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY));
if (edram_rov_used_) {
a_.OpRetC(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
} else {
a_.OpDiscard(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
}
if (used_result_components) {
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(1.0f));
}
break;
case AluVectorOpcode::kKillGt:
result_swizzle = dxbc::Src::kXXXX;
a_.OpLT(dxbc::Dest::R(system_temp_result_), operands[1], operands[0]);
a_.OpOr(dxbc::Dest::R(system_temp_result_, 0b0011),
dxbc::Src::R(system_temp_result_, 0b0100),
dxbc::Src::R(system_temp_result_, 0b1110));
a_.OpOr(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY));
if (edram_rov_used_) {
a_.OpRetC(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
} else {
a_.OpDiscard(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
}
if (used_result_components) {
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(1.0f));
}
break;
case AluVectorOpcode::kKillGe:
result_swizzle = dxbc::Src::kXXXX;
a_.OpGE(dxbc::Dest::R(system_temp_result_), operands[0], operands[1]);
a_.OpOr(dxbc::Dest::R(system_temp_result_, 0b0011),
dxbc::Src::R(system_temp_result_, 0b0100),
dxbc::Src::R(system_temp_result_, 0b1110));
a_.OpOr(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY));
if (edram_rov_used_) {
a_.OpRetC(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
} else {
a_.OpDiscard(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
}
if (used_result_components) {
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(1.0f));
}
break;
case AluVectorOpcode::kKillNe:
result_swizzle = dxbc::Src::kXXXX;
a_.OpNE(dxbc::Dest::R(system_temp_result_), operands[0], operands[1]);
a_.OpOr(dxbc::Dest::R(system_temp_result_, 0b0011),
dxbc::Src::R(system_temp_result_, 0b0100),
dxbc::Src::R(system_temp_result_, 0b1110));
a_.OpOr(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY));
if (edram_rov_used_) {
a_.OpRetC(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
} else {
a_.OpDiscard(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX));
}
if (used_result_components) {
a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::R(system_temp_result_, dxbc::Src::kXXXX),
dxbc::Src::LF(1.0f));
}
break;
case AluVectorOpcode::kDst:
if (used_result_components & 0b0001) {
a_.OpMov(dxbc::Dest::R(system_temp_result_, 0b0001),
dxbc::Src::LF(1.0f));
}
if (used_result_components & 0b0010) {
a_.OpMul(dxbc::Dest::R(system_temp_result_, 0b0010),
operands[0].SelectFromSwizzled(1),
operands[1].SelectFromSwizzled(1));
if (!(instr.vector_operands[0].GetIdenticalComponents(
instr.vector_operands[1]) &
0b0010)) {
// Shader Model 3: +-0 or denormal * anything = +0.
a_.OpMin(dxbc::Dest::R(system_temp_result_, 0b0100),
operands[0].SelectFromSwizzled(1).Abs(),
operands[1].SelectFromSwizzled(1).Abs());
// min isn't required to flush denormals, eq is.
a_.OpEq(dxbc::Dest::R(system_temp_result_, 0b0100),
dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f));
a_.OpMovC(dxbc::Dest::R(system_temp_result_, 0b0010),
dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f),
dxbc::Src::R(system_temp_result_, dxbc::Src::kYYYY));
}
}
if (used_result_components & 0b0100) {
a_.OpMov(dxbc::Dest::R(system_temp_result_, 0b0100),
operands[0].SelectFromSwizzled(2));
}
if (used_result_components & 0b1000) {
a_.OpMov(dxbc::Dest::R(system_temp_result_, 0b1000),
operands[1].SelectFromSwizzled(2));
}
break;
case AluVectorOpcode::kMaxA:
a_.OpAdd(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
operands[0].SelectFromSwizzled(3), dxbc::Src::LF(0.5f));
a_.OpRoundNI(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kWWWW));
a_.OpMax(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kWWWW),
dxbc::Src::LF(-256.0f));
a_.OpMin(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kWWWW),
dxbc::Src::LF(255.0f));
a_.OpFToI(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kWWWW));
if (used_result_components) {
uint32_t identical = instr.vector_operands[0].GetIdenticalComponents(
instr.vector_operands[1]) &
used_result_components;
uint32_t different = used_result_components & ~identical;
if (different) {
// Shader Model 3 NaN behavior (a >= b ? a : b, not fmax).
a_.OpGE(dxbc::Dest::R(system_temp_result_, different), operands[0],
operands[1]);
a_.OpMovC(dxbc::Dest::R(system_temp_result_, different),
dxbc::Src::R(system_temp_result_), operands[0],
operands[1]);
}
if (identical) {
a_.OpMov(dxbc::Dest::R(system_temp_result_, identical), operands[0]);
}
}
break;
default:
assert_unhandled_case(instr.vector_opcode);
EmitTranslationError("Unknown ALU vector operation");
a_.OpMov(dxbc::Dest::R(system_temp_result_), dxbc::Src::LF(0.0f));
}
PopSystemTemp(operand_temps);
}
void DxbcShaderTranslator::ProcessScalarAluOperation(
const ParsedAluInstruction& instr, bool& predicate_written) {
predicate_written = false;
if (instr.scalar_opcode == ucode::AluScalarOpcode::kRetainPrev) {
return;
}
// Load operands.
dxbc::Src operands_loaded[2]{dxbc::Src::LF(0.0f), dxbc::Src::LF(0.0f)};
uint32_t operand_temps = 0;
for (uint32_t i = 0; i < instr.scalar_operand_count; ++i) {
bool operand_temp_pushed = false;
operands_loaded[i] =
LoadOperand(instr.scalar_operands[i],
(1 << instr.scalar_operands[i].component_count) - 1,
operand_temp_pushed);
operand_temps += uint32_t(operand_temp_pushed);
}
// Don't return without PopSystemTemp(operand_temps) from now on!
dxbc::Src operand_0_a(operands_loaded[0].SelectFromSwizzled(0));
dxbc::Src operand_0_b(operands_loaded[0].SelectFromSwizzled(1));
dxbc::Src operand_1(operands_loaded[1].SelectFromSwizzled(0));
dxbc::Dest ps_dest(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0001));
dxbc::Src ps_src(dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kXXXX));
switch (instr.scalar_opcode) {
case AluScalarOpcode::kAdds:
a_.OpAdd(ps_dest, operand_0_a, operand_0_b);
break;
case AluScalarOpcode::kAddsPrev:
a_.OpAdd(ps_dest, operand_0_a, ps_src);
break;
case AluScalarOpcode::kMuls:
a_.OpMul(ps_dest, operand_0_a, operand_0_b);
if (instr.scalar_operands[0].components[0] !=
instr.scalar_operands[0].components[1]) {
// Shader Model 3: +-0 or denormal * anything = +0.
uint32_t is_zero_temp = PushSystemTemp();
a_.OpMin(dxbc::Dest::R(is_zero_temp, 0b0001), operand_0_a.Abs(),
operand_0_b.Abs());
// min isn't required to flush denormals, eq is.
a_.OpEq(dxbc::Dest::R(is_zero_temp, 0b0001),
dxbc::Src::R(is_zero_temp, dxbc::Src::kXXXX),
dxbc::Src::LF(0.0f));
a_.OpMovC(ps_dest, dxbc::Src::R(is_zero_temp, dxbc::Src::kXXXX),
dxbc::Src::LF(0.0f), ps_src);
// Release is_zero_temp.
PopSystemTemp();
}
break;
case AluScalarOpcode::kMulsPrev:
case AluScalarOpcode::kMulsPrev2: {
uint32_t test_temp = PushSystemTemp();
if (instr.scalar_opcode == AluScalarOpcode::kMulsPrev2) {
// Check if need to select the src0.a * ps case.
// ps != -FLT_MAX.
a_.OpNE(dxbc::Dest::R(test_temp, 0b0001), ps_src,
dxbc::Src::LF(-FLT_MAX));
// isfinite(ps), or |ps| <= FLT_MAX, or -|ps| >= -FLT_MAX, since
// -FLT_MAX is already loaded to an SGPR, this is also false if it's
// NaN.
a_.OpGE(dxbc::Dest::R(test_temp, 0b0010), -ps_src.Abs(),
dxbc::Src::LF(-FLT_MAX));
a_.OpAnd(dxbc::Dest::R(test_temp, 0b0001),
dxbc::Src::R(test_temp, dxbc::Src::kXXXX),
dxbc::Src::R(test_temp, dxbc::Src::kYYYY));
// isfinite(src0.b).
a_.OpGE(dxbc::Dest::R(test_temp, 0b0010), -operand_0_b.Abs(),
dxbc::Src::LF(-FLT_MAX));
a_.OpAnd(dxbc::Dest::R(test_temp, 0b0001),
dxbc::Src::R(test_temp, dxbc::Src::kXXXX),
dxbc::Src::R(test_temp, dxbc::Src::kYYYY));
// src0.b > 0 (need !(src0.b <= 0), but src0.b has already been checked
// for NaN).
a_.OpLT(dxbc::Dest::R(test_temp, 0b0010), dxbc::Src::LF(0.0f),
operand_0_b);
a_.OpAnd(dxbc::Dest::R(test_temp, 0b0001),
dxbc::Src::R(test_temp, dxbc::Src::kXXXX),
dxbc::Src::R(test_temp, dxbc::Src::kYYYY));
a_.OpIf(true, dxbc::Src::R(test_temp, dxbc::Src::kXXXX));
}
// Shader Model 3: +-0 or denormal * anything = +0.
a_.OpMin(dxbc::Dest::R(test_temp, 0b0001), operand_0_a.Abs(),
ps_src.Abs());
// min isn't required to flush denormals, eq is.
a_.OpEq(dxbc::Dest::R(test_temp, 0b0001),
dxbc::Src::R(test_temp, dxbc::Src::kXXXX), dxbc::Src::LF(0.0f));
a_.OpMul(ps_dest, operand_0_a, ps_src);
a_.OpMovC(ps_dest, dxbc::Src::R(test_temp, dxbc::Src::kXXXX),
dxbc::Src::LF(0.0f), ps_src);
if (instr.scalar_opcode == AluScalarOpcode::kMulsPrev2) {
a_.OpElse();
a_.OpMov(ps_dest, dxbc::Src::LF(-FLT_MAX));
a_.OpEndIf();
}
// Release test_temp.
PopSystemTemp();
} break;
case AluScalarOpcode::kMaxs:
case AluScalarOpcode::kMins:
// max is commonly used as mov.
if (instr.scalar_operands[0].components[0] ==
instr.scalar_operands[0].components[1]) {
a_.OpMov(ps_dest, operand_0_a);
} else {
// Shader Model 3 NaN behavior (a op b ? a : b, not fmax/fmin).
if (instr.scalar_opcode == AluScalarOpcode::kMins) {
a_.OpLT(ps_dest, operand_0_a, operand_0_b);
} else {
a_.OpGE(ps_dest, operand_0_a, operand_0_b);
}
a_.OpMovC(ps_dest, ps_src, operand_0_a, operand_0_b);
}
break;
case AluScalarOpcode::kSeqs:
a_.OpEq(ps_dest, operand_0_a, dxbc::Src::LF(0.0f));
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kSgts:
a_.OpLT(ps_dest, dxbc::Src::LF(0.0f), operand_0_a);
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kSges:
a_.OpGE(ps_dest, operand_0_a, dxbc::Src::LF(0.0f));
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kSnes:
a_.OpNE(ps_dest, operand_0_a, dxbc::Src::LF(0.0f));
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kFrcs:
a_.OpFrc(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kTruncs:
a_.OpRoundZ(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kFloors:
a_.OpRoundNI(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kExp:
a_.OpExp(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kLogc: {
a_.OpLog(ps_dest, operand_0_a);
uint32_t is_neg_infinity_temp = PushSystemTemp();
a_.OpEq(dxbc::Dest::R(is_neg_infinity_temp, 0b0001), ps_src,
dxbc::Src::LF(-INFINITY));
a_.OpMovC(ps_dest, dxbc::Src::R(is_neg_infinity_temp, dxbc::Src::kXXXX),
dxbc::Src::LF(-FLT_MAX), ps_src);
// Release is_neg_infinity_temp.
PopSystemTemp();
} break;
case AluScalarOpcode::kLog:
a_.OpLog(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kRcpc:
case AluScalarOpcode::kRsqc: {
if (instr.scalar_opcode == AluScalarOpcode::kRsqc) {
a_.OpRSq(ps_dest, operand_0_a);
} else {
a_.OpRcp(ps_dest, operand_0_a);
}
uint32_t is_infinity_temp = PushSystemTemp();
a_.OpEq(dxbc::Dest::R(is_infinity_temp, 0b0001), ps_src.Abs(),
dxbc::Src::LF(INFINITY));
// If +-Infinity (0x7F800000 or 0xFF800000), add -1 (0xFFFFFFFF) to turn
// into +-FLT_MAX (0x7F7FFFFF or 0xFF7FFFFF).
a_.OpIAdd(ps_dest, ps_src,
dxbc::Src::R(is_infinity_temp, dxbc::Src::kXXXX));
// Release is_infinity_temp.
PopSystemTemp();
} break;
case AluScalarOpcode::kRcpf:
case AluScalarOpcode::kRsqf: {
if (instr.scalar_opcode == AluScalarOpcode::kRsqf) {
a_.OpRSq(ps_dest, operand_0_a);
} else {
a_.OpRcp(ps_dest, operand_0_a);
}
uint32_t is_not_infinity_temp = PushSystemTemp();
a_.OpNE(dxbc::Dest::R(is_not_infinity_temp, 0b0001), ps_src.Abs(),
dxbc::Src::LF(INFINITY));
// Keep the sign bit if infinity.
a_.OpOr(dxbc::Dest::R(is_not_infinity_temp, 0b0001),
dxbc::Src::R(is_not_infinity_temp, dxbc::Src::kXXXX),
dxbc::Src::LU(uint32_t(1) << 31));
a_.OpAnd(ps_dest, ps_src,
dxbc::Src::R(is_not_infinity_temp, dxbc::Src::kXXXX));
// Release is_not_infinity_temp.
PopSystemTemp();
} break;
case AluScalarOpcode::kRcp:
a_.OpRcp(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kRsq:
a_.OpRSq(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kMaxAs:
case AluScalarOpcode::kMaxAsf:
if (instr.scalar_opcode == AluScalarOpcode::kMaxAsf) {
a_.OpRoundNI(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
operand_0_a);
} else {
a_.OpAdd(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000), operand_0_a,
dxbc::Src::LF(0.5f));
a_.OpRoundNI(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kWWWW));
}
a_.OpMax(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kWWWW),
dxbc::Src::LF(-256.0f));
a_.OpMin(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kWWWW),
dxbc::Src::LF(255.0f));
a_.OpFToI(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b1000),
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kWWWW));
if (instr.scalar_operands[0].components[0] ==
instr.scalar_operands[0].components[1]) {
a_.OpMov(ps_dest, operand_0_a);
} else {
// Shader Model 3 NaN behavior (a >= b ? a : b, not fmax).
a_.OpGE(ps_dest, operand_0_a, operand_0_b);
a_.OpMovC(ps_dest, ps_src, operand_0_a, operand_0_b);
}
break;
case AluScalarOpcode::kSubs:
a_.OpAdd(ps_dest, operand_0_a, -operand_0_b);
break;
case AluScalarOpcode::kSubsPrev:
a_.OpAdd(ps_dest, operand_0_a, -ps_src);
break;
case AluScalarOpcode::kSetpEq:
predicate_written = true;
a_.OpEq(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
dxbc::Src::LF(0.0f));
a_.OpMovC(ps_dest,
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f), dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kSetpNe:
predicate_written = true;
a_.OpNE(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
dxbc::Src::LF(0.0f));
a_.OpMovC(ps_dest,
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f), dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kSetpGt:
predicate_written = true;
a_.OpLT(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100),
dxbc::Src::LF(0.0f), operand_0_a);
a_.OpMovC(ps_dest,
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f), dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kSetpGe:
predicate_written = true;
a_.OpGE(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
dxbc::Src::LF(0.0f));
a_.OpMovC(ps_dest,
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f), dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kSetpInv:
predicate_written = true;
// Calculate ps as if src0.a != 1.0 (the false predicate value case).
a_.OpEq(ps_dest, operand_0_a, dxbc::Src::LF(0.0f));
a_.OpMovC(ps_dest, ps_src, dxbc::Src::LF(1.0f), operand_0_a);
// Set the predicate to src0.a == 1.0, and, if it's true, zero ps.
a_.OpEq(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
dxbc::Src::LF(1.0f));
a_.OpMovC(ps_dest,
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f), ps_src);
break;
case AluScalarOpcode::kSetpPop:
predicate_written = true;
a_.OpAdd(ps_dest, operand_0_a, dxbc::Src::LF(-1.0f));
a_.OpGE(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100),
dxbc::Src::LF(0.0f), ps_src);
a_.OpMovC(ps_dest,
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f), ps_src);
break;
case AluScalarOpcode::kSetpClr:
predicate_written = true;
a_.OpMov(ps_dest, dxbc::Src::LF(FLT_MAX));
a_.OpMov(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100),
dxbc::Src::LU(0));
break;
case AluScalarOpcode::kSetpRstr:
predicate_written = true;
a_.OpEq(dxbc::Dest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
dxbc::Src::LF(0.0f));
// Just copying src0.a to ps (since it's set to 0 if it's 0) could work,
// but flush denormals and zero sign just for safety.
a_.OpMovC(ps_dest,
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kZZZZ),
dxbc::Src::LF(0.0f), operand_0_a);
break;
case AluScalarOpcode::kKillsEq:
a_.OpEq(ps_dest, operand_0_a, dxbc::Src::LF(0.0f));
if (edram_rov_used_) {
a_.OpRetC(true, ps_src);
} else {
a_.OpDiscard(true, ps_src);
}
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kKillsGt:
a_.OpLT(ps_dest, dxbc::Src::LF(0.0f), operand_0_a);
if (edram_rov_used_) {
a_.OpRetC(true, ps_src);
} else {
a_.OpDiscard(true, ps_src);
}
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kKillsGe:
a_.OpGE(ps_dest, operand_0_a, dxbc::Src::LF(0.0f));
if (edram_rov_used_) {
a_.OpRetC(true, ps_src);
} else {
a_.OpDiscard(true, ps_src);
}
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kKillsNe:
a_.OpNE(ps_dest, operand_0_a, dxbc::Src::LF(0.0f));
if (edram_rov_used_) {
a_.OpRetC(true, ps_src);
} else {
a_.OpDiscard(true, ps_src);
}
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kKillsOne:
a_.OpEq(ps_dest, operand_0_a, dxbc::Src::LF(1.0f));
if (edram_rov_used_) {
a_.OpRetC(true, ps_src);
} else {
a_.OpDiscard(true, ps_src);
}
a_.OpAnd(ps_dest, ps_src, dxbc::Src::LF(1.0f));
break;
case AluScalarOpcode::kSqrt:
a_.OpSqRt(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kMulsc0:
case AluScalarOpcode::kMulsc1:
a_.OpMul(ps_dest, operand_0_a, operand_1);
if (!(instr.scalar_operands[0].GetIdenticalComponents(
instr.scalar_operands[1]) &
0b0001)) {
// Shader Model 3: +-0 or denormal * anything = +0.
uint32_t is_zero_temp = PushSystemTemp();
a_.OpMin(dxbc::Dest::R(is_zero_temp, 0b0001), operand_0_a.Abs(),
operand_1.Abs());
// min isn't required to flush denormals, eq is.
a_.OpEq(dxbc::Dest::R(is_zero_temp, 0b0001),
dxbc::Src::R(is_zero_temp, dxbc::Src::kXXXX),
dxbc::Src::LF(0.0f));
a_.OpMovC(ps_dest, dxbc::Src::R(is_zero_temp, dxbc::Src::kXXXX),
dxbc::Src::LF(0.0f), ps_src);
// Release is_zero_temp.
PopSystemTemp();
}
break;
case AluScalarOpcode::kAddsc0:
case AluScalarOpcode::kAddsc1:
a_.OpAdd(ps_dest, operand_0_a, operand_1);
break;
case AluScalarOpcode::kSubsc0:
case AluScalarOpcode::kSubsc1:
a_.OpAdd(ps_dest, operand_0_a, -operand_1);
break;
case AluScalarOpcode::kSin:
a_.OpSinCos(ps_dest, dxbc::Dest::Null(), operand_0_a);
break;
case AluScalarOpcode::kCos:
a_.OpSinCos(dxbc::Dest::Null(), ps_dest, operand_0_a);
break;
default:
assert_unhandled_case(instr.scalar_opcode);
EmitTranslationError("Unknown ALU scalar operation");
a_.OpMov(ps_dest, dxbc::Src::LF(0.0f));
}
PopSystemTemp(operand_temps);
}
void DxbcShaderTranslator::ProcessAluInstruction(
const ParsedAluInstruction& instr) {
if (instr.IsNop()) {
// Don't even disassemble or update predication.
return;
}
if (emit_source_map_) {
instruction_disassembly_buffer_.Reset();
instr.Disassemble(&instruction_disassembly_buffer_);
}
UpdateInstructionPredicationAndEmitDisassembly(instr.is_predicated,
instr.predicate_condition);
// Whether the instruction has changed the predicate, and it needs to be
// checked again later.
bool predicate_written_vector = false;
uint32_t vector_result_swizzle = dxbc::Src::kXYZW;
ProcessVectorAluOperation(instr, vector_result_swizzle,
predicate_written_vector);
bool predicate_written_scalar = false;
ProcessScalarAluOperation(instr, predicate_written_scalar);
StoreResult(instr.vector_and_constant_result,
dxbc::Src::R(system_temp_result_, vector_result_swizzle),
instr.GetMemExportStreamConstant() != UINT32_MAX);
StoreResult(instr.scalar_result,
dxbc::Src::R(system_temp_ps_pc_p0_a0_, dxbc::Src::kXXXX));
if (predicate_written_vector || predicate_written_scalar) {
cf_exec_predicate_written_ = true;
CloseInstructionPredication();
}
}
} // namespace gpu
} // namespace xe