mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-31 14:35:19 +00:00
Allow shader patching
This commit is contained in:
parent
ea4ae56f4d
commit
5156482ce8
14
documents/patching-shader.md
Normal file
14
documents/patching-shader.md
Normal file
@ -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
|
@ -21,6 +21,7 @@ static bool isLibc = true;
|
|||||||
static bool isShowSplash = false;
|
static bool isShowSplash = false;
|
||||||
static bool isNullGpu = false;
|
static bool isNullGpu = false;
|
||||||
static bool shouldDumpShaders = false;
|
static bool shouldDumpShaders = false;
|
||||||
|
static bool shouldPatchShaders = true;
|
||||||
static bool shouldDumpPM4 = false;
|
static bool shouldDumpPM4 = false;
|
||||||
static u32 vblankDivider = 1;
|
static u32 vblankDivider = 1;
|
||||||
static bool vkValidation = false;
|
static bool vkValidation = false;
|
||||||
@ -94,6 +95,10 @@ bool dumpShaders() {
|
|||||||
return shouldDumpShaders;
|
return shouldDumpShaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool patchShaders() {
|
||||||
|
return shouldPatchShaders;
|
||||||
|
}
|
||||||
|
|
||||||
bool dumpPM4() {
|
bool dumpPM4() {
|
||||||
return shouldDumpPM4;
|
return shouldDumpPM4;
|
||||||
}
|
}
|
||||||
@ -245,6 +250,7 @@ void load(const std::filesystem::path& path) {
|
|||||||
screenHeight = toml::find_or<int>(gpu, "screenHeight", screenHeight);
|
screenHeight = toml::find_or<int>(gpu, "screenHeight", screenHeight);
|
||||||
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false);
|
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false);
|
||||||
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false);
|
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false);
|
||||||
|
shouldPatchShaders = toml::find_or<bool>(gpu, "patchShaders", true);
|
||||||
shouldDumpPM4 = toml::find_or<bool>(gpu, "dumpPM4", false);
|
shouldDumpPM4 = toml::find_or<bool>(gpu, "dumpPM4", false);
|
||||||
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1);
|
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1);
|
||||||
}
|
}
|
||||||
@ -325,6 +331,7 @@ void save(const std::filesystem::path& path) {
|
|||||||
data["GPU"]["screenHeight"] = screenHeight;
|
data["GPU"]["screenHeight"] = screenHeight;
|
||||||
data["GPU"]["nullGpu"] = isNullGpu;
|
data["GPU"]["nullGpu"] = isNullGpu;
|
||||||
data["GPU"]["dumpShaders"] = shouldDumpShaders;
|
data["GPU"]["dumpShaders"] = shouldDumpShaders;
|
||||||
|
data["GPU"]["patchShaders"] = shouldPatchShaders;
|
||||||
data["GPU"]["dumpPM4"] = shouldDumpPM4;
|
data["GPU"]["dumpPM4"] = shouldDumpPM4;
|
||||||
data["GPU"]["vblankDivider"] = vblankDivider;
|
data["GPU"]["vblankDivider"] = vblankDivider;
|
||||||
data["Vulkan"]["gpuId"] = gpuId;
|
data["Vulkan"]["gpuId"] = gpuId;
|
||||||
|
@ -25,6 +25,7 @@ bool isLleLibc();
|
|||||||
bool showSplash();
|
bool showSplash();
|
||||||
bool nullGpu();
|
bool nullGpu();
|
||||||
bool dumpShaders();
|
bool dumpShaders();
|
||||||
|
bool patchShaders();
|
||||||
bool dumpPM4();
|
bool dumpPM4();
|
||||||
bool isRdocEnabled();
|
bool isRdocEnabled();
|
||||||
u32 vblankDiv();
|
u32 vblankDiv();
|
||||||
|
@ -274,7 +274,15 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
|
|||||||
|
|
||||||
// Compile IR to SPIR-V
|
// Compile IR to SPIR-V
|
||||||
auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, programs[i], binding);
|
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");
|
DumpShader(spv_code, hash, stage, "spv");
|
||||||
}
|
}
|
||||||
stages[i] = CompileSPV(spv_code, instance.GetDevice());
|
stages[i] = CompileSPV(spv_code, instance.GetDevice());
|
||||||
@ -315,8 +323,16 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline() {
|
|||||||
|
|
||||||
// Compile IR to SPIR-V
|
// Compile IR to SPIR-V
|
||||||
u32 binding{};
|
u32 binding{};
|
||||||
const auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, program, binding);
|
auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, program, binding);
|
||||||
if (Config::dumpShaders()) {
|
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");
|
DumpShader(spv_code, compute_key, Shader::Stage::Compute, "spv");
|
||||||
}
|
}
|
||||||
const auto module = CompileSPV(spv_code, instance.GetDevice());
|
const auto module = CompileSPV(spv_code, instance.GetDevice());
|
||||||
@ -343,4 +359,22 @@ void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stag
|
|||||||
file.WriteSpan(code);
|
file.WriteSpan(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<u32>> 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<u32> code(file.GetSize() / sizeof(u32));
|
||||||
|
file.Read(code);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
@ -33,6 +33,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
void RefreshGraphicsKey();
|
void RefreshGraphicsKey();
|
||||||
void DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage, std::string_view ext);
|
void DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage, std::string_view ext);
|
||||||
|
std::optional<std::vector<u32>> GetShaderPatch(u64 hash, Shader::Stage stage,
|
||||||
|
std::string_view ext);
|
||||||
|
|
||||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline();
|
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline();
|
||||||
std::unique_ptr<ComputePipeline> CreateComputePipeline();
|
std::unique_ptr<ComputePipeline> CreateComputePipeline();
|
||||||
|
Loading…
Reference in New Issue
Block a user