Files
AnyPS5/core/shader/recompiler/ShaderCacheVersion.cmake
Nicola GalluccioandClaude Opus 5.5 50e383ca4a fix(shader): resolve each shader cache source and include path once
ShaderCacheVersion.cmake resolved every source's real path twice and
every include's real path once per include line: 1102 REALPATH calls
per run, about 1.6 ms each on Windows. Reuse the source paths from the
first pass and resolve each include path once. The generated version
file is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 16:46:39 +02:00

173 lines
7.0 KiB
CMake

cmake_minimum_required(VERSION 3.20)
file(STRINGS "${LIST_FILE}" entries ENCODING UTF-8)
set(sources)
set(guard)
set(submodules)
set(includeDirectories)
set(reviewed)
foreach(entry IN LISTS entries)
string(FIND "${entry}" " " split)
string(SUBSTRING "${entry}" 0 ${split} section)
math(EXPR split "${split} + 1")
string(SUBSTRING "${entry}" ${split} -1 value)
if(section STREQUAL "sources")
list(APPEND sources "${value}")
elseif(section STREQUAL "guard")
list(APPEND guard "${value}")
elseif(section STREQUAL "submodules")
list(APPEND submodules "${value}")
elseif(section STREQUAL "includes")
list(APPEND includeDirectories "${value}")
elseif(section STREQUAL "reviewed")
list(APPEND reviewed "${value}")
endif()
endforeach()
list(SORT sources)
list(SORT guard)
get_filename_component(rootReal "${ROOT}" REALPATH)
get_filename_component(recompilerReal "${RECOMPILER_DIR}" REALPATH)
set(sourcesReal)
foreach(source IN LISTS sources)
get_filename_component(real "${source}" REALPATH)
list(APPEND sourcesReal "${real}")
endforeach()
set(failures)
set(manifest "${EXTRA}\n")
set(switches)
foreach(source sourceReal IN ZIP_LISTS sources sourcesReal)
if(NOT EXISTS "${source}")
continue()
endif()
file(READ "${source}" text)
string(REPLACE "\r\n" "\n" text "${text}")
string(SHA256 digest "${text}")
file(RELATIVE_PATH relative "${rootReal}" "${sourceReal}")
string(APPEND manifest "${relative} ${digest}\n")
if(NOT source MATCHES "\\.(cpp|hpp|h)$")
continue()
endif()
file(STRINGS "${source}" lines ENCODING UTF-8 REGEX "\"APS5_[A-Z0-9_]+\"")
foreach(line IN LISTS lines)
string(REGEX MATCHALL "\"APS5_[A-Z0-9_]+\"" names "${line}")
list(APPEND switches ${names})
endforeach()
string(FIND "${sourceReal}" "${recompilerReal}/" inRecompiler)
if(NOT inRecompiler EQUAL 0)
continue()
endif()
get_filename_component(sourceDirectory "${sourceReal}" DIRECTORY)
file(STRINGS "${source}" lines ENCODING UTF-8 REGEX "^[ \t]*#[ \t]*include[ \t]*\"[^\"]+\"")
foreach(line IN LISTS lines)
string(REGEX REPLACE "^[ \t]*#[ \t]*include[ \t]*\"([^\"]+)\".*$" "\\1" included "${line}")
set(resolved)
foreach(directory IN ITEMS "${sourceDirectory}" ${includeDirectories})
if(EXISTS "${directory}/${included}" AND NOT IS_DIRECTORY "${directory}/${included}")
string(SHA1 includedKey "${directory}/${included}")
if(NOT DEFINED includedReal_${includedKey})
get_filename_component(includedReal_${includedKey} "${directory}/${included}" REALPATH)
endif()
set(resolved "${includedReal_${includedKey}}")
break()
endif()
endforeach()
file(RELATIVE_PATH relativeSource "${rootReal}" "${sourceReal}")
if(NOT resolved)
continue()
endif()
string(FIND "${resolved}" "${rootReal}/3rdparty/" resolvedInThirdParty)
if(resolvedInThirdParty EQUAL 0 OR resolved IN_LIST sourcesReal)
continue()
endif()
file(RELATIVE_PATH relativeResolved "${rootReal}" "${resolved}")
list(APPEND failures "${relativeSource} includes ${relativeResolved}, which the shader disk cache key does not hash: add it to shaderCacheKeySources in core/shader/recompiler/CMakeLists.txt")
endforeach()
endforeach()
foreach(submodule IN LISTS submodules)
file(RELATIVE_PATH relative "${ROOT}" "${submodule}")
set(commit)
if(GIT)
execute_process(COMMAND "${GIT}" -C "${submodule}" rev-parse --show-toplevel HEAD
OUTPUT_VARIABLE output OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE status ERROR_QUIET)
if(status EQUAL 0)
string(REPLACE "\n" ";" output "${output}")
list(GET output 0 topLevel)
list(GET output 1 head)
get_filename_component(topLevel "${topLevel}" REALPATH)
get_filename_component(submoduleReal "${submodule}" REALPATH)
if(topLevel STREQUAL submoduleReal)
set(commit "${head}")
endif()
endif()
endif()
if(NOT commit AND EXISTS "${submodule}/CMakeLists.txt")
file(SHA256 "${submodule}/CMakeLists.txt" digest)
set(commit "unversioned ${digest}")
endif()
string(APPEND manifest "submodule ${relative} ${commit}\n")
endforeach()
foreach(source IN LISTS guard)
if(NOT EXISTS "${source}")
continue()
endif()
file(RELATIVE_PATH relative "${ROOT}" "${source}")
file(STRINGS "${source}" lines ENCODING UTF-8 REGEX "ShaderRecompiler|setenv|putenv")
foreach(line IN LISTS lines)
if(line MATCHES "using[ \t]+namespace[ \t]+ShaderRecompiler" OR line MATCHES "namespace[ \t]+[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*(::)?ShaderRecompiler[ \t]*;")
list(APPEND failures "${relative} brings the ShaderRecompiler namespace in whole, which hides its calls from the shader disk cache key review: qualify them")
endif()
if(line MATCHES "(^|[^A-Za-z0-9_])(setenv|putenv|_putenv_s)[ \t]*\\(")
list(APPEND failures "${relative} sets an environment variable, which can change the recompiler's APS5_* switches behind the shader disk cache key")
endif()
string(REGEX MATCHALL "ShaderRecompiler::[A-Za-z_][A-Za-z0-9_]*(::[A-Za-z_][A-Za-z0-9_]*)*[ \t]*\\(" calls "${line}")
string(REGEX MATCHALL "using[ \t]+ShaderRecompiler::[A-Za-z_][A-Za-z0-9_]*(::[A-Za-z_][A-Za-z0-9_]*)*" declarations "${line}")
foreach(use IN LISTS calls declarations)
string(REGEX REPLACE "^(using[ \t]+)?ShaderRecompiler::([A-Za-z0-9_:]+).*$" "\\2" name "${use}")
if(NOT name IN_LIST reviewed)
list(APPEND failures "${relative} uses ShaderRecompiler::${name}, which has not been reviewed for the shader disk cache key: if it can change what a recompile produces other than through the RecompileRequest, key that input in ShaderDiskCache::BuildKey, then add the name to shaderCacheReviewedNames in core/shader/recompiler/CMakeLists.txt")
endif()
endforeach()
endforeach()
endforeach()
if(failures)
list(REMOVE_DUPLICATES failures)
list(JOIN failures "\n " failureText)
message(FATAL_ERROR "Shader disk cache key check failed:\n ${failureText}")
endif()
string(SHA256 version "${manifest}")
string(SUBSTRING "${version}" 0 16 version)
list(REMOVE_DUPLICATES switches)
list(SORT switches)
list(JOIN switches ",\n " switchList)
set(content "#include \"ShaderCacheSourceVersion.hpp\"\n")
string(APPEND content "
namespace ShaderRecompiler::ShaderDiskCache::Generated {
namespace {
constexpr std::string_view recompilerSwitches[] = {
${switchList}
};
}
constinit const std::uint64_t SourceVersion = 0x${version}ull;
constinit const std::span<const std::string_view> RecompilerSwitches = recompilerSwitches;
}
")
if(EXISTS "${OUTPUT}")
file(READ "${OUTPUT}" previous)
if(previous STREQUAL content)
return()
endif()
endif()
file(WRITE "${OUTPUT}" "${content}")