mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
Share median classifications and preserve RDNA2 signed-zero selection
This commit is contained in:
@@ -160,19 +160,6 @@ uint32_t EmitFPMad32(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
|
|||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EmitFPMedTri32(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
|
|
||||||
const auto min_ab = EmitMinMaxF32Value(state, a, b, false);
|
|
||||||
const auto min3 = EmitMinMaxF32Value(state, min_ab, c, false);
|
|
||||||
const auto max_ab = EmitMinMaxF32Value(state, a, b, true);
|
|
||||||
const auto high_min = EmitMinMaxF32Value(state, max_ab, c, false);
|
|
||||||
const auto median = EmitMinMaxF32Value(state, min_ab, high_min, true);
|
|
||||||
const auto nan_ab = Binary(state, spv::OpLogicalOr, TypeBool(state),
|
|
||||||
EmitClassifyF32(state, a).nan, EmitClassifyF32(state, b).nan);
|
|
||||||
const auto any_nan =
|
|
||||||
Binary(state, spv::OpLogicalOr, TypeBool(state), nan_ab, EmitClassifyF32(state, c).nan);
|
|
||||||
return Select(state, TypeF32(state), any_nan, min3, median);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t EmitFindUMsb64(EmitterState& state, uint32_t value) {
|
uint32_t EmitFindUMsb64(EmitterState& state, uint32_t value) {
|
||||||
// GLSL FindUMsb is restricted to 32-bit components.
|
// GLSL FindUMsb is restricted to 32-bit components.
|
||||||
const auto unpacked = Unary(state, spv::OpBitcast, TypeU32Vector(state, 2), value);
|
const auto unpacked = Unary(state, spv::OpBitcast, TypeU32Vector(state, 2), value);
|
||||||
|
|||||||
@@ -100,10 +100,9 @@ uint32_t EmitClassMaskF16(EmitterState& state, uint32_t bits, uint32_t mask) {
|
|||||||
return EmitClassMaskBits(state, bits, mask, true);
|
return EmitClassMaskBits(state, bits, mask, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) {
|
static uint32_t EmitMinMaxF32(EmitterState& state, uint32_t lhs, uint32_t rhs,
|
||||||
const auto lhs_class = EmitClassifyF32(state, lhs);
|
const F32Class& lhs_class, const F32Class& rhs_class,
|
||||||
const auto rhs_class = EmitClassifyF32(state, rhs);
|
bool max_value) {
|
||||||
|
|
||||||
const auto numeric_cond = state.builder.AllocateId();
|
const auto numeric_cond = state.builder.AllocateId();
|
||||||
state.builder.AddFunction(max_value ? spv::OpFOrdGreaterThanEqual : spv::OpFOrdLessThan,
|
state.builder.AddFunction(max_value ? spv::OpFOrdGreaterThanEqual : spv::OpFOrdLessThan,
|
||||||
TypeBool(state), numeric_cond, lhs, rhs);
|
TypeBool(state), numeric_cond, lhs, rhs);
|
||||||
@@ -121,6 +120,34 @@ uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, boo
|
|||||||
return EmitBitcastU32ToF32(state, result_bits);
|
return EmitBitcastU32ToF32(state, result_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) {
|
||||||
|
const auto lhs_class = EmitClassifyF32(state, lhs);
|
||||||
|
const auto rhs_class = EmitClassifyF32(state, rhs);
|
||||||
|
return EmitMinMaxF32(state, lhs, rhs, lhs_class, rhs_class, max_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t EmitFPMedTri32(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
|
||||||
|
const auto a_class = EmitClassifyF32(state, a);
|
||||||
|
const auto b_class = EmitClassifyF32(state, b);
|
||||||
|
const auto c_class = EmitClassifyF32(state, c);
|
||||||
|
const auto min_ab = EmitMinMaxF32(state, a, b, a_class, b_class, false);
|
||||||
|
const auto min_ab_class = EmitClassifyF32(state, min_ab);
|
||||||
|
const auto min3 = EmitMinMaxF32(state, min_ab, c, min_ab_class, c_class, false);
|
||||||
|
const auto max_ab = EmitMinMaxF32(state, a, b, a_class, b_class, true);
|
||||||
|
const auto max_ab_class = EmitClassifyF32(state, max_ab);
|
||||||
|
const auto max3 = EmitMinMaxF32(state, max_ab, c, max_ab_class, c_class, true);
|
||||||
|
const auto max_bc = EmitMinMaxF32(state, b, c, b_class, c_class, true);
|
||||||
|
const auto max_ac = EmitMinMaxF32(state, a, c, a_class, c_class, true);
|
||||||
|
// RDNA2 compares numerically against MAX3, including ties between signed zeros.
|
||||||
|
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 median = Select(state, TypeF32(state), a_is_max, max_bc,
|
||||||
|
Select(state, TypeF32(state), b_is_max, max_ac, max_ab));
|
||||||
|
const auto nan_ab = EmitLogicalOrBool(state, a_class.nan, b_class.nan);
|
||||||
|
const auto any_nan = EmitLogicalOrBool(state, nan_ab, c_class.nan);
|
||||||
|
return Select(state, TypeF32(state), any_nan, min3, median);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value) {
|
uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value) {
|
||||||
const auto bits = state.builder.AllocateId();
|
const auto bits = state.builder.AllocateId();
|
||||||
const auto abs_bits = state.builder.AllocateId();
|
const auto abs_bits = state.builder.AllocateId();
|
||||||
|
|||||||
@@ -27720,29 +27720,60 @@ TestCase VectorMinMaxF32NanAndSignedZeroEdges() {
|
|||||||
O::V_MAX3_F32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
O::V_MAX3_F32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase VectorMed3F32NanUsesMin3Path() {
|
TestCase VectorMed3F32NanUsesMin3Path(u32 wave) {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
// Runtime inputs keep every MED3 in the emitted module; compare raw result bits.
|
||||||
std::vector<u32> code;
|
constexpr u32 cases[][4] = {
|
||||||
AppendVMovLiteral(&code, 0, 0x40000000u);
|
{0x40000000u, 0x40400000u, 0x7fa00001u, 0x40000000u},
|
||||||
AppendVMovLiteral(&code, 1, 0x40400000u);
|
{0x40000000u, 0x40400000u, 0x7fc00000u, 0x40000000u},
|
||||||
AppendVMovLiteral(&code, 2, 0x7fa00001u);
|
{0x7fa00001u, 0x40000000u, 0x40400000u, 0x40000000u},
|
||||||
AppendVMovLiteral(&code, 3, 0x7fc00000u);
|
{0x40400000u, 0x7fc01234u, 0x40000000u, 0x40000000u},
|
||||||
|
{0x80000000u, 0x00000000u, 0x3f800000u, 0x00000000u},
|
||||||
AppendVop3(&code, 0x157, 10, Vgpr(0), Vgpr(1), Vgpr(2));
|
{0x00000000u, 0x80000000u, 0xbf800000u, 0x80000000u},
|
||||||
AppendVop3(&code, 0x157, 11, Vgpr(0), Vgpr(1), Vgpr(3));
|
{0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u},
|
||||||
AppendVop3(&code, 0x157, 12, Vgpr(2), Vgpr(0), Vgpr(1));
|
{0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u},
|
||||||
|
{0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u},
|
||||||
AppendStoreVgpr(&code, 10, 0);
|
{0x00000000u, 0x80000000u, 0x80000000u, 0x80000000u},
|
||||||
AppendStoreVgpr(&code, 11, 1);
|
{0xbf800000u, 0x80000000u, 0x00000000u, 0x00000000u},
|
||||||
AppendStoreVgpr(&code, 12, 2);
|
{0xbf800000u, 0x00000000u, 0x80000000u, 0x80000000u},
|
||||||
|
{0x80000000u, 0xbf800000u, 0x00000000u, 0x00000000u},
|
||||||
|
{0x00000000u, 0xbf800000u, 0x80000000u, 0x80000000u},
|
||||||
|
{0x80000000u, 0x00000000u, 0xbf800000u, 0x00000000u},
|
||||||
|
{0x7fc01234u, 0x80000000u, 0x00000000u, 0x80000000u},
|
||||||
|
{0x3f800000u, 0x40400000u, 0x40000000u, 0x40000000u},
|
||||||
|
{0xc0400000u, 0xbf800000u, 0xc0000000u, 0xc0000000u},
|
||||||
|
{0xff800000u, 0x7f800000u, 0x3f800000u, 0x3f800000u},
|
||||||
|
{0x00000001u, 0x7f800000u, 0x7fc01234u, 0x00000001u},
|
||||||
|
{0x80000001u, 0x80000001u, 0x00000000u, 0x80000001u},
|
||||||
|
{0x7fc01234u, 0x7fa00001u, 0xffc05678u, 0xffc05678u},
|
||||||
|
{0x7fa00001u, 0xffc05678u, 0x40000000u, 0x40000000u}};
|
||||||
|
TestCase test;
|
||||||
|
test.name = "VectorMed3F32NanUsesMin3Path";
|
||||||
|
test.has_compute_info = true;
|
||||||
|
test.compute_info.wave_size = wave;
|
||||||
|
test.compute_info.float_mode = 0xf0; // Preserve FP32 input/output denormals.
|
||||||
|
test.compute_info.threads_num[0] = test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1u;
|
||||||
|
for (const auto &values : cases)
|
||||||
|
test.initial.insert(test.initial.end(), values, values + 3);
|
||||||
|
test.expected = test.initial;
|
||||||
|
auto &code = test.code;
|
||||||
|
for (u32 index = 0; index < std::size(cases); ++index) {
|
||||||
|
for (u32 operand = 0; operand < 3; ++operand) {
|
||||||
|
AppendVMovU32(&code, 30, (index * 3u + operand) * 4u);
|
||||||
|
AppendBufferLoadDword(&code, operand, 30);
|
||||||
|
}
|
||||||
|
AppendVop3(&code, 0x157, 10, Vgpr(0), Vgpr(1), Vgpr(2));
|
||||||
|
AppendStoreVgpr(&code, 10, static_cast<u32>(test.expected.size()));
|
||||||
|
test.expected.push_back(cases[index][3]);
|
||||||
|
}
|
||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
|
test.initial.resize(test.expected.size());
|
||||||
return {"VectorMed3F32NanUsesMin3Path",
|
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_MED3_F32,
|
||||||
code,
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
{},
|
test.ir_counts = {{"FPMedTri32", std::size(cases)}};
|
||||||
{0x40000000u, 0x40000000u, 0x40000000u},
|
// Measured 17,593/36,607 words; keep classification growth below a single MED3.
|
||||||
{O::V_MOV_B32, O::V_MED3_F32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
test.max_spirv_words = wave == 32u ? 17600u : 36620u;
|
||||||
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase VectorFloatConversionOps() {
|
TestCase VectorFloatConversionOps() {
|
||||||
@@ -42807,7 +42838,8 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(VectorFloatControlContractPreservesInfNan);
|
AddCase(VectorFloatControlContractPreservesInfNan);
|
||||||
AddCase(VectorFloatArithmeticOps);
|
AddCase(VectorFloatArithmeticOps);
|
||||||
AddCase(VectorMinMaxF32NanAndSignedZeroEdges);
|
AddCase(VectorMinMaxF32NanAndSignedZeroEdges);
|
||||||
AddCase(VectorMed3F32NanUsesMin3Path);
|
cases.push_back(VectorMed3F32NanUsesMin3Path(32));
|
||||||
|
cases.push_back(VectorMed3F32NanUsesMin3Path(64));
|
||||||
AddCase(VectorFloatConversionOps);
|
AddCase(VectorFloatConversionOps);
|
||||||
AddCase(VectorFrexpF32Edges);
|
AddCase(VectorFrexpF32Edges);
|
||||||
AddCase(CvtF32ToIntSaturatesNaNAndOutOfRange);
|
AddCase(CvtF32ToIntSaturatesNaNAndOutOfRange);
|
||||||
@@ -48691,8 +48723,10 @@ int main(int argc, char **argv) {
|
|||||||
RunCase(&vulkan, VectorCompareF32DenormalModes(mode));
|
RunCase(&vulkan, VectorCompareF32DenormalModes(mode));
|
||||||
}
|
}
|
||||||
RunCase(&vulkan, VectorCompareClassF32());
|
RunCase(&vulkan, VectorCompareClassF32());
|
||||||
for (const u32 wave : {32u, 64u})
|
for (const u32 wave : {32u, 64u}) {
|
||||||
RunCase(&vulkan, VectorCompareClassF32ImmediateAndDynamicMasks(wave));
|
RunCase(&vulkan, VectorCompareClassF32ImmediateAndDynamicMasks(wave));
|
||||||
|
RunCase(&vulkan, VectorMed3F32NanUsesMin3Path(wave));
|
||||||
|
}
|
||||||
RunCase(&vulkan, VectorCompareF16Ops());
|
RunCase(&vulkan, VectorCompareF16Ops());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user