From eddbe59793554214c75a88f35c54a9ff80ae6318 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Tue, 15 Oct 2024 22:14:44 -0300 Subject: [PATCH] devtools: option to show collapsed frame dump keep most popups open when selection changes best popup windows positioning --- src/core/debug_state.h | 6 +-- src/core/devtools/layer.cpp | 8 ++++ src/core/devtools/options.cpp | 6 +++ src/core/devtools/options.h | 3 +- src/core/devtools/widget/cmd_list.cpp | 35 +++++++++++---- src/core/devtools/widget/cmd_list.h | 4 +- src/core/devtools/widget/frame_dump.cpp | 59 +++++++++++++++++-------- src/core/devtools/widget/frame_dump.h | 1 + src/core/devtools/widget/reg_popup.cpp | 35 ++++++++++----- src/core/devtools/widget/reg_popup.h | 6 ++- src/core/devtools/widget/reg_view.cpp | 59 +++++++++++++++++++------ src/core/devtools/widget/reg_view.h | 4 ++ 12 files changed, 169 insertions(+), 57 deletions(-) diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 7a72059d0..6c43ae28a 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -36,9 +36,9 @@ class FrameGraph; namespace DebugStateType { enum class QueueType { - acb, - dcb, - ccb, + dcb = 0, + ccb = 1, + acb = 2, }; struct QueueDump { diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 17ef43bbc..69959181c 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -154,20 +154,28 @@ void L::DrawAdvanced() { if (BeginPopupModal("GPU Tools Options", &close_popup_options, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { static char disassembly_cli[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'; + frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse; } InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); if (IsItemHovered()) { SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); } + Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse); + if (IsItemHovered()) { + SetTooltip("When a frame dump is collapsed, it will keep\n" + "showing all opened popups related to it"); + } if (Button("Save")) { Options.disassembly_cli = disassembly_cli; + Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse; SaveIniSettingsToDisk(io.IniFilename); CloseCurrentPopup(); } diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp index 82fa6e87e..1b49da76b 100644 --- a/src/core/devtools/options.cpp +++ b/src/core/devtools/options.cpp @@ -11,14 +11,20 @@ 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; return; } + if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) { + Options.frame_dump_render_on_collapse = i != 0; + return; + } } void SerializeOptionsConfig(ImGuiTextBuffer* buf) { buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); + buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse); } } // namespace Core::Devtools diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h index 9d291d768..c3a8aaf31 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 disassembly_cli{}; + bool frame_dump_render_on_collapse{false}; }; extern TOptions Options; diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index aa11c181b..9a42f8238 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -1173,7 +1173,7 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, } } -void CmdListViewer::Draw() { +void CmdListViewer::Draw(bool only_batches_view) { const auto& ctx = *GetCurrentContext(); if (batch_view.open) { @@ -1188,6 +1188,10 @@ void CmdListViewer::Draw() { ++it; } + if (only_batches_view) { + return; + } + if (cmdb_view.Open) { MemoryEditor::Sizes s; cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr); @@ -1313,12 +1317,17 @@ void CmdListViewer::Draw() { pop.SetData(data, name, batch_id); pop.open = true; } else { - batch_view.SetData(data, name, batch_id); - if (!batch_view.open) { - batch_view.open = true; - const auto pos = GImGui->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f); - SetNextWindowPos(pos, ImGuiCond_Always); - batch_view.Draw(); + if (batch_view.open && + this->last_selected_batch == static_cast(batch_id)) { + batch_view.open = false; + } else { + this->last_selected_batch = static_cast(batch_id); + batch_view.SetData(data, name, batch_id); + if (!batch_view.open || !batch_view.moved) { + batch_view.open = true; + const auto pos = GetItemRectMax() + ImVec2{5.0f, 0.0f}; + batch_view.SetPos(pos); + } } } } @@ -1330,7 +1339,10 @@ void CmdListViewer::Draw() { show_batch_content = CollapsingHeader(batch_hdr, ImGuiTreeNodeFlags_AllowOverlap); SameLine(GetContentRegionAvail().x - 40.0f); - if (Button("->", {40.0f, 0.0f})) { + const char* text = + last_selected_batch == static_cast(batch_id) && batch_view.open ? "X" + : "->"; + if (Button(text, {40.0f, 0.0f})) { open_batch_view(); } } @@ -1360,7 +1372,12 @@ void CmdListViewer::Draw() { if (!group_batches) { if (IsDrawCall(op)) { SameLine(GetContentRegionAvail().x - 40.0f); - if (Button("->", {40.0f, 0.0f})) { + const char* text = + last_selected_batch == static_cast(batch_id) && + batch_view.open + ? "X" + : "->"; + if (Button(text, {40.0f, 0.0f})) { open_batch_view(); } } diff --git a/src/core/devtools/widget/cmd_list.h b/src/core/devtools/widget/cmd_list.h index 971c8fffe..ed71d0b76 100644 --- a/src/core/devtools/widget/cmd_list.h +++ b/src/core/devtools/widget/cmd_list.h @@ -53,6 +53,8 @@ class CmdListViewer { u32 highlight_batch{~0u}; RegView batch_view; + int last_selected_batch{-1}; + std::vector extra_batch_view; static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body); @@ -68,7 +70,7 @@ public: explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector& cmd_list, uintptr_t base_addr = 0, std::string name = ""); - void Draw(); + void Draw(bool only_batches_view = false); }; } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 2725305a2..86ba7b86e 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -7,6 +7,7 @@ #include #include "common/io_file.h" +#include "core/devtools/options.h" #include "frame_dump.h" #include "imgui_internal.h" #include "imgui_memory_editor.h" @@ -45,11 +46,14 @@ FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump) selected_submit_num = 0; selected_queue_num2 = 0; + has_queue_type.fill(false); cmd_list_viewer.reserve(frame_dump->queues.size()); for (const auto& cmd : frame_dump->queues) { + if (!cmd.data.empty()) { + has_queue_type[static_cast(cmd.type)] = true; + } const auto fname = fmt::format("F{} {}_{:02}_{:02}", frame_dump->frame_id, - magic_enum::enum_name(selected_queue_type), - selected_submit_num, selected_queue_num2); + magic_enum::enum_name(cmd.type), cmd.submit_num, cmd.num2); cmd_list_viewer.emplace_back(frame_dump.get(), cmd.data, cmd.base_addr, fname); if (cmd.type == QueueType::dcb && cmd.submit_num == 0 && cmd.num2 == 0) { selected_cmd = static_cast(cmd_list_viewer.size() - 1); @@ -64,9 +68,28 @@ void FrameDumpViewer::Draw() { return; } + const auto try_select = [&, this] { + const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) { + return cmd.type == selected_queue_type && + (selected_submit_num == -1 || cmd.submit_num == selected_submit_num) && + (selected_queue_num2 == -1 || cmd.num2 == selected_queue_num2); + }); + if (it != frame_dump->queues.end()) { + selected_cmd = static_cast(std::distance(frame_dump->queues.begin(), it)); + selected_submit_num = static_cast(frame_dump->queues[selected_cmd].submit_num); + selected_queue_num2 = static_cast(frame_dump->queues[selected_cmd].num2); + } + }; + + bool is_showing = Options.frame_dump_render_on_collapse; + bool is_collapsed = true; + char name[32]; snprintf(name, sizeof(name), "Frame #%d dump", frame_dump->frame_id); if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) { + is_showing = true; + is_collapsed = false; + if (IsWindowAppearing()) { auto window = GetCurrentWindow(); static ImGuiID dock_id = ImHashStr("FrameDumpDock"); @@ -79,12 +102,15 @@ void FrameDumpViewer::Draw() { if (BeginCombo("##select_queue_type", magic_enum::enum_name(selected_queue_type).data(), ImGuiComboFlags_WidthFitPreview)) { bool selected = false; -#define COMBO(x) C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected) - COMBO(QueueType::acb) +#define COMBO(x) \ + if (has_queue_type[static_cast(x)]) \ + C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected) COMBO(QueueType::dcb); COMBO(QueueType::ccb); + COMBO(QueueType::acb); if (selected) { selected_submit_num = selected_queue_num2 = -1; + try_select(); } EndCombo(); } @@ -111,9 +137,9 @@ void FrameDumpViewer::Draw() { SameLine(); if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(), ImGuiComboFlags_WidthFitPreview)) { - std::array available_submits{}; + std::array available_submits{false}; for (const auto& cmd : frame_dump->queues) { - if (cmd.type == selected_queue_type) { + if (cmd.type == selected_queue_type && !cmd.data.empty()) { available_submits[cmd.submit_num] = true; } } @@ -128,6 +154,7 @@ void FrameDumpViewer::Draw() { } if (selected) { selected_queue_num2 = -1; + try_select(); } EndCombo(); } @@ -136,9 +163,10 @@ void FrameDumpViewer::Draw() { SameLine(); if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(), ImGuiComboFlags_WidthFitPreview)) { - std::array available_queues{}; + std::array available_queues{false}; for (const auto& cmd : frame_dump->queues) { - if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) { + if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num && + !cmd.data.empty()) { available_queues[cmd.num2] = true; } } @@ -152,21 +180,14 @@ void FrameDumpViewer::Draw() { } } if (selected) { - const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) { - return cmd.type == selected_queue_type && - cmd.submit_num == selected_submit_num && cmd.num2 == selected_queue_num2; - }); - if (it != frame_dump->queues.end()) { - selected_cmd = static_cast(std::distance(frame_dump->queues.begin(), it)); - } + try_select(); } EndCombo(); } EndGroup(); - - if (selected_cmd != -1) { - cmd_list_viewer[selected_cmd].Draw(); - } + } + if (is_showing && selected_cmd != -1) { + cmd_list_viewer[selected_cmd].Draw(is_collapsed); } End(); } diff --git a/src/core/devtools/widget/frame_dump.h b/src/core/devtools/widget/frame_dump.h index 2b3ff2411..cc4fe6381 100644 --- a/src/core/devtools/widget/frame_dump.h +++ b/src/core/devtools/widget/frame_dump.h @@ -20,6 +20,7 @@ class FrameDumpViewer { int id; std::vector cmd_list_viewer; + std::array has_queue_type; DebugStateType::QueueType selected_queue_type; s32 selected_submit_num; diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp index 6036cf098..0633e76e6 100644 --- a/src/core/devtools/widget/reg_popup.cpp +++ b/src/core/devtools/widget/reg_popup.cpp @@ -154,21 +154,36 @@ void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBu this->title = fmt::format("{}/Depth", base_title); } -void RegPopup::Draw(bool auto_resize) { +void RegPopup::SetPos(ImVec2 pos, bool auto_resize) { char name[128]; snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id); - if (Begin(title.c_str(), &open, flags)) { + Begin(name, &open, flags); + SetWindowPos(pos); + if (auto_resize) { + if (std::holds_alternative(data)) { + SetWindowSize({365.0f, 520.0f}); + KeepWindowInside(); + } else if (std::holds_alternative(data)) { + SetWindowSize({404.0f, 543.0f}); + KeepWindowInside(); + } + } + last_pos = GetWindowPos(); + moved = false; + End(); +} + +void RegPopup::Draw() { + char name[128]; + snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id); + if (Begin(name, &open, flags)) { + if (GetWindowPos() != last_pos) { + moved = true; + } + if (const auto* buffer = std::get_if(&data)) { - if (auto_resize) { - SetWindowSize({365.0f, 520.0f}); - KeepWindowInside(); - } DrawColorBuffer(*buffer); } else if (const auto* depth_data = std::get_if(&data)) { - if (auto_resize) { - SetWindowSize({404.0f, 543.0f}); - KeepWindowInside(); - } DrawDepthBuffer(*depth_data); } } diff --git a/src/core/devtools/widget/reg_popup.h b/src/core/devtools/widget/reg_popup.h index f92983306..9ccd60ac0 100644 --- a/src/core/devtools/widget/reg_popup.h +++ b/src/core/devtools/widget/reg_popup.h @@ -18,6 +18,7 @@ class RegPopup { using DepthBuffer = std::tuple; + ImVec2 last_pos; std::variant data; std::string title{}; @@ -27,6 +28,7 @@ class RegPopup { public: bool open = false; + bool moved = false; RegPopup(); @@ -36,7 +38,9 @@ public: void SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer, AmdGpu::Liverpool::DepthControl depth_control); - void Draw(bool auto_resize = false); + void SetPos(ImVec2 pos, bool auto_resize = 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 b72eca1a1..6426cc4fb 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -23,6 +23,8 @@ using namespace ImGui; using magic_enum::enum_name; +constexpr auto depth_id = 0xF3; + static std::optional exec_cli(const char* cli) { std::array buffer{}; std::string output; @@ -110,21 +112,20 @@ void RegView::DrawRegs() { DrawRow("Color control", "%X (%s)", cc_mode, enum_name(cc_mode).data()); const auto open_new_popup = [&](int cb, auto... args) { + const auto pos = GetItemRectMax() + ImVec2(5.0f, 0.0f); if (GetIO().KeyShift) { auto& pop = extra_reg_popup.emplace_back(); pop.SetData(title, args...); pop.open = true; - pop.Draw(true); + pop.SetPos(pos, true); } else if (last_selected_cb == cb && default_reg_popup.open) { default_reg_popup.open = false; } else { last_selected_cb = cb; default_reg_popup.SetData(title, args...); - if (!default_reg_popup.open) { + if (!default_reg_popup.open || !default_reg_popup.moved) { default_reg_popup.open = true; - const auto pos = GImGui->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f); - SetNextWindowPos(pos, ImGuiCond_Always); - default_reg_popup.Draw(true); + default_reg_popup.SetPos(pos, true); } } }; @@ -158,7 +159,6 @@ void RegView::DrawRegs() { if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) { TextUnformatted("N/A"); } else { - constexpr auto depth_id = 0xF3; const char* text = last_selected_cb == depth_id && default_reg_popup.open ? "x" : "->"; if (SmallButton(text)) { open_new_popup(depth_id, regs.depth_buffer, regs.depth_control); @@ -176,7 +176,7 @@ RegView::RegView() { char name[128]; snprintf(name, sizeof(name), "###reg_dump_%d", id); SetNextWindowPos({400.0f, 200.0f}); - SetNextWindowSize({290.0f, 425.0f}); + SetNextWindowSize({290.0f, 435.0f}); ImGuiID root_dock_id; Begin(name); { @@ -204,25 +204,58 @@ RegView::RegView() { DockBuilderFinish(root_dock_id); } -void RegView::SetData(DebugStateType::RegDump data, const std::string& base_title, u32 batch_id) { - this->data = std::move(data); +void RegView::SetData(DebugStateType::RegDump _data, const std::string& base_title, u32 batch_id) { + this->data = std::move(_data); this->batch_id = batch_id; this->title = fmt::format("{}/Batch {}", base_title, batch_id); // clear cache - selected_shader = -1; shader_decomp.clear(); - default_reg_popup.open = false; + const auto& regs = data.regs; + if (selected_shader >= 0 && !regs.stage_enable.IsStageEnabled(selected_shader)) { + selected_shader = -1; + } + if (default_reg_popup.open) { + default_reg_popup.open = false; + if (last_selected_cb == depth_id) { + const auto& has_depth = + regs.depth_buffer.Address() != 0 && regs.depth_control.depth_enable; + if (has_depth) { + default_reg_popup.SetData(title, regs.depth_buffer, regs.depth_control); + default_reg_popup.open = true; + } + } else if (last_selected_cb >= 0 && last_selected_cb < AmdGpu::Liverpool::NumColorBuffers) { + const auto& buffer = regs.color_buffers[last_selected_cb]; + const bool has_cb = buffer && regs.color_target_mask.GetMask(last_selected_cb); + if (has_cb) { + default_reg_popup.SetData(title, buffer, last_selected_cb); + default_reg_popup.open = true; + } + } + } extra_reg_popup.clear(); } +void RegView::SetPos(ImVec2 pos) { + char name[128]; + snprintf(name, sizeof(name), "%s###reg_dump_%d", title.c_str(), id); + Begin(name, &open, ImGuiWindowFlags_MenuBar); + SetWindowPos(pos); + KeepWindowInside(); + last_pos = GetWindowPos(); + moved = false; + End(); +} + void RegView::Draw() { char name[128]; snprintf(name, sizeof(name), "%s###reg_dump_%d", title.c_str(), id); if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) { - const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"}; + if (GetWindowPos() != last_pos) { + moved = true; + } - KeepWindowInside(); + const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"}; if (BeginMenuBar()) { if (BeginMenu("Windows")) { diff --git a/src/core/devtools/widget/reg_view.h b/src/core/devtools/widget/reg_view.h index c90ed5536..ac7c1a1ae 100644 --- a/src/core/devtools/widget/reg_view.h +++ b/src/core/devtools/widget/reg_view.h @@ -21,6 +21,7 @@ class RegView { std::string title; DebugStateType::RegDump data; u32 batch_id{~0u}; + ImVec2 last_pos; std::unordered_map shader_decomp; int selected_shader{-1}; @@ -40,11 +41,14 @@ class RegView { public: bool open = false; + bool moved = false; RegView(); void SetData(DebugStateType::RegDump data, const std::string& base_title, u32 batch_id); + void SetPos(ImVec2 pos); + void Draw(); };