From f90a8b1589d08ef48d933bd472d4ba864addaefb Mon Sep 17 00:00:00 2001 From: nmzik Date: Mon, 26 Oct 2026 07:40:55 +0100 Subject: [PATCH] graphics: decode register clears of RGBA16 UNORM, UINT and SINT and RG32 UINT and SINT targets Co-authored-by: Senaxx Co-authored-by: Jetsku <59046009+Jetsku@users.noreply.github.com> --- .../host_gpu/renderer/image/imageInfo.h | 33 +++++++++++++++---- tests/ShaderRecompilerComputeTests.cpp | 20 +++++++++-- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/graphics/host_gpu/renderer/image/imageInfo.h b/src/graphics/host_gpu/renderer/image/imageInfo.h index 41351267c..207fd37af 100644 --- a/src/graphics/host_gpu/renderer/image/imageInfo.h +++ b/src/graphics/host_gpu/renderer/image/imageInfo.h @@ -378,6 +378,9 @@ inline constexpr std::array VIDEO_OUT_FORMAT_POLICIES { const auto encoded = static_cast(value & 0xffu) / 255.0f; return encoded <= 0.04045f ? encoded / 12.92f : std::pow((encoded + 0.055f) / 1.055f, 2.4f); }; + const auto unorm16 = [](uint64_t value) { + return static_cast(value & 0xffffu) / 65535.0f; + }; const auto float16 = [](uint32_t value) { const auto sign = (value & 0x8000u) << 16u; const auto exponent = (value >> 10u) & 0x1fu; @@ -403,19 +406,35 @@ inline constexpr std::array VIDEO_OUT_FORMAT_POLICIES { next.float32[1] = float16(packed >> 16u); [[fallthrough]]; case vk::Format::eR16Sfloat: next.float32[0] = float16(packed); break; - case vk::Format::eR16G16Unorm: - next.float32[1] = static_cast(packed >> 16u) / 65535.0f; + case vk::Format::eR16G16B16A16Unorm: + next.float32[2] = unorm16(clear_words >> 32u); + next.float32[3] = unorm16(clear_words >> 48u); [[fallthrough]]; - case vk::Format::eR16Unorm: - next.float32[0] = static_cast(packed & 0xffffu) / 65535.0f; + case vk::Format::eR16G16Unorm: + next.float32[1] = unorm16(packed >> 16u); + [[fallthrough]]; + case vk::Format::eR16Unorm: next.float32[0] = unorm16(packed); break; + case vk::Format::eR16G16B16A16Uint: + for (uint32_t i = 0; i < 4; i++) { + next.uint32[i] = static_cast(clear_words >> (16u * i)); + } break; - // Each clear register stores one raw 32-bit float channel. + case vk::Format::eR16G16B16A16Sint: + for (uint32_t i = 0; i < 4; i++) { + next.int32[i] = static_cast(clear_words >> (16u * i)); + } + break; + // Each clear register stores one raw 32-bit channel. case vk::Format::eR32G32Sfloat: next.float32[1] = std::bit_cast(static_cast(clear_words >> 32u)); [[fallthrough]]; case vk::Format::eR32Sfloat: next.float32[0] = std::bit_cast(packed); break; - case vk::Format::eR32Uint: next.uint32[0] = packed; break; - case vk::Format::eR32Sint: next.int32[0] = static_cast(packed); break; + case vk::Format::eR32G32Uint: + case vk::Format::eR32G32Sint: + next.uint32[1] = static_cast(clear_words >> 32u); + [[fallthrough]]; + case vk::Format::eR32Uint: + case vk::Format::eR32Sint: next.uint32[0] = packed; break; // Narrow targets hold their channels in the low bits of the first word. UFC 6 (PPSA23566) // fast-clears an R8_UINT map through CMASK every frame and then writes only part of it; // without these the clear was dropped and old contents piled up. diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 96c5bd700..a6a64f143 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -12506,6 +12506,8 @@ public: PackedCase{vk::Format::eR16G16Unorm, 0x0000ffffu, {1.0f, 0.0f}}, PackedCase{vk::Format::eR16G16Unorm, 0x80000001u, {1.0f / 65535.0f, 32768.0f / 65535.0f}}, + PackedCase{vk::Format::eR16G16B16A16Unorm, 0x000180000000ffffull, + {1.0f, 0.0f, 32768.0f / 65535.0f, 1.0f / 65535.0f}}, PackedCase{vk::Format::eR8Unorm, 0xffffff80u, {128.0f / 255.0f}}, PackedCase{vk::Format::eR8G8Unorm, 0xffff40ffu, {1.0f, 64.0f / 255.0f}}, }; @@ -12541,11 +12543,19 @@ public: }; for (const auto &test : {UintCase{vk::Format::eR8Uint, 0xffffff05u, {5u}}, UintCase{vk::Format::eR8G8Uint, 0xffff0a05u, {5u, 10u}}, - UintCase{vk::Format::eR16Uint, 0xffff1234u, {0x1234u}}}) { - Require(name, "packed unsigned color clear", + UintCase{vk::Format::eR16Uint, 0xffff1234u, {0x1234u}}, + UintCase{vk::Format::eR16G16B16A16Uint, 0x4444333322221111ull, + {0x1111u, 0x2222u, 0x3333u, 0x4444u}}, + UintCase{vk::Format::eR16G16B16A16Sint, 0x7fff0001ffff8000ull, + {0xffff8000u, 0xffffffffu, 1u, 0x7fffu}}, + UintCase{vk::Format::eR32G32Uint, 0x89abcdef01234567ull, + {0x01234567u, 0x89abcdefu}}, + UintCase{vk::Format::eR32G32Sint, 0x80000000ffffffffull, + {0xffffffffu, 0x80000000u}}}) { + Require(name, "packed integer color clear", DecodePackedColorClear(test.format, test.word, clear) && std::bit_cast>(clear.uint32) == test.channels, - "narrow unsigned clear lost a channel or kept bits of the next one"); + "integer clear lost a channel, its sign or bits of the next one"); } Require(name, "R8 DWORD fill", !DecodeColorDwordFill(vk::Format::eR8Uint, 0x01020304u, clear) && @@ -12561,6 +12571,10 @@ public: DecodeColorDwordFill(vk::Format::eR32G32Sfloat, 0x3f800000u, clear) && clear.float32[0] == 1.0f && clear.float32[1] == 1.0f, "a repeated DWORD fill was decoded as a two-register clear"); + Require(name, "RGBA16 DWORD fill", + DecodeColorDwordFill(vk::Format::eR16G16B16A16Uint, 0x22221111u, clear) && + clear.uint32[2] == 0x1111u && clear.uint32[3] == 0x2222u, + "a DWORD fill did not repeat across the 64-bit pixel"); constexpr uintptr_t base = 0x0000000204100000ull; constexpr uint64_t allocation_size = 0x1000000; constexpr uint64_t allocation_alignment = 0x10000;