mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
shader: use compact integer RTZ half conversion
This commit is contained in:
@@ -67,6 +67,44 @@ uint32_t EmitExt(EmitterState& state, uint32_t type, uint32_t opcode,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t value) {
|
||||||
|
// Native half conversion causes regressions; use compact integer RTZ conversion.
|
||||||
|
const auto bits = EmitBitcastF32ToU32(state, value);
|
||||||
|
const auto abs = EmitAndConstant(state, bits, 0x7fffffffu);
|
||||||
|
const auto sign = EmitAndConstant(
|
||||||
|
state, Binary(state, spv::OpShiftRightLogical, TypeU32(state), bits, ConstantU32(state, 16)),
|
||||||
|
0x8000u);
|
||||||
|
const auto exp = Binary(state, spv::OpShiftRightLogical, TypeU32(state), abs,
|
||||||
|
ConstantU32(state, 23));
|
||||||
|
const auto mant = EmitAndConstant(state, abs, 0x007fffffu);
|
||||||
|
|
||||||
|
// Align the hidden bit to the half subnormal mantissa. Even unused shifts must be < 32.
|
||||||
|
const auto shift = EmitUMin32(
|
||||||
|
state, Binary(state, spv::OpISub, TypeU32(state), ConstantU32(state, 126), exp),
|
||||||
|
ConstantU32(state, 31));
|
||||||
|
const auto sub = Binary(state, spv::OpShiftRightLogical, TypeU32(state),
|
||||||
|
EmitOrU32(state, mant, ConstantU32(state, 0x00800000u)), shift);
|
||||||
|
|
||||||
|
// Drop 13 mantissa bits and rebias the exponent; finite overflow saturates to max half.
|
||||||
|
const auto truncated = Binary(state, spv::OpShiftRightLogical, TypeU32(state), abs,
|
||||||
|
ConstantU32(state, 13));
|
||||||
|
const auto normal = EmitUMin32(
|
||||||
|
state, Binary(state, spv::OpISub, TypeU32(state), truncated, ConstantU32(state, 0x1c000u)),
|
||||||
|
ConstantU32(state, 0x7bffu));
|
||||||
|
const auto finite = Select(state, TypeU32(state),
|
||||||
|
EmitCompareU32Constant(state, spv::OpULessThan, abs, 0x38800000u),
|
||||||
|
sub, normal);
|
||||||
|
const auto nan = EmitOrU32(state, EmitAndConstant(state, truncated, 0x03ffu),
|
||||||
|
ConstantU32(state, 0x7e00u));
|
||||||
|
const auto special = Select(state, TypeU32(state),
|
||||||
|
EmitCompareU32Constant(state, spv::OpIEqual, mant, 0),
|
||||||
|
ConstantU32(state, 0x7c00u), nan);
|
||||||
|
const auto magnitude = Select(
|
||||||
|
state, TypeU32(state),
|
||||||
|
EmitCompareU32Constant(state, spv::OpUGreaterThanEqual, abs, 0x7f800000u), special, finite);
|
||||||
|
return EmitOrU32(state, sign, magnitude);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t EmitF32ToU32(EmitterState& state, uint32_t src, bool signed_value) {
|
uint32_t EmitF32ToU32(EmitterState& state, uint32_t src, bool signed_value) {
|
||||||
const auto trunc = EmitTruncF32Value(state, src);
|
const auto trunc = EmitTruncF32Value(state, src);
|
||||||
const auto converted_raw = state.builder.AllocateId();
|
const auto converted_raw = state.builder.AllocateId();
|
||||||
@@ -210,23 +248,10 @@ uint32_t EmitCompositeExtractU32x2(EmitterState& state, uint32_t arg0, IR::Value
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EmitPackFloat2x16Rtz(EmitterState& state, uint32_t arg0, uint32_t arg1) {
|
uint32_t EmitPackFloat2x16Rtz(EmitterState& state, uint32_t arg0, uint32_t arg1) {
|
||||||
const auto pair = state.builder.AllocateId();
|
const auto low = EmitF32ToF16RtzBits(state, arg0);
|
||||||
state.builder.AddFunction(spv::OpCompositeConstruct, TypeF32Vector(state, 2), pair, arg0, arg1);
|
const auto high = Binary(state, spv::OpShiftLeftLogical, TypeU32(state),
|
||||||
const auto packed = EmitPackHalf2x16(state, pair);
|
EmitF32ToF16RtzBits(state, arg1), ConstantU32(state, 16));
|
||||||
const auto back = EmitExt(state, TypeF32Vector(state, 2), GLSLstd450UnpackHalf2x16, {packed});
|
return EmitOrU32(state, low, high);
|
||||||
auto result = packed;
|
|
||||||
for (uint32_t i = 0; i < 2; i++) {
|
|
||||||
const auto rounded = state.builder.AllocateId();
|
|
||||||
state.builder.AddFunction(spv::OpCompositeExtract, TypeF32(state), rounded, back, i);
|
|
||||||
const auto above =
|
|
||||||
Binary(state, spv::OpFOrdGreaterThan, TypeBool(state),
|
|
||||||
EmitExt(state, TypeF32(state), GLSLstd450FAbs, {rounded}),
|
|
||||||
EmitExt(state, TypeF32(state), GLSLstd450FAbs, {i == 0 ? arg0 : arg1}));
|
|
||||||
result = Binary(state, spv::OpISub, TypeU32(state), result,
|
|
||||||
Select(state, TypeU32(state), above,
|
|
||||||
ConstantU32(state, i == 0 ? 1u : 0x10000u), ConstantU32(state, 0)));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EmitFPSaturate32(EmitterState& state, uint32_t arg0) {
|
uint32_t EmitFPSaturate32(EmitterState& state, uint32_t arg0) {
|
||||||
|
|||||||
@@ -25877,6 +25877,49 @@ TestCase CvtPkrtzF16F32SubnormalRoundsTowardZero() {
|
|||||||
O::S_ENDPGM}};
|
O::S_ENDPGM}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase CvtPkrtzF16F32RuntimeEdges() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
// Independent expected half encodings, including truncation boundaries and NaN payloads.
|
||||||
|
constexpr std::array<std::array<u32, 2>, 31> inputs{{
|
||||||
|
{0x00000000u, 0x0000u}, {0x80000000u, 0x8000u},
|
||||||
|
{0x00000001u, 0x0000u}, {0x007fffffu, 0x0000u},
|
||||||
|
{0x00800000u, 0x0000u}, {0x32ffffffu, 0x0000u},
|
||||||
|
{0x33000000u, 0x0000u}, {0x337fffffu, 0x0000u},
|
||||||
|
{0x33800000u, 0x0001u}, {0x33c00000u, 0x0001u},
|
||||||
|
{0x34000000u, 0x0002u}, {0x387fc000u, 0x03ffu},
|
||||||
|
{0x387fe000u, 0x03ffu}, {0x387fffffu, 0x03ffu},
|
||||||
|
{0x38800000u, 0x0400u}, {0x38802000u, 0x0401u},
|
||||||
|
{0x3e22f983u, 0x3117u}, {0x3f800000u, 0x3c00u},
|
||||||
|
{0x3f801000u, 0x3c00u}, {0x3f801001u, 0x3c00u},
|
||||||
|
{0x3f802000u, 0x3c01u}, {0x477fe000u, 0x7bffu},
|
||||||
|
{0x477ff000u, 0x7bffu}, {0x477fffffu, 0x7bffu},
|
||||||
|
{0x47800000u, 0x7bffu}, {0x7f7fffffu, 0x7bffu},
|
||||||
|
{0x7f800000u, 0x7c00u}, {0x7f800001u, 0x7e00u},
|
||||||
|
{0x7fa02000u, 0x7f01u}, {0x7fc12000u, 0x7e09u},
|
||||||
|
{0x7fffffffu, 0x7fffu},
|
||||||
|
}};
|
||||||
|
TestCase test;
|
||||||
|
test.name = "CvtPkrtzF16F32RuntimeEdges";
|
||||||
|
for (const auto &input : inputs)
|
||||||
|
test.initial.insert(test.initial.end(), {input[0], input[0] ^ 0x80000000u});
|
||||||
|
test.expected = test.initial;
|
||||||
|
for (u32 i = 0; i < inputs.size(); ++i) {
|
||||||
|
for (u32 reg = 0; reg < 2; ++reg) {
|
||||||
|
AppendVMovU32(&test.code, 30, (i * 2u + reg) * 4u);
|
||||||
|
AppendBufferLoadDword(&test.code, reg, 30);
|
||||||
|
}
|
||||||
|
test.code.push_back(EncodeVop2(0x2f, 2, Vgpr(0), 1));
|
||||||
|
AppendStoreVgpr(&test.code, 2, static_cast<u32>(test.expected.size()));
|
||||||
|
const auto half = inputs[i][1];
|
||||||
|
test.expected.push_back(half | ((half ^ 0x8000u) << 16));
|
||||||
|
}
|
||||||
|
AppendEnd(&test.code);
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_CVT_PKRTZ_F16_F32,
|
||||||
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
test.forbidden_spirv = {"PackHalf2x16", "UnpackHalf2x16"};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase CvtPkrtzF16F32SdwaAndOutputModifiers() {
|
TestCase CvtPkrtzF16F32SdwaAndOutputModifiers() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -42213,6 +42256,7 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(Vop3pInt16Ops);
|
AddCase(Vop3pInt16Ops);
|
||||||
AddCase(CvtPkU8F32PacksSelectedByte);
|
AddCase(CvtPkU8F32PacksSelectedByte);
|
||||||
AddCase(CvtPkrtzF16F32SubnormalRoundsTowardZero);
|
AddCase(CvtPkrtzF16F32SubnormalRoundsTowardZero);
|
||||||
|
AddCase(CvtPkrtzF16F32RuntimeEdges);
|
||||||
AddCase(CvtPkrtzF16F32SdwaAndOutputModifiers);
|
AddCase(CvtPkrtzF16F32SdwaAndOutputModifiers);
|
||||||
AddCase(PackedMinMaxF16NanAndSignedZeroEdges);
|
AddCase(PackedMinMaxF16NanAndSignedZeroEdges);
|
||||||
AddCase(VectorMinMaxF16Ops);
|
AddCase(VectorMinMaxF16Ops);
|
||||||
@@ -48029,6 +48073,13 @@ int main(int argc, char **argv) {
|
|||||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
EnsureConfigInitialized();
|
EnsureConfigInitialized();
|
||||||
CheckLeastRecentlyUsedCacheOrdering();
|
CheckLeastRecentlyUsedCacheOrdering();
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--cvt-pkrtz-only") == 0) {
|
||||||
|
VulkanHarness vulkan;
|
||||||
|
RunCase(&vulkan, CvtPkrtzF16F32SubnormalRoundsTowardZero());
|
||||||
|
RunCase(&vulkan, CvtPkrtzF16F32RuntimeEdges());
|
||||||
|
RunCase(&vulkan, CvtPkrtzF16F32SdwaAndOutputModifiers());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--thread-dimensions-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--thread-dimensions-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
CheckComputeThreadDimensions(vulkan);
|
CheckComputeThreadDimensions(vulkan);
|
||||||
|
|||||||
Reference in New Issue
Block a user