Skip to content

Commit

Permalink
Allow shader patching
Browse files Browse the repository at this point in the history
  • Loading branch information
viniciuslrangel committed Nov 30, 2024
1 parent 3604404 commit 86c30a8
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 1 deletion.
19 changes: 19 additions & 0 deletions documents/patching-shader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!--
SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->

### 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
7 changes: 7 additions & 0 deletions src/common/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ static bool isAutoUpdate = false;
static bool isNullGpu = false;
static bool shouldCopyGPUBuffers = false;
static bool shouldDumpShaders = false;
static bool shouldPatchShaders = true;
static u32 vblankDivider = 1;
static bool vkValidation = false;
static bool vkValidationSync = false;
Expand Down Expand Up @@ -178,6 +179,10 @@ bool dumpShaders() {
return shouldDumpShaders;
}

bool patchShaders() {
return shouldPatchShaders;
}

bool isRdocEnabled() {
return rdocEnable;
}
Expand Down Expand Up @@ -546,6 +551,7 @@ void load(const std::filesystem::path& path) {
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false);
shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", false);
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false);
shouldPatchShaders = toml::find_or<bool>(gpu, "patchShaders", true);
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1);
}

Expand Down Expand Up @@ -646,6 +652,7 @@ void save(const std::filesystem::path& path) {
data["GPU"]["nullGpu"] = isNullGpu;
data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers;
data["GPU"]["dumpShaders"] = shouldDumpShaders;
data["GPU"]["patchShaders"] = shouldPatchShaders;
data["GPU"]["vblankDivider"] = vblankDivider;
data["Vulkan"]["gpuId"] = gpuId;
data["Vulkan"]["validation"] = vkValidation;
Expand Down
1 change: 1 addition & 0 deletions src/common/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ bool autoUpdate();
bool nullGpu();
bool copyGPUCmdBuffers();
bool dumpShaders();
bool patchShaders();
bool isRdocEnabled();
u32 vblankDiv();

Expand Down
30 changes: 29 additions & 1 deletion src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,13 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info,
DumpShader(code, info.pgm_hash, info.stage, perm_idx, "bin");

const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile);
const auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding);
auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding);
DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv");
auto patch = GetShaderPatch(info.pgm_hash, info.stage, perm_idx, "spv");
if (patch) {
spv = *patch;
LOG_INFO(Loader, "Loaded patch for {} shader {:#x}", info.stage, info.pgm_hash);
}

const auto module = CompileSPV(spv, instance.GetDevice());
const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx);
Expand Down Expand Up @@ -465,4 +470,27 @@ void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stag
file.WriteSpan(code);
}

std::optional<std::vector<u32>> PipelineCache::GetShaderPatch(u64 hash, Shader::Stage stage,
size_t perm_idx,
std::string_view ext) {
if (!Config::patchShaders()) {
return {};
}

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, perm_idx, ext);
const auto filepath = patch_dir / filename;
if (!std::filesystem::exists(filepath)) {
return {};
}
const auto file = IOFile{patch_dir / filename, FileAccessMode::Read};
std::vector<u32> code(file.GetSize() / sizeof(u32));
file.Read(code);
return code;
}

} // namespace Vulkan
2 changes: 2 additions & 0 deletions src/video_core/renderer_vulkan/vk_pipeline_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class PipelineCache {

void DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage, size_t perm_idx,
std::string_view ext);
std::optional<std::vector<u32>> GetShaderPatch(u64 hash, Shader::Stage stage, size_t perm_idx,
std::string_view ext);
vk::ShaderModule CompileModule(Shader::Info& info, const Shader::RuntimeInfo& runtime_info,
std::span<const u32> code, size_t perm_idx,
Shader::Backend::Bindings& binding);
Expand Down

0 comments on commit 86c30a8

Please sign in to comment.