mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-01-14 19:05:15 +00:00
Devtools - Shader editing (#1705)
Some checks are pending
Build and Release / reuse (push) Waiting to run
Build and Release / clang-format (push) Waiting to run
Build and Release / get-info (push) Waiting to run
Build and Release / windows-sdl (push) Blocked by required conditions
Build and Release / windows-qt (push) Blocked by required conditions
Build and Release / macos-sdl (push) Blocked by required conditions
Build and Release / macos-qt (push) Blocked by required conditions
Build and Release / linux-sdl (push) Blocked by required conditions
Build and Release / linux-qt (push) Blocked by required conditions
Build and Release / pre-release (push) Blocked by required conditions
Some checks are pending
Build and Release / reuse (push) Waiting to run
Build and Release / clang-format (push) Waiting to run
Build and Release / get-info (push) Waiting to run
Build and Release / windows-sdl (push) Blocked by required conditions
Build and Release / windows-qt (push) Blocked by required conditions
Build and Release / macos-sdl (push) Blocked by required conditions
Build and Release / macos-qt (push) Blocked by required conditions
Build and Release / linux-sdl (push) Blocked by required conditions
Build and Release / linux-qt (push) Blocked by required conditions
Build and Release / pre-release (push) Blocked by required conditions
* devtools: shader editing and compiling * devtools: patch shader at runtime * devtools: shader editing load patch even with config disabled
This commit is contained in:
parent
f623613d12
commit
f1b23c616e
|
@ -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 =
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
void DebugStateImpl::CollectShader(const std::string& name, vk::ShaderModule module,
|
||||||
std::span<const u32> raw_code) {
|
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::span<const u32> patch_spv, bool is_patched) {
|
||||||
std::vector<u32>{raw_code.begin(), raw_code.end()});
|
shader_dump_list.emplace_back(name, module, std::vector<u32>{spv.begin(), spv.end()},
|
||||||
std::ranges::sort(shader_dump_list, {}, &ShaderDump::name);
|
std::vector<u32>{raw_code.begin(), raw_code.end()},
|
||||||
|
std::vector<u32>{patch_spv.begin(), patch_spv.end()}, is_patched);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "video_core/amdgpu/liverpool.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
|
#ifdef _WIN32
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
@ -76,29 +76,46 @@ struct FrameDump {
|
||||||
|
|
||||||
struct ShaderDump {
|
struct ShaderDump {
|
||||||
std::string name;
|
std::string name;
|
||||||
|
vk::ShaderModule module;
|
||||||
|
|
||||||
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{};
|
||||||
|
|
||||||
|
bool loaded_data = false;
|
||||||
|
bool is_patched = false;
|
||||||
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, vk::ShaderModule module, std::vector<u32> spv,
|
||||||
: name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {}
|
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(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)}, module{std::move(other.module)}, spv{std::move(other.spv)},
|
||||||
raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)},
|
isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)},
|
||||||
cache_raw_disasm{std::move(other.cache_raw_disasm)} {}
|
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=(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);
|
||||||
|
module = std::move(other.module);
|
||||||
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -186,8 +203,9 @@ public:
|
||||||
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||||
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, vk::ShaderModule module, std::span<const u32> spv,
|
||||||
std::span<const u32> raw_code);
|
std::span<const u32> raw_code, std::span<const u32> patch_spv,
|
||||||
|
bool is_patched);
|
||||||
};
|
};
|
||||||
} // namespace DebugStateType
|
} // namespace DebugStateType
|
||||||
|
|
||||||
|
|
|
@ -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};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -1,66 +1,221 @@
|
||||||
// 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"
|
||||||
|
#include "video_core/renderer_vulkan/vk_rasterizer.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) {
|
||||||
if (!loaded_data) {
|
isa_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||||
loaded_data = true;
|
isa_editor.SetReadOnly(true);
|
||||||
if (value.cache_raw_disasm.empty()) {
|
glsl_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||||
value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code);
|
glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
|
||||||
}
|
presenter->GetWindow().RequestKeyboard();
|
||||||
isa_editor.SetText(value.cache_raw_disasm);
|
}
|
||||||
|
|
||||||
|
ShaderList::Selection::~Selection() {
|
||||||
|
presenter->GetWindow().ReleaseKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderList::Selection::ReloadShader(DebugStateType::ShaderDump& value) {
|
||||||
|
auto& spv = value.is_patched ? value.patch_spv : value.spv;
|
||||||
|
if (spv.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& cache = presenter->GetRasterizer().GetPipelineCache();
|
||||||
|
if (const auto m = cache.ReplaceShader(value.module, spv); m) {
|
||||||
|
value.module = *m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
|
||||||
|
if (!value.loaded_data) {
|
||||||
|
value.loaded_data = true;
|
||||||
|
if (value.cache_isa_disasm.empty()) {
|
||||||
|
value.cache_isa_disasm = RunDisassembler(Options.disassembler_cli_isa, value.isa);
|
||||||
|
}
|
||||||
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 (SmallButton("<-")) {
|
value.is_patched = !value.patch_spv.empty();
|
||||||
selected_shader = -1;
|
if (!value.is_patched) { // No patch
|
||||||
|
isa_editor.SetText(value.cache_isa_disasm);
|
||||||
|
glsl_editor.SetText(value.cache_spv_disasm);
|
||||||
|
} else {
|
||||||
|
isa_editor.SetText(value.cache_patch_disasm);
|
||||||
|
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
|
||||||
|
glsl_editor.SetText(value.patch_source);
|
||||||
|
glsl_editor.SetReadOnly(false);
|
||||||
}
|
}
|
||||||
SameLine();
|
}
|
||||||
|
|
||||||
|
char name[64];
|
||||||
|
snprintf(name, sizeof(name), "Shader %s", value.name.c_str());
|
||||||
|
SetNextWindowSize({450.0f, 600.0f}, ImGuiCond_FirstUseEver);
|
||||||
|
if (!Begin(name, &open, ImGuiWindowFlags_NoNav)) {
|
||||||
|
End();
|
||||||
|
return open;
|
||||||
|
}
|
||||||
|
|
||||||
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", &value.is_patched)) {
|
||||||
if (Selectable("SPIRV")) {
|
if (value.is_patched) {
|
||||||
showing_isa = false;
|
if (value.patch_source.empty()) {
|
||||||
|
value.patch_source = value.cache_spv_disasm;
|
||||||
}
|
}
|
||||||
if (Selectable("ISA")) {
|
isa_editor.SetText(value.cache_patch_disasm);
|
||||||
showing_isa = true;
|
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
|
||||||
|
glsl_editor.SetText(value.patch_source);
|
||||||
|
glsl_editor.SetReadOnly(false);
|
||||||
|
if (!value.patch_spv.empty()) {
|
||||||
|
ReloadShader(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isa_editor.SetText(value.cache_isa_disasm);
|
||||||
|
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition());
|
||||||
|
glsl_editor.SetText(value.cache_spv_disasm);
|
||||||
|
glsl_editor.SetReadOnly(true);
|
||||||
|
ReloadShader(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is_patched) {
|
||||||
|
if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL",
|
||||||
|
ImGuiComboFlags_WidthFitPreview)) {
|
||||||
|
if (Selectable("GLSL")) {
|
||||||
|
showing_bin = false;
|
||||||
|
}
|
||||||
|
if (Selectable("SPIRV")) {
|
||||||
|
showing_bin = true;
|
||||||
}
|
}
|
||||||
EndCombo();
|
EndCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showing_isa) {
|
|
||||||
isa_editor.Render("ISA", GetContentRegionAvail());
|
|
||||||
} 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 (value.is_patched) {
|
||||||
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);
|
||||||
|
ReloadShader(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showing_bin) {
|
||||||
|
isa_editor.Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail());
|
||||||
|
} else {
|
||||||
|
glsl_editor.Render("GLSL", GetContentRegionAvail());
|
||||||
|
}
|
||||||
|
|
||||||
|
End();
|
||||||
|
return open;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderList::Draw() {
|
void ShaderList::Draw() {
|
||||||
|
for (auto it = open_shaders.begin(); it != open_shaders.end();) {
|
||||||
|
auto& selection = *it;
|
||||||
|
auto& shader = DebugState.shader_dump_list[selection.index];
|
||||||
|
if (!selection.DrawShader(shader)) {
|
||||||
|
it = open_shaders.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver);
|
SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver);
|
||||||
if (!Begin("Shader list", &open)) {
|
if (!Begin("Shader list", &open)) {
|
||||||
End();
|
End();
|
||||||
|
@ -73,18 +228,19 @@ void ShaderList::Draw() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected_shader >= 0) {
|
|
||||||
DrawShader(DebugState.shader_dump_list[selected_shader]);
|
|
||||||
End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
char name[128];
|
||||||
selected_shader = i;
|
if (shader.is_patched) {
|
||||||
loaded_data = false;
|
snprintf(name, sizeof(name), "%s (PATCH ON)", shader.name.c_str());
|
||||||
|
} else if (!shader.patch_spv.empty()) {
|
||||||
|
snprintf(name, sizeof(name), "%s (PATCH OFF)", shader.name.c_str());
|
||||||
|
} else {
|
||||||
|
snprintf(name, sizeof(name), "%s", shader.name.c_str());
|
||||||
|
}
|
||||||
|
if (ButtonEx(name, {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) {
|
||||||
|
open_shaders.emplace_back(i);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{};
|
explicit Selection(int index);
|
||||||
TextEditor spv_editor{};
|
~Selection();
|
||||||
bool loaded_data = false;
|
|
||||||
bool showing_isa = false;
|
|
||||||
|
|
||||||
void DrawShader(DebugStateType::ShaderDump& value);
|
void ReloadShader(DebugStateType::ShaderDump& value);
|
||||||
|
|
||||||
|
bool DrawShader(DebugStateType::ShaderDump& value);
|
||||||
|
|
||||||
|
int index;
|
||||||
|
TextEditor isa_editor{};
|
||||||
|
TextEditor glsl_editor{};
|
||||||
|
bool open = true;
|
||||||
|
bool showing_bin = false;
|
||||||
|
|
||||||
|
std::filesystem::path patch_path;
|
||||||
|
std::filesystem::path patch_bin_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Selection> open_shaders{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ShaderList();
|
|
||||||
|
|
||||||
bool open = false;
|
bool open = false;
|
||||||
|
|
||||||
void Draw();
|
void Draw();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Vulkan {
|
||||||
|
|
||||||
ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler_,
|
ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler_,
|
||||||
DescriptorHeap& desc_heap_, vk::PipelineCache pipeline_cache,
|
DescriptorHeap& desc_heap_, vk::PipelineCache pipeline_cache,
|
||||||
u64 compute_key_, const Shader::Info& info_,
|
ComputePipelineKey compute_key_, const Shader::Info& info_,
|
||||||
vk::ShaderModule module)
|
vk::ShaderModule module)
|
||||||
: Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} {
|
: Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} {
|
||||||
auto& info = stages[int(Shader::Stage::Compute)];
|
auto& info = stages[int(Shader::Stage::Compute)];
|
||||||
|
|
|
@ -17,15 +17,33 @@ class Instance;
|
||||||
class Scheduler;
|
class Scheduler;
|
||||||
class DescriptorHeap;
|
class DescriptorHeap;
|
||||||
|
|
||||||
|
struct ComputePipelineKey {
|
||||||
|
size_t value;
|
||||||
|
|
||||||
|
friend bool operator==(const ComputePipelineKey& lhs, const ComputePipelineKey& rhs) {
|
||||||
|
return lhs.value == rhs.value;
|
||||||
|
}
|
||||||
|
friend bool operator!=(const ComputePipelineKey& lhs, const ComputePipelineKey& rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class ComputePipeline : public Pipeline {
|
class ComputePipeline : public Pipeline {
|
||||||
public:
|
public:
|
||||||
ComputePipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
|
ComputePipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
|
||||||
vk::PipelineCache pipeline_cache, u64 compute_key, const Shader::Info& info,
|
vk::PipelineCache pipeline_cache, ComputePipelineKey compute_key,
|
||||||
vk::ShaderModule module);
|
const Shader::Info& info, vk::ShaderModule module);
|
||||||
~ComputePipeline();
|
~ComputePipeline();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u64 compute_key;
|
ComputePipelineKey compute_key;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::hash<Vulkan::ComputePipelineKey> {
|
||||||
|
std::size_t operator()(const Vulkan::ComputePipelineKey& key) const noexcept {
|
||||||
|
return std::hash<size_t>{}(key.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// 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
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <xxhash.h>
|
#include <xxhash.h>
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
|
@ -189,10 +189,19 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() {
|
||||||
}
|
}
|
||||||
const auto [it, is_new] = graphics_pipelines.try_emplace(graphics_key);
|
const auto [it, is_new] = graphics_pipelines.try_emplace(graphics_key);
|
||||||
if (is_new) {
|
if (is_new) {
|
||||||
it.value() = graphics_pipeline_pool.Create(instance, scheduler, desc_heap, graphics_key,
|
it.value() =
|
||||||
|
std::make_unique<GraphicsPipeline>(instance, scheduler, desc_heap, graphics_key,
|
||||||
*pipeline_cache, infos, fetch_shader, modules);
|
*pipeline_cache, infos, fetch_shader, modules);
|
||||||
|
if (Config::collectShadersForDebug()) {
|
||||||
|
for (auto stage = 0; stage < MaxShaderStages; ++stage) {
|
||||||
|
if (infos[stage]) {
|
||||||
|
auto& m = modules[stage];
|
||||||
|
module_related_pipelines[m].emplace_back(graphics_key);
|
||||||
}
|
}
|
||||||
return it->second;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return it->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComputePipeline* PipelineCache::GetComputePipeline() {
|
const ComputePipeline* PipelineCache::GetComputePipeline() {
|
||||||
|
@ -201,10 +210,14 @@ const ComputePipeline* PipelineCache::GetComputePipeline() {
|
||||||
}
|
}
|
||||||
const auto [it, is_new] = compute_pipelines.try_emplace(compute_key);
|
const auto [it, is_new] = compute_pipelines.try_emplace(compute_key);
|
||||||
if (is_new) {
|
if (is_new) {
|
||||||
it.value() = compute_pipeline_pool.Create(instance, scheduler, desc_heap, *pipeline_cache,
|
it.value() = std::make_unique<ComputePipeline>(
|
||||||
compute_key, *infos[0], modules[0]);
|
instance, scheduler, desc_heap, *pipeline_cache, compute_key, *infos[0], modules[0]);
|
||||||
|
if (Config::collectShadersForDebug()) {
|
||||||
|
auto& m = modules[0];
|
||||||
|
module_related_pipelines[m].emplace_back(compute_key);
|
||||||
}
|
}
|
||||||
return it->second;
|
}
|
||||||
|
return it->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PipelineCache::RefreshGraphicsKey() {
|
bool PipelineCache::RefreshGraphicsKey() {
|
||||||
|
@ -401,7 +414,7 @@ bool PipelineCache::RefreshComputeKey() {
|
||||||
Shader::Backend::Bindings binding{};
|
Shader::Backend::Bindings binding{};
|
||||||
const auto* cs_pgm = &liverpool->regs.cs_program;
|
const auto* cs_pgm = &liverpool->regs.cs_program;
|
||||||
const auto cs_params = Liverpool::GetParams(*cs_pgm);
|
const auto cs_params = Liverpool::GetParams(*cs_pgm);
|
||||||
std::tie(infos[0], modules[0], fetch_shader, compute_key) =
|
std::tie(infos[0], modules[0], fetch_shader, compute_key.value) =
|
||||||
GetProgram(Shader::Stage::Compute, cs_params, binding);
|
GetProgram(Shader::Stage::Compute, cs_params, binding);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -417,17 +430,23 @@ 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) {
|
const bool is_patched = patch && Config::patchShaders();
|
||||||
spv = *patch;
|
if (is_patched) {
|
||||||
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, module, spv, code, patch ? *patch : std::span<const u32>{},
|
||||||
|
is_patched);
|
||||||
}
|
}
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
@ -438,17 +457,17 @@ PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params,
|
||||||
const auto runtime_info = BuildRuntimeInfo(stage);
|
const auto runtime_info = BuildRuntimeInfo(stage);
|
||||||
auto [it_pgm, new_program] = program_cache.try_emplace(params.hash);
|
auto [it_pgm, new_program] = program_cache.try_emplace(params.hash);
|
||||||
if (new_program) {
|
if (new_program) {
|
||||||
Program* program = program_pool.Create(stage, params);
|
it_pgm.value() = std::make_unique<Program>(stage, params);
|
||||||
|
auto& program = it_pgm.value();
|
||||||
auto start = binding;
|
auto start = binding;
|
||||||
const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding);
|
const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding);
|
||||||
const auto spec = Shader::StageSpecialization(program->info, runtime_info, profile, start);
|
const auto spec = Shader::StageSpecialization(program->info, runtime_info, profile, start);
|
||||||
program->AddPermut(module, std::move(spec));
|
program->AddPermut(module, std::move(spec));
|
||||||
it_pgm.value() = program;
|
|
||||||
return std::make_tuple(&program->info, module, spec.fetch_shader_data,
|
return std::make_tuple(&program->info, module, spec.fetch_shader_data,
|
||||||
HashCombine(params.hash, 0));
|
HashCombine(params.hash, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Program* program = it_pgm->second;
|
auto& program = it_pgm.value();
|
||||||
auto& info = program->info;
|
auto& info = program->info;
|
||||||
info.RefreshFlatBuf();
|
info.RefreshFlatBuf();
|
||||||
const auto spec = Shader::StageSpecialization(info, runtime_info, profile, binding);
|
const auto spec = Shader::StageSpecialization(info, runtime_info, profile, binding);
|
||||||
|
@ -469,6 +488,34 @@ PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params,
|
||||||
HashCombine(params.hash, perm_idx));
|
HashCombine(params.hash, perm_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<vk::ShaderModule> PipelineCache::ReplaceShader(vk::ShaderModule module,
|
||||||
|
std::span<const u32> spv_code) {
|
||||||
|
std::optional<vk::ShaderModule> new_module{};
|
||||||
|
for (const auto& [_, program] : program_cache) {
|
||||||
|
for (auto& m : program->modules) {
|
||||||
|
if (m.module == module) {
|
||||||
|
const auto& d = instance.GetDevice();
|
||||||
|
d.destroyShaderModule(m.module);
|
||||||
|
m.module = CompileSPV(spv_code, d);
|
||||||
|
new_module = m.module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (module_related_pipelines.contains(module)) {
|
||||||
|
auto& pipeline_keys = module_related_pipelines[module];
|
||||||
|
for (auto& key : pipeline_keys) {
|
||||||
|
if (std::holds_alternative<GraphicsPipelineKey>(key)) {
|
||||||
|
auto& graphics_key = std::get<GraphicsPipelineKey>(key);
|
||||||
|
graphics_pipelines.erase(graphics_key);
|
||||||
|
} else if (std::holds_alternative<ComputePipelineKey>(key)) {
|
||||||
|
auto& compute_key = std::get<ComputePipelineKey>(key);
|
||||||
|
compute_pipelines.erase(compute_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_module;
|
||||||
|
}
|
||||||
|
|
||||||
void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage,
|
void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage,
|
||||||
size_t perm_idx, std::string_view ext) {
|
size_t perm_idx, std::string_view ext) {
|
||||||
if (!Config::dumpShaders()) {
|
if (!Config::dumpShaders()) {
|
||||||
|
@ -488,9 +535,6 @@ void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stag
|
||||||
std::optional<std::vector<u32>> PipelineCache::GetShaderPatch(u64 hash, Shader::Stage stage,
|
std::optional<std::vector<u32>> PipelineCache::GetShaderPatch(u64 hash, Shader::Stage stage,
|
||||||
size_t perm_idx,
|
size_t perm_idx,
|
||||||
std::string_view ext) {
|
std::string_view ext) {
|
||||||
if (!Config::patchShaders()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace Common::FS;
|
using namespace Common::FS;
|
||||||
const auto patch_dir = GetUserPath(PathType::ShaderDir) / "patch";
|
const auto patch_dir = GetUserPath(PathType::ShaderDir) / "patch";
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
#include <tsl/robin_map.h>
|
#include <tsl/robin_map.h>
|
||||||
#include "shader_recompiler/profile.h"
|
#include "shader_recompiler/profile.h"
|
||||||
#include "shader_recompiler/recompiler.h"
|
#include "shader_recompiler/recompiler.h"
|
||||||
|
@ -11,6 +12,13 @@
|
||||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||||
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::hash<vk::ShaderModule> {
|
||||||
|
std::size_t operator()(const vk::ShaderModule& module) const noexcept {
|
||||||
|
return std::hash<size_t>{}(reinterpret_cast<size_t>((VkShaderModule)module));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
namespace Shader {
|
namespace Shader {
|
||||||
struct Info;
|
struct Info;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +60,9 @@ public:
|
||||||
GetProgram(Shader::Stage stage, Shader::ShaderParams params,
|
GetProgram(Shader::Stage stage, Shader::ShaderParams params,
|
||||||
Shader::Backend::Bindings& binding);
|
Shader::Backend::Bindings& binding);
|
||||||
|
|
||||||
|
std::optional<vk::ShaderModule> ReplaceShader(vk::ShaderModule module,
|
||||||
|
std::span<const u32> spv_code);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool RefreshGraphicsKey();
|
bool RefreshGraphicsKey();
|
||||||
bool RefreshComputeKey();
|
bool RefreshComputeKey();
|
||||||
|
@ -74,17 +85,19 @@ private:
|
||||||
vk::UniquePipelineLayout pipeline_layout;
|
vk::UniquePipelineLayout pipeline_layout;
|
||||||
Shader::Profile profile{};
|
Shader::Profile profile{};
|
||||||
Shader::Pools pools;
|
Shader::Pools pools;
|
||||||
tsl::robin_map<size_t, Program*> program_cache;
|
tsl::robin_map<size_t, std::unique_ptr<Program>> program_cache;
|
||||||
Common::ObjectPool<Program> program_pool;
|
tsl::robin_map<ComputePipelineKey, std::unique_ptr<ComputePipeline>> compute_pipelines;
|
||||||
Common::ObjectPool<GraphicsPipeline> graphics_pipeline_pool;
|
tsl::robin_map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>> graphics_pipelines;
|
||||||
Common::ObjectPool<ComputePipeline> compute_pipeline_pool;
|
|
||||||
tsl::robin_map<size_t, ComputePipeline*> compute_pipelines;
|
|
||||||
tsl::robin_map<GraphicsPipelineKey, GraphicsPipeline*> graphics_pipelines;
|
|
||||||
std::array<const Shader::Info*, MaxShaderStages> infos{};
|
std::array<const Shader::Info*, MaxShaderStages> infos{};
|
||||||
std::array<vk::ShaderModule, MaxShaderStages> modules{};
|
std::array<vk::ShaderModule, MaxShaderStages> modules{};
|
||||||
std::optional<Shader::Gcn::FetchShaderData> fetch_shader{};
|
std::optional<Shader::Gcn::FetchShaderData> fetch_shader{};
|
||||||
GraphicsPipelineKey graphics_key{};
|
GraphicsPipelineKey graphics_key{};
|
||||||
u64 compute_key{};
|
ComputePipelineKey compute_key{};
|
||||||
|
|
||||||
|
// Only if Config::collectShadersForDebug()
|
||||||
|
tsl::robin_map<vk::ShaderModule,
|
||||||
|
std::vector<std::variant<GraphicsPipelineKey, ComputePipelineKey>>>
|
||||||
|
module_related_pipelines;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -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};
|
||||||
|
@ -90,6 +94,10 @@ public:
|
||||||
draw_scheduler.Flush(info);
|
draw_scheduler.Flush(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rasterizer& GetRasterizer() const {
|
||||||
|
return *rasterizer.get();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreatePostProcessPipeline();
|
void CreatePostProcessPipeline();
|
||||||
Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true);
|
Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true);
|
||||||
|
|
|
@ -54,6 +54,10 @@ public:
|
||||||
u64 Flush();
|
u64 Flush();
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
|
PipelineCache& GetPipelineCache() {
|
||||||
|
return pipeline_cache;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderState PrepareRenderState(u32 mrt_mask);
|
RenderState PrepareRenderState(u32 mrt_mask);
|
||||||
void BeginRendering(const GraphicsPipeline& pipeline, RenderState& state);
|
void BeginRendering(const GraphicsPipeline& pipeline, RenderState& state);
|
||||||
|
|
Loading…
Reference in a new issue