1105 lines
47 KiB
C++
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
|