From 5156482ce86025f76892072a81f8659f6741427f Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Thu, 8 Aug 2024 13:56:00 -0300 Subject: [PATCH] Allow shader patching --- documents/patching-shader.md | 14 +++++++ src/common/config.cpp | 7 ++++ src/common/config.h | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 40 +++++++++++++++++-- .../renderer_vulkan/vk_pipeline_cache.h | 2 + 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 documents/patching-shader.md diff --git a/documents/patching-shader.md b/documents/patching-shader.md new file mode 100644 index 000000000..e9a180dbb --- /dev/null +++ b/documents/patching-shader.md @@ -0,0 +1,14 @@ +### Install Vulkan SDK and \*ensure `spirv-cross` and `glslc` are in PATH\*. + +1. Enable `dumpShaders` in config.toml + +2. Run `spirv-cross -V fs_0x000000.spv --output fs_0x000000.glsl` to decompile the SPIR-V IR to GLSL. + +3. Edit the GLSL file as you wish + +4. To compile back to SPIR-V, run (change the _**-fshader-stage**_ to correct stage): + `glslc --target-env=vulkan1.3 --target-spv=spv1.6 -fshader-stage=frag fs_0x000000.glsl -o fs_0x000000.spv` + +5. Put the updated .spv file to `shader/patch` folder with the same name as the original shader + +6. Enable `patchShaders` in config.toml \ No newline at end of file diff --git a/src/common/config.cpp b/src/common/config.cpp index f676ab949..6098fdf68 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -21,6 +21,7 @@ static bool isLibc = true; static bool isShowSplash = false; static bool isNullGpu = false; static bool shouldDumpShaders = false; +static bool shouldPatchShaders = true; static bool shouldDumpPM4 = false; static u32 vblankDivider = 1; static bool vkValidation = false; @@ -94,6 +95,10 @@ bool dumpShaders() { return shouldDumpShaders; } +bool patchShaders() { + return shouldPatchShaders; +} + bool dumpPM4() { return shouldDumpPM4; } @@ -245,6 +250,7 @@ void load(const std::filesystem::path& path) { screenHeight = toml::find_or(gpu, "screenHeight", screenHeight); isNullGpu = toml::find_or(gpu, "nullGpu", false); shouldDumpShaders = toml::find_or(gpu, "dumpShaders", false); + shouldPatchShaders = toml::find_or(gpu, "patchShaders", true); shouldDumpPM4 = toml::find_or(gpu, "dumpPM4", false); vblankDivider = toml::find_or(gpu, "vblankDivider", 1); } @@ -325,6 +331,7 @@ void save(const std::filesystem::path& path) { data["GPU"]["screenHeight"] = screenHeight; data["GPU"]["nullGpu"] = isNullGpu; data["GPU"]["dumpShaders"] = shouldDumpShaders; + data["GPU"]["patchShaders"] = shouldPatchShaders; data["GPU"]["dumpPM4"] = shouldDumpPM4; data["GPU"]["vblankDivider"] = vblankDivider; data["Vulkan"]["gpuId"] = gpuId; diff --git a/src/common/config.h b/src/common/config.h index 53c88ec9e..0b808366c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -25,6 +25,7 @@ bool isLleLibc(); bool showSplash(); bool nullGpu(); bool dumpShaders(); +bool patchShaders(); bool dumpPM4(); bool isRdocEnabled(); u32 vblankDiv(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 8d27d252c..64d79274b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -274,7 +274,15 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline() { // Compile IR to SPIR-V auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, programs[i], binding); - if (Config::dumpShaders()) { + bool patched = false; + if (Config::patchShaders()) { + auto patch = GetShaderPatch(hash, stage, "spv"); + if (patch) { + spv_code = *patch; + patched = true; + } + } + if (Config::dumpShaders() && !patched) { DumpShader(spv_code, hash, stage, "spv"); } stages[i] = CompileSPV(spv_code, instance.GetDevice()); @@ -315,8 +323,16 @@ std::unique_ptr PipelineCache::CreateComputePipeline() { // Compile IR to SPIR-V u32 binding{}; - const auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, program, binding); - if (Config::dumpShaders()) { + auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, program, binding); + bool patched = false; + if (Config::patchShaders()) { + auto patch = GetShaderPatch(compute_key, Shader::Stage::Compute, "spv"); + if (patch) { + spv_code = *patch; + patched = true; + } + } + if (Config::dumpShaders() && !patched) { DumpShader(spv_code, compute_key, Shader::Stage::Compute, "spv"); } const auto module = CompileSPV(spv_code, instance.GetDevice()); @@ -343,4 +359,22 @@ void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stag file.WriteSpan(code); } +std::optional> PipelineCache::GetShaderPatch(u64 hash, Shader::Stage stage, + std::string_view ext) { + using namespace Common::FS; + const auto patch_dir = GetUserPath(PathType::ShaderDir) / "patch"; + if (!std::filesystem::exists(patch_dir)) { + std::filesystem::create_directories(patch_dir); + } + const auto filename = fmt::format("{}_{:#018x}.{}", stage, hash, ext); + const auto filepath = patch_dir / filename; + if (!std::filesystem::exists(filepath)) { + return {}; + } + const auto file = IOFile{patch_dir / filename, FileAccessMode::Read}; + std::vector code(file.GetSize() / sizeof(u32)); + file.Read(code); + return code; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index d41723ec8..033d529b4 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -33,6 +33,8 @@ public: private: void RefreshGraphicsKey(); void DumpShader(std::span code, u64 hash, Shader::Stage stage, std::string_view ext); + std::optional> GetShaderPatch(u64 hash, Shader::Stage stage, + std::string_view ext); std::unique_ptr CreateGraphicsPipeline(); std::unique_ptr CreateComputePipeline();