From 94e4127931ab96de9ae1a20d5b21ff0bb8b8f743 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 5 Apr 2026 21:13:44 -0700 Subject: [PATCH 1/3] fix: hash defines for disk cache --- src/ShaderCache.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index 16128e531e..baec6360ee 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -1300,13 +1300,24 @@ namespace SIE std::wstring GetDiskPath(const std::string_view& name, uint32_t descriptor, ShaderClass shaderClass) { + // If extra defines are active (e.g. LLFDEBUG), fold them into the filename + // via a hash suffix so the disk cache is keyed per-defines-set. Without + // this, toggling extra defines would silently reload a stale cached blob. + const auto& definesStr = globals::state->shaderDefinesString; + std::wstring suffix; + if (!definesStr.empty()) { + const std::size_t h = std::hash{}(definesStr); + suffix = std::format(L"_{:08X}", static_cast(h)); + } + + const auto wname = std::wstring(name.begin(), name.end()); switch (shaderClass) { case ShaderClass::Pixel: - return std::format(L"Data/ShaderCache/{}/{:X}.pso", std::wstring(name.begin(), name.end()), descriptor); + return std::format(L"Data/ShaderCache/{}/{:X}{}.pso", wname, descriptor, suffix); case ShaderClass::Vertex: - return std::format(L"Data/ShaderCache/{}/{:X}.vso", std::wstring(name.begin(), name.end()), descriptor); + return std::format(L"Data/ShaderCache/{}/{:X}{}.vso", wname, descriptor, suffix); case ShaderClass::Compute: - return std::format(L"Data/ShaderCache/{}/{:X}.cso", std::wstring(name.begin(), name.end()), descriptor); + return std::format(L"Data/ShaderCache/{}/{:X}{}.cso", wname, descriptor, suffix); } return {}; } From 6b4d50c2f5279dcb2e30851b718ece3868cc354e Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 5 Apr 2026 21:42:09 -0700 Subject: [PATCH 2/3] chore: address ai comments --- src/FrameAnnotations.cpp | 4 +++- src/ShaderCache.cpp | 12 +++--------- src/Utils/Format.cpp | 8 ++++++++ src/Utils/Format.h | 10 ++++++++++ 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/FrameAnnotations.cpp b/src/FrameAnnotations.cpp index 2bda0a1acf..14fafd6e98 100644 --- a/src/FrameAnnotations.cpp +++ b/src/FrameAnnotations.cpp @@ -1,6 +1,7 @@ #include "FrameAnnotations.h" #include "State.h" +#include "Util.h" #pragma comment(lib, "dxguid.lib") @@ -34,7 +35,8 @@ namespace FrameAnnotations if (globals::game::currentPixelShader && *globals::game::currentPixelShader) { descriptor = (*globals::game::currentPixelShader)->id; } - std::string diskPath = std::format("Data/ShaderCache/{}/{:X}.pso", shader->fxpFilename, descriptor); + const std::string definesSuffix = Util::GetShaderDefinesSuffix(globals::state->shaderDefinesString); + std::string diskPath = std::format("Data/ShaderCache/{}/{:X}{}.pso", shader->fxpFilename, descriptor, definesSuffix); const std::string passName = std::format("[{}:{:X}] ({:X}) <{}> {} -> {}", magic_enum::enum_name(ShaderType), descriptor, pass->passEnum, pass->accumulationHint, pass->geometry->name.c_str(), diskPath); globals::state->BeginPerfEvent(passName); diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index baec6360ee..efe5ef7c11 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -1,6 +1,7 @@ #include "ShaderCache.h" #include "Globals.h" #include "ShaderFileWatcher.h" +#include "Util.h" #include @@ -1300,15 +1301,8 @@ namespace SIE std::wstring GetDiskPath(const std::string_view& name, uint32_t descriptor, ShaderClass shaderClass) { - // If extra defines are active (e.g. LLFDEBUG), fold them into the filename - // via a hash suffix so the disk cache is keyed per-defines-set. Without - // this, toggling extra defines would silently reload a stale cached blob. - const auto& definesStr = globals::state->shaderDefinesString; - std::wstring suffix; - if (!definesStr.empty()) { - const std::size_t h = std::hash{}(definesStr); - suffix = std::format(L"_{:08X}", static_cast(h)); - } + const auto suffixNarrow = Util::GetShaderDefinesSuffix(globals::state->shaderDefinesString); + const std::wstring suffix(suffixNarrow.begin(), suffixNarrow.end()); const auto wname = std::wstring(name.begin(), name.end()); switch (shaderClass) { diff --git a/src/Utils/Format.cpp b/src/Utils/Format.cpp index 2b0d88905b..9b2a4e10ca 100644 --- a/src/Utils/Format.cpp +++ b/src/Utils/Format.cpp @@ -264,4 +264,12 @@ namespace Util return std::tolower(static_cast(ca)) == std::tolower(static_cast(cb)); }); } + + std::string GetShaderDefinesSuffix(const std::string& definesStr) + { + if (definesStr.empty()) + return {}; + const std::size_t h = std::hash{}(definesStr); + return std::format("_{:08X}", static_cast(h)); + } } // namespace Util diff --git a/src/Utils/Format.h b/src/Utils/Format.h index dcee025bce..4a49b2ae65 100644 --- a/src/Utils/Format.h +++ b/src/Utils/Format.h @@ -120,4 +120,14 @@ namespace Util /** Case-insensitive equality for two strings. */ bool IEquals(std::string_view a, std::string_view b); + + /** + * Returns the defines-based shader cache filename suffix for the given shader + * defines string, or an empty string when definesStr is empty. The suffix + * has the form "_{08X}" where the hex value is a 32-bit hash of the string. + * + * This matches the suffix logic used by SIE::SShaderCache::GetDiskPath so + * that any code building a cache path by hand stays in sync. + */ + std::string GetShaderDefinesSuffix(const std::string& definesStr); } // namespace Util \ No newline at end of file From 10205a1fb7c0f5c688ea460af139ddfefa3ae141 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 5 Apr 2026 21:58:45 -0700 Subject: [PATCH 3/3] chore: address more ai comments --- src/Utils/Format.cpp | 8 ++++++-- src/Utils/Format.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Utils/Format.cpp b/src/Utils/Format.cpp index 9b2a4e10ca..5ec1ebea34 100644 --- a/src/Utils/Format.cpp +++ b/src/Utils/Format.cpp @@ -269,7 +269,11 @@ namespace Util { if (definesStr.empty()) return {}; - const std::size_t h = std::hash{}(definesStr); - return std::format("_{:08X}", static_cast(h)); + uint32_t h = 2166136261u; // FNV-1a 32-bit offset basis + for (unsigned char c : definesStr) { + h ^= c; + h *= 16777619u; // FNV prime + } + return std::format("_{:08X}", h); } } // namespace Util diff --git a/src/Utils/Format.h b/src/Utils/Format.h index 4a49b2ae65..476b9b1847 100644 --- a/src/Utils/Format.h +++ b/src/Utils/Format.h @@ -124,7 +124,7 @@ namespace Util /** * Returns the defines-based shader cache filename suffix for the given shader * defines string, or an empty string when definesStr is empty. The suffix - * has the form "_{08X}" where the hex value is a 32-bit hash of the string. + * has the form "_{:08X}" where the hex value is a 32-bit FNV-1a hash of the string. * * This matches the suffix logic used by SIE::SShaderCache::GetDiskPath so * that any code building a cache path by hand stays in sync.