Files
KytyPS5/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp
T

204 lines
11 KiB
C++

#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
uint32_t EmitAndConstant(EmitterState& state, uint32_t value, uint32_t mask) {
const auto ret = state.builder.AllocateId();
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), ret, value,
ConstantU32(state, mask));
return ret;
}
uint32_t EmitCompareU32Constant(EmitterState& state, spv::Op opcode, uint32_t value,
uint32_t constant) {
const auto ret = state.builder.AllocateId();
state.builder.AddFunction(opcode, TypeBool(state), ret, value, ConstantU32(state, constant));
return ret;
}
F32Class EmitClassifyF32Bits(EmitterState& state, uint32_t bits) {
const auto abs_bits = EmitAndConstant(state, bits, 0x7fffffffu);
return {EmitCompareU32Constant(state, spv::OpUGreaterThan, abs_bits, 0x7f800000u),
EmitCompareU32Constant(state, spv::OpIEqual, abs_bits, 0)};
}
uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit,
uint32_t class_match) {
const auto selected =
EmitCompareU32Constant(state, spv::OpINotEqual, EmitAndConstant(state, mask, 1u << bit), 0);
return EmitLogicalAndBool(state, selected, class_match);
}
static uint32_t EmitClassMaskBits(EmitterState& state, uint32_t bits, uint32_t mask, bool half) {
const uint32_t sign_mask = half ? 0x8000u : 0x80000000u;
const uint32_t exponent_mask = half ? 0x7c00u : 0x7f800000u;
const uint32_t mantissa_mask = half ? 0x03ffu : 0x007fffffu;
const uint32_t quiet_mask = half ? 0x0200u : 0x00400000u;
const auto sign_bits = EmitAndConstant(state, bits, sign_mask);
const auto abs_bits = EmitAndConstant(state, bits, sign_mask - 1u);
const auto exponent_bits = EmitAndConstant(state, abs_bits, exponent_mask);
const auto mantissa_bits = EmitAndConstant(state, abs_bits, mantissa_mask);
const auto quiet_bits = EmitAndConstant(state, mantissa_bits, quiet_mask);
const auto sign = EmitCompareU32Constant(state, spv::OpINotEqual, sign_bits, 0);
const auto positive = EmitLogicalNotBool(state, sign);
const auto exponent_zero = EmitCompareU32Constant(state, spv::OpIEqual, exponent_bits, 0);
const auto exponent_nonzero = EmitLogicalNotBool(state, exponent_zero);
const auto exponent_inf =
EmitCompareU32Constant(state, spv::OpIEqual, exponent_bits, exponent_mask);
const auto finite_exponent = EmitLogicalNotBool(state, exponent_inf);
const auto mantissa_zero = EmitCompareU32Constant(state, spv::OpIEqual, mantissa_bits, 0);
const auto mantissa_nonzero = EmitLogicalNotBool(state, mantissa_zero);
const auto quiet = EmitCompareU32Constant(state, spv::OpINotEqual, quiet_bits, 0);
const auto nan_common = EmitLogicalAndBool(state, exponent_inf, mantissa_nonzero);
const auto snan = EmitLogicalAndBool(state, nan_common, EmitLogicalNotBool(state, quiet));
const auto qnan = EmitLogicalAndBool(state, nan_common, quiet);
const auto inf = EmitLogicalAndBool(state, exponent_inf, mantissa_zero);
const auto normal = EmitLogicalAndBool(state, exponent_nonzero, finite_exponent);
const auto denorm = EmitLogicalAndBool(state, exponent_zero, mantissa_nonzero);
const auto zero = EmitCompareU32Constant(state, spv::OpIEqual, abs_bits, 0);
uint32_t match = EmitClassMaskBitMatch(state, mask, 0, snan);
match = EmitLogicalOrBool(state, match, EmitClassMaskBitMatch(state, mask, 1, qnan));
match = EmitLogicalOrBool(
state, match, EmitClassMaskBitMatch(state, mask, 2, EmitLogicalAndBool(state, inf, sign)));
match = EmitLogicalOrBool(
state, match,
EmitClassMaskBitMatch(state, mask, 3, EmitLogicalAndBool(state, normal, sign)));
match = EmitLogicalOrBool(
state, match,
EmitClassMaskBitMatch(state, mask, 4, EmitLogicalAndBool(state, denorm, sign)));
match = EmitLogicalOrBool(
state, match, EmitClassMaskBitMatch(state, mask, 5, EmitLogicalAndBool(state, zero, sign)));
match = EmitLogicalOrBool(
state, match,
EmitClassMaskBitMatch(state, mask, 6, EmitLogicalAndBool(state, zero, positive)));
match = EmitLogicalOrBool(
state, match,
EmitClassMaskBitMatch(state, mask, 7, EmitLogicalAndBool(state, denorm, positive)));
match = EmitLogicalOrBool(
state, match,
EmitClassMaskBitMatch(state, mask, 8, EmitLogicalAndBool(state, normal, positive)));
return EmitLogicalOrBool(
state, match,
EmitClassMaskBitMatch(state, mask, 9, EmitLogicalAndBool(state, inf, positive)));
}
uint32_t EmitClassMaskF32(EmitterState& state, uint32_t value, uint32_t mask) {
return EmitClassMaskBits(state, EmitBitcastF32ToU32(state, value), mask, false);
}
uint32_t EmitClassMaskF16(EmitterState& state, uint32_t bits, uint32_t mask) {
return EmitClassMaskBits(state, bits, mask, true);
}
static uint32_t EmitMinMaxF32(EmitterState& state, uint32_t lhs, uint32_t rhs, uint32_t lhs_bits,
uint32_t rhs_bits, bool max_value) {
// RDNA2 V_MIN/V_MAX_F32 return the other source for a NaN source (S1 when both are NaN)
// and order -0 below +0. An unordered compare keeps S0 for a NaN S1 and, unlike NMin/NMax,
// always returns the exact bits of one source.
const auto either = EmitOrU32(state, lhs_bits, rhs_bits);
const auto both_zero = EmitCompareU32Constant(state, spv::OpIEqual,
EmitAndConstant(state, either, 0x7fffffffu), 0);
const auto zero =
EmitBitcastU32ToF32(state, max_value ? EmitAndU32(state, lhs_bits, rhs_bits) : either);
const auto keep_lhs =
Binary(state, max_value ? spv::OpFUnordGreaterThanEqual : spv::OpFUnordLessThan,
TypeBool(state), lhs, rhs);
const auto ordered = Select(state, TypeF32(state), keep_lhs, lhs, rhs);
const auto numeric = Select(state, TypeF32(state), both_zero, zero, ordered);
return Select(state, TypeF32(state), Unary(state, spv::OpIsNan, TypeBool(state), lhs), rhs,
numeric);
}
uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) {
if (state.profile.native_float_min_max) {
const auto result = state.builder.AllocateId();
state.builder.AddFunction(spv::OpExtInst, TypeF32(state), result, GlslStd450(state),
max_value ? GLSLstd450NMax : GLSLstd450NMin, lhs, rhs);
return result;
}
return EmitMinMaxF32(state, lhs, rhs, EmitBitcastF32ToU32(state, lhs),
EmitBitcastF32ToU32(state, rhs), max_value);
}
uint32_t EmitFPMedTri32(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
const auto a_bits = EmitBitcastF32ToU32(state, a);
const auto b_bits = EmitBitcastF32ToU32(state, b);
const auto c_bits = EmitBitcastF32ToU32(state, c);
const auto min_ab = EmitMinMaxF32(state, a, b, a_bits, b_bits, false);
const auto min3 =
EmitMinMaxF32(state, min_ab, c, EmitBitcastF32ToU32(state, min_ab), c_bits, false);
const auto max_ab = EmitMinMaxF32(state, a, b, a_bits, b_bits, true);
const auto max3 =
EmitMinMaxF32(state, max_ab, c, EmitBitcastF32ToU32(state, max_ab), c_bits, true);
// RDNA2 compares numerically against MAX3, including ties between signed zeros, then takes
// V_MAX_F32 of the other two sources in their original order.
const auto a_is_max = Binary(state, spv::OpFOrdEqual, TypeBool(state), max3, a);
const auto b_is_max = Binary(state, spv::OpFOrdEqual, TypeBool(state), max3, b);
const auto x = Select(state, TypeF32(state), a_is_max, b, a);
const auto y =
Select(state, TypeF32(state), EmitLogicalOrBool(state, a_is_max, b_is_max), c, b);
const auto median = EmitMinMaxF32Value(state, x, y, true);
const auto is_nan = [&](uint32_t value) {
return Unary(state, spv::OpIsNan, TypeBool(state), value);
};
const auto any_nan =
EmitLogicalOrBool(state, EmitLogicalOrBool(state, is_nan(a), is_nan(b)), is_nan(c));
return Select(state, TypeF32(state), any_nan, min3, median);
}
uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value) {
// x * 0 keeps the sign of a denormal; NaN and infinity fail the ordered compare.
const auto magnitude = state.builder.AllocateId();
state.builder.AddFunction(spv::OpExtInst, TypeF32(state), magnitude, GlslStd450(state),
GLSLstd450FAbs, value);
const auto denormal = Binary(state, spv::OpFOrdLessThan, TypeBool(state), magnitude,
ConstantF32(state, 0x00800000u));
return Select(state, TypeF32(state), denormal,
Binary(state, spv::OpFMul, TypeF32(state), value, ConstantF32(state, 0)), value);
}
uint32_t EmitTrigCycleF32(EmitterState& state, uint32_t src, bool preserve_signed_zero) {
const auto fract = state.builder.AllocateId();
const auto bits = state.builder.AllocateId();
const auto abs_bits = state.builder.AllocateId();
const auto large = state.builder.AllocateId();
const auto finite = state.builder.AllocateId();
const auto large_finite = state.builder.AllocateId();
const auto reduced = state.builder.AllocateId();
state.builder.AddFunction(spv::OpExtInst, TypeF32(state), fract, GlslStd450(state),
GLSLstd450Fract, src);
state.builder.AddFunction(spv::OpBitcast, TypeU32(state), bits, src);
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), abs_bits, bits,
ConstantU32(state, 0x7fffffffu));
state.builder.AddFunction(spv::OpUGreaterThanEqual, TypeBool(state), large, abs_bits,
ConstantU32(state, 0x4b000000u));
state.builder.AddFunction(spv::OpULessThan, TypeBool(state), finite, abs_bits,
ConstantU32(state, 0x7f800000u));
state.builder.AddFunction(spv::OpLogicalAnd, TypeBool(state), large_finite, large, finite);
state.builder.AddFunction(spv::OpSelect, TypeF32(state), reduced, large_finite,
ConstantF32(state, 0), fract);
if (!preserve_signed_zero) {
return reduced;
}
const auto zero = state.builder.AllocateId();
const auto ret = state.builder.AllocateId();
state.builder.AddFunction(spv::OpIEqual, TypeBool(state), zero, abs_bits,
ConstantU32(state, 0));
state.builder.AddFunction(spv::OpSelect, TypeF32(state), ret, zero, src, reduced);
return ret;
}
uint32_t EmitF16BitsToF32(EmitterState& state, uint32_t bits) {
const auto unpacked = state.builder.AllocateId();
const auto ret = state.builder.AllocateId();
state.builder.AddFunction(spv::OpExtInst, TypeF32Vector(state, 2), unpacked, GlslStd450(state),
GLSLstd450UnpackHalf2x16, bits);
state.builder.AddFunction(spv::OpCompositeExtract, TypeF32(state), ret, unpacked, 0);
return ret;
}
} // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter