Skip to content

Commit

Permalink
Devtools - Shader editing (#1705)
Browse files Browse the repository at this point in the history
* devtools: shader editing and compiling

* devtools: patch shader at runtime

* devtools: shader editing load patch even with config disabled
  • Loading branch information
viniciuslrangel authored Dec 9, 2024
1 parent f623613 commit f1b23c6
Show file tree
Hide file tree
Showing 19 changed files with 465 additions and 102 deletions.
4 changes: 4 additions & 0 deletions src/common/string_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ std::vector<std::string> SplitString(const std::string& str, char delimiter) {
return output;
}

std::string_view U8stringToString(std::u8string_view u8str) {
return std::string_view{reinterpret_cast<const char*>(u8str.data()), u8str.size()};
}

#ifdef _WIN32
static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
const auto size =
Expand Down
2 changes: 2 additions & 0 deletions src/common/string_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ void ToLowerInPlace(std::string& str);

std::vector<std::string> SplitString(const std::string& str, char delimiter);

std::string_view U8stringToString(std::u8string_view u8str);

#ifdef _WIN32
[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input);
[[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str);
Expand Down
11 changes: 6 additions & 5 deletions src/core/debug_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,10 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
}
}

void DebugStateImpl::CollectShader(const std::string& name, std::span<const u32> spv,
std::span<const u32> raw_code) {
shader_dump_list.emplace_back(name, std::vector<u32>{spv.begin(), spv.end()},
std::vector<u32>{raw_code.begin(), raw_code.end()});
std::ranges::sort(shader_dump_list, {}, &ShaderDump::name);
void DebugStateImpl::CollectShader(const std::string& name, vk::ShaderModule module,
std::span<const u32> spv, std::span<const u32> raw_code,
std::span<const u32> patch_spv, bool is_patched) {
shader_dump_list.emplace_back(name, module, std::vector<u32>{spv.begin(), spv.end()},
std::vector<u32>{raw_code.begin(), raw_code.end()},
std::vector<u32>{patch_spv.begin(), patch_spv.end()}, is_patched);
}
42 changes: 30 additions & 12 deletions src/core/debug_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

#include "common/types.h"
#include "video_core/amdgpu/liverpool.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"

#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
Expand Down Expand Up @@ -76,29 +76,46 @@ struct FrameDump {

struct ShaderDump {
std::string name;
vk::ShaderModule module;

std::vector<u32> spv;
std::vector<u32> raw_code;
std::vector<u32> isa;

std::vector<u32> patch_spv;
std::string patch_source{};

bool loaded_data = false;
bool is_patched = false;
std::string cache_spv_disasm{};
std::string cache_raw_disasm{};
std::string cache_isa_disasm{};
std::string cache_patch_disasm{};

ShaderDump(std::string name, std::vector<u32> spv, std::vector<u32> raw_code)
: name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {}
ShaderDump(std::string name, vk::ShaderModule module, std::vector<u32> spv,
std::vector<u32> isa, std::vector<u32> patch_spv, bool is_patched)
: name(std::move(name)), module(module), spv(std::move(spv)), isa(std::move(isa)),
patch_spv(std::move(patch_spv)), is_patched(is_patched) {}

ShaderDump(const ShaderDump& other) = delete;
ShaderDump(ShaderDump&& other) noexcept
: name{std::move(other.name)}, spv{std::move(other.spv)},
raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)},
cache_raw_disasm{std::move(other.cache_raw_disasm)} {}
: name{std::move(other.name)}, module{std::move(other.module)}, spv{std::move(other.spv)},
isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)},
patch_source{std::move(other.patch_source)},
cache_spv_disasm{std::move(other.cache_spv_disasm)},
cache_isa_disasm{std::move(other.cache_isa_disasm)},
cache_patch_disasm{std::move(other.cache_patch_disasm)} {}
ShaderDump& operator=(const ShaderDump& other) = delete;
ShaderDump& operator=(ShaderDump&& other) noexcept {
if (this == &other)
return *this;
name = std::move(other.name);
module = std::move(other.module);
spv = std::move(other.spv);
raw_code = std::move(other.raw_code);
isa = std::move(other.isa);
patch_spv = std::move(other.patch_spv);
patch_source = std::move(other.patch_source);
cache_spv_disasm = std::move(other.cache_spv_disasm);
cache_raw_disasm = std::move(other.cache_raw_disasm);
cache_isa_disasm = std::move(other.cache_isa_disasm);
cache_patch_disasm = std::move(other.cache_patch_disasm);
return *this;
}
};
Expand Down Expand Up @@ -186,8 +203,9 @@ class DebugStateImpl {
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);

void CollectShader(const std::string& name, std::span<const u32> spv,
std::span<const u32> raw_code);
void CollectShader(const std::string& name, vk::ShaderModule module, std::span<const u32> spv,
std::span<const u32> raw_code, std::span<const u32> patch_spv,
bool is_patched);
};
} // namespace DebugStateType

Expand Down
4 changes: 2 additions & 2 deletions src/core/devtools/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ struct ImGuiTextBuffer;
namespace Core::Devtools {

struct TOptions {
std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""};
std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""};
std::string disassembler_cli_isa{"clrxdisasm --raw {src}"};
std::string disassembler_cli_spv{"spirv-cross -V {src}"};
bool frame_dump_render_on_collapse{false};
};

Expand Down
31 changes: 22 additions & 9 deletions src/core/devtools/widget/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
inline std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{};
std::string output;
const auto f = popen(cli, "r");
const auto f = popen(cli, "rt");
if (!f) {
pclose(f);
return {};
Expand All @@ -129,31 +129,44 @@ inline std::optional<std::string> exec_cli(const char* cli) {
return output;
}

inline std::string RunDisassembler(const std::string& disassembler_cli,
const std::vector<u32>& shader_code) {
template <typename T>
inline std::string RunDisassembler(const std::string& disassembler_cli, const T& shader_code,
bool* success = nullptr) {
std::string shader_dis;

if (disassembler_cli.empty()) {
shader_dis = "No disassembler set";
if (success) {
*success = false;
}
} else {
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";

constexpr std::string_view src_arg = "{src}";
std::string cli = disassembler_cli;
std::string cli = disassembler_cli + " 2>&1";
const auto pos = cli.find(src_arg);
if (pos == std::string::npos) {
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" +
disassembler_cli);
shader_dis = "Disassembler CLI does not contain {src} argument";
if (success) {
*success = false;
}
} else {
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
file.Write(shader_code);
file.Close();

auto result = exec_cli(cli.c_str());
shader_dis = result.value_or("Could not disassemble shader");
if (shader_dis.empty()) {
shader_dis = "Disassembly empty or failed";
if (result) {
shader_dis = result.value();
if (success) {
*success = true;
}
} else {
if (success) {
*success = false;
}
shader_dis = "Could not disassemble shader";
}

std::filesystem::remove(bin_path);
Expand Down
Loading

0 comments on commit f1b23c6

Please sign in to comment.