Files
Xenia-Canary/src/xenia/gpu/spirv_shader_translator_rb.cc
Triang3l 0acb97d383 [Vulkan] EDRAM range ownership transfers, resolve clears, 2x-as-4x MSAA
Transfers are functional on a D3D12-like level, but need additional work so fallbacks are used when multisampled integer sampled images are not supported, and to eliminate transfers between render targets within Vulkan format compatibility classes by using different views directly.
2022-04-03 16:40:29 +03:00

426 lines
20 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 "third_party/glslang/SPIRV/GLSL.std.450.h"
#include "xenia/base/assert.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 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.
// ((biased_f32 + 3 + ((biased_f32 >> 3) & 1)) >> 3) & 0xFFFFFF
return builder.createTriOp(
spv::OpBitFieldUExtract, type_uint,
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))),
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;
}
} // namespace gpu
} // namespace xe