diff --git a/Common/Render/Text/draw_text.cpp b/Common/Render/Text/draw_text.cpp index 1fa2a070b63f..d89a8b080ef7 100644 --- a/Common/Render/Text/draw_text.cpp +++ b/Common/Render/Text/draw_text.cpp @@ -167,6 +167,9 @@ void TextDrawer::MeasureStringRect(std::string_view str, const Bounds &bounds, f } void TextDrawer::DrawStringRect(DrawBuffer &target, std::string_view str, const Bounds &bounds, uint32_t color, int align) { + if (bounds.w < 0.0f || bounds.h < 0.0f) { + return; + } float x = bounds.x; float y = bounds.y; if (align & ALIGN_HCENTER) { diff --git a/GPU/Debugger/Debugger.cpp b/GPU/Debugger/Debugger.cpp index ed1729c67990..fdad58cda158 100644 --- a/GPU/Debugger/Debugger.cpp +++ b/GPU/Debugger/Debugger.cpp @@ -36,7 +36,7 @@ static int primsLastFrame = 0; static int primsThisFrame = 0; static int thisFlipNum = 0; -bool g_drawNotified = false; +bool g_primAfterDraw = false; static double lastStepTime = -1.0; static uint32_t g_skipPcOnce = 0; @@ -79,6 +79,15 @@ void SetActive(bool flag) { GPUStepping::ResumeFromStepping(); lastStepTime = -1.0; } + /* + active = false; + breakAtCount = -1; + hasBreakpoints = false; + thisFlipNum = 0; + g_primAfterDraw = false; + lastStepTime = -1.0f; + g_skipPcOnce = false; + */ } bool IsActive() { @@ -103,7 +112,12 @@ void SetBreakNext(BreakNext next) { } else if (next == BreakNext::CURVE) { GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true); GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true); + } else if (next == BreakNext::DRAW) { + // This is now handled by switching to BreakNext::PRIM when we encounter a flush. + // This will take us to the following actual draw. + g_primAfterDraw = true; } + if (GPUStepping::IsStepping()) { GPUStepping::ResumeFromStepping(); } @@ -124,12 +138,6 @@ NotifyResult NotifyCommand(u32 pc) { return NotifyResult::Skip; // return false } - // Hack to handle draw notifications, that don't come in via NotifyCommand. - if (g_drawNotified) { - g_drawNotified = false; - return NotifyResult::Break; - } - u32 op = Memory::ReadUnchecked_U32(pc); u32 cmd = op >> 24; if (thisFlipNum != gpuStats.numFlips) { @@ -198,13 +206,13 @@ void NotifyDraw() { if (!active) return; if (breakNext == BreakNext::DRAW && !GPUStepping::IsStepping()) { - if (lastStepTime >= 0.0) { - NOTICE_LOG(Log::GeDebugger, "Waiting at a draw (%fms)", (time_now_d() - lastStepTime) * 1000.0); - lastStepTime = -1.0; - } else { - NOTICE_LOG(Log::GeDebugger, "Waiting at a draw"); + // Hack to handle draw notifications, that don't come in via NotifyCommand. + if (g_primAfterDraw) { + NOTICE_LOG(Log::GeDebugger, "Flush detected, breaking at next PRIM"); + g_primAfterDraw = false; + // Switch to PRIM mode. + SetBreakNext(BreakNext::PRIM); } - g_drawNotified = true; } } diff --git a/GPU/Debugger/State.cpp b/GPU/Debugger/State.cpp index 4398d79768f4..ec4a09378713 100644 --- a/GPU/Debugger/State.cpp +++ b/GPU/Debugger/State.cpp @@ -12,6 +12,9 @@ #include "Core/System.h" void FormatStateRow(GPUDebugInterface *gpudebug, char *dest, size_t destSize, CmdFormatType fmt, u32 value, bool enabled, u32 otherValue, u32 otherValue2) { + value &= 0xFFFFFF; + otherValue &= 0xFFFFFF; + otherValue2 &= 0xFFFFFF; switch (fmt) { case CMD_FMT_HEX: snprintf(dest, destSize, "%06x", value); diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index b4a0a81d1093..4395a3275a5e 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -71,6 +71,7 @@ static int bufferLevel; static bool lastWasFramebuffer; static u32 pauseSetCmdValue; +// This is used only to highlight differences. Should really be owned by the debugger. static GPUgstate lastGState; const char *PauseActionToString(PauseAction action) { @@ -161,21 +162,6 @@ void WaitForPauseAction() { actionWait.wait(guard); } -static void StartStepping() { - if (lastGState.cmdmem[1] == 0) { - lastGState = gstate; - // Play it safe so we don't keep resetting. - lastGState.cmdmem[1] |= 0x01000000; - } - isStepping = true; - stepCounter++; -} - -static void StopStepping() { - lastGState = gstate; - isStepping = false; -} - bool ProcessStepping() { _dbg_assert_(gpuDebug); @@ -215,7 +201,15 @@ bool EnterStepping() { return false; } - StartStepping(); + // StartStepping + if (lastGState.cmdmem[1] == 0) { + lastGState = gstate; + // Play it safe so we don't keep resetting. + lastGState.cmdmem[1] |= 0x01000000; + } + + isStepping = true; + stepCounter++; // Just to be sure. if (pauseAction == PAUSE_CONTINUE) { @@ -227,7 +221,8 @@ bool EnterStepping() { } void ResumeFromStepping() { - StopStepping(); + lastGState = gstate; + isStepping = false; SetPauseAction(PAUSE_CONTINUE, false); } @@ -300,7 +295,7 @@ bool GPU_FlushDrawing() { return true; } -GPUgstate LastState() { +const GPUgstate &LastState() { return lastGState; } diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index 1393ffc7c74d..0146053a8aa8 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -46,5 +46,6 @@ namespace GPUStepping { bool GPU_SetCmdValue(u32 op); bool GPU_FlushDrawing(); - GPUgstate LastState(); + // Can be used to highlight differences in a debugger. + const GPUgstate &LastState(); }; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 6725eda50499..687bf3256b05 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1539,6 +1539,11 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { } } + // Running it early allows things like direct readbacks of buffers, things we can't do + // when we have started the final render pass. Well, technically we probably could with some manipulation + // of pass order in the render managers.. + runImDebugger(); + bool blockedExecution = Achievements::IsBlockingExecution(); uint32_t clearColor = 0; if (!blockedExecution) { @@ -1655,7 +1660,7 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { return flags; } -void EmuScreen::renderImDebugger() { +void EmuScreen::runImDebugger() { if (g_Config.bShowImDebugger) { Draw::DrawContext *draw = screenManager()->getDrawContext(); if (!imguiInited_) { @@ -1691,6 +1696,14 @@ void EmuScreen::renderImDebugger() { imDebugger_->Frame(currentDebugMIPS, gpuDebug); ImGui::Render(); + } + } +} + +void EmuScreen::renderImDebugger() { + if (g_Config.bShowImDebugger) { + Draw::DrawContext *draw = screenManager()->getDrawContext(); + if (PSP_IsInited()) { ImGui_ImplThin3d_RenderDrawData(ImGui::GetDrawData(), draw); } } diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 0947bc2d565b..2fc7335b58c1 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -79,6 +79,7 @@ class EmuScreen : public UIScreen { void bootComplete(); bool hasVisibleUI(); void renderUI(); + void runImDebugger(); void renderImDebugger(); void onVKey(int virtualKeyCode, bool down); diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index c7d0a09c8827..2953a66a6c3c 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -65,6 +65,9 @@ void ShowInWindowMenuItems(uint32_t addr, ImControl &control) { } void StatusBar(std::string_view status) { + if (!status.size()) { + return; + } ImGui::TextUnformatted(status.data(), status.data() + status.length()); ImGui::SameLine(); if (ImGui::SmallButton("Copy")) { @@ -120,85 +123,125 @@ void DrawSchedulerView(ImConfig &cfg) { ImGui::End(); } -void DrawRegisterView(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug) { +static void DrawGPRs(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) { ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Registers", &config.regsOpen)) { + if (!ImGui::Begin("MIPS GPRs", &config.vfpuOpen)) { ImGui::End(); return; } - if (ImGui::BeginTabBar("RegisterGroups", ImGuiTabBarFlags_None)) { - if (ImGui::BeginTabItem("GPR")) { - if (ImGui::BeginTable("gpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { - ImGui::TableSetupColumn("regname", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("value_i", ImGuiTableColumnFlags_WidthStretch); - - auto gprLine = [&](int index, const char *regname, int value) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(regname); - ImGui::TableNextColumn(); - if (Memory::IsValid4AlignedAddress(value)) { - ImGui::PushID(index); - ImClickableAddress(value, control, index == MIPS_REG_RA ? ImCmd::SHOW_IN_CPU_DISASM : ImCmd::SHOW_IN_MEMORY_VIEWER); - ImGui::PopID(); - } else { - ImGui::Text("%08x", value); - } - if (value >= -1000000 && value <= 1000000) { - ImGui::TableSetColumnIndex(2); - ImGui::Text("%d", value); - } - }; - for (int i = 0; i < 32; i++) { - gprLine(i, mipsDebug->GetRegName(0, i).c_str(), mipsDebug->GetGPR32Value(i)); - } - gprLine(32, "hi", mipsDebug->GetHi()); - gprLine(33, "lo", mipsDebug->GetLo()); - gprLine(34, "pc", mipsDebug->GetPC()); - gprLine(35, "ll", mipsDebug->GetLLBit()); - ImGui::EndTable(); - } + bool noDiff = coreState == CORE_RUNNING_CPU || coreState == CORE_STEPPING_GE; - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("FPU")) { - if (ImGui::BeginTable("fpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { - ImGui::TableSetupColumn("regname", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("value_i", ImGuiTableColumnFlags_WidthStretch); + if (ImGui::BeginTable("gpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { + ImGui::TableSetupColumn("Reg", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Decimal", ImGuiTableColumnFlags_WidthStretch); - // fpcond - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::TextUnformatted("fpcond"); - ImGui::TableNextColumn(); - ImGui::Text("%08x", mipsDebug->GetFPCond()); - - for (int i = 0; i < 32; i++) { - float fvalue = mipsDebug->GetFPR32Value(i); - u32 fivalue; - memcpy(&fivalue, &fvalue, sizeof(fivalue)); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(mipsDebug->GetRegName(1, i).c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%0.7f", fvalue); - ImGui::TableNextColumn(); - ImGui::Text("%08x", fivalue); - } + ImGui::TableHeadersRow(); + + auto gprLine = [&](int index, const char *regname, int value, int prevValue) { + bool diff = value != prevValue && !noDiff; + bool disabled = value == 0xdeadbeef; - ImGui::EndTable(); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(regname); + ImGui::TableNextColumn(); + if (diff) { + ImGui::PushStyleColor(ImGuiCol_Text, !disabled ? ImDebuggerColor_Diff : ImDebuggerColor_DiffAlpha); + } else if (disabled) { + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128)); } - ImGui::EndTabItem(); + if (Memory::IsValid4AlignedAddress(value)) { + ImGui::PushID(index); + ImClickableAddress(value, control, index == MIPS_REG_RA ? ImCmd::SHOW_IN_CPU_DISASM : ImCmd::SHOW_IN_MEMORY_VIEWER); + ImGui::PopID(); + } else { + ImGui::Text("%08x", value); + } + ImGui::TableNextColumn(); + if (value >= -1000000 && value <= 1000000) { + ImGui::Text("%d", value); + } + if (diff || disabled) { + ImGui::PopStyleColor(); + } + }; + for (int i = 0; i < 32; i++) { + ImGui::TableNextRow(); + gprLine(i, mipsDebug->GetRegName(0, i).c_str(), mipsDebug->GetGPR32Value(i), prev.gpr[i]); } - if (ImGui::BeginTabItem("VFPU")) { - ImGui::Text("TODO"); - ImGui::EndTabItem(); + ImGui::TableNextRow(); + gprLine(32, "hi", mipsDebug->GetHi(), prev.hi); + ImGui::TableNextRow(); + gprLine(33, "lo", mipsDebug->GetLo(), prev.lo); + ImGui::TableNextRow(); + gprLine(34, "pc", mipsDebug->GetPC(), prev.pc); + ImGui::TableNextRow(); + gprLine(35, "ll", mipsDebug->GetLLBit(), prev.ll); + ImGui::EndTable(); + } + ImGui::End(); +} + +static void DrawFPRs(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) { + ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("MIPS FPRs", &config.fprOpen)) { + ImGui::End(); + return; + } + + bool noDiff = coreState == CORE_RUNNING_CPU || coreState == CORE_STEPPING_GE; + + if (ImGui::BeginTable("fpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { + ImGui::TableSetupColumn("Reg", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Hex", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableHeadersRow(); + + // fpcond + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextUnformatted("fpcond"); + ImGui::TableNextColumn(); + ImGui::Text("%08x", mipsDebug->GetFPCond()); + + for (int i = 0; i < 32; i++) { + float fvalue = mipsDebug->GetFPR32Value(i); + float prevValue = prev.fpr[i]; + + // NOTE: Using memcmp to avoid NaN problems. + bool diff = memcmp(&fvalue, &prevValue, 4) != 0 && !noDiff; + + u32 fivalue; + memcpy(&fivalue, &fvalue, sizeof(fivalue)); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (diff) { + ImGui::PushStyleColor(ImGuiCol_Text, ImDebuggerColor_Diff); + } + ImGui::TextUnformatted(mipsDebug->GetRegName(1, i).c_str()); + ImGui::TableNextColumn(); + ImGui::Text("%0.7f", fvalue); + ImGui::TableNextColumn(); + ImGui::Text("%08x", fivalue); + if (diff) { + ImGui::PopStyleColor(); + } } - ImGui::EndTabBar(); + + ImGui::EndTable(); + } + ImGui::End(); +} + +static void DrawVFPU(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) { + ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("MIPS VFPU regs", &config.vfpuOpen)) { + ImGui::End(); + return; } + ImGui::Text("TODO"); ImGui::End(); } @@ -910,6 +953,8 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu if (lastCpuStepCount_ != Core_GetSteppingCounter()) { lastCpuStepCount_ = Core_GetSteppingCounter(); + snapshot_ = newSnapshot_; // Compare against the previous snapshot. + Snapshot(currentMIPS); disasm_.NotifyStep(); } @@ -988,7 +1033,9 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu } if (ImGui::BeginMenu("CPU")) { ImGui::MenuItem("CPU debugger", nullptr, &cfg_.disasmOpen); - ImGui::MenuItem("Registers", nullptr, &cfg_.regsOpen); + ImGui::MenuItem("GPR regs", nullptr, &cfg_.gprOpen); + ImGui::MenuItem("FPR regs", nullptr, &cfg_.fprOpen); + ImGui::MenuItem("VFPU regs", nullptr, &cfg_.vfpuOpen); ImGui::MenuItem("Callstacks", nullptr, &cfg_.callstackOpen); ImGui::MenuItem("Breakpoints", nullptr, &cfg_.breakpointsOpen); ImGui::MenuItem("Scheduler", nullptr, &cfg_.schedulerOpen); @@ -1049,8 +1096,16 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu disasm_.Draw(mipsDebug, cfg_, control, coreState); } - if (cfg_.regsOpen) { - DrawRegisterView(cfg_, control, mipsDebug); + if (cfg_.gprOpen) { + DrawGPRs(cfg_, control, mipsDebug, snapshot_); + } + + if (cfg_.fprOpen) { + DrawFPRs(cfg_, control, mipsDebug, snapshot_); + } + + if (cfg_.vfpuOpen) { + DrawVFPU(cfg_, control, mipsDebug, snapshot_); } if (cfg_.breakpointsOpen) { @@ -1156,8 +1211,14 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu } } -void ImDebugger::Snapshot() { - +void ImDebugger::Snapshot(MIPSState *mips) { + memcpy(newSnapshot_.gpr, mips->r, sizeof(newSnapshot_.gpr)); + memcpy(newSnapshot_.fpr, mips->fs, sizeof(newSnapshot_.fpr)); + memcpy(newSnapshot_.vpr, mips->v, sizeof(newSnapshot_.vpr)); + newSnapshot_.pc = mips->pc; + newSnapshot_.lo = mips->lo; + newSnapshot_.hi = mips->hi; + newSnapshot_.ll = mips->llBit; } void ImMemWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImControl &control, int index) { @@ -1450,7 +1511,9 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) { sync.SetSection(ini->GetOrCreateSection("Windows")); sync.Sync("disasmOpen", &disasmOpen, true); sync.Sync("demoOpen ", &demoOpen, false); - sync.Sync("regsOpen", ®sOpen, true); + sync.Sync("gprOpen", &gprOpen, false); + sync.Sync("fprOpen", &fprOpen, false); + sync.Sync("vfpuOpen", &vfpuOpen, false); sync.Sync("threadsOpen", &threadsOpen, false); sync.Sync("callstackOpen", &callstackOpen, false); sync.Sync("breakpointsOpen", &breakpointsOpen, false); diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index 62bd71b49c97..8b2e16739911 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -99,12 +99,25 @@ class ImMemWindow { u32 gotoAddr_ = 0x08800000; }; +// Snapshot of the MIPS CPU and other things we want to show diffs off. +struct ImSnapshotState { + u32 gpr[32]; + float fpr[32]; + float vpr[128]; + u32 pc; + u32 lo; + u32 hi; + u32 ll; +}; + struct ImConfig { // Defaults for saved settings are set in SyncConfig. bool disasmOpen; bool demoOpen; - bool regsOpen; + bool gprOpen; + bool fprOpen; + bool vfpuOpen; bool threadsOpen; bool callstackOpen; bool breakpointsOpen; @@ -173,7 +186,7 @@ class ImDebugger { // 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(); + void Snapshot(MIPSState *mips); private: Path ConfigPath(); @@ -186,6 +199,9 @@ class ImDebugger { ImMemWindow mem_[4]; // We support 4 separate instances of the memory viewer. ImStructViewer structViewer_; + ImSnapshotState newSnapshot_; + ImSnapshotState snapshot_; + int lastCpuStepCount_ = -1; int lastGpuStepCount_ = -1; diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index 96a3362ba0df..33638556782c 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -746,7 +746,7 @@ static const StateItem g_vertexState[] = { }; void ImGeStateWindow::Snapshot() { - + // Not needed for now, we have GPUStepping::LastState() } // TODO: Separate window or merge into Ge debugger? @@ -769,6 +769,8 @@ void ImGeStateWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterface bool sectionOpen = false; for (size_t i = 0; i < numRows; i++) { const GECmdInfo &info = GECmdInfoByCmd(rows[i].cmd); + const GPUgstate &lastState = GPUStepping::LastState(); + bool diff = lastState.cmdmem[rows[i].cmd] != gstate.cmdmem[rows[i].cmd]; if (rows[i].header) { anySection = true; @@ -788,31 +790,37 @@ void ImGeStateWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterface } const bool enabled = info.enableCmd == 0 || (gstate.cmdmem[info.enableCmd] & 1) == 1; - if (!enabled) + if (diff) { + ImGui::PushStyleColor(ImGuiCol_Text, enabled ? ImDebuggerColor_Diff : ImDebuggerColor_DiffAlpha); + } else if (!enabled) { ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128)); + } if (!rows[i].header) { ImGui::TextUnformatted(info.uiName); ImGui::TableNextColumn(); } if (rows[i].cmd != GE_CMD_NOP) { - char temp[256]; + char temp[128], temp2[128]; - const u32 value = gstate.cmdmem[info.cmd] & 0xFFFFFF; - const u32 otherValue = gstate.cmdmem[info.otherCmd] & 0xFFFFFF; - const u32 otherValue2 = gstate.cmdmem[info.otherCmd2] & 0xFFFFFF; + const u32 value = gstate.cmdmem[info.cmd]; + const u32 otherValue = gstate.cmdmem[info.otherCmd]; - // Special handling for pointer and pointer/width entries + // Special handling for pointer and pointer/width entries - create an address control 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); + FormatStateRow(gpuDebug, temp, sizeof(temp), info.fmt, value, true, otherValue, gstate.cmdmem[info.otherCmd2]); ImGui::TextUnformatted(temp); } + if (diff && ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort)) { + FormatStateRow(gpuDebug, temp2, sizeof(temp2), info.fmt, lastState.cmdmem[info.cmd], true, lastState.cmdmem[info.otherCmd], lastState.cmdmem[info.otherCmd2]); + ImGui::SetTooltip("Previous: %s", temp2); + } } - if (!enabled) + if (diff || !enabled) ImGui::PopStyleColor(); } if (sectionOpen) { diff --git a/UI/ImDebugger/ImGe.h b/UI/ImDebugger/ImGe.h index fda9d3e2bd52..895ba8964579 100644 --- a/UI/ImDebugger/ImGe.h +++ b/UI/ImDebugger/ImGe.h @@ -10,6 +10,9 @@ struct ImControl; class FramebufferManagerCommon; class TextureCacheCommon; +constexpr ImU32 ImDebuggerColor_Diff = IM_COL32(255, 96, 32, 255); +constexpr ImU32 ImDebuggerColor_DiffAlpha = IM_COL32(255, 96, 32, 128); + void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager); void DrawTexturesWindow(ImConfig &cfg, TextureCacheCommon *textureCache); void DrawDisplayWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager); diff --git a/ext/imgui/imgui_impl_thin3d.cpp b/ext/imgui/imgui_impl_thin3d.cpp index ab7b561546ba..4b1e90d69534 100644 --- a/ext/imgui/imgui_impl_thin3d.cpp +++ b/ext/imgui/imgui_impl_thin3d.cpp @@ -49,6 +49,10 @@ static BackendData *ImGui_ImplThin3d_GetBackendData() { // Render function void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *draw) { + if (!draw_data) { + // Possible race condition. + return; + } // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);