mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
SourceVersion hashed every libSceAgcDriver source, so any driver-only change sent the shader disk cache cold: 88 of the last 200 merges on main did; with the inputs below, 51 of them would have. It now covers: - core/shader/recompiler/** (sources, headers, CMake); - prx/libc/include/HostThreadLocal.hpp, the one header the recompiler includes from outside its directory (checked against the ninja deps); - the checked-out commits of SPIRV-Headers and glslang, plus SPIRV-Tools when ANYPS5_ENABLE_SPIRV_TOOLS is on (the submodule HEAD files are build dependencies, so moving a submodule regenerates the version); - the compiler, build type, architecture and C++ flags. Everything else the driver passes to the recompiler arrives in the RecompileRequest, which the per-entry key covers. Two build-time checks keep that true, failing the version step with a message naming the file: - a recompiler source that includes a project header outside the hashed set; - a driver source that calls or using-declares a ShaderRecompiler name not on the reviewed list, pulls in the whole namespace, or sets an environment variable (which could flip a recompiler APS5_* switch behind the key). APS5_* switches are collected from every hashed source. The version script sets cmake_minimum_required: without it CMake 3.x runs it with CMP0054 OLD and reads the quoted section names in its if() tests as variables.
169 lines
6.7 KiB
CMake
169 lines
6.7 KiB
CMake
cmake_minimum_required(VERSION 3.20)
|
|
|
|
file(STRINGS "${LIST_FILE}" entries)
|
|
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 IN LISTS sources)
|
|
if(NOT EXISTS "${source}")
|
|
continue()
|
|
endif()
|
|
file(SHA256 "${source}" digest)
|
|
file(RELATIVE_PATH relative "${ROOT}" "${source}")
|
|
string(APPEND manifest "${relative} ${digest}\n")
|
|
if(NOT source MATCHES "\\.(cpp|hpp|h)$")
|
|
continue()
|
|
endif()
|
|
file(STRINGS "${source}" lines 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 "${source}" "${RECOMPILER_DIR}/" inRecompiler)
|
|
if(NOT inRecompiler EQUAL 0)
|
|
continue()
|
|
endif()
|
|
get_filename_component(sourceDirectory "${source}" DIRECTORY)
|
|
file(STRINGS "${source}" lines 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}")
|
|
get_filename_component(resolved "${directory}/${included}" REALPATH)
|
|
break()
|
|
endif()
|
|
endforeach()
|
|
file(RELATIVE_PATH relativeSource "${ROOT}" "${source}")
|
|
if(NOT resolved)
|
|
continue()
|
|
endif()
|
|
string(FIND "${resolved}" "${recompilerReal}/" resolvedInRecompiler)
|
|
string(FIND "${resolved}" "${rootReal}/3rdparty/" resolvedInThirdParty)
|
|
if(resolvedInRecompiler EQUAL 0 OR 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 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 "#ifndef CORE_SHADER_RECOMPILER_SHADERCACHEVERSION_HPP
|
|
#define CORE_SHADER_RECOMPILER_SHADERCACHEVERSION_HPP
|
|
|
|
#include <cstdint>
|
|
#include <string_view>
|
|
|
|
namespace ShaderRecompiler::ShaderDiskCache::Generated {
|
|
|
|
inline constexpr std::uint64_t SourceVersion = 0x${version}ull;
|
|
|
|
inline constexpr std::string_view RecompilerSwitches[] = {
|
|
${switchList}
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|
|
")
|
|
|
|
if(EXISTS "${OUTPUT}")
|
|
file(READ "${OUTPUT}" previous)
|
|
if(previous STREQUAL content)
|
|
return()
|
|
endif()
|
|
endif()
|
|
file(WRITE "${OUTPUT}" "${content}")
|