devtools: option to show collapsed frame dump

keep most popups open when selection changes
best popup windows positioning
This commit is contained in:
Vinicius Rangel 2024-10-15 22:14:44 -03:00
parent d56abb0026
commit eddbe59793
No known key found for this signature in database
GPG Key ID: A5B154D904B761D9
12 changed files with 169 additions and 57 deletions

View File

@ -36,9 +36,9 @@ class FrameGraph;
namespace DebugStateType {
enum class QueueType {
acb,
dcb,
ccb,
dcb = 0,
ccb = 1,
acb = 2,
};
struct QueueDump {

View File

@ -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();
}

View File

@ -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

View File

@ -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;

View File

@ -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 {
if (batch_view.open &&
this->last_selected_batch == static_cast<int>(batch_id)) {
batch_view.open = false;
} else {
this->last_selected_batch = static_cast<int>(batch_id);
batch_view.SetData(data, name, batch_id);
if (!batch_view.open) {
if (!batch_view.open || !batch_view.moved) {
batch_view.open = true;
const auto pos = GImGui->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f);
SetNextWindowPos(pos, ImGuiCond_Always);
batch_view.Draw();
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<int>(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<int>(batch_id) &&
batch_view.open
? "X"
: "->";
if (Button(text, {40.0f, 0.0f})) {
open_batch_view();
}
}

View File

@ -53,6 +53,8 @@ class CmdListViewer {
u32 highlight_batch{~0u};
RegView batch_view;
int last_selected_batch{-1};
std::vector<RegView> 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<u32>& cmd_list,
uintptr_t base_addr = 0, std::string name = "");
void Draw();
void Draw(bool only_batches_view = false);
};
} // namespace Core::Devtools::Widget

View File

@ -7,6 +7,7 @@
#include <magic_enum.hpp>
#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<s32>(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<s32>(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<s32>(std::distance(frame_dump->queues.begin(), it));
selected_submit_num = static_cast<s32>(frame_dump->queues[selected_cmd].submit_num);
selected_queue_num2 = static_cast<s32>(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<s32>(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<bool, 32> available_submits{};
std::array<bool, 32> 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<bool, 32> available_queues{};
std::array<bool, 32> 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<s32>(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();
}

View File

@ -20,6 +20,7 @@ class FrameDumpViewer {
int id;
std::vector<CmdListViewer> cmd_list_viewer;
std::array<bool, 3> has_queue_type;
DebugStateType::QueueType selected_queue_type;
s32 selected_submit_num;

View File

@ -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)) {
if (const auto* buffer = std::get_if<AmdGpu::Liverpool::ColorBuffer>(&data)) {
Begin(name, &open, flags);
SetWindowPos(pos);
if (auto_resize) {
if (std::holds_alternative<AmdGpu::Liverpool::ColorBuffer>(data)) {
SetWindowSize({365.0f, 520.0f});
KeepWindowInside();
}
DrawColorBuffer(*buffer);
} else if (const auto* depth_data = std::get_if<DepthBuffer>(&data)) {
if (auto_resize) {
} else if (std::holds_alternative<DepthBuffer>(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<AmdGpu::Liverpool::ColorBuffer>(&data)) {
DrawColorBuffer(*buffer);
} else if (const auto* depth_data = std::get_if<DepthBuffer>(&data)) {
DrawDepthBuffer(*depth_data);
}
}

View File

@ -18,6 +18,7 @@ class RegPopup {
using DepthBuffer = std::tuple<AmdGpu::Liverpool::DepthBuffer, AmdGpu::Liverpool::DepthControl>;
ImVec2 last_pos;
std::variant<AmdGpu::Liverpool::ColorBuffer, DepthBuffer> 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

View File

@ -23,6 +23,8 @@
using namespace ImGui;
using magic_enum::enum_name;
constexpr auto depth_id = 0xF3;
static std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> 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();
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")) {

View File

@ -21,6 +21,7 @@ class RegView {
std::string title;
DebugStateType::RegDump data;
u32 batch_id{~0u};
ImVec2 last_pos;
std::unordered_map<int, ShaderCache> 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();
};