From 0835dc71b3caf1f24352d19bd597bc56595675af Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Sun, 1 Dec 2024 15:34:29 -0300 Subject: [PATCH] More devtools stuff (#1637) * devtools: memory map viewer * devtools: batch highlight only for non-group viewer * devtools: fix not showing entire user data * devtools: shader debug viewer * devtools: add more reg naming --- CMakeLists.txt | 4 + src/common/config.cpp | 12 ++ src/common/config.h | 2 + src/core/debug_state.cpp | 11 +- src/core/debug_state.h | 48 ++++++- src/core/devtools/gcn/gcn_context_regs.cpp | 10 ++ src/core/devtools/layer.cpp | 45 +++++- src/core/devtools/options.cpp | 11 +- src/core/devtools/options.h | 3 +- src/core/devtools/widget/cmd_list.cpp | 4 +- src/core/devtools/widget/common.h | 57 ++++++++ src/core/devtools/widget/memory_map.cpp | 134 ++++++++++++++++++ src/core/devtools/widget/memory_map.h | 33 +++++ src/core/devtools/widget/reg_view.cpp | 49 +------ src/core/devtools/widget/shader_list.cpp | 95 +++++++++++++ src/core/devtools/widget/shader_list.h | 28 ++++ src/core/memory.h | 6 + .../renderer_vulkan/vk_pipeline_cache.cpp | 4 + 18 files changed, 491 insertions(+), 65 deletions(-) create mode 100644 src/core/devtools/widget/memory_map.cpp create mode 100644 src/core/devtools/widget/memory_map.h create mode 100644 src/core/devtools/widget/shader_list.cpp create mode 100644 src/core/devtools/widget/shader_list.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ed366a0b5..2f503832c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,10 +435,14 @@ set(DEV_TOOLS src/core/devtools/layer.cpp src/core/devtools/widget/frame_graph.cpp src/core/devtools/widget/frame_graph.h src/core/devtools/widget/imgui_memory_editor.h + src/core/devtools/widget/memory_map.cpp + src/core/devtools/widget/memory_map.h src/core/devtools/widget/reg_popup.cpp src/core/devtools/widget/reg_popup.h src/core/devtools/widget/reg_view.cpp src/core/devtools/widget/reg_view.h + src/core/devtools/widget/shader_list.cpp + src/core/devtools/widget/shader_list.h src/core/devtools/widget/text_editor.cpp src/core/devtools/widget/text_editor.h ) diff --git a/src/common/config.cpp b/src/common/config.cpp index 5c2c8cda6..eae8897c8 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -47,6 +47,7 @@ static std::string backButtonBehavior = "left"; static bool useSpecialPad = false; static int specialPadClass = 1; static bool isDebugDump = false; +static bool isShaderDebug = false; static bool isShowSplash = false; static bool isAutoUpdate = false; static bool isNullGpu = false; @@ -159,6 +160,10 @@ bool debugDump() { return isDebugDump; } +bool collectShadersForDebug() { + return isShaderDebug; +} + bool showSplash() { return isShowSplash; } @@ -235,6 +240,10 @@ void setDebugDump(bool enable) { isDebugDump = enable; } +void setCollectShaderForDebug(bool enable) { + isShaderDebug = enable; +} + void setShowSplash(bool enable) { isShowSplash = enable; } @@ -571,6 +580,7 @@ void load(const std::filesystem::path& path) { const toml::value& debug = data.at("Debug"); isDebugDump = toml::find_or(debug, "DebugDump", false); + isShaderDebug = toml::find_or(debug, "CollectShader", false); } if (data.contains("GUI")) { @@ -662,6 +672,7 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["rdocMarkersEnable"] = vkMarkers; data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic; data["Debug"]["DebugDump"] = isDebugDump; + data["Debug"]["CollectShader"] = isShaderDebug; data["GUI"]["theme"] = mw_themes; data["GUI"]["iconSize"] = m_icon_size; data["GUI"]["sliderPos"] = m_slider_pos; @@ -717,6 +728,7 @@ void setDefaultValues() { useSpecialPad = false; specialPadClass = 1; isDebugDump = false; + isShaderDebug = false; isShowSplash = false; isAutoUpdate = false; isNullGpu = false; diff --git a/src/common/config.h b/src/common/config.h index 400115745..d98c94480 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -37,6 +37,7 @@ u32 getScreenHeight(); s32 getGpuId(); bool debugDump(); +bool collectShadersForDebug(); bool showSplash(); bool autoUpdate(); bool nullGpu(); @@ -47,6 +48,7 @@ bool isRdocEnabled(); u32 vblankDiv(); void setDebugDump(bool enable); +void setCollectShaderForDebug(bool enable); void setShowSplash(bool enable); void setAutoUpdate(bool enable); void setNullGpu(bool enable); diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 1dc4297c3..562cb62e8 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -157,7 +157,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, if (is_compute) { dump.is_compute = true; const auto& cs = dump.regs.cs_program; - dump.cs_data = ComputerShaderDump{ + dump.cs_data = PipelineComputerProgramDump{ .cs_program = cs, .code = std::vector{cs.Code().begin(), cs.Code().end()}, }; @@ -167,7 +167,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, auto stage = regs.ProgramForStage(i); if (stage->address_lo != 0) { auto code = stage->Code(); - dump.stages[i] = ShaderDump{ + dump.stages[i] = PipelineShaderProgramDump{ .user_data = *stage, .code = std::vector{code.begin(), code.end()}, }; @@ -176,3 +176,10 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, } } } + +void DebugStateImpl::CollectShader(const std::string& name, std::span spv, + std::span raw_code) { + shader_dump_list.emplace_back(name, std::vector{spv.begin(), spv.end()}, + std::vector{raw_code.begin(), raw_code.end()}); + std::ranges::sort(shader_dump_list, {}, &ShaderDump::name); +} diff --git a/src/core/debug_state.h b/src/core/debug_state.h index cd1c6aa93..759755b52 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -30,7 +30,8 @@ namespace Core::Devtools { class Layer; namespace Widget { class FrameGraph; -} +class ShaderList; +} // namespace Widget } // namespace Core::Devtools namespace DebugStateType { @@ -49,12 +50,12 @@ struct QueueDump { uintptr_t base_addr; }; -struct ShaderDump { +struct PipelineShaderProgramDump { Vulkan::Liverpool::ShaderProgram user_data{}; std::vector code{}; }; -struct ComputerShaderDump { +struct PipelineComputerProgramDump { Vulkan::Liverpool::ComputeProgram cs_program{}; std::vector code{}; }; @@ -63,8 +64,8 @@ struct RegDump { bool is_compute{false}; static constexpr size_t MaxShaderStages = 5; Vulkan::Liverpool::Regs regs{}; - std::array stages{}; - ComputerShaderDump cs_data{}; + std::array stages{}; + PipelineComputerProgramDump cs_data{}; }; struct FrameDump { @@ -73,9 +74,41 @@ struct FrameDump { std::unordered_map regs; // address -> reg dump }; +struct ShaderDump { + std::string name; + std::vector spv; + std::vector raw_code; + + std::string cache_spv_disasm{}; + std::string cache_raw_disasm{}; + + ShaderDump(std::string name, std::vector spv, std::vector raw_code) + : name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {} + + 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)} {} + ShaderDump& operator=(const ShaderDump& other) = delete; + ShaderDump& operator=(ShaderDump&& other) noexcept { + if (this == &other) + return *this; + 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); + return *this; + } +}; + class DebugStateImpl { friend class Core::Devtools::Layer; friend class Core::Devtools::Widget::FrameGraph; + friend class Core::Devtools::Widget::ShaderList; + + std::queue debug_message_popup; std::mutex guest_threads_mutex{}; std::vector guest_threads{}; @@ -94,7 +127,7 @@ class DebugStateImpl { std::shared_mutex frame_dump_list_mutex; std::vector frame_dump_list{}; - std::queue debug_message_popup; + std::vector shader_dump_list{}; public: void ShowDebugMessage(std::string message) { @@ -152,6 +185,9 @@ public: 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 spv, + std::span raw_code); }; } // namespace DebugStateType diff --git a/src/core/devtools/gcn/gcn_context_regs.cpp b/src/core/devtools/gcn/gcn_context_regs.cpp index 843ba9e65..5a591111e 100644 --- a/src/core/devtools/gcn/gcn_context_regs.cpp +++ b/src/core/devtools/gcn/gcn_context_regs.cpp @@ -289,6 +289,16 @@ const char* GetContextRegName(u32 reg_offset) { return "mmSPI_PS_INPUT_CNTL_2"; case mmSPI_PS_INPUT_CNTL_3: return "mmSPI_PS_INPUT_CNTL_3"; + case mmPA_SU_POLY_OFFSET_FRONT_SCALE: + return "mmPA_SU_POLY_OFFSET_FRONT_SCALE"; + case mmPA_SU_POLY_OFFSET_FRONT_OFFSET: + return "mmPA_SU_POLY_OFFSET_FRONT_OFFSET"; + case mmPA_SU_POLY_OFFSET_BACK_SCALE: + return "mmPA_SU_POLY_OFFSET_BACK_SCALE"; + case mmPA_SU_POLY_OFFSET_BACK_OFFSET: + return "mmPA_SU_POLY_OFFSET_BACK_OFFSET"; + case mmPA_SU_POLY_OFFSET_CLAMP: + return "mmPA_SU_POLY_OFFSET_CLAMP"; default: break; } diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 2c4ce20b6..2c2099f4d 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "layer.h" + #include #include "common/config.h" @@ -9,11 +11,12 @@ #include "core/debug_state.h" #include "imgui/imgui_std.h" #include "imgui_internal.h" -#include "layer.h" #include "options.h" #include "video_core/renderer_vulkan/vk_presenter.h" #include "widget/frame_dump.h" #include "widget/frame_graph.h" +#include "widget/memory_map.h" +#include "widget/shader_list.h" extern std::unique_ptr presenter; @@ -35,6 +38,9 @@ static float debug_popup_timing = 3.0f; static bool just_opened_options = false; +static Widget::MemoryMapViewer memory_map; +static Widget::ShaderList shader_list; + // clang-format off static std::string help_text = #include "help.txt" @@ -63,6 +69,7 @@ void L::DrawMenuBar() { } if (BeginMenu("GPU Tools")) { MenuItem("Show frame info", nullptr, &frame_graph.is_open); + MenuItem("Show loaded shaders", nullptr, &shader_list.open); if (BeginMenu("Dump frames")) { SliderInt("Count", &dump_frame_count, 1, 5); if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) { @@ -81,6 +88,12 @@ void L::DrawMenuBar() { } ImGui::EndMenu(); } + if (BeginMenu("Debug")) { + if (MenuItem("Memory map")) { + memory_map.open = true; + } + ImGui::EndMenu(); + } EndMainMenuBar(); } @@ -175,19 +188,29 @@ void L::DrawAdvanced() { bool close_popup_options = true; if (BeginPopupModal("GPU Tools Options", &close_popup_options, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { - static char disassembly_cli[512]; + static char disassembler_cli_isa[512]; + static char disassembler_cli_spv[512]; static bool frame_dump_render_on_collapse; if (just_opened_options) { just_opened_options = false; - auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1); - disassembly_cli[s] = '\0'; + auto s = Options.disassembler_cli_isa.copy(disassembler_cli_isa, + sizeof(disassembler_cli_isa) - 1); + disassembler_cli_isa[s] = '\0'; + s = Options.disassembler_cli_spv.copy(disassembler_cli_spv, + sizeof(disassembler_cli_spv) - 1); + disassembler_cli_spv[s] = '\0'; frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse; } - InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); + InputText("Shader isa disassembler: ", disassembler_cli_isa, sizeof(disassembler_cli_isa)); if (IsItemHovered()) { - SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); + SetTooltip(R"(Command to disassemble shaders. Example: dis.exe --raw "{src}")"); + } + InputText("Shader SPIRV disassembler: ", disassembler_cli_spv, + sizeof(disassembler_cli_spv)); + if (IsItemHovered()) { + SetTooltip(R"(Command to disassemble shaders. Example: spirv-cross -V "{src}")"); } Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse); if (IsItemHovered()) { @@ -196,7 +219,8 @@ void L::DrawAdvanced() { } if (Button("Save")) { - Options.disassembly_cli = disassembly_cli; + Options.disassembler_cli_isa = disassembler_cli_isa; + Options.disassembler_cli_spv = disassembler_cli_spv; Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse; SaveIniSettingsToDisk(io.IniFilename); CloseCurrentPopup(); @@ -219,6 +243,13 @@ void L::DrawAdvanced() { EndPopup(); } + + if (memory_map.open) { + memory_map.Draw(); + } + if (shader_list.open) { + shader_list.Draw(); + } } void L::DrawSimple() { diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp index 1b49da76b..2def42071 100644 --- a/src/core/devtools/options.cpp +++ b/src/core/devtools/options.cpp @@ -12,8 +12,12 @@ TOptions Options; void LoadOptionsConfig(const char* line) { char str[512]; int i; - if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) { - Options.disassembly_cli = str; + if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) { + Options.disassembler_cli_isa = str; + return; + } + if (sscanf(line, "disassembler_cli_spv=%511[^\n]", str) == 1) { + Options.disassembler_cli_spv = str; return; } if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) { @@ -23,7 +27,8 @@ void LoadOptionsConfig(const char* line) { } void SerializeOptionsConfig(ImGuiTextBuffer* buf) { - buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); + buf->appendf("disassembler_cli_isa=%s\n", Options.disassembler_cli_isa.c_str()); + buf->appendf("disassembler_cli_spv=%s\n", Options.disassembler_cli_spv.c_str()); buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse); } diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h index c3a8aaf31..70e1d137b 100644 --- a/src/core/devtools/options.h +++ b/src/core/devtools/options.h @@ -10,7 +10,8 @@ struct ImGuiTextBuffer; namespace Core::Devtools { struct TOptions { - std::string disassembly_cli{}; + std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""}; + std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""}; bool frame_dump_render_on_collapse{false}; }; diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index 219d25d6a..7c550cf2e 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -1306,7 +1306,7 @@ void CmdListViewer::Draw(bool only_batches_view) { if (batch.id == batch_bp) { // highlight batch at breakpoint PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f}); } - if (batch.id == highlight_batch) { + if (batch.id == highlight_batch && !group_batches) { PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); } @@ -1459,7 +1459,7 @@ void CmdListViewer::Draw(bool only_batches_view) { } } - if (batch.id == highlight_batch) { + if (batch.id == highlight_batch && !group_batches) { PopStyleColor(); } diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index e650f5fc7..4429f5581 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -10,9 +11,16 @@ #include #include "common/bit_field.h" +#include "common/io_file.h" #include "common/types.h" +#include "core/debug_state.h" #include "video_core/amdgpu/pm4_opcodes.h" +#if defined(_WIN32) +#define popen _popen +#define pclose _pclose +#endif + namespace Core::Devtools::Widget { /* * Generic PM4 header @@ -106,4 +114,53 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) { } } +inline std::optional exec_cli(const char* cli) { + std::array buffer{}; + std::string output; + const auto f = popen(cli, "r"); + if (!f) { + pclose(f); + return {}; + } + while (fgets(buffer.data(), buffer.size(), f)) { + output += buffer.data(); + } + pclose(f); + return output; +} + +inline std::string RunDisassembler(const std::string& disassembler_cli, + const std::vector& shader_code) { + std::string shader_dis; + + if (disassembler_cli.empty()) { + shader_dis = "No disassembler set"; + } 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; + const auto pos = cli.find(src_arg); + if (pos == std::string::npos) { + DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" + + disassembler_cli); + } 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"; + } + + std::filesystem::remove(bin_path); + } + } + + return shader_dis; +} + } // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/memory_map.cpp b/src/core/devtools/widget/memory_map.cpp new file mode 100644 index 000000000..afafd2853 --- /dev/null +++ b/src/core/devtools/widget/memory_map.cpp @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "core/debug_state.h" +#include "core/memory.h" +#include "memory_map.h" + +using namespace ImGui; + +namespace Core::Devtools::Widget { + +bool MemoryMapViewer::Iterator::DrawLine() { + if (is_vma) { + if (vma.it == vma.end) { + return false; + } + auto m = vma.it->second; + if (m.type == VMAType::Free) { + ++vma.it; + return DrawLine(); + } + TableNextColumn(); + Text("%zX", m.base); + TableNextColumn(); + Text("%zX", m.size); + TableNextColumn(); + Text("%s", magic_enum::enum_name(m.type).data()); + TableNextColumn(); + Text("%s", magic_enum::enum_name(m.prot).data()); + TableNextColumn(); + if (m.is_exec) { + Text("X"); + } + TableNextColumn(); + Text("%s", m.name.c_str()); + ++vma.it; + return true; + } + if (dmem.it == dmem.end) { + return false; + } + auto m = dmem.it->second; + if (m.is_free) { + ++dmem.it; + return DrawLine(); + } + TableNextColumn(); + Text("%llX", m.base); + TableNextColumn(); + Text("%llX", m.size); + TableNextColumn(); + auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type); + Text("%s", magic_enum::enum_name(type).data()); + TableNextColumn(); + Text("%d", m.is_pooled); + ++dmem.it; + return true; +} + +void MemoryMapViewer::Draw() { + SetNextWindowSize({600.0f, 500.0f}, ImGuiCond_FirstUseEver); + if (!Begin("Memory map", &open)) { + End(); + return; + } + + auto mem = Memory::Instance(); + std::scoped_lock lck{mem->mutex}; + + { + bool next_showing_vma = showing_vma; + if (showing_vma) { + PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + if (Button("VMem")) { + next_showing_vma = true; + } + if (showing_vma) { + PopStyleColor(); + } + SameLine(); + if (!showing_vma) { + PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + if (Button("DMem")) { + next_showing_vma = false; + } + if (!showing_vma) { + PopStyleColor(); + } + showing_vma = next_showing_vma; + } + + Iterator it{}; + if (showing_vma) { + it.is_vma = true; + it.vma.it = mem->vma_map.begin(); + it.vma.end = mem->vma_map.end(); + } else { + it.is_vma = false; + it.dmem.it = mem->dmem_map.begin(); + it.dmem.end = mem->dmem_map.end(); + } + + if (BeginTable("memory_view_table", showing_vma ? 6 : 4, + ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | + ImGuiTableFlags_SizingFixedFit)) { + if (showing_vma) { + TableSetupColumn("Address"); + TableSetupColumn("Size"); + TableSetupColumn("Type"); + TableSetupColumn("Prot"); + TableSetupColumn("Is Exec"); + TableSetupColumn("Name"); + } else { + TableSetupColumn("Address"); + TableSetupColumn("Size"); + TableSetupColumn("Type"); + TableSetupColumn("Pooled"); + } + TableHeadersRow(); + + while (it.DrawLine()) + ; + EndTable(); + } + + End(); +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/memory_map.h b/src/core/devtools/widget/memory_map.h new file mode 100644 index 000000000..cc7697c8c --- /dev/null +++ b/src/core/devtools/widget/memory_map.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/memory.h" + +namespace Core::Devtools::Widget { + +class MemoryMapViewer { + struct Iterator { + bool is_vma; + struct { + MemoryManager::DMemMap::iterator it; + MemoryManager::DMemMap::iterator end; + } dmem; + struct { + MemoryManager::VMAMap::iterator it; + MemoryManager::VMAMap::iterator end; + } vma; + + bool DrawLine(); + }; + + bool showing_vma = true; + +public: + bool open = false; + + void Draw(); +}; + +} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp index 10cc88085..a60090a8c 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -25,21 +25,6 @@ using magic_enum::enum_name; constexpr auto depth_id = 0xF3; -static std::optional exec_cli(const char* cli) { - std::array buffer{}; - std::string output; - const auto f = popen(cli, "r"); - if (!f) { - pclose(f); - return {}; - } - while (fgets(buffer.data(), buffer.size(), f)) { - output += buffer.data(); - } - pclose(f); - return output; -} - namespace Core::Devtools::Widget { void RegView::ProcessShader(int shader_id) { @@ -54,38 +39,12 @@ void RegView::ProcessShader(int shader_id) { user_data = s.user_data.user_data; } - std::string shader_dis; - - if (Options.disassembly_cli.empty()) { - shader_dis = "No disassembler set"; - } else { - auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; - - constexpr std::string_view src_arg = "{src}"; - std::string cli = Options.disassembly_cli; - const auto pos = cli.find(src_arg); - if (pos == std::string::npos) { - DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument"); - } 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"; - } - - std::filesystem::remove(bin_path); - } - } + std::string shader_dis = RunDisassembler(Options.disassembler_cli_isa, shader_code); MemoryEditor hex_view; hex_view.Open = true; hex_view.ReadOnly = true; - hex_view.Cols = 8; + hex_view.Cols = 16; hex_view.OptShowAscii = false; hex_view.OptShowOptions = false; @@ -376,7 +335,9 @@ void RegView::Draw() { if (!shader) { Text("Stage not selected"); } else { - shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size()); + shader->hex_view.DrawContents(shader->user_data.data(), + shader->user_data.size() * + sizeof(Vulkan::Liverpool::UserData::value_type)); } } End(); diff --git a/src/core/devtools/widget/shader_list.cpp b/src/core/devtools/widget/shader_list.cpp new file mode 100644 index 000000000..b056880dd --- /dev/null +++ b/src/core/devtools/widget/shader_list.cpp @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_list.h" + +#include + +#include "common.h" +#include "common/config.h" +#include "core/debug_state.h" +#include "core/devtools/options.h" +#include "imgui/imgui_std.h" + +using namespace ImGui; + +namespace Core::Devtools::Widget { + +void ShaderList::DrawShader(DebugStateType::ShaderDump& value) { + if (!loaded_data) { + loaded_data = true; + if (value.cache_raw_disasm.empty()) { + value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code); + } + isa_editor.SetText(value.cache_raw_disasm); + + if (value.cache_spv_disasm.empty()) { + value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv); + } + spv_editor.SetText(value.cache_spv_disasm); + } + + if (SmallButton("<-")) { + selected_shader = -1; + } + SameLine(); + Text("%s", value.name.c_str()); + SameLine(0.0f, 7.0f); + if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) { + if (Selectable("SPIRV")) { + showing_isa = false; + } + if (Selectable("ISA")) { + showing_isa = true; + } + EndCombo(); + } + + if (showing_isa) { + isa_editor.Render("ISA", GetContentRegionAvail()); + } else { + spv_editor.Render("SPIRV", GetContentRegionAvail()); + } +} + +ShaderList::ShaderList() { + isa_editor.SetPalette(TextEditor::GetDarkPalette()); + isa_editor.SetReadOnly(true); + spv_editor.SetPalette(TextEditor::GetDarkPalette()); + spv_editor.SetReadOnly(true); + spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); +} + +void ShaderList::Draw() { + SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver); + if (!Begin("Shader list", &open)) { + End(); + return; + } + + if (!Config::collectShadersForDebug()) { + DrawCenteredText("Enable 'CollectShader' in config to see shaders"); + End(); + return; + } + + if (selected_shader >= 0) { + DrawShader(DebugState.shader_dump_list[selected_shader]); + End(); + return; + } + + auto width = GetContentRegionAvail().x; + int i = 0; + for (const auto& shader : DebugState.shader_dump_list) { + if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) { + selected_shader = i; + loaded_data = false; + } + i++; + } + + End(); +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/shader_list.h b/src/core/devtools/widget/shader_list.h new file mode 100644 index 000000000..5a47f656d --- /dev/null +++ b/src/core/devtools/widget/shader_list.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/debug_state.h" +#include "text_editor.h" + +namespace Core::Devtools::Widget { + +class ShaderList { + int selected_shader = -1; + TextEditor isa_editor{}; + TextEditor spv_editor{}; + bool loaded_data = false; + bool showing_isa = false; + + void DrawShader(DebugStateType::ShaderDump& value); + +public: + ShaderList(); + + bool open = false; + + void Draw(); +}; + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/memory.h b/src/core/memory.h index a9a42e1c2..2efa02763 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -20,6 +20,10 @@ namespace Libraries::Kernel { struct OrbisQueryInfo; } +namespace Core::Devtools::Widget { +class MemoryMapViewer; +} + namespace Core { enum class MemoryProt : u32 { @@ -257,6 +261,8 @@ private: size_t total_flexible_size{}; size_t flexible_usage{}; Vulkan::Rasterizer* rasterizer{}; + + friend class ::Core::Devtools::Widget::MemoryMapViewer; }; using Memory = Common::Singleton; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a4c213ca8..612e950bb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -7,6 +7,7 @@ #include "common/hash.h" #include "common/io_file.h" #include "common/path_util.h" +#include "core/debug_state.h" #include "shader_recompiler/backend/spirv/emit_spirv.h" #include "shader_recompiler/info.h" #include "shader_recompiler/recompiler.h" @@ -416,6 +417,9 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, const auto module = CompileSPV(spv, instance.GetDevice()); const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx); Vulkan::SetObjectName(instance.GetDevice(), module, name); + if (Config::collectShadersForDebug()) { + DebugState.CollectShader(name, spv, code); + } return module; }