Files
Xenia-Canary/src/xenia/gpu/dxbc_shader_translator_alu.cc
2020-05-10 20:57:33 +03:00

1105 lines
47 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 = DxbcSrc::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;
}
DxbcSrc operands[3]{DxbcSrc::LF(0.0f), DxbcSrc::LF(0.0f), DxbcSrc::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!
DxbcDest per_component_dest(
DxbcDest::R(system_temp_result_, used_result_components));
switch (instr.vector_opcode) {
case AluVectorOpcode::kAdd:
DxbcOpAdd(per_component_dest, operands[0], operands[1]);
break;
case AluVectorOpcode::kMul:
case AluVectorOpcode::kMad: {
bool is_mad = instr.vector_opcode == AluVectorOpcode::kMad;
if (is_mad) {
DxbcOpMAd(per_component_dest, operands[0], operands[1], operands[2]);
} else {
DxbcOpMul(per_component_dest, operands[0], operands[1]);
}
// Shader Model 3: 0 or denormal * anything = 0.
// FIXME(Triang3l): Signed zero needs research and handling.
uint32_t absolute_different =
used_result_components &
~instr.vector_operands[0].GetAbsoluteIdenticalComponents(
instr.vector_operands[1]);
if (absolute_different) {
uint32_t is_zero_temp = PushSystemTemp();
DxbcOpMin(DxbcDest::R(is_zero_temp, absolute_different),
operands[0].Abs(), operands[1].Abs());
// min isn't required to flush denormals, eq is.
DxbcOpEq(DxbcDest::R(is_zero_temp, absolute_different),
DxbcSrc::R(is_zero_temp), DxbcSrc::LF(0.0f));
DxbcOpMovC(DxbcDest::R(system_temp_result_, absolute_different),
DxbcSrc::R(is_zero_temp),
is_mad ? operands[2] : DxbcSrc::LF(0.0f),
DxbcSrc::R(system_temp_result_));
// Release is_zero_temp.
PopSystemTemp();
}
} 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) {
DxbcOpLT(DxbcDest::R(system_temp_result_, different), operands[0],
operands[1]);
} else {
DxbcOpGE(DxbcDest::R(system_temp_result_, different), operands[0],
operands[1]);
}
DxbcOpMovC(DxbcDest::R(system_temp_result_, different),
DxbcSrc::R(system_temp_result_), operands[0], operands[1]);
}
if (identical) {
DxbcOpMov(DxbcDest::R(system_temp_result_, identical), operands[0]);
}
} break;
case AluVectorOpcode::kSeq:
DxbcOpEq(per_component_dest, operands[0], operands[1]);
DxbcOpAnd(per_component_dest, DxbcSrc::R(system_temp_result_),
DxbcSrc::LF(1.0f));
break;
case AluVectorOpcode::kSgt:
DxbcOpLT(per_component_dest, operands[1], operands[0]);
DxbcOpAnd(per_component_dest, DxbcSrc::R(system_temp_result_),
DxbcSrc::LF(1.0f));
break;
case AluVectorOpcode::kSge:
DxbcOpGE(per_component_dest, operands[0], operands[1]);
DxbcOpAnd(per_component_dest, DxbcSrc::R(system_temp_result_),
DxbcSrc::LF(1.0f));
break;
case AluVectorOpcode::kSne:
DxbcOpNE(per_component_dest, operands[0], operands[1]);
DxbcOpAnd(per_component_dest, DxbcSrc::R(system_temp_result_),
DxbcSrc::LF(1.0f));
break;
case AluVectorOpcode::kFrc:
DxbcOpFrc(per_component_dest, operands[0]);
break;
case AluVectorOpcode::kTrunc:
DxbcOpRoundZ(per_component_dest, operands[0]);
break;
case AluVectorOpcode::kFloor:
DxbcOpRoundNI(per_component_dest, operands[0]);
break;
case AluVectorOpcode::kCndEq:
DxbcOpEq(per_component_dest, operands[0], DxbcSrc::LF(0.0f));
DxbcOpMovC(per_component_dest, DxbcSrc::R(system_temp_result_),
operands[1], operands[2]);
break;
case AluVectorOpcode::kCndGe:
DxbcOpGE(per_component_dest, operands[0], DxbcSrc::LF(0.0f));
DxbcOpMovC(per_component_dest, DxbcSrc::R(system_temp_result_),
operands[1], operands[2]);
break;
case AluVectorOpcode::kCndGt:
DxbcOpLT(per_component_dest, DxbcSrc::LF(0.0f), operands[0]);
DxbcOpMovC(per_component_dest, DxbcSrc::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 = DxbcSrc::kXXXX;
uint32_t absolute_different =
uint32_t((1 << component_count) - 1) &
~instr.vector_operands[0].GetAbsoluteIdenticalComponents(
instr.vector_operands[1]);
if (absolute_different) {
// Shader Model 3: 0 or denormal * anything = 0.
// FIXME(Triang3l): Signed zero needs research and handling.
// Add component products only if non-zero. For dp4, 16 scalar
// operations in the worst case (as opposed to always 20 for
// eq/movc/eq/movc/dp4 or min/eq/movc/movc/dp4 for preparing operands
// for dp4).
DxbcOpMul(DxbcDest::R(system_temp_result_, 0b0001),
operands[0].SelectFromSwizzled(0),
operands[1].SelectFromSwizzled(0));
if (absolute_different & 0b0001) {
DxbcOpMin(DxbcDest::R(system_temp_result_, 0b0010),
operands[0].SelectFromSwizzled(0).Abs(),
operands[1].SelectFromSwizzled(0).Abs());
DxbcOpEq(DxbcDest::R(system_temp_result_, 0b0010),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY),
DxbcSrc::LF(0.0f));
DxbcOpMovC(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY),
DxbcSrc::LF(0.0f),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
}
for (uint32_t i = 1; i < component_count; ++i) {
bool component_different = (absolute_different & (1 << i)) != 0;
DxbcOpMAd(DxbcDest::R(system_temp_result_,
component_different ? 0b0010 : 0b0001),
operands[0].SelectFromSwizzled(i),
operands[1].SelectFromSwizzled(i),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
if (component_different) {
DxbcOpMin(DxbcDest::R(system_temp_result_, 0b0100),
operands[0].SelectFromSwizzled(i).Abs(),
operands[1].SelectFromSwizzled(i).Abs());
DxbcOpEq(DxbcDest::R(system_temp_result_, 0b0100),
DxbcSrc::R(system_temp_result_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f));
DxbcOpMovC(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kZZZZ),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY));
}
}
} else {
if (component_count == 2) {
DxbcOpDP2(DxbcDest::R(system_temp_result_, 0b0001), operands[0],
operands[1]);
} else if (component_count == 3) {
DxbcOpDP3(DxbcDest::R(system_temp_result_, 0b0001), operands[0],
operands[1]);
} else {
assert_true(component_count == 4);
DxbcOpDP4(DxbcDest::R(system_temp_result_, 0b0001), operands[0],
operands[1]);
}
}
if (component_count == 2) {
// Add the third operand. Since floating-point addition isn't
// associative, even though adding this in multiply-add for the first
// component would be faster, it's safer to add here, in the end.
DxbcOpAdd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::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;
DxbcSrc cube_x_src(operands[0].SelectFromSwizzled(kCubeX));
DxbcSrc cube_y_src(operands[0].SelectFromSwizzled(kCubeY));
DxbcSrc cube_z_src(operands[0].SelectFromSwizzled(kCubeZ));
// result.xy = bool2(abs(z) >= abs(x), abs(z) >= abs(y))
DxbcOpGE(DxbcDest::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)
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY));
DxbcDest tc_dest(DxbcDest::R(system_temp_result_, 0b0001));
DxbcDest sc_dest(DxbcDest::R(system_temp_result_, 0b0010));
DxbcDest ma_dest(DxbcDest::R(system_temp_result_, 0b0100));
DxbcDest id_dest(DxbcDest::R(system_temp_result_, 0b1000));
DxbcOpIf(true, DxbcSrc::R(system_temp_result_, DxbcSrc::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) {
DxbcOpLT(DxbcDest::R(system_temp_result_, 1 << ma_neg_component),
cube_z_src, DxbcSrc::LF(0.0f));
}
if (used_result_components & 0b0001) {
DxbcOpMov(tc_dest, -cube_y_src);
}
if (used_result_components & 0b0010) {
DxbcOpMovC(sc_dest,
DxbcSrc::R(system_temp_result_).Select(ma_neg_component),
-cube_x_src, cube_x_src);
}
if (used_result_components & 0b0100) {
DxbcOpMul(ma_dest, DxbcSrc::LF(2.0f), cube_z_src);
}
if (used_result_components & 0b1000) {
DxbcOpMovC(id_dest,
DxbcSrc::R(system_temp_result_).Select(ma_neg_component),
DxbcSrc::LF(5.0f), DxbcSrc::LF(4.0f));
}
}
DxbcOpElse();
{
// result.x = abs(y) >= abs(x)
DxbcOpGE(DxbcDest::R(system_temp_result_, 0b0001), cube_y_src.Abs(),
cube_x_src.Abs());
DxbcOpIf(true, DxbcSrc::R(system_temp_result_, DxbcSrc::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) {
DxbcOpLT(DxbcDest::R(system_temp_result_, 1 << ma_neg_component),
cube_y_src, DxbcSrc::LF(0.0f));
}
if (used_result_components & 0b0001) {
DxbcOpMovC(tc_dest,
DxbcSrc::R(system_temp_result_).Select(ma_neg_component),
-cube_z_src, cube_z_src);
}
if (used_result_components & 0b0010) {
DxbcOpMov(sc_dest, cube_x_src);
}
if (used_result_components & 0b0100) {
DxbcOpMul(ma_dest, DxbcSrc::LF(2.0f), cube_y_src);
}
if (used_result_components & 0b1000) {
DxbcOpMovC(id_dest,
DxbcSrc::R(system_temp_result_).Select(ma_neg_component),
DxbcSrc::LF(3.0f), DxbcSrc::LF(2.0f));
}
}
DxbcOpElse();
{
// 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) {
DxbcOpLT(DxbcDest::R(system_temp_result_, 1 << ma_neg_component),
cube_x_src, DxbcSrc::LF(0.0f));
}
if (used_result_components & 0b0001) {
DxbcOpMov(tc_dest, -cube_y_src);
}
if (used_result_components & 0b0010) {
DxbcOpMovC(sc_dest,
DxbcSrc::R(system_temp_result_).Select(ma_neg_component),
cube_z_src, -cube_z_src);
}
if (used_result_components & 0b0100) {
DxbcOpMul(ma_dest, DxbcSrc::LF(2.0f), cube_x_src);
}
if (used_result_components & 0b1000) {
DxbcOpAnd(id_dest,
DxbcSrc::R(system_temp_result_).Select(ma_neg_component),
DxbcSrc::LF(1.0f));
}
}
DxbcOpEndIf();
}
DxbcOpEndIf();
} break;
case AluVectorOpcode::kMax4: {
result_swizzle = DxbcSrc::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);
DxbcOpMax(DxbcDest::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);
DxbcOpMax(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
operands[0].Select(unique_component));
}
} else {
DxbcOpMov(DxbcDest::R(system_temp_result_, 0b0001),
operands[0].Select(unique_component_0));
}
} break;
case AluVectorOpcode::kSetpEqPush:
predicate_written = true;
result_swizzle = DxbcSrc::kXXXX;
// result.xy = src0.xw == 0.0 (x only if needed).
DxbcOpEq(DxbcDest::R(system_temp_result_,
used_result_components ? 0b0011 : 0b0010),
operands[0].SwizzleSwizzled(0b1100), DxbcSrc::LF(0.0f));
// result.zw = src1.xw == 0.0 (z only if needed).
DxbcOpEq(DxbcDest::R(system_temp_result_,
used_result_components ? 0b1100 : 0b1000),
operands[1].SwizzleSwizzled(0b11000000), DxbcSrc::LF(0.0f));
// p0 = src0.w == 0.0 && src1.w == 0.0
DxbcOpAnd(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY),
DxbcSrc::R(system_temp_result_, DxbcSrc::kWWWW));
if (used_result_components) {
// result = (src0.x == 0.0 && src1.x == 0.0) ? 0.0 : src0.x + 1.0
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kZZZZ));
// If the condition is true, 1 will be added to make it 0.
DxbcOpMovC(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(-1.0f), operands[0].SelectFromSwizzled(0));
DxbcOpAdd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(1.0f));
}
break;
case AluVectorOpcode::kSetpNePush:
predicate_written = true;
result_swizzle = DxbcSrc::kXXXX;
// result.xy = src0.xw == 0.0 (x only if needed).
DxbcOpEq(DxbcDest::R(system_temp_result_,
used_result_components ? 0b0011 : 0b0010),
operands[0].SwizzleSwizzled(0b1100), DxbcSrc::LF(0.0f));
// result.zw = src1.xw != 0.0 (z only if needed).
DxbcOpNE(DxbcDest::R(system_temp_result_,
used_result_components ? 0b1100 : 0b1000),
operands[1].SwizzleSwizzled(0b11000000), DxbcSrc::LF(0.0f));
// p0 = src0.w == 0.0 && src1.w != 0.0
DxbcOpAnd(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY),
DxbcSrc::R(system_temp_result_, DxbcSrc::kWWWW));
if (used_result_components) {
// result = (src0.x == 0.0 && src1.x != 0.0) ? 0.0 : src0.x + 1.0
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kZZZZ));
// If the condition is true, 1 will be added to make it 0.
DxbcOpMovC(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(-1.0f), operands[0].SelectFromSwizzled(0));
DxbcOpAdd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(1.0f));
}
break;
case AluVectorOpcode::kSetpGtPush:
predicate_written = true;
result_swizzle = DxbcSrc::kXXXX;
// result.xy = src0.xw == 0.0 (x only if needed).
DxbcOpEq(DxbcDest::R(system_temp_result_,
used_result_components ? 0b0011 : 0b0010),
operands[0].SwizzleSwizzled(0b1100), DxbcSrc::LF(0.0f));
// result.zw = src1.xw > 0.0 (z only if needed).
DxbcOpLT(DxbcDest::R(system_temp_result_,
used_result_components ? 0b1100 : 0b1000),
DxbcSrc::LF(0.0f), operands[1].SwizzleSwizzled(0b11000000));
// p0 = src0.w == 0.0 && src1.w > 0.0
DxbcOpAnd(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY),
DxbcSrc::R(system_temp_result_, DxbcSrc::kWWWW));
if (used_result_components) {
// result = (src0.x == 0.0 && src1.x > 0.0) ? 0.0 : src0.x + 1.0
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kZZZZ));
// If the condition is true, 1 will be added to make it 0.
DxbcOpMovC(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(-1.0f), operands[0].SelectFromSwizzled(0));
DxbcOpAdd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(1.0f));
}
break;
case AluVectorOpcode::kSetpGePush:
predicate_written = true;
result_swizzle = DxbcSrc::kXXXX;
// result.xy = src0.xw == 0.0 (x only if needed).
DxbcOpEq(DxbcDest::R(system_temp_result_,
used_result_components ? 0b0011 : 0b0010),
operands[0].SwizzleSwizzled(0b1100), DxbcSrc::LF(0.0f));
// result.zw = src1.xw >= 0.0 (z only if needed).
DxbcOpGE(DxbcDest::R(system_temp_result_,
used_result_components ? 0b1100 : 0b1000),
operands[1].SwizzleSwizzled(0b11000000), DxbcSrc::LF(0.0f));
// p0 = src0.w == 0.0 && src1.w >= 0.0
DxbcOpAnd(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY),
DxbcSrc::R(system_temp_result_, DxbcSrc::kWWWW));
if (used_result_components) {
// result = (src0.x == 0.0 && src1.x >= 0.0) ? 0.0 : src0.x + 1.0
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kZZZZ));
// If the condition is true, 1 will be added to make it 0.
DxbcOpMovC(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(-1.0f), operands[0].SelectFromSwizzled(0));
DxbcOpAdd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(1.0f));
}
break;
case AluVectorOpcode::kKillEq:
result_swizzle = DxbcSrc::kXXXX;
DxbcOpEq(DxbcDest::R(system_temp_result_), operands[0], operands[1]);
DxbcOpOr(DxbcDest::R(system_temp_result_, 0b0011),
DxbcSrc::R(system_temp_result_, 0b0100),
DxbcSrc::R(system_temp_result_, 0b1110));
DxbcOpOr(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY));
if (edram_rov_used_) {
DxbcOpRetC(true, DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
} else {
DxbcOpDiscard(true, DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
}
if (used_result_components) {
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(1.0f));
}
break;
case AluVectorOpcode::kKillGt:
result_swizzle = DxbcSrc::kXXXX;
DxbcOpLT(DxbcDest::R(system_temp_result_), operands[1], operands[0]);
DxbcOpOr(DxbcDest::R(system_temp_result_, 0b0011),
DxbcSrc::R(system_temp_result_, 0b0100),
DxbcSrc::R(system_temp_result_, 0b1110));
DxbcOpOr(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY));
if (edram_rov_used_) {
DxbcOpRetC(true, DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
} else {
DxbcOpDiscard(true, DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
}
if (used_result_components) {
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(1.0f));
}
break;
case AluVectorOpcode::kKillGe:
result_swizzle = DxbcSrc::kXXXX;
DxbcOpGE(DxbcDest::R(system_temp_result_), operands[0], operands[1]);
DxbcOpOr(DxbcDest::R(system_temp_result_, 0b0011),
DxbcSrc::R(system_temp_result_, 0b0100),
DxbcSrc::R(system_temp_result_, 0b1110));
DxbcOpOr(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY));
if (edram_rov_used_) {
DxbcOpRetC(true, DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
} else {
DxbcOpDiscard(true, DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
}
if (used_result_components) {
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(1.0f));
}
break;
case AluVectorOpcode::kKillNe:
result_swizzle = DxbcSrc::kXXXX;
DxbcOpNE(DxbcDest::R(system_temp_result_), operands[0], operands[1]);
DxbcOpOr(DxbcDest::R(system_temp_result_, 0b0011),
DxbcSrc::R(system_temp_result_, 0b0100),
DxbcSrc::R(system_temp_result_, 0b1110));
DxbcOpOr(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY));
if (edram_rov_used_) {
DxbcOpRetC(true, DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
} else {
DxbcOpDiscard(true, DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX));
}
if (used_result_components) {
DxbcOpAnd(DxbcDest::R(system_temp_result_, 0b0001),
DxbcSrc::R(system_temp_result_, DxbcSrc::kXXXX),
DxbcSrc::LF(1.0f));
}
break;
case AluVectorOpcode::kDst:
if (used_result_components & 0b0001) {
DxbcOpMov(DxbcDest::R(system_temp_result_, 0b0001), DxbcSrc::LF(1.0f));
}
if (used_result_components & 0b0010) {
// Shader Model 3: 0 or denormal * anything = 0.
// FIXME(Triang3l): Signed zero needs research and handling.
DxbcOpMul(DxbcDest::R(system_temp_result_, 0b0010),
operands[0].SelectFromSwizzled(1),
operands[1].SelectFromSwizzled(1));
if (!(instr.vector_operands[0].GetAbsoluteIdenticalComponents(
instr.vector_operands[1]) &
0b0010)) {
DxbcOpMin(DxbcDest::R(system_temp_result_, 0b0100),
operands[0].SelectFromSwizzled(1).Abs(),
operands[1].SelectFromSwizzled(1).Abs());
// min isn't required to flush denormals, eq is.
DxbcOpEq(DxbcDest::R(system_temp_result_, 0b0100),
DxbcSrc::R(system_temp_result_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f));
DxbcOpMovC(DxbcDest::R(system_temp_result_, 0b0010),
DxbcSrc::R(system_temp_result_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f),
DxbcSrc::R(system_temp_result_, DxbcSrc::kYYYY));
}
}
if (used_result_components & 0b0100) {
DxbcOpMov(DxbcDest::R(system_temp_result_, 0b0100),
operands[0].SelectFromSwizzled(2));
}
if (used_result_components & 0b1000) {
DxbcOpMov(DxbcDest::R(system_temp_result_, 0b1000),
operands[1].SelectFromSwizzled(2));
}
break;
case AluVectorOpcode::kMaxA:
DxbcOpAdd(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
operands[0].SelectFromSwizzled(3), DxbcSrc::LF(0.5f));
DxbcOpRoundNI(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kWWWW));
DxbcOpMax(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kWWWW),
DxbcSrc::LF(-256.0f));
DxbcOpMin(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kWWWW),
DxbcSrc::LF(255.0f));
DxbcOpFToI(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::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).
DxbcOpGE(DxbcDest::R(system_temp_result_, different), operands[0],
operands[1]);
DxbcOpMovC(DxbcDest::R(system_temp_result_, different),
DxbcSrc::R(system_temp_result_), operands[0], operands[1]);
}
if (identical) {
DxbcOpMov(DxbcDest::R(system_temp_result_, identical), operands[0]);
}
}
break;
default:
assert_unhandled_case(instr.vector_opcode);
EmitTranslationError("Unknown ALU vector operation");
DxbcOpMov(DxbcDest::R(system_temp_result_), DxbcSrc::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.
DxbcSrc operands_loaded[2]{DxbcSrc::LF(0.0f), DxbcSrc::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!
DxbcSrc operand_0_a(operands_loaded[0].SelectFromSwizzled(0));
DxbcSrc operand_0_b(operands_loaded[0].SelectFromSwizzled(1));
DxbcSrc operand_1(operands_loaded[1].SelectFromSwizzled(0));
DxbcDest ps_dest(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0001));
DxbcSrc ps_src(DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kXXXX));
switch (instr.scalar_opcode) {
case AluScalarOpcode::kAdds:
DxbcOpAdd(ps_dest, operand_0_a, operand_0_b);
break;
case AluScalarOpcode::kAddsPrev:
DxbcOpAdd(ps_dest, operand_0_a, ps_src);
break;
case AluScalarOpcode::kMuls:
DxbcOpMul(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.
// FIXME(Triang3l): Signed zero needs research and handling.
uint32_t is_zero_temp = PushSystemTemp();
DxbcOpMin(DxbcDest::R(is_zero_temp, 0b0001), operand_0_a.Abs(),
operand_0_b.Abs());
// min isn't required to flush denormals, eq is.
DxbcOpEq(DxbcDest::R(is_zero_temp, 0b0001),
DxbcSrc::R(is_zero_temp, DxbcSrc::kXXXX), DxbcSrc::LF(0.0f));
DxbcOpMovC(ps_dest, DxbcSrc::R(is_zero_temp, DxbcSrc::kXXXX),
DxbcSrc::LF(0.0f), ps_src);
// Release is_zero_temp.
PopSystemTemp();
}
break;
case AluScalarOpcode::kMulsPrev: {
// Shader Model 3: 0 or denormal * anything = 0.
// FIXME(Triang3l): Signed zero needs research and handling.
uint32_t is_zero_temp = PushSystemTemp();
DxbcOpMin(DxbcDest::R(is_zero_temp, 0b0001), operand_0_a.Abs(),
ps_src.Abs());
// min isn't required to flush denormals, eq is.
DxbcOpEq(DxbcDest::R(is_zero_temp, 0b0001),
DxbcSrc::R(is_zero_temp, DxbcSrc::kXXXX), DxbcSrc::LF(0.0f));
DxbcOpMul(ps_dest, operand_0_a, ps_src);
DxbcOpMovC(ps_dest, DxbcSrc::R(is_zero_temp, DxbcSrc::kXXXX),
DxbcSrc::LF(0.0f), ps_src);
// Release is_zero_temp.
PopSystemTemp();
} break;
case AluScalarOpcode::kMulsPrev2: {
uint32_t test_temp = PushSystemTemp();
// Check if need to select the src0.a * ps case.
// ps != -FLT_MAX.
DxbcOpNE(DxbcDest::R(test_temp, 0b0001), ps_src, DxbcSrc::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.
DxbcOpGE(DxbcDest::R(test_temp, 0b0010), -ps_src.Abs(),
DxbcSrc::LF(-FLT_MAX));
DxbcOpAnd(DxbcDest::R(test_temp, 0b0001),
DxbcSrc::R(test_temp, DxbcSrc::kXXXX),
DxbcSrc::R(test_temp, DxbcSrc::kYYYY));
// isfinite(src0.b).
DxbcOpGE(DxbcDest::R(test_temp, 0b0010), -operand_0_b.Abs(),
DxbcSrc::LF(-FLT_MAX));
DxbcOpAnd(DxbcDest::R(test_temp, 0b0001),
DxbcSrc::R(test_temp, DxbcSrc::kXXXX),
DxbcSrc::R(test_temp, DxbcSrc::kYYYY));
// src0.b > 0 (need !(src0.b <= 0), but src0.b has already been checked
// for NaN).
DxbcOpLT(DxbcDest::R(test_temp, 0b0010), DxbcSrc::LF(0.0f), operand_0_b);
DxbcOpAnd(DxbcDest::R(test_temp, 0b0001),
DxbcSrc::R(test_temp, DxbcSrc::kXXXX),
DxbcSrc::R(test_temp, DxbcSrc::kYYYY));
DxbcOpIf(true, DxbcSrc::R(test_temp, DxbcSrc::kXXXX));
// Shader Model 3: 0 or denormal * anything = 0.
// ps is already known to be not NaN or Infinity, so multiplying it by 0
// will result in 0. However, src0.a can be anything, so the result should
// be zero if ps is zero.
// FIXME(Triang3l): Signed zero needs research and handling.
DxbcOpEq(DxbcDest::R(test_temp, 0b0001), ps_src, DxbcSrc::LF(0.0f));
DxbcOpMul(ps_dest, operand_0_a, ps_src);
DxbcOpMovC(ps_dest, DxbcSrc::R(test_temp, DxbcSrc::kXXXX),
DxbcSrc::LF(0.0f), ps_src);
DxbcOpElse();
DxbcOpMov(ps_dest, DxbcSrc::LF(-FLT_MAX));
DxbcOpEndIf();
// 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]) {
DxbcOpMov(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) {
DxbcOpLT(ps_dest, operand_0_a, operand_0_b);
} else {
DxbcOpGE(ps_dest, operand_0_a, operand_0_b);
}
DxbcOpMovC(ps_dest, ps_src, operand_0_a, operand_0_b);
}
break;
case AluScalarOpcode::kSeqs:
DxbcOpEq(ps_dest, operand_0_a, DxbcSrc::LF(0.0f));
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kSgts:
DxbcOpLT(ps_dest, DxbcSrc::LF(0.0f), operand_0_a);
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kSges:
DxbcOpGE(ps_dest, operand_0_a, DxbcSrc::LF(0.0f));
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kSnes:
DxbcOpNE(ps_dest, operand_0_a, DxbcSrc::LF(0.0f));
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kFrcs:
DxbcOpFrc(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kTruncs:
DxbcOpRoundZ(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kFloors:
DxbcOpRoundNI(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kExp:
DxbcOpExp(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kLogc: {
DxbcOpLog(ps_dest, operand_0_a);
uint32_t is_neg_infinity_temp = PushSystemTemp();
DxbcOpEq(DxbcDest::R(is_neg_infinity_temp, 0b0001), ps_src,
DxbcSrc::LF(-INFINITY));
DxbcOpMovC(ps_dest, DxbcSrc::R(is_neg_infinity_temp, DxbcSrc::kXXXX),
DxbcSrc::LF(-FLT_MAX), ps_src);
// Release is_neg_infinity_temp.
PopSystemTemp();
} break;
case AluScalarOpcode::kLog:
DxbcOpLog(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kRcpc:
case AluScalarOpcode::kRsqc: {
if (instr.scalar_opcode == AluScalarOpcode::kRsqc) {
DxbcOpRSq(ps_dest, operand_0_a);
} else {
DxbcOpRcp(ps_dest, operand_0_a);
}
uint32_t is_infinity_temp = PushSystemTemp();
DxbcOpEq(DxbcDest::R(is_infinity_temp, 0b0001), ps_src.Abs(),
DxbcSrc::LF(INFINITY));
// If +-Infinity (0x7F800000 or 0xFF800000), add -1 (0xFFFFFFFF) to turn
// into +-FLT_MAX (0x7F7FFFFF or 0xFF7FFFFF).
DxbcOpIAdd(ps_dest, ps_src, DxbcSrc::R(is_infinity_temp, DxbcSrc::kXXXX));
// Release is_infinity_temp.
PopSystemTemp();
} break;
case AluScalarOpcode::kRcpf:
case AluScalarOpcode::kRsqf: {
if (instr.scalar_opcode == AluScalarOpcode::kRsqf) {
DxbcOpRSq(ps_dest, operand_0_a);
} else {
DxbcOpRcp(ps_dest, operand_0_a);
}
uint32_t is_not_infinity_temp = PushSystemTemp();
DxbcOpNE(DxbcDest::R(is_not_infinity_temp, 0b0001), ps_src.Abs(),
DxbcSrc::LF(INFINITY));
// Keep the sign bit if infinity.
DxbcOpOr(DxbcDest::R(is_not_infinity_temp, 0b0001),
DxbcSrc::R(is_not_infinity_temp, DxbcSrc::kXXXX),
DxbcSrc::LU(uint32_t(1) << 31));
DxbcOpAnd(ps_dest, ps_src,
DxbcSrc::R(is_not_infinity_temp, DxbcSrc::kXXXX));
// Release is_not_infinity_temp.
PopSystemTemp();
} break;
case AluScalarOpcode::kRcp:
DxbcOpRcp(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kRsq:
DxbcOpRSq(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kMaxAs:
case AluScalarOpcode::kMaxAsf:
if (instr.scalar_opcode == AluScalarOpcode::kMaxAsf) {
DxbcOpRoundNI(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
operand_0_a);
} else {
DxbcOpAdd(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000), operand_0_a,
DxbcSrc::LF(0.5f));
DxbcOpRoundNI(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kWWWW));
}
DxbcOpMax(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kWWWW),
DxbcSrc::LF(-256.0f));
DxbcOpMin(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kWWWW),
DxbcSrc::LF(255.0f));
DxbcOpFToI(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b1000),
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kWWWW));
if (instr.scalar_operands[0].components[0] ==
instr.scalar_operands[0].components[1]) {
DxbcOpMov(ps_dest, operand_0_a);
} else {
// Shader Model 3 NaN behavior (a >= b ? a : b, not fmax).
DxbcOpGE(ps_dest, operand_0_a, operand_0_b);
DxbcOpMovC(ps_dest, ps_src, operand_0_a, operand_0_b);
}
break;
case AluScalarOpcode::kSubs:
DxbcOpAdd(ps_dest, operand_0_a, -operand_0_b);
break;
case AluScalarOpcode::kSubsPrev:
DxbcOpAdd(ps_dest, operand_0_a, -ps_src);
break;
case AluScalarOpcode::kSetpEq:
predicate_written = true;
DxbcOpEq(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
DxbcSrc::LF(0.0f));
DxbcOpMovC(ps_dest, DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f), DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kSetpNe:
predicate_written = true;
DxbcOpNE(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
DxbcSrc::LF(0.0f));
DxbcOpMovC(ps_dest, DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f), DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kSetpGt:
predicate_written = true;
DxbcOpLT(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100), DxbcSrc::LF(0.0f),
operand_0_a);
DxbcOpMovC(ps_dest, DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f), DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kSetpGe:
predicate_written = true;
DxbcOpGE(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
DxbcSrc::LF(0.0f));
DxbcOpMovC(ps_dest, DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f), DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kSetpInv:
predicate_written = true;
// Calculate ps as if src0.a != 1.0 (the false predicate value case).
DxbcOpEq(ps_dest, operand_0_a, DxbcSrc::LF(0.0f));
DxbcOpMovC(ps_dest, ps_src, DxbcSrc::LF(1.0f), operand_0_a);
// Set the predicate to src0.a == 1.0, and, if it's true, zero ps.
DxbcOpEq(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
DxbcSrc::LF(1.0f));
DxbcOpMovC(ps_dest, DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f), ps_src);
break;
case AluScalarOpcode::kSetpPop:
predicate_written = true;
DxbcOpAdd(ps_dest, operand_0_a, DxbcSrc::LF(-1.0f));
DxbcOpGE(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100), DxbcSrc::LF(0.0f),
ps_src);
DxbcOpMovC(ps_dest, DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f), ps_src);
break;
case AluScalarOpcode::kSetpClr:
predicate_written = true;
DxbcOpMov(ps_dest, DxbcSrc::LF(FLT_MAX));
DxbcOpMov(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100), DxbcSrc::LU(0));
break;
case AluScalarOpcode::kSetpRstr:
predicate_written = true;
DxbcOpEq(DxbcDest::R(system_temp_ps_pc_p0_a0_, 0b0100), operand_0_a,
DxbcSrc::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.
DxbcOpMovC(ps_dest, DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kZZZZ),
DxbcSrc::LF(0.0f), operand_0_a);
break;
case AluScalarOpcode::kKillsEq:
DxbcOpEq(ps_dest, operand_0_a, DxbcSrc::LF(0.0f));
if (edram_rov_used_) {
DxbcOpRetC(true, ps_src);
} else {
DxbcOpDiscard(true, ps_src);
}
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kKillsGt:
DxbcOpLT(ps_dest, DxbcSrc::LF(0.0f), operand_0_a);
if (edram_rov_used_) {
DxbcOpRetC(true, ps_src);
} else {
DxbcOpDiscard(true, ps_src);
}
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kKillsGe:
DxbcOpGE(ps_dest, operand_0_a, DxbcSrc::LF(0.0f));
if (edram_rov_used_) {
DxbcOpRetC(true, ps_src);
} else {
DxbcOpDiscard(true, ps_src);
}
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kKillsNe:
DxbcOpNE(ps_dest, operand_0_a, DxbcSrc::LF(0.0f));
if (edram_rov_used_) {
DxbcOpRetC(true, ps_src);
} else {
DxbcOpDiscard(true, ps_src);
}
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kKillsOne:
DxbcOpEq(ps_dest, operand_0_a, DxbcSrc::LF(1.0f));
if (edram_rov_used_) {
DxbcOpRetC(true, ps_src);
} else {
DxbcOpDiscard(true, ps_src);
}
DxbcOpAnd(ps_dest, ps_src, DxbcSrc::LF(1.0f));
break;
case AluScalarOpcode::kSqrt:
DxbcOpSqRt(ps_dest, operand_0_a);
break;
case AluScalarOpcode::kMulsc0:
case AluScalarOpcode::kMulsc1:
DxbcOpMul(ps_dest, operand_0_a, operand_1);
if (!(instr.scalar_operands[0].GetAbsoluteIdenticalComponents(
instr.scalar_operands[1]) &
0b0001)) {
// Shader Model 3: 0 or denormal * anything = 0.
// FIXME(Triang3l): Signed zero needs research and handling.
uint32_t is_zero_temp = PushSystemTemp();
DxbcOpMin(DxbcDest::R(is_zero_temp, 0b0001), operand_0_a.Abs(),
operand_1.Abs());
// min isn't required to flush denormals, eq is.
DxbcOpEq(DxbcDest::R(is_zero_temp, 0b0001),
DxbcSrc::R(is_zero_temp, DxbcSrc::kXXXX), DxbcSrc::LF(0.0f));
DxbcOpMovC(ps_dest, DxbcSrc::R(is_zero_temp, DxbcSrc::kXXXX),
DxbcSrc::LF(0.0f), ps_src);
// Release is_zero_temp.
PopSystemTemp();
}
break;
case AluScalarOpcode::kAddsc0:
case AluScalarOpcode::kAddsc1:
DxbcOpAdd(ps_dest, operand_0_a, operand_1);
break;
case AluScalarOpcode::kSubsc0:
case AluScalarOpcode::kSubsc1:
DxbcOpAdd(ps_dest, operand_0_a, -operand_1);
break;
case AluScalarOpcode::kSin:
DxbcOpSinCos(ps_dest, DxbcDest::Null(), operand_0_a);
break;
case AluScalarOpcode::kCos:
DxbcOpSinCos(DxbcDest::Null(), ps_dest, operand_0_a);
break;
default:
assert_unhandled_case(instr.scalar_opcode);
EmitTranslationError("Unknown ALU scalar operation");
DxbcOpMov(ps_dest, DxbcSrc::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 = DxbcSrc::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,
DxbcSrc::R(system_temp_result_, vector_result_swizzle),
instr.GetMemExportStreamConstant() != UINT32_MAX);
StoreResult(instr.scalar_result,
DxbcSrc::R(system_temp_ps_pc_p0_a0_, DxbcSrc::kXXXX));
if (predicate_written_vector || predicate_written_scalar) {
cf_exec_predicate_written_ = true;
CloseInstructionPredication();
}
}
} // namespace gpu
} // namespace xe