From dffdb5002e7275bb07507e92ca158f3b742411ad Mon Sep 17 00:00:00 2001 From: MarcZX100 Date: Tue, 13 Oct 2026 12:33:24 +0000 Subject: [PATCH 1/3] fix(shader): read both version 21 request layouts --- CMakeLists.txt | 8 +++++ .../ControlFlow/src/RequestSerializer.cpp | 12 ++++++- .../tests/RequestSerializerTests.cpp | 33 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 core/shader/recompiler/tests/RequestSerializerTests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eb8dd533..8704dfdbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,14 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler) add_test(NAME shader_cache_file_read COMMAND shader_cache_file_read_tests) set_tests_properties(shader_cache_file_read PROPERTIES TIMEOUT 20) + add_test_executable(request_serializer_tests + ${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/tests/RequestSerializerTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp) + target_include_directories(request_serializer_tests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler + ${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/ControlFlow/include) + add_test(NAME request_serializer COMMAND request_serializer_tests) + set_tests_properties(request_serializer PROPERTIES TIMEOUT 20) find_package(Python3 QUIET COMPONENTS Interpreter) if(Python3_Interpreter_FOUND) add_test(NAME pr_overlap_encoding COMMAND ${Python3_EXECUTABLE} diff --git a/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp b/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp index 217d54eee..eb3c70970 100644 --- a/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp +++ b/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp @@ -153,6 +153,8 @@ public: bool ReadBool() { return ReadU8() != 0u; } + std::uint64_t Remaining() const { return m_data.size() - m_offset; } + std::uint32_t ReadU32() { std::uint32_t value = 0u; for (std::uint32_t i = 0; i < 4u; i++) { @@ -808,7 +810,15 @@ DeserializedRequest RequestSerializer::Deserialize(std::string_view text) const if (version >= 17u) result.request.target.registrationModules = reader.ReadBool(); if (version >= 17u) result.request.target.registrationWordLimit = reader.ReadU32(); if (version >= 18u) result.request.target.vertexSubgroups = reader.ReadBool(); - if (version >= 21u) result.request.target.float16DenormPreserve = reader.ReadBool(); + if (version >= 21u) { + if (reader.Remaining() == 1u) { + const auto float16DenormPreserve = reader.ReadU8(); + if (float16DenormPreserve > 1u) throw std::runtime_error("RequestSerializer: invalid float16DenormPreserve value"); + result.request.target.float16DenormPreserve = float16DenormPreserve != 0u; + } else if (reader.Remaining() != 0u) { + throw std::runtime_error("RequestSerializer: unexpected trailing data"); + } + } return result; } diff --git a/core/shader/recompiler/tests/RequestSerializerTests.cpp b/core/shader/recompiler/tests/RequestSerializerTests.cpp new file mode 100644 index 000000000..9066ce55c --- /dev/null +++ b/core/shader/recompiler/tests/RequestSerializerTests.cpp @@ -0,0 +1,33 @@ +#include "ControlFlow/RequestSerializer.hpp" + +#include +#include + +namespace { + +constexpr char kHistoricalVersion21[] = + "NVNQQRUAAAAFAAADAAAAAAABAAAAAAAAAAAAgb8AAAAAAAAAAAAAAAAAAAAAQAAAAAgAAAADAAAAAAAAAN+bVxPgrGgk776t3gABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAOQAAAAAAAAAAAAAAAAAAAAAAQEAAAEAAAAAAAAAAAAFAAAAAAAEAAAAAAAAABI0VngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAALAAAAEAAAAEAAAAAAAAF4VjQSAQEBOTAAAAA="; + +void Require(bool condition) { + if (!condition) std::abort(); +} + +} + +int main() { + const ShaderRecompiler::RequestSerializer serializer; + + const auto historical = serializer.Deserialize(kHistoricalVersion21); + Require(!historical.request.target.float16DenormPreserve); + + ShaderRecompiler::RecompileRequest request{}; + request.target.float16DenormPreserve = false; + const auto withoutPreserve = serializer.Deserialize(serializer.Serialize(request)); + Require(!withoutPreserve.request.target.float16DenormPreserve); + + request.target.float16DenormPreserve = true; + const auto withPreserve = serializer.Deserialize(serializer.Serialize(request)); + Require(withPreserve.request.target.float16DenormPreserve); + + return 0; +} From 0e087e974e62aa884d650b98447865ed8f59159d Mon Sep 17 00:00:00 2001 From: MarcZX100 Date: Tue, 13 Oct 2026 12:46:15 +0000 Subject: [PATCH 2/3] test(shader): cover version 21 F16 request layout --- core/shader/recompiler/tests/RequestSerializerTests.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/shader/recompiler/tests/RequestSerializerTests.cpp b/core/shader/recompiler/tests/RequestSerializerTests.cpp index 9066ce55c..28bb1eae0 100644 --- a/core/shader/recompiler/tests/RequestSerializerTests.cpp +++ b/core/shader/recompiler/tests/RequestSerializerTests.cpp @@ -7,6 +7,8 @@ namespace { constexpr char kHistoricalVersion21[] = "NVNQQRUAAAAFAAADAAAAAAABAAAAAAAAAAAAgb8AAAAAAAAAAAAAAAAAAAAAQAAAAAgAAAADAAAAAAAAAN+bVxPgrGgk776t3gABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAOQAAAAAAAAAAAAAAAAAAAAAAQEAAAEAAAAAAAAAAAAFAAAAAAAEAAAAAAAAABI0VngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAALAAAAEAAAAEAAAAAAAAF4VjQSAQEBOTAAAAA="; +constexpr char kVersion21WithF16Tail[] = + "NVNQQRUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAQA0DAAEB"; void Require(bool condition) { if (!condition) std::abort(); @@ -20,6 +22,9 @@ int main() { const auto historical = serializer.Deserialize(kHistoricalVersion21); Require(!historical.request.target.float16DenormPreserve); + const auto version21WithTail = serializer.Deserialize(kVersion21WithF16Tail); + Require(version21WithTail.request.target.float16DenormPreserve); + ShaderRecompiler::RecompileRequest request{}; request.target.float16DenormPreserve = false; const auto withoutPreserve = serializer.Deserialize(serializer.Serialize(request)); From 2ad825f6590e3e94bd836af5ca02db6199be315e Mon Sep 17 00:00:00 2001 From: MarcZX100 Date: Tue, 13 Oct 2026 13:31:35 +0000 Subject: [PATCH 3/3] fix(shader): require the version 22 F16 request flag --- .../recompiler/ControlFlow/src/RequestSerializer.cpp | 1 + .../recompiler/tests/RequestSerializerTests.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp b/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp index eb3c70970..8cf344e0b 100644 --- a/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp +++ b/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp @@ -811,6 +811,7 @@ DeserializedRequest RequestSerializer::Deserialize(std::string_view text) const if (version >= 17u) result.request.target.registrationWordLimit = reader.ReadU32(); if (version >= 18u) result.request.target.vertexSubgroups = reader.ReadBool(); if (version >= 21u) { + if (version >= 22u) reader.Require(1u); if (reader.Remaining() == 1u) { const auto float16DenormPreserve = reader.ReadU8(); if (float16DenormPreserve > 1u) throw std::runtime_error("RequestSerializer: invalid float16DenormPreserve value"); diff --git a/core/shader/recompiler/tests/RequestSerializerTests.cpp b/core/shader/recompiler/tests/RequestSerializerTests.cpp index 28bb1eae0..18b926c5e 100644 --- a/core/shader/recompiler/tests/RequestSerializerTests.cpp +++ b/core/shader/recompiler/tests/RequestSerializerTests.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace { @@ -9,6 +10,8 @@ constexpr char kHistoricalVersion21[] = "NVNQQRUAAAAFAAADAAAAAAABAAAAAAAAAAAAgb8AAAAAAAAAAAAAAAAAAAAAQAAAAAgAAAADAAAAAAAAAN+bVxPgrGgk776t3gABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAOQAAAAAAAAAAAAAAAAAAAAAAQEAAAEAAAAAAAAAAAAFAAAAAAAEAAAAAAAAABI0VngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAALAAAAEAAAAEAAAAAAAAF4VjQSAQEBOTAAAAA="; constexpr char kVersion21WithF16Tail[] = "NVNQQRUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAQA0DAAEB"; +constexpr char kVersion22WithoutF16Tail[] = + "NVNQQRYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAQA0DAAE="; void Require(bool condition) { if (!condition) std::abort(); @@ -25,6 +28,14 @@ int main() { const auto version21WithTail = serializer.Deserialize(kVersion21WithF16Tail); Require(version21WithTail.request.target.float16DenormPreserve); + bool rejectedTruncatedVersion22 = false; + try { + static_cast(serializer.Deserialize(kVersion22WithoutF16Tail)); + } catch (const std::runtime_error& error) { + rejectedTruncatedVersion22 = std::string_view(error.what()) == "RequestSerializer: truncated data"; + } + Require(rejectedTruncatedVersion22); + ShaderRecompiler::RecompileRequest request{}; request.target.float16DenormPreserve = false; const auto withoutPreserve = serializer.Deserialize(serializer.Serialize(request));