shader: respect FP32 mode in comparisons

This commit is contained in:
nmzik
2026-09-27 22:52:28 +04:00
parent 6956b454f3
commit d71facee4f
10 changed files with 226 additions and 28 deletions
+44
View File
@@ -396,6 +396,49 @@ void TestUniformFirstLaneSamplerLod() {
"uniform sampler LOD clamp evaluated incorrectly");
}
void TestFloatComparisonDescriptorInputs() {
struct Input {
uint32_t bits;
uint32_t less_equal;
uint32_t greater_equal;
};
constexpr std::array inputs = {
Input{0x00000001u, 0, 1}, Input{0x80000001u, 1, 0},
Input{0x007fffffu, 0, 1}, Input{0x807fffffu, 1, 0},
Input{0x00800000u, 0, 1}, Input{0x80800000u, 1, 0},
Input{0x00000000u, 1, 1}, Input{0x80000000u, 1, 1},
Input{0x7fc00000u, 0, 0}};
for (const bool flush : {false, true}) {
Fixture fixture;
const auto user = fixture.Emit(
ValueOpcode::GetUserData, {Value(static_cast<ScalarReg>(2))});
const auto value = fixture.Emit(ValueOpcode::BitCastF32U32, {user});
const auto less_equal = fixture.Emit(ValueOpcode::FPOrdLessThanEqual32,
{value, Value::F32(0.0f)});
const auto greater_equal = fixture.Emit(ValueOpcode::FPOrdGreaterThanEqual32,
{value, Value::F32(0.0f)});
less_equal.Instruction()->SetFlags(FPCompareFlags{flush});
greater_equal.Instruction()->SetFlags(FPCompareFlags{flush});
const auto low = fixture.Emit(ValueOpcode::SelectU32,
{less_equal, Value(1u), Value(0u)});
const auto high = fixture.Emit(ValueOpcode::SelectU32,
{greater_equal, Value(1u), Value(0u)});
fixture.program.descriptor_sources.push_back(
{.dwords = {low, high}, .dword_count = 2});
fixture.Plan();
for (size_t index = 0; index < inputs.size(); index++) {
const auto &input = inputs[index];
const std::array user_data{input.bits};
DescriptorValue result;
Check(SrtWalker(fixture.program, {.user_data = user_data})
.EvaluateDescriptor(0, result) &&
result.dwords[0] == (flush && index < 4 ? 1 : input.less_equal) &&
result.dwords[1] == (flush && index < 4 ? 1 : input.greater_equal),
"descriptor comparison disagrees with native FP32 input mode");
}
}
}
void TestSharedIntegerRuntimeDependencies() {
Fixture fixture;
const auto lane = fixture.Emit(ValueOpcode::LaneId);
@@ -591,6 +634,7 @@ int main() {
TestControlDependentStandaloneLoadStaysTyped();
TestRuntime64BitDescriptorOps();
TestUniformFirstLaneSamplerLod();
TestFloatComparisonDescriptorInputs();
TestSharedIntegerRuntimeDependencies();
TestConstantBufferBounds();
TestReadLaneElimination();
+97
View File
@@ -22182,6 +22182,87 @@ TestCase VectorSinCosMaxFiniteSpecialCases() {
O::S_ENDPGM}};
}
TestCase VectorCompareF32ExposureGuard() {
using O = ShaderOpcode;
TestCase test;
test.name = "VectorCompareF32ExposureGuard";
test.initial = {0x00000001u, 0x80000001u, 0x007fffffu, 0x807fffffu,
0x00000000u, 0x80000000u, 0x3f800000u, 0x40000000u};
test.expected = test.initial;
AppendVMovLiteral(&test.code, 1, 0x3f800000u);
for (u32 i = 0; i < test.initial.size(); i++) {
AppendVMovU32(&test.code, 31, i * sizeof(u32));
AppendBufferLoadDword(&test.code, 0, 31);
// Captured exposure guard: replace zero with 1.0 before taking its reciprocal.
test.code.push_back(EncodeVopc(0x02, InlineU32(0), 0));
test.code.push_back(EncodeVop2(0x01, 2, Vgpr(0), 1));
test.code.push_back(EncodeVop1(0x2a, 3, Vgpr(2)));
AppendStoreVgpr(&test.code, 2, static_cast<u32>(test.expected.size()));
AppendStoreVgpr(&test.code, 3, static_cast<u32>(test.expected.size() + 1));
test.expected.push_back(i == 7 ? 0x40000000u : 0x3f800000u);
test.expected.push_back(i == 7 ? 0x3f000000u : 0x3f800000u);
}
test.initial.resize(test.expected.size());
AppendEnd(&test.code);
test.opcodes = {O::BUFFER_LOAD_DWORD, O::V_CMP_EQ_F32, O::V_CNDMASK_B32,
O::V_RCP_F32, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
return test;
}
TestCase VectorCompareF32DenormalModes(u32 mode) {
using O = ShaderOpcode;
enum Relation : u32 { Less, Equal, Greater, Unordered };
struct Pair {
u32 lhs;
u32 rhs;
Relation relation;
};
constexpr Pair pairs[] = {
{0x00000001u, 0x00000000u, Equal},
{0x80000001u, 0x80000000u, Equal},
{0x00000000u, 0x00000001u, Equal},
{0x80000000u, 0x80000001u, Equal},
{0x007fffffu, 0x80000001u, Equal},
{0x00800000u, 0x007fffffu, Greater},
{0xbf800000u, 0x80000001u, Less},
{0xff800000u, 0xbf800000u, Less},
{0x7f800000u, 0x7f800000u, Equal},
{0x7fc12345u, 0x00000000u, Unordered},
{0x00000000u, 0x7fa12345u, Unordered},
{0x00000000u, 0x80000000u, Equal},
};
TestCase test;
constexpr const char* names[] = {"VectorCompareF32DenormalModeC0",
"VectorCompareF32DenormalModeE0"};
test.name = names[(mode >> 5u) & 1u];
test.compute_info.float_mode = static_cast<uint8_t>(mode);
test.has_compute_info = true;
for (const auto& pair : pairs) {
test.initial.insert(test.initial.end(), {pair.lhs, pair.rhs});
}
test.expected = test.initial;
AppendVMovU32(&test.code, 1, 1);
for (u32 i = 0; i < std::size(pairs); i++) {
AppendVMovU32(&test.code, 31, i * 2u * sizeof(u32));
AppendBufferLoadDword(&test.code, 2, 31);
AppendVMovU32(&test.code, 31, (i * 2u + 1u) * sizeof(u32));
AppendBufferLoadDword(&test.code, 3, 31);
// The sixteen architectural predicates select subsets of LT/EQ/GT/unordered.
for (u32 predicate = 0; predicate < 16; predicate++) {
test.code.push_back(EncodeVopc(predicate, Vgpr(2), 3));
test.code.push_back(EncodeVop2(0x01, 4, InlineU32(0), 1));
AppendStoreVgpr(&test.code, 4, static_cast<u32>(test.expected.size()));
test.expected.push_back((predicate >> pairs[i].relation) & 1u);
}
}
test.initial.resize(test.expected.size());
AppendEnd(&test.code);
test.opcodes = {O::BUFFER_LOAD_DWORD, O::V_CMP_EQ_F32, O::V_CMP_LE_F32,
O::V_CMP_GE_F32, O::V_CNDMASK_B32, O::BUFFER_STORE_DWORD,
O::S_ENDPGM};
return test;
}
TestCase VectorCompareOps() {
using O = ShaderOpcode;
@@ -22532,6 +22613,8 @@ TestCase VectorCompareClassF32() {
append_class(0x40000000u, 1u << 8u, true);
append_class(0x7f800000u, 1u << 9u, true);
append_class(0x40000000u, 1u << 3u, false);
append_class(0x00000001u, 1u << 7u, true);
append_class(0x80000001u, 1u << 4u, true);
AppendVMovLiteral(&code, 0, 0x3f800000u);
AppendVMovU32(&code, 2, 1u << 3u);
@@ -29686,6 +29769,10 @@ std::vector<TestCase> MakeCases() {
AddCase(VectorF64CapturedScreenSpaceShadows);
AddCase(VectorF64ModesModifiersAndExec);
AddCase(VectorSinCosMaxFiniteSpecialCases);
AddCase(VectorCompareF32ExposureGuard);
for (const auto mode : {0xc0u, 0xe0u}) {
cases.push_back(VectorCompareF32DenormalModes(mode));
}
AddCase(VectorCompareOps);
AddCase(VectorVop3CompareEqI64OnGpu);
AddCase(VectorVop3CompareEqU64OnGpu);
@@ -34554,6 +34641,16 @@ int main(int argc, char **argv) {
RunCase(&vulkan, VectorVopcCmpxOrderedCapturedExecMask());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--fp32-compare-only") == 0) {
VulkanHarness vulkan;
RunCase(&vulkan, VectorCompareF32ExposureGuard());
for (const auto mode : {0xc0u, 0xe0u}) {
RunCase(&vulkan, VectorCompareF32DenormalModes(mode));
}
RunCase(&vulkan, VectorCompareClassF32());
RunCase(&vulkan, VectorCompareF16Ops());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--buffer-format-store-only") == 0) {
VulkanHarness vulkan;
RunCase(&vulkan, BufferStoreFormatXyzwPackedUnormSkinningVectors());
+30
View File
@@ -3923,6 +3923,35 @@ void TestNewShaderRecompilerScalarB64LaneTranslation() {
CheckSpirvBinaryValidates(result.spirv);
}
void TestFloatComparisonInputModes() {
using namespace ShaderRecompiler;
const uint32_t shader[] = {
EncodeVopc(0x02, 256, 1), // v_cmp_eq_f32 v0, v1
EncodeVopc(0xca, 256, 1), // v_cmp_eq_f16 v0, v1
EncodeSopp(0x01),
};
Decoder::Program decoded;
Decoder::DecodeProgram(shader, decoded);
for (const uint8_t mode : {0xc0, 0xd0, 0xe0, 0xf0}) {
ShaderComputeInputInfo compute{};
compute.float_mode = mode;
Frontend::TranslateOptions options{.stage = ShaderType::Compute};
options.input_info.compute = &compute;
const auto program = Frontend::TranslateProgram(decoded, CFG::BuildGraph(decoded), options);
uint32_t comparisons = 0;
for (const auto* block : program.blocks) {
for (const auto& inst : *block) {
if (inst.GetOpcode() != IR::ValueOpcode::FPOrdEqual32) continue;
Check(inst.Flags<IR::FPCompareFlags>().flush_input_denorms ==
(comparisons == 0 && (mode & 0x10u) == 0),
"compute input mode leaked into preserved or promoted F16 comparison");
comparisons++;
}
}
Check(comparisons == 2, "comparison input-mode fixture did not translate both precisions");
}
}
void TestNewShaderRecompilerSignedCompareAlu() {
const uint32_t shader[] = {
EncodeSMovB32(0, 193), // s0 = -1
@@ -13582,6 +13611,7 @@ int main() {
TestNewShaderRecompilerCapturedVopcSdwaCmpxLtU16();
TestNewShaderRecompilerIrLookupMissFailsExplicitly();
TestNewShaderRecompilerRejectsDppOn64BitCompares();
TestFloatComparisonInputModes();
TestPsInputCountRegisterDecode();
TestPixelAncillaryLayerInput();
TestNewShaderRecompilerUnbasedFlatUsesBda();