devtools: shader editing and compiling

This commit is contained in:
Vinicius Rangel 2024-12-05 22:42:23 -03:00
parent fea2593ab4
commit 56d9eb3905
No known key found for this signature in database
GPG Key ID: A5B154D904B761D9
14 changed files with 319 additions and 69 deletions

View File

@ -37,6 +37,10 @@ std::vector<std::string> SplitString(const std::string& str, char delimiter) {
return output; return output;
} }
std::string_view U8stringToString(std::u8string_view u8str) {
return std::string_view{reinterpret_cast<const char*>(u8str.data()), u8str.size()};
}
#ifdef _WIN32 #ifdef _WIN32
static std::wstring CPToUTF16(u32 code_page, std::string_view input) { static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
const auto size = const auto size =

View File

@ -16,6 +16,8 @@ void ToLowerInPlace(std::string& str);
std::vector<std::string> SplitString(const std::string& str, char delimiter); std::vector<std::string> SplitString(const std::string& str, char delimiter);
std::string_view U8stringToString(std::u8string_view u8str);
#ifdef _WIN32 #ifdef _WIN32
[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input); [[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input);
[[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str); [[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str);

View File

@ -178,8 +178,8 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
} }
void DebugStateImpl::CollectShader(const std::string& name, std::span<const u32> spv, void DebugStateImpl::CollectShader(const std::string& name, std::span<const u32> spv,
std::span<const u32> raw_code) { std::span<const u32> raw_code, std::span<const u32> patch_spv) {
shader_dump_list.emplace_back(name, std::vector<u32>{spv.begin(), spv.end()}, shader_dump_list.emplace_back(name, std::vector<u32>{spv.begin(), spv.end()},
std::vector<u32>{raw_code.begin(), raw_code.end()}); std::vector<u32>{raw_code.begin(), raw_code.end()},
std::ranges::sort(shader_dump_list, {}, &ShaderDump::name); std::vector<u32>{patch_spv.begin(), patch_spv.end()});
} }

View File

@ -76,29 +76,41 @@ struct FrameDump {
struct ShaderDump { struct ShaderDump {
std::string name; std::string name;
std::vector<u32> spv; std::vector<u32> spv;
std::vector<u32> raw_code; std::vector<u32> isa;
std::vector<u32> patch_spv;
std::string patch_source{};
std::string cache_spv_disasm{}; 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) ShaderDump(std::string name, std::vector<u32> spv, std::vector<u32> isa,
: name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {} std::vector<u32> patch_spv)
: name(std::move(name)), spv(std::move(spv)), isa(std::move(isa)),
patch_spv(std::move(patch_spv)) {}
ShaderDump(const ShaderDump& other) = delete; ShaderDump(const ShaderDump& other) = delete;
ShaderDump(ShaderDump&& other) noexcept ShaderDump(ShaderDump&& other) noexcept
: name{std::move(other.name)}, spv{std::move(other.spv)}, : name{std::move(other.name)}, spv{std::move(other.spv)}, isa{std::move(other.isa)},
raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)}, patch_spv{std::move(other.patch_spv)}, patch_source{std::move(other.patch_source)},
cache_raw_disasm{std::move(other.cache_raw_disasm)} {} 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=(const ShaderDump& other) = delete;
ShaderDump& operator=(ShaderDump&& other) noexcept { ShaderDump& operator=(ShaderDump&& other) noexcept {
if (this == &other) if (this == &other)
return *this; return *this;
name = std::move(other.name); name = std::move(other.name);
spv = std::move(other.spv); 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_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; return *this;
} }
}; };
@ -187,7 +199,7 @@ public:
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
void CollectShader(const std::string& name, std::span<const u32> spv, void CollectShader(const std::string& name, std::span<const u32> spv,
std::span<const u32> raw_code); std::span<const u32> raw_code, std::span<const u32> patch_spv);
}; };
} // namespace DebugStateType } // namespace DebugStateType

View File

@ -10,8 +10,8 @@ struct ImGuiTextBuffer;
namespace Core::Devtools { namespace Core::Devtools {
struct TOptions { struct TOptions {
std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""}; std::string disassembler_cli_isa{"clrxdisasm --raw {src}"};
std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""}; std::string disassembler_cli_spv{"spirv-cross -V {src}"};
bool frame_dump_render_on_collapse{false}; bool frame_dump_render_on_collapse{false};
}; };

View File

@ -117,7 +117,7 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
inline std::optional<std::string> exec_cli(const char* cli) { inline std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{}; std::array<char, 64> buffer{};
std::string output; std::string output;
const auto f = popen(cli, "r"); const auto f = popen(cli, "rt");
if (!f) { if (!f) {
pclose(f); pclose(f);
return {}; return {};
@ -129,21 +129,27 @@ inline std::optional<std::string> exec_cli(const char* cli) {
return output; return output;
} }
inline std::string RunDisassembler(const std::string& disassembler_cli, template <typename T>
const std::vector<u32>& shader_code) { inline std::string RunDisassembler(const std::string& disassembler_cli, const T& shader_code,
bool* success = nullptr) {
std::string shader_dis; std::string shader_dis;
if (disassembler_cli.empty()) { if (disassembler_cli.empty()) {
shader_dis = "No disassembler set"; shader_dis = "No disassembler set";
if (success) {
*success = false;
}
} else { } else {
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
constexpr std::string_view src_arg = "{src}"; 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); const auto pos = cli.find(src_arg);
if (pos == std::string::npos) { if (pos == std::string::npos) {
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" + shader_dis = "Disassembler CLI does not contain {src} argument";
disassembler_cli); if (success) {
*success = false;
}
} else { } else {
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
@ -151,9 +157,16 @@ inline std::string RunDisassembler(const std::string& disassembler_cli,
file.Close(); file.Close();
auto result = exec_cli(cli.c_str()); auto result = exec_cli(cli.c_str());
shader_dis = result.value_or("Could not disassemble shader"); if (result) {
if (shader_dis.empty()) { shader_dis = result.value();
shader_dis = "Disassembly empty or failed"; if (success) {
*success = true;
}
} else {
if (success) {
*success = false;
}
shader_dis = "Could not disassemble shader";
} }
std::filesystem::remove(bin_path); std::filesystem::remove(bin_path);

View File

@ -1,63 +1,191 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include "shader_list.h" #include "shader_list.h"
#include <imgui.h> #include <imgui.h>
#include "common.h" #include "common.h"
#include "common/config.h" #include "common/config.h"
#include "common/path_util.h"
#include "common/string_util.h"
#include "core/debug_state.h" #include "core/debug_state.h"
#include "core/devtools/options.h" #include "core/devtools/options.h"
#include "imgui/imgui_std.h" #include "imgui/imgui_std.h"
#include "sdl_window.h"
#include "video_core/renderer_vulkan/vk_presenter.h"
extern std::unique_ptr<Vulkan::Presenter> presenter;
using namespace ImGui; using namespace ImGui;
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
void ShaderList::DrawShader(DebugStateType::ShaderDump& value) { ShaderList::Selection::Selection(int index) : index(index) {
isa_editor.SetPalette(TextEditor::GetDarkPalette());
isa_editor.SetReadOnly(true);
glsl_editor.SetPalette(TextEditor::GetDarkPalette());
glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
presenter->GetWindow().RequestKeyboard();
}
ShaderList::Selection::~Selection() {
presenter->GetWindow().ReleaseKeyboard();
}
bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
if (!loaded_data) { if (!loaded_data) {
loaded_data = true; loaded_data = true;
if (value.cache_raw_disasm.empty()) { if (value.cache_isa_disasm.empty()) {
value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code); value.cache_isa_disasm = RunDisassembler(Options.disassembler_cli_isa, value.isa);
} }
isa_editor.SetText(value.cache_raw_disasm);
if (value.cache_spv_disasm.empty()) { if (value.cache_spv_disasm.empty()) {
value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv); value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv);
} }
spv_editor.SetText(value.cache_spv_disasm); if (!value.patch_spv.empty() && value.cache_patch_disasm.empty()) {
value.cache_patch_disasm = RunDisassembler("spirv-dis {src}", value.patch_spv);
}
patch_path =
Common::FS::GetUserPath(Common::FS::PathType::ShaderDir) / "patch" / value.name;
patch_bin_path = patch_path;
patch_bin_path += ".spv";
patch_path += ".glsl";
if (std::filesystem::exists(patch_path)) {
std::ifstream file{patch_path};
value.patch_source =
std::string{std::istreambuf_iterator{file}, std::istreambuf_iterator<char>{}};
}
if (value.patch_spv.empty()) { // No patch
showing_patch = false;
isa_editor.SetText(value.cache_isa_disasm);
glsl_editor.SetText(value.cache_spv_disasm);
} else {
showing_patch = true;
isa_editor.SetText(value.cache_patch_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
glsl_editor.SetText(value.patch_source);
glsl_editor.SetReadOnly(false);
}
} }
if (SmallButton("<-")) { char name[64];
selected_shader = -1; snprintf(name, sizeof(name), "Shader %s", value.name.c_str());
if (!Begin(name, &open, ImGuiWindowFlags_NoNav)) {
End();
return open;
} }
SameLine();
Text("%s", value.name.c_str()); Text("%s", value.name.c_str());
SameLine(0.0f, 7.0f); SameLine(0.0f, 7.0f);
if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) { if (Checkbox("Enable patch", &showing_patch)) {
if (Selectable("SPIRV")) { if (showing_patch) {
showing_isa = false; if (value.patch_source.empty()) {
value.patch_source = value.cache_spv_disasm;
}
isa_editor.SetText(value.cache_patch_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
glsl_editor.SetText(value.patch_source);
glsl_editor.SetReadOnly(false);
} else {
isa_editor.SetText(value.cache_isa_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition());
glsl_editor.SetText(value.cache_spv_disasm);
glsl_editor.SetReadOnly(true);
} }
if (Selectable("ISA")) {
showing_isa = true;
}
EndCombo();
} }
if (showing_isa) { if (showing_patch) {
isa_editor.Render("ISA", GetContentRegionAvail()); if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL",
ImGuiComboFlags_WidthFitPreview)) {
if (Selectable("GLSL")) {
showing_bin = false;
}
if (Selectable("SPIRV")) {
showing_bin = true;
}
EndCombo();
}
} else { } else {
spv_editor.Render("SPIRV", GetContentRegionAvail()); if (BeginCombo("Shader type", showing_bin ? "ISA" : "GLSL",
ImGuiComboFlags_WidthFitPreview)) {
if (Selectable("GLSL")) {
showing_bin = false;
}
if (Selectable("ISA")) {
showing_bin = true;
}
EndCombo();
}
} }
}
ShaderList::ShaderList() { if (showing_patch) {
isa_editor.SetPalette(TextEditor::GetDarkPalette()); bool save = false;
isa_editor.SetReadOnly(true); bool compile = false;
spv_editor.SetPalette(TextEditor::GetDarkPalette()); SameLine(0.0f, 3.0f);
spv_editor.SetReadOnly(true); if (Button("Save")) {
spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); save = true;
}
SameLine();
if (Button("Save & Compile")) {
save = true;
compile = true;
}
if (save) {
value.patch_source = glsl_editor.GetText();
std::ofstream file{patch_path, std::ios::binary | std::ios::trunc};
file << value.patch_source;
std::string msg = "Patch saved to ";
msg += Common::U8stringToString(patch_path.u8string());
DebugState.ShowDebugMessage(msg);
}
if (compile) {
static std::map<std::string, std::string> stage_arg = {
{"vs", "vert"},
{"gs", "geom"},
{"fs", "frag"},
{"cs", "comp"},
};
auto stage = stage_arg.find(value.name.substr(0, 2));
if (stage == stage_arg.end()) {
DebugState.ShowDebugMessage(std::string{"Invalid shader stage: "} +
value.name.substr(0, 2));
} else {
std::string cmd =
fmt::format("glslc --target-env=vulkan1.3 --target-spv=spv1.6 "
"-fshader-stage={} {{src}} -o \"{}\"",
stage->second, Common::U8stringToString(patch_bin_path.u8string()));
bool success = false;
auto res = RunDisassembler(cmd, value.patch_source, &success);
if (!res.empty() || !success) {
DebugState.ShowDebugMessage("Compilation failed:\n" + res);
} else {
Common::FS::IOFile file{patch_bin_path, Common::FS::FileAccessMode::Read};
value.patch_spv.resize(file.GetSize() / sizeof(u32));
file.Read(value.patch_spv);
value.cache_patch_disasm =
RunDisassembler("spirv-dis {src}", value.patch_spv, &success);
if (!success) {
DebugState.ShowDebugMessage("Decompilation failed (Compile was ok):\n" +
res);
} else {
isa_editor.SetText(value.cache_patch_disasm);
}
}
}
}
}
if (showing_bin) {
isa_editor.Render(showing_patch ? "SPIRV" : "ISA", GetContentRegionAvail());
} else {
glsl_editor.Render("GLSL", GetContentRegionAvail());
}
End();
return open;
} }
void ShaderList::Draw() { void ShaderList::Draw() {
@ -73,18 +201,21 @@ void ShaderList::Draw() {
return; return;
} }
if (selected_shader >= 0) { for (auto it = open_shaders.begin(); it != open_shaders.end();) {
DrawShader(DebugState.shader_dump_list[selected_shader]); auto& selection = *it;
End(); auto& shader = DebugState.shader_dump_list[selection.index];
return; if (!selection.DrawShader(shader)) {
it = open_shaders.erase(it);
} else {
++it;
}
} }
auto width = GetContentRegionAvail().x; auto width = GetContentRegionAvail().x;
int i = 0; int i = 0;
for (const auto& shader : DebugState.shader_dump_list) { for (const auto& shader : DebugState.shader_dump_list) {
if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) { if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) {
selected_shader = i; open_shaders.emplace_back(i);
loaded_data = false;
} }
i++; i++;
} }

View File

@ -6,20 +6,32 @@
#include "core/debug_state.h" #include "core/debug_state.h"
#include "text_editor.h" #include "text_editor.h"
#include <filesystem>
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
class ShaderList { class ShaderList {
int selected_shader = -1; struct Selection {
TextEditor isa_editor{}; int index;
TextEditor spv_editor{}; TextEditor isa_editor{};
bool loaded_data = false; TextEditor glsl_editor{};
bool showing_isa = false; bool open = true;
bool loaded_data = false;
bool showing_bin = false;
bool showing_patch = false;
void DrawShader(DebugStateType::ShaderDump& value); std::filesystem::path patch_path;
std::filesystem::path patch_bin_path;
explicit Selection(int index);
~Selection();
bool DrawShader(DebugStateType::ShaderDump& value);
};
std::vector<Selection> open_shaders{};
public: public:
ShaderList();
bool open = false; bool open = false;
void Draw(); void Draw();

View File

@ -1059,7 +1059,8 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) {
if (!mIgnoreImGuiChild) if (!mIgnoreImGuiChild)
ImGui::BeginChild(aTitle, aSize, aBorder, ImGui::BeginChild(aTitle, aSize, aBorder,
ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_HorizontalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove); ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoNav);
if (mHandleKeyboardInputs) { if (mHandleKeyboardInputs) {
HandleKeyboardInputs(); HandleKeyboardInputs();
@ -2331,4 +2332,50 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() {
return langDef; return langDef;
} }
// Source: https://github.com/dfranx/ImGuiColorTextEdit/blob/master/TextEditor.cpp
const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SPIRV() {
static bool inited = false;
static LanguageDefinition langDef;
if (!inited) {
/*
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[
\\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]",
PaletteIndex::Punctuation));
*/
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(
std::make_pair<std::string, PaletteIndex>("[ =\\t]Op[a-zA-Z]*", PaletteIndex::Keyword));
langDef.mTokenRegexStrings.push_back(
std::make_pair<std::string, PaletteIndex>("%[_a-zA-Z0-9]*", PaletteIndex::Identifier));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
langDef.mCommentStart = "/*";
langDef.mCommentEnd = "*/";
langDef.mSingleLineComment = ";";
langDef.mCaseSensitive = true;
langDef.mAutoIndentation = false;
langDef.mName = "SPIR-V";
inited = true;
}
return langDef;
}
} // namespace Core::Devtools::Widget } // namespace Core::Devtools::Widget

View File

@ -161,6 +161,7 @@ public:
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {} : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {}
static const LanguageDefinition& GLSL(); static const LanguageDefinition& GLSL();
static const LanguageDefinition& SPIRV();
}; };
TextEditor(); TextEditor();

View File

@ -168,6 +168,21 @@ void WindowSDL::InitTimers() {
SDL_AddTimer(100, &PollController, controller); SDL_AddTimer(100, &PollController, controller);
} }
void WindowSDL::RequestKeyboard() {
if (keyboard_grab == 0) {
SDL_StartTextInput(window);
}
keyboard_grab++;
}
void WindowSDL::ReleaseKeyboard() {
ASSERT(keyboard_grab > 0);
keyboard_grab--;
if (keyboard_grab == 0) {
SDL_StopTextInput(window);
}
}
void WindowSDL::OnResize() { void WindowSDL::OnResize() {
SDL_GetWindowSizeInPixels(window, &width, &height); SDL_GetWindowSizeInPixels(window, &width, &height);
ImGui::Core::OnResize(); ImGui::Core::OnResize();

View File

@ -41,6 +41,8 @@ struct WindowSystemInfo {
}; };
class WindowSDL { class WindowSDL {
int keyboard_grab = 0;
public: public:
explicit WindowSDL(s32 width, s32 height, Input::GameController* controller, explicit WindowSDL(s32 width, s32 height, Input::GameController* controller,
std::string_view window_title); std::string_view window_title);
@ -69,6 +71,9 @@ public:
void WaitEvent(); void WaitEvent();
void InitTimers(); void InitTimers();
void RequestKeyboard();
void ReleaseKeyboard();
private: private:
void OnResize(); void OnResize();
void OnKeyPress(const SDL_Event* event); void OnKeyPress(const SDL_Event* event);

View File

@ -417,17 +417,21 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info,
const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile); const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile);
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"); DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv");
vk::ShaderModule module;
auto patch = GetShaderPatch(info.pgm_hash, info.stage, perm_idx, "spv"); auto patch = GetShaderPatch(info.pgm_hash, info.stage, perm_idx, "spv");
if (patch) { if (patch) {
spv = *patch;
LOG_INFO(Loader, "Loaded patch for {} shader {:#x}", info.stage, info.pgm_hash); LOG_INFO(Loader, "Loaded patch for {} shader {:#x}", info.stage, info.pgm_hash);
module = CompileSPV(*patch, instance.GetDevice());
} else {
module = CompileSPV(spv, instance.GetDevice());
} }
const auto module = CompileSPV(spv, instance.GetDevice()); const auto name = fmt::format("{}_{:#018x}_{}", info.stage, info.pgm_hash, perm_idx);
const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx);
Vulkan::SetObjectName(instance.GetDevice(), module, name); Vulkan::SetObjectName(instance.GetDevice(), module, name);
if (Config::collectShadersForDebug()) { if (Config::collectShadersForDebug()) {
DebugState.CollectShader(name, spv, code); DebugState.CollectShader(name, spv, code, patch ? *patch : std::span<const u32>{});
} }
return module; return module;
} }

View File

@ -53,6 +53,10 @@ public:
return pp_settings.gamma; return pp_settings.gamma;
} }
Frontend::WindowSDL& GetWindow() const {
return window;
}
Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute, Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute,
VAddr cpu_address, bool is_eop) { VAddr cpu_address, bool is_eop) {
auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address}; auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address};