diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index fab9a455bb..68b233fef8 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -41,6 +41,37 @@ extern bool g_TakeScreenshot; +// TODO: Style it. +// Left click performs the preferred action, if any. Right click opens a menu for more. +void ImClickableAddress(uint32_t addr, ImControl &control, ImCmd cmd) { + char temp[32]; + snprintf(temp, sizeof(temp), "%08x", addr); + if (ImGui::SmallButton(temp)) { + control.command = { cmd, addr }; + } + + // Create a right-click popup menu + if (ImGui::BeginPopupContextItem(temp)) { + if (ImGui::MenuItem("Copy address to clipboard")) { + System_CopyStringToClipboard(temp); + } + ImGui::Separator(); + // Enable when we implement the memory viewer + if (cmd != ImCmd::SHOW_IN_MEMORY_VIEWER) { + if (ImGui::MenuItem("Show in memory viewer 1")) { + control.command = { ImCmd::SHOW_IN_MEMORY_VIEWER, addr }; + } + } + if (ImGui::MenuItem("Show in CPU debugger")) { + control.command = { ImCmd::SHOW_IN_CPU_DISASM, addr }; + } + if (ImGui::MenuItem("Show in GE debugger")) { + control.command = { ImCmd::SHOW_IN_GE_DISASM, addr }; + } + ImGui::EndPopup(); + } +} + void DrawSchedulerView(ImConfig &cfg) { ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Event Scheduler", &cfg.schedulerOpen)) { @@ -842,6 +873,8 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu return; } + ImControl control{}; + if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("Debug")) { switch (coreState) { @@ -1024,16 +1057,30 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu } if (cfg_.geDebuggerOpen) { - geDebugger_.Draw(cfg_, gpuDebug); + geDebugger_.Draw(cfg_, control, gpuDebug); } if (cfg_.geStateOpen) { - DrawGeStateWindow(cfg_, gpuDebug); + geStateWindow_.Draw(cfg_, control, gpuDebug); } if (cfg_.schedulerOpen) { DrawSchedulerView(cfg_); } + + // Process UI commands + switch (control.command.cmd) { + case ImCmd::SHOW_IN_CPU_DISASM: + disasm_.View().gotoAddr(control.command.param); + break; + case ImCmd::SHOW_IN_GE_DISASM: + geDebugger_.View().GotoAddr(control.command.param); + break; + } +} + +void ImDebugger::Snapshot() { + } void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, CoreState coreState) { diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index 5ab01e9fed..036b6162ac 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -103,12 +103,21 @@ struct ImConfig { void SyncConfig(IniFile *ini, bool save); }; -enum ImUiCmd { - TRIGGER_FIND_POPUP = 0, +enum class ImCmd { + NONE = 0, + TRIGGER_FIND_POPUP, + SHOW_IN_CPU_DISASM, + SHOW_IN_GE_DISASM, + SHOW_IN_MEMORY_VIEWER, }; -struct ImUiCommand { - ImUiCmd cmd; +struct ImCommand { + ImCmd cmd; + uint32_t param; +}; + +struct ImControl { + ImCommand command; }; class ImDebugger { @@ -118,6 +127,10 @@ public: void Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebug); + // Should be called just before starting a step or run, so that things can + // save state that they can later compare with, to highlight changes. + void Snapshot(); + private: Path ConfigPath(); @@ -125,8 +138,12 @@ private: ImDisasmWindow disasm_; ImGeDebuggerWindow geDebugger_; + ImGeStateWindow geStateWindow_; ImStructViewer structViewer_; // Open variables. ImConfig cfg_{}; }; + +// Simple custom controls +void ImClickableAddress(uint32_t addr, ImControl &control, ImCmd cmd); diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index 781eea2cbd..433b1a08b5 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -236,7 +236,7 @@ void ImGeDisasmView::Draw(GPUDebugInterface *gpuDebug) { } } -void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) { +void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterface *gpuDebug) { ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); if (!ImGui::Begin("GE Debugger", &cfg.geDebuggerOpen)) { ImGui::End(); @@ -304,11 +304,18 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) { // Display any pending step event. if (GPUDebug::GetBreakNext() != GPUDebug::BreakNext::NONE) { - ImGui::Text("Step pending (waiting for CPU): %s", GPUDebug::BreakNextToString(GPUDebug::GetBreakNext())); - ImGui::SameLine(); - if (ImGui::Button("Cancel step")) { - GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONE); + if (showBannerInFrames_ > 0) { + showBannerInFrames_--; } + if (showBannerInFrames_ == 0) { + ImGui::Text("Step pending (waiting for CPU): %s", GPUDebug::BreakNextToString(GPUDebug::GetBreakNext())); + ImGui::SameLine(); + if (ImGui::Button("Cancel step")) { + GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONE); + } + } + } else { + showBannerInFrames_ = 2; } // First, let's list any active display lists in the left column, on top of the disassembly. @@ -320,8 +327,11 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) { char title[64]; snprintf(title, sizeof(title), "List %d", list.id); if (ImGui::CollapsingHeader(title, ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Text("PC: %08x", list.pc); - ImGui::Text("StartPC: %08x", list.startpc); + ImGui::TextUnformatted("PC:"); + ImGui::SameLine(); + ImClickableAddress(list.pc, control, ImCmd::SHOW_IN_GE_DISASM); + ImGui::Text("StartPC:"); + ImClickableAddress(list.startpc, control, ImCmd::SHOW_IN_GE_DISASM); ImGui::Text("Pending interrupt: %d", (int)list.pendingInterrupt); ImGui::Text("Stack depth: %d", (int)list.stackptr); ImGui::Text("BBOX result: %d", (int)list.bboxResult); @@ -571,8 +581,12 @@ static const StateItem g_vertexState[] = { {false, GE_CMD_PATCHFACING}, }; +void ImGeStateWindow::Snapshot() { + +} + // TODO: Separate window or merge into Ge debugger? -void DrawGeStateWindow(ImConfig &cfg, GPUDebugInterface *gpuDebug) { +void ImGeStateWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterface *gpuDebug) { ImGui::SetNextWindowSize(ImVec2(300, 500), ImGuiCond_FirstUseEver); if (!ImGui::Begin("GE State", &cfg.geStateOpen)) { ImGui::End(); @@ -623,8 +637,16 @@ void DrawGeStateWindow(ImConfig &cfg, GPUDebugInterface *gpuDebug) { const u32 otherValue = gstate.cmdmem[info.otherCmd] & 0xFFFFFF; const u32 otherValue2 = gstate.cmdmem[info.otherCmd2] & 0xFFFFFF; - FormatStateRow(gpuDebug, temp, sizeof(temp), info.fmt, value, true, otherValue, otherValue2); - ImGui::TextUnformatted(temp); + // Special handling for pointer and pointer/width entries + if (info.fmt == CMD_FMT_PTRWIDTH) { + const u32 val = value | (otherValue & 0x00FF0000) << 8; + ImClickableAddress(val, control, ImCmd::NONE); + ImGui::SameLine(); + ImGui::Text("w=%d", otherValue & 0xFFFF); + } else { + FormatStateRow(gpuDebug, temp, sizeof(temp), info.fmt, value, true, otherValue, otherValue2); + ImGui::TextUnformatted(temp); + } } if (!enabled) ImGui::PopStyleColor(); diff --git a/UI/ImDebugger/ImGe.h b/UI/ImDebugger/ImGe.h index e686d71e2f..a001977cf4 100644 --- a/UI/ImDebugger/ImGe.h +++ b/UI/ImDebugger/ImGe.h @@ -3,6 +3,7 @@ // GE-related windows of the ImDebugger struct ImConfig; +struct ImControl; class FramebufferManagerCommon; class TextureCacheCommon; @@ -12,7 +13,6 @@ void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebuffer void DrawTexturesWindow(ImConfig &cfg, TextureCacheCommon *textureCache); void DrawDisplayWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager); void DrawDebugStatsWindow(ImConfig &cfg); -void DrawGeStateWindow(ImConfig &cfg, GPUDebugInterface *gpuDebug); class ImGeDisasmView { public: @@ -24,6 +24,10 @@ public: gotoPC_ = true; } + void GotoAddr(uint32_t addr) { + selectedAddr_ = addr; + } + private: u32 selectedAddr_ = INVALID_ADDR; u32 dragAddr_ = INVALID_ADDR; @@ -34,13 +38,22 @@ private: }; }; +class ImGeStateWindow { +public: + void Draw(ImConfig &cfg, ImControl &control, GPUDebugInterface *gpuDebug); + void Snapshot(); +private: + u32 prevState_[256]{}; +}; + class ImGeDebuggerWindow { public: - void Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug); + void Draw(ImConfig &cfg, ImControl &control, GPUDebugInterface *gpuDebug); ImGeDisasmView &View() { return disasmView_; } private: ImGeDisasmView disasmView_; + int showBannerInFrames_ = 0; };