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 16128e531e..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,13 +1301,17 @@ namespace SIE std::wstring GetDiskPath(const std::string_view& name, uint32_t descriptor, ShaderClass shaderClass) { + 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) { 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 {}; } diff --git a/src/Utils/Format.cpp b/src/Utils/Format.cpp index 2b0d88905b..5ec1ebea34 100644 --- a/src/Utils/Format.cpp +++ b/src/Utils/Format.cpp @@ -264,4 +264,16 @@ namespace Util return std::tolower(static_cast(ca)) == std::tolower(static_cast(cb)); }); } + + std::string GetShaderDefinesSuffix(const std::string& definesStr) + { + if (definesStr.empty()) + return {}; + 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 dcee025bce..476b9b1847 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 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. + */ + std::string GetShaderDefinesSuffix(const std::string& definesStr); } // namespace Util \ No newline at end of file