Files
Xenia-Canary/src/xenia/gpu/spirv_shader_translator_rb.cc
2022-06-30 22:20:51 +03:00

649 lines
31 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/spirv_shader_translator.h"
#include <cstdint>
#include <memory>
#include <utility>
#include "third_party/glslang/SPIRV/GLSL.std.450.h"
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
namespace xe {
namespace gpu {
spv::Id SpirvShaderTranslator::PreClampedFloat32To7e3(
spv::Builder& builder, spv::Id f32_scalar, spv::Id ext_inst_glsl_std_450) {
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
// Assuming the value is already clamped to [0, 31.875].
spv::Id type_uint = builder.makeUintType(32);
// Need the source as uint for bit operations.
{
spv::Id source_type = builder.getTypeId(f32_scalar);
assert_true(builder.isScalarType(source_type));
if (!builder.isUintType(source_type)) {
f32_scalar = builder.createUnaryOp(spv::OpBitcast, type_uint, f32_scalar);
}
}
// The denormal 7e3 case.
// denormal_biased_f32 = (f32 & 0x7FFFFF) | 0x800000
spv::Id denormal_biased_f32;
{
spv::Instruction* denormal_insert_instruction = new spv::Instruction(
builder.getUniqueId(), type_uint, spv::OpBitFieldInsert);
denormal_insert_instruction->addIdOperand(f32_scalar);
denormal_insert_instruction->addIdOperand(builder.makeUintConstant(1));
denormal_insert_instruction->addIdOperand(builder.makeUintConstant(23));
denormal_insert_instruction->addIdOperand(builder.makeUintConstant(9));
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(denormal_insert_instruction));
denormal_biased_f32 = denormal_insert_instruction->getResultId();
}
// denormal_biased_f32_shift_amount = min(125 - (f32 >> 23), 24)
// Not allowing the shift to overflow as that's undefined in SPIR-V.
spv::Id denormal_biased_f32_shift_amount;
{
spv::Instruction* denormal_shift_amount_instruction =
new spv::Instruction(builder.getUniqueId(), type_uint, spv::OpExtInst);
denormal_shift_amount_instruction->addIdOperand(ext_inst_glsl_std_450);
denormal_shift_amount_instruction->addImmediateOperand(GLSLstd450UMin);
denormal_shift_amount_instruction->addIdOperand(builder.createBinOp(
spv::OpISub, type_uint, builder.makeUintConstant(125),
builder.createBinOp(spv::OpShiftRightLogical, type_uint, f32_scalar,
builder.makeUintConstant(23))));
denormal_shift_amount_instruction->addIdOperand(
builder.makeUintConstant(24));
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(denormal_shift_amount_instruction));
denormal_biased_f32_shift_amount =
denormal_shift_amount_instruction->getResultId();
}
// denormal_biased_f32 =
// ((f32 & 0x7FFFFF) | 0x800000) >> min(125 - (f32 >> 23), 24)
denormal_biased_f32 = builder.createBinOp(spv::OpShiftRightLogical, type_uint,
denormal_biased_f32,
denormal_biased_f32_shift_amount);
// The normal 7e3 case.
// Bias the exponent.
// normal_biased_f32 = f32 - (124 << 23)
spv::Id normal_biased_f32 =
builder.createBinOp(spv::OpISub, type_uint, f32_scalar,
builder.makeUintConstant(UINT32_C(124) << 23));
// Select the needed conversion depending on whether the number is too small
// to be represented as normalized 7e3.
spv::Id biased_f32 = builder.createTriOp(
spv::OpSelect, type_uint,
builder.createBinOp(spv::OpULessThan, builder.makeBoolType(), f32_scalar,
builder.makeUintConstant(0x3E800000)),
denormal_biased_f32, normal_biased_f32);
// Build the 7e3 number rounding to the nearest even.
// ((biased_f32 + 0x7FFF + ((biased_f32 >> 16) & 1)) >> 16) & 0x3FF
return builder.createTriOp(
spv::OpBitFieldUExtract, type_uint,
builder.createBinOp(
spv::OpIAdd, type_uint,
builder.createBinOp(spv::OpIAdd, type_uint, biased_f32,
builder.makeUintConstant(0x7FFF)),
builder.createTriOp(spv::OpBitFieldUExtract, type_uint, biased_f32,
builder.makeUintConstant(16),
builder.makeUintConstant(1))),
builder.makeUintConstant(16), builder.makeUintConstant(10));
}
spv::Id SpirvShaderTranslator::UnclampedFloat32To7e3(
spv::Builder& builder, spv::Id f32_scalar, spv::Id ext_inst_glsl_std_450) {
spv::Id type_float = builder.makeFloatType(32);
// Need the source as float for clamping.
{
spv::Id source_type = builder.getTypeId(f32_scalar);
assert_true(builder.isScalarType(source_type));
if (!builder.isFloatType(source_type)) {
f32_scalar =
builder.createUnaryOp(spv::OpBitcast, type_float, f32_scalar);
}
}
{
spv::Instruction* clamp_instruction =
new spv::Instruction(builder.getUniqueId(), type_float, spv::OpExtInst);
clamp_instruction->addIdOperand(ext_inst_glsl_std_450);
clamp_instruction->addImmediateOperand(GLSLstd450NClamp);
clamp_instruction->addIdOperand(f32_scalar);
clamp_instruction->addIdOperand(builder.makeFloatConstant(0.0f));
clamp_instruction->addIdOperand(builder.makeFloatConstant(31.875f));
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(clamp_instruction));
f32_scalar = clamp_instruction->getResultId();
}
return PreClampedFloat32To7e3(builder, f32_scalar, ext_inst_glsl_std_450);
}
spv::Id SpirvShaderTranslator::Float7e3To32(spv::Builder& builder,
spv::Id f10_uint_scalar,
uint32_t f10_shift,
bool result_as_uint,
spv::Id ext_inst_glsl_std_450) {
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
assert_true(builder.isUintType(builder.getTypeId(f10_uint_scalar)));
assert_true(f10_shift <= (32 - 10));
spv::Id type_bool = builder.makeBoolType();
spv::Id type_int = builder.makeIntType(32);
spv::Id type_uint = builder.makeUintType(32);
spv::Id f10_unbiased_exponent = builder.createTriOp(
spv::OpBitFieldUExtract, type_uint, f10_uint_scalar,
builder.makeUintConstant(f10_shift + 7), builder.makeUintConstant(3));
spv::Id f10_mantissa = builder.createTriOp(
spv::OpBitFieldUExtract, type_uint, f10_uint_scalar,
builder.makeUintConstant(f10_shift), builder.makeUintConstant(7));
// The denormal nonzero 7e3 case.
// denormal_mantissa_msb = findMSB(f10_mantissa)
spv::Id denormal_mantissa_msb;
{
spv::Instruction* denormal_mantissa_msb_instruction =
new spv::Instruction(builder.getUniqueId(), type_int, spv::OpExtInst);
denormal_mantissa_msb_instruction->addIdOperand(ext_inst_glsl_std_450);
denormal_mantissa_msb_instruction->addImmediateOperand(GLSLstd450FindUMsb);
denormal_mantissa_msb_instruction->addIdOperand(f10_mantissa);
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(denormal_mantissa_msb_instruction));
denormal_mantissa_msb = denormal_mantissa_msb_instruction->getResultId();
}
denormal_mantissa_msb =
builder.createUnaryOp(spv::OpBitcast, type_uint, denormal_mantissa_msb);
// denormal_f32_unbiased_exponent = 1 - (7 - findMSB(f10_mantissa))
// Or:
// denormal_f32_unbiased_exponent = findMSB(f10_mantissa) - 6
spv::Id denormal_f32_unbiased_exponent =
builder.createBinOp(spv::OpISub, type_uint, denormal_mantissa_msb,
builder.makeUintConstant(6));
// Normalize the mantissa.
// denormal_f32_mantissa = f10_mantissa << (7 - findMSB(f10_mantissa))
spv::Id denormal_f32_mantissa = builder.createBinOp(
spv::OpShiftLeftLogical, type_uint, f10_mantissa,
builder.createBinOp(spv::OpISub, type_uint, builder.makeUintConstant(7),
denormal_mantissa_msb));
// If the 7e3 number is zero, make sure the float32 number is zero too.
spv::Id f10_mantissa_is_nonzero = builder.createBinOp(
spv::OpINotEqual, type_bool, f10_mantissa, builder.makeUintConstant(0));
// Set the unbiased exponent to -124 for zero - 124 will be added later,
// resulting in zero float32.
denormal_f32_unbiased_exponent = builder.createTriOp(
spv::OpSelect, type_uint, f10_mantissa_is_nonzero,
denormal_f32_unbiased_exponent, builder.makeUintConstant(uint32_t(-124)));
denormal_f32_mantissa =
builder.createTriOp(spv::OpSelect, type_uint, f10_mantissa_is_nonzero,
denormal_f32_mantissa, builder.makeUintConstant(0));
// Select the needed conversion depending on whether the number is normal.
spv::Id f10_is_normal =
builder.createBinOp(spv::OpINotEqual, type_bool, f10_unbiased_exponent,
builder.makeUintConstant(0));
spv::Id f32_unbiased_exponent = builder.createTriOp(
spv::OpSelect, type_uint, f10_is_normal, f10_unbiased_exponent,
denormal_f32_unbiased_exponent);
spv::Id f32_mantissa =
builder.createTriOp(spv::OpSelect, type_uint, f10_is_normal, f10_mantissa,
denormal_f32_mantissa);
// Bias the exponent and construct the build the float32 number.
spv::Id f32_shifted;
{
spv::Instruction* f32_insert_instruction = new spv::Instruction(
builder.getUniqueId(), type_uint, spv::OpBitFieldInsert);
f32_insert_instruction->addIdOperand(f32_mantissa);
f32_insert_instruction->addIdOperand(
builder.createBinOp(spv::OpIAdd, type_uint, f32_unbiased_exponent,
builder.makeUintConstant(124)));
f32_insert_instruction->addIdOperand(builder.makeUintConstant(7));
f32_insert_instruction->addIdOperand(builder.makeUintConstant(8));
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(f32_insert_instruction));
f32_shifted = f32_insert_instruction->getResultId();
}
spv::Id f32 =
builder.createBinOp(spv::OpShiftLeftLogical, type_uint, f32_shifted,
builder.makeUintConstant(23 - 7));
if (!result_as_uint) {
f32 = builder.createUnaryOp(spv::OpBitcast, builder.makeFloatType(32), f32);
}
return f32;
}
spv::Id SpirvShaderTranslator::PreClampedDepthTo20e4(
spv::Builder& builder, spv::Id f32_scalar, bool round_to_nearest_even,
bool remap_from_0_to_0_5, spv::Id ext_inst_glsl_std_450) {
// CFloat24 from d3dref9.dll +
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
// Assuming the value is already clamped to [0, 2) (in all places, the depth
// is written with saturation).
uint32_t remap_bias = uint32_t(remap_from_0_to_0_5);
spv::Id type_uint = builder.makeUintType(32);
// Need the source as uint for bit operations.
{
spv::Id source_type = builder.getTypeId(f32_scalar);
assert_true(builder.isScalarType(source_type));
if (!builder.isUintType(source_type)) {
f32_scalar = builder.createUnaryOp(spv::OpBitcast, type_uint, f32_scalar);
}
}
// The denormal 20e4 case.
// denormal_biased_f32 = (f32 & 0x7FFFFF) | 0x800000
spv::Id denormal_biased_f32;
{
spv::Instruction* denormal_insert_instruction = new spv::Instruction(
builder.getUniqueId(), type_uint, spv::OpBitFieldInsert);
denormal_insert_instruction->addIdOperand(f32_scalar);
denormal_insert_instruction->addIdOperand(builder.makeUintConstant(1));
denormal_insert_instruction->addIdOperand(builder.makeUintConstant(23));
denormal_insert_instruction->addIdOperand(builder.makeUintConstant(9));
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(denormal_insert_instruction));
denormal_biased_f32 = denormal_insert_instruction->getResultId();
}
// denormal_biased_f32_shift_amount = min(113 - (f32 >> 23), 24)
// Not allowing the shift to overflow as that's undefined in SPIR-V.
spv::Id denormal_biased_f32_shift_amount;
{
spv::Instruction* denormal_shift_amount_instruction =
new spv::Instruction(builder.getUniqueId(), type_uint, spv::OpExtInst);
denormal_shift_amount_instruction->addIdOperand(ext_inst_glsl_std_450);
denormal_shift_amount_instruction->addImmediateOperand(GLSLstd450UMin);
denormal_shift_amount_instruction->addIdOperand(builder.createBinOp(
spv::OpISub, type_uint, builder.makeUintConstant(113 - remap_bias),
builder.createBinOp(spv::OpShiftRightLogical, type_uint, f32_scalar,
builder.makeUintConstant(23))));
denormal_shift_amount_instruction->addIdOperand(
builder.makeUintConstant(24));
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(denormal_shift_amount_instruction));
denormal_biased_f32_shift_amount =
denormal_shift_amount_instruction->getResultId();
}
// denormal_biased_f32 =
// ((f32 & 0x7FFFFF) | 0x800000) >> min(113 - (f32 >> 23), 24)
denormal_biased_f32 = builder.createBinOp(spv::OpShiftRightLogical, type_uint,
denormal_biased_f32,
denormal_biased_f32_shift_amount);
// The normal 20e4 case.
// Bias the exponent.
// normal_biased_f32 = f32 - (112 << 23)
spv::Id normal_biased_f32 = builder.createBinOp(
spv::OpISub, type_uint, f32_scalar,
builder.makeUintConstant((UINT32_C(112) - remap_bias) << 23));
// Select the needed conversion depending on whether the number is too small
// to be represented as normalized 20e4.
spv::Id biased_f32 = builder.createTriOp(
spv::OpSelect, type_uint,
builder.createBinOp(
spv::OpULessThan, builder.makeBoolType(), f32_scalar,
builder.makeUintConstant(0x38800000 - (remap_bias << 23))),
denormal_biased_f32, normal_biased_f32);
// Build the 20e4 number rounding to the nearest even or towards zero.
if (round_to_nearest_even) {
// biased_f32 += 3 + ((biased_f32 >> 3) & 1)
biased_f32 = builder.createBinOp(
spv::OpIAdd, type_uint,
builder.createBinOp(spv::OpIAdd, type_uint, biased_f32,
builder.makeUintConstant(3)),
builder.createTriOp(spv::OpBitFieldUExtract, type_uint, biased_f32,
builder.makeUintConstant(3),
builder.makeUintConstant(1)));
}
return builder.createTriOp(spv::OpBitFieldUExtract, type_uint, biased_f32,
builder.makeUintConstant(3),
builder.makeUintConstant(24));
}
spv::Id SpirvShaderTranslator::Depth20e4To32(spv::Builder& builder,
spv::Id f24_uint_scalar,
uint32_t f24_shift,
bool remap_to_0_to_0_5,
bool result_as_uint,
spv::Id ext_inst_glsl_std_450) {
// CFloat24 from d3dref9.dll +
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
assert_true(builder.isUintType(builder.getTypeId(f24_uint_scalar)));
assert_true(f24_shift <= (32 - 24));
uint32_t remap_bias = uint32_t(remap_to_0_to_0_5);
spv::Id type_bool = builder.makeBoolType();
spv::Id type_int = builder.makeIntType(32);
spv::Id type_uint = builder.makeUintType(32);
spv::Id f24_unbiased_exponent = builder.createTriOp(
spv::OpBitFieldUExtract, type_uint, f24_uint_scalar,
builder.makeUintConstant(f24_shift + 20), builder.makeUintConstant(4));
spv::Id f24_mantissa = builder.createTriOp(
spv::OpBitFieldUExtract, type_uint, f24_uint_scalar,
builder.makeUintConstant(f24_shift), builder.makeUintConstant(20));
// The denormal nonzero 20e4 case.
// denormal_mantissa_msb = findMSB(f24_mantissa)
spv::Id denormal_mantissa_msb;
{
spv::Instruction* denormal_mantissa_msb_instruction =
new spv::Instruction(builder.getUniqueId(), type_int, spv::OpExtInst);
denormal_mantissa_msb_instruction->addIdOperand(ext_inst_glsl_std_450);
denormal_mantissa_msb_instruction->addImmediateOperand(GLSLstd450FindUMsb);
denormal_mantissa_msb_instruction->addIdOperand(f24_mantissa);
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(denormal_mantissa_msb_instruction));
denormal_mantissa_msb = denormal_mantissa_msb_instruction->getResultId();
}
denormal_mantissa_msb =
builder.createUnaryOp(spv::OpBitcast, type_uint, denormal_mantissa_msb);
// denormal_f32_unbiased_exponent = 1 - (20 - findMSB(f24_mantissa))
// Or:
// denormal_f32_unbiased_exponent = findMSB(f24_mantissa) - 19
spv::Id denormal_f32_unbiased_exponent =
builder.createBinOp(spv::OpISub, type_uint, denormal_mantissa_msb,
builder.makeUintConstant(19));
// Normalize the mantissa.
// denormal_f32_mantissa = f24_mantissa << (20 - findMSB(f24_mantissa))
spv::Id denormal_f32_mantissa = builder.createBinOp(
spv::OpShiftLeftLogical, type_uint, f24_mantissa,
builder.createBinOp(spv::OpISub, type_uint, builder.makeUintConstant(20),
denormal_mantissa_msb));
// If the 20e4 number is zero, make sure the float32 number is zero too.
spv::Id f24_mantissa_is_nonzero = builder.createBinOp(
spv::OpINotEqual, type_bool, f24_mantissa, builder.makeUintConstant(0));
// Set the unbiased exponent to -112 for zero - 112 will be added later,
// resulting in zero float32.
denormal_f32_unbiased_exponent = builder.createTriOp(
spv::OpSelect, type_uint, f24_mantissa_is_nonzero,
denormal_f32_unbiased_exponent,
builder.makeUintConstant(uint32_t(-int32_t(112 - remap_bias))));
denormal_f32_mantissa =
builder.createTriOp(spv::OpSelect, type_uint, f24_mantissa_is_nonzero,
denormal_f32_mantissa, builder.makeUintConstant(0));
// Select the needed conversion depending on whether the number is normal.
spv::Id f24_is_normal =
builder.createBinOp(spv::OpINotEqual, type_bool, f24_unbiased_exponent,
builder.makeUintConstant(0));
spv::Id f32_unbiased_exponent = builder.createTriOp(
spv::OpSelect, type_uint, f24_is_normal, f24_unbiased_exponent,
denormal_f32_unbiased_exponent);
spv::Id f32_mantissa =
builder.createTriOp(spv::OpSelect, type_uint, f24_is_normal, f24_mantissa,
denormal_f32_mantissa);
// Bias the exponent and construct the build the float32 number.
spv::Id f32_shifted;
{
spv::Instruction* f32_insert_instruction = new spv::Instruction(
builder.getUniqueId(), type_uint, spv::OpBitFieldInsert);
f32_insert_instruction->addIdOperand(f32_mantissa);
f32_insert_instruction->addIdOperand(
builder.createBinOp(spv::OpIAdd, type_uint, f32_unbiased_exponent,
builder.makeUintConstant(112 - remap_bias)));
f32_insert_instruction->addIdOperand(builder.makeUintConstant(20));
f32_insert_instruction->addIdOperand(builder.makeUintConstant(8));
builder.getBuildPoint()->addInstruction(
std::unique_ptr<spv::Instruction>(f32_insert_instruction));
f32_shifted = f32_insert_instruction->getResultId();
}
spv::Id f32 =
builder.createBinOp(spv::OpShiftLeftLogical, type_uint, f32_shifted,
builder.makeUintConstant(23 - 20));
if (!result_as_uint) {
f32 = builder.createUnaryOp(spv::OpBitcast, builder.makeFloatType(32), f32);
}
return f32;
}
void SpirvShaderTranslator::CompleteFragmentShaderInMain() {
id_vector_temp_.clear();
id_vector_temp_.push_back(builder_->makeIntConstant(kSystemConstantFlags));
spv::Id system_constant_flags = builder_->createLoad(
builder_->createAccessChain(spv::StorageClassUniform,
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision);
if (current_shader().writes_color_target(0) &&
!IsExecutionModeEarlyFragmentTests()) {
// Alpha test.
// TODO(Triang3l): Check how alpha test works with NaN on Direct3D 9.
// Extract the comparison function (less, equal, greater bits).
spv::Id alpha_test_function = builder_->createTriOp(
spv::OpBitFieldUExtract, type_uint_, main_system_constant_flags_,
builder_->makeUintConstant(kSysFlag_AlphaPassIfLess_Shift),
builder_->makeUintConstant(3));
// Check if the comparison function is not "always" - that should pass even
// for NaN likely, unlike "less, equal or greater".
spv::Id alpha_test_function_is_non_always = builder_->createBinOp(
spv::OpINotEqual, type_bool_, alpha_test_function,
builder_->makeUintConstant(uint32_t(xenos::CompareFunction::kAlways)));
spv::Block& block_alpha_test = builder_->makeNewBlock();
spv::Block& block_alpha_test_merge = builder_->makeNewBlock();
SpirvCreateSelectionMerge(block_alpha_test_merge.getId(),
spv::SelectionControlDontFlattenMask);
builder_->createConditionalBranch(alpha_test_function_is_non_always,
&block_alpha_test,
&block_alpha_test_merge);
builder_->setBuildPoint(&block_alpha_test);
{
id_vector_temp_.clear();
id_vector_temp_.push_back(builder_->makeIntConstant(3));
spv::Id alpha_test_alpha =
builder_->createLoad(builder_->createAccessChain(
spv::StorageClassOutput,
output_fragment_data_[0], id_vector_temp_),
spv::NoPrecision);
id_vector_temp_.clear();
id_vector_temp_.push_back(
builder_->makeIntConstant(kSystemConstantAlphaTestReference));
spv::Id alpha_test_reference =
builder_->createLoad(builder_->createAccessChain(
spv::StorageClassUniform,
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision);
// The comparison function is not "always" - perform the alpha test.
// Handle "not equal" specially (specifically as "not equal" so it's true
// for NaN, not "less or greater" which is false for NaN).
spv::Id alpha_test_function_is_not_equal = builder_->createBinOp(
spv::OpIEqual, type_bool_, alpha_test_function,
builder_->makeUintConstant(
uint32_t(xenos::CompareFunction::kNotEqual)));
spv::Block& block_alpha_test_not_equal = builder_->makeNewBlock();
spv::Block& block_alpha_test_non_not_equal = builder_->makeNewBlock();
spv::Block& block_alpha_test_not_equal_merge = builder_->makeNewBlock();
SpirvCreateSelectionMerge(block_alpha_test_not_equal_merge.getId(),
spv::SelectionControlDontFlattenMask);
builder_->createConditionalBranch(alpha_test_function_is_not_equal,
&block_alpha_test_not_equal,
&block_alpha_test_non_not_equal);
spv::Id alpha_test_result_not_equal, alpha_test_result_non_not_equal;
builder_->setBuildPoint(&block_alpha_test_not_equal);
{
// "Not equal" function.
alpha_test_result_not_equal =
builder_->createBinOp(spv::OpFUnordNotEqual, type_bool_,
alpha_test_alpha, alpha_test_reference);
builder_->createBranch(&block_alpha_test_not_equal_merge);
}
builder_->setBuildPoint(&block_alpha_test_non_not_equal);
{
// Function other than "not equal".
static const spv::Op kAlphaTestOps[] = {
spv::OpFOrdLessThan, spv::OpFOrdEqual, spv::OpFOrdGreaterThan};
for (uint32_t i = 0; i < 3; ++i) {
spv::Id alpha_test_comparison_result = builder_->createBinOp(
spv::OpLogicalAnd, type_bool_,
builder_->createBinOp(kAlphaTestOps[i], type_bool_,
alpha_test_alpha, alpha_test_reference),
builder_->createBinOp(
spv::OpINotEqual, type_bool_,
builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_, alpha_test_function,
builder_->makeUintConstant(UINT32_C(1) << i)),
const_uint_0_));
if (i) {
alpha_test_result_non_not_equal = builder_->createBinOp(
spv::OpLogicalOr, type_bool_, alpha_test_result_non_not_equal,
alpha_test_comparison_result);
} else {
alpha_test_result_non_not_equal = alpha_test_comparison_result;
}
}
builder_->createBranch(&block_alpha_test_not_equal_merge);
}
builder_->setBuildPoint(&block_alpha_test_not_equal_merge);
spv::Id alpha_test_result;
{
std::unique_ptr<spv::Instruction> alpha_test_result_phi_op =
std::make_unique<spv::Instruction>(builder_->getUniqueId(),
type_bool_, spv::OpPhi);
alpha_test_result_phi_op->addIdOperand(alpha_test_result_not_equal);
alpha_test_result_phi_op->addIdOperand(
block_alpha_test_not_equal.getId());
alpha_test_result_phi_op->addIdOperand(alpha_test_result_non_not_equal);
alpha_test_result_phi_op->addIdOperand(
block_alpha_test_non_not_equal.getId());
alpha_test_result = alpha_test_result_phi_op->getResultId();
builder_->getBuildPoint()->addInstruction(
std::move(alpha_test_result_phi_op));
}
// Discard the pixel if the alpha test has failed. Creating a merge block
// even though it will contain just one OpBranch since SPIR-V requires
// structured control flow in shaders.
spv::Block& block_alpha_test_kill = builder_->makeNewBlock();
spv::Block& block_alpha_test_kill_merge = builder_->makeNewBlock();
SpirvCreateSelectionMerge(block_alpha_test_kill_merge.getId(),
spv::SelectionControlDontFlattenMask);
builder_->createConditionalBranch(alpha_test_result,
&block_alpha_test_kill_merge,
&block_alpha_test_kill);
builder_->setBuildPoint(&block_alpha_test_kill);
builder_->createNoResultOp(spv::OpKill);
// OpKill terminates the block.
builder_->setBuildPoint(&block_alpha_test_kill_merge);
builder_->createBranch(&block_alpha_test_merge);
}
builder_->setBuildPoint(&block_alpha_test_merge);
}
uint32_t color_targets_remaining = current_shader().writes_color_targets();
uint32_t color_target_index;
while (xe::bit_scan_forward(color_targets_remaining, &color_target_index)) {
color_targets_remaining &= ~(UINT32_C(1) << color_target_index);
spv::Id color_variable = output_fragment_data_[color_target_index];
spv::Id color = builder_->createLoad(color_variable, spv::NoPrecision);
// Apply the exponent bias after the alpha test and alpha to coverage
// because they need the unbiased alpha from the shader.
id_vector_temp_.clear();
id_vector_temp_.reserve(2);
id_vector_temp_.push_back(
builder_->makeIntConstant(kSystemConstantColorExpBias));
id_vector_temp_.push_back(
builder_->makeIntConstant(int32_t(color_target_index)));
color = builder_->createBinOp(
spv::OpVectorTimesScalar, type_float4_, color,
builder_->createLoad(builder_->createAccessChain(
spv::StorageClassUniform,
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision));
builder_->addDecoration(color, spv::DecorationNoContraction);
// Convert to gamma space - this is incorrect, since it must be done after
// blending on the Xbox 360, but this is just one of many blending issues in
// the host render target path.
// TODO(Triang3l): Gamma as sRGB check.
spv::Id color_rgb;
{
std::unique_ptr<spv::Instruction> color_rgb_shuffle_op =
std::make_unique<spv::Instruction>(
builder_->getUniqueId(), type_float3_, spv::OpVectorShuffle);
color_rgb_shuffle_op->addIdOperand(color);
color_rgb_shuffle_op->addIdOperand(color);
color_rgb_shuffle_op->addImmediateOperand(0);
color_rgb_shuffle_op->addImmediateOperand(1);
color_rgb_shuffle_op->addImmediateOperand(2);
color_rgb = color_rgb_shuffle_op->getResultId();
builder_->getBuildPoint()->addInstruction(
std::move(color_rgb_shuffle_op));
}
spv::Id is_gamma = builder_->createBinOp(
spv::OpINotEqual, type_bool_,
builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_,
builder_->makeUintConstant(kSysFlag_ConvertColor0ToGamma
<< color_target_index)),
const_uint_0_);
spv::Block& block_gamma_head = *builder_->getBuildPoint();
spv::Block& block_gamma = builder_->makeNewBlock();
spv::Block& block_gamma_merge = builder_->makeNewBlock();
SpirvCreateSelectionMerge(block_gamma_merge.getId());
builder_->createConditionalBranch(is_gamma, &block_gamma,
&block_gamma_merge);
builder_->setBuildPoint(&block_gamma);
spv::Id color_rgb_gamma = LinearToPWLGamma(color_rgb, false);
builder_->createBranch(&block_gamma_merge);
builder_->setBuildPoint(&block_gamma_merge);
{
std::unique_ptr<spv::Instruction> gamma_phi_op =
std::make_unique<spv::Instruction>(builder_->getUniqueId(),
type_float3_, spv::OpPhi);
gamma_phi_op->addIdOperand(color_rgb_gamma);
gamma_phi_op->addIdOperand(block_gamma.getId());
gamma_phi_op->addIdOperand(color_rgb);
gamma_phi_op->addIdOperand(block_gamma_head.getId());
color_rgb = gamma_phi_op->getResultId();
builder_->getBuildPoint()->addInstruction(std::move(gamma_phi_op));
}
{
std::unique_ptr<spv::Instruction> color_rgba_shuffle_op =
std::make_unique<spv::Instruction>(
builder_->getUniqueId(), type_float4_, spv::OpVectorShuffle);
color_rgba_shuffle_op->addIdOperand(color_rgb);
color_rgba_shuffle_op->addIdOperand(color);
color_rgba_shuffle_op->addImmediateOperand(0);
color_rgba_shuffle_op->addImmediateOperand(1);
color_rgba_shuffle_op->addImmediateOperand(2);
color_rgba_shuffle_op->addImmediateOperand(3 + 3);
color = color_rgba_shuffle_op->getResultId();
builder_->getBuildPoint()->addInstruction(
std::move(color_rgba_shuffle_op));
}
builder_->createStore(color, color_variable);
}
}
} // namespace gpu
} // namespace xe