From b3346df646664e3556b4602c9dca7dd7a448e3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 7 Dec 2024 16:05:55 +0100 Subject: [PATCH] ImDebugger: Add a window to inspect upcoming CoreTiming events --- Common/Common.vcxproj | 1 + Common/Common.vcxproj.filters | 3 +++ Common/Data/Collections/LinkedList.h | 6 +++++ Common/Serialize/Serializer.h | 7 +----- Core/CoreTiming.cpp | 24 +++++++----------- Core/CoreTiming.h | 28 +++++++++++++++++---- Core/System.cpp | 15 +++++++++++ Core/System.h | 2 ++ GPU/Debugger/Debugger.cpp | 21 ++++++++++++++++ GPU/Debugger/Debugger.h | 2 ++ UI/ImDebugger/ImDebugger.cpp | 37 +++++++++++++++++++++++++++- UI/ImDebugger/ImDebugger.h | 1 + UI/ImDebugger/ImGe.cpp | 14 +++++++++++ 13 files changed, 134 insertions(+), 27 deletions(-) create mode 100644 Common/Data/Collections/LinkedList.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 1eea1179031a..7674aa7b53fb 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -459,6 +459,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index f7d6ab2b9554..44e2ebe30726 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -674,6 +674,9 @@ Data\Collections + + Data\Collections + diff --git a/Common/Data/Collections/LinkedList.h b/Common/Data/Collections/LinkedList.h new file mode 100644 index 000000000000..f000b88587a4 --- /dev/null +++ b/Common/Data/Collections/LinkedList.h @@ -0,0 +1,6 @@ +#pragma once + +template +struct LinkedListItem : public T { + LinkedListItem *next; +}; diff --git a/Common/Serialize/Serializer.h b/Common/Serialize/Serializer.h index 4c1b4c050bad..e15ffd970418 100644 --- a/Common/Serialize/Serializer.h +++ b/Common/Serialize/Serializer.h @@ -37,17 +37,12 @@ #include "Common/CommonTypes.h" #include "Common/Log.h" #include "Common/File/Path.h" +#include "Common/Data/Collections/LinkedList.h" namespace File { class IOFile; }; -template -struct LinkedListItem : public T -{ - LinkedListItem *next; -}; - class PointerWrap; class PointerWrapSection diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 3e4f611b5ea9..c3cd6a604274 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -40,13 +40,7 @@ int CPU_HZ = 222000000; #define INITIAL_SLICE_LENGTH 20000 #define MAX_SLICE_LENGTH 100000000 -namespace CoreTiming -{ - -struct EventType { - TimedCallback callback; - const char *name; -}; +namespace CoreTiming { static std::vector event_types; // Only used during restore. @@ -54,14 +48,6 @@ static std::set usedEventTypes; static std::set restoredEventTypes; static int nextEventTypeRestoreId = -1; -struct BaseEvent { - s64 time; - u64 userdata; - int type; -}; - -typedef LinkedListItem Event; - Event *first; Event *eventPool = 0; @@ -120,6 +106,14 @@ u64 GetGlobalTimeUs() { return lastGlobalTimeUs + usSinceLast; } +const Event *GetFirstEvent() { + return first; +} + +const std::vector &GetEventTypes() { + return event_types; +} + Event* GetNewEvent() { if(!eventPool) diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index a520672976b8..bfad32cc62aa 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -18,7 +18,9 @@ #pragma once #include +#include #include "Common/CommonTypes.h" +#include "Common/Data/Collections/LinkedList.h" // This is a system to schedule events into the emulated machine's future. Time is measured // in main CPU clock cycles. @@ -70,14 +72,25 @@ inline s64 cyclesToUs(s64 cycles) { return (cycles * 1000000) / CPU_HZ; } -namespace CoreTiming -{ - void Init(); - void Shutdown(); - +namespace CoreTiming { typedef void (*MHzChangeCallback)(); typedef void (*TimedCallback)(u64 userdata, int cyclesLate); + struct EventType { + TimedCallback callback; + const char *name; + }; + + struct BaseEvent { + s64 time; + u64 userdata; + int type; + }; + typedef LinkedListItem Event; + + void Init(); + void Shutdown(); + u64 GetTicks(); u64 GetIdleTicks(); u64 GetGlobalTimeUs(); @@ -85,6 +98,7 @@ namespace CoreTiming // Returns the event_type identifier. int RegisterEvent(const char *name, TimedCallback callback); + // For save states. void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callback); void UnregisterAllEvents(); @@ -94,6 +108,8 @@ namespace CoreTiming void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata=0); s64 UnscheduleEvent(int event_type, u64 userdata); + const std::vector &GetEventTypes(); + const Event *GetFirstEvent(); void RemoveEvent(int event_type); bool IsScheduled(int event_type); void Advance(); @@ -116,6 +132,8 @@ namespace CoreTiming void SetClockFrequencyHz(int cpuHz); int GetClockFrequencyHz(); + + // TODO: Add accessors? extern int slicelength; }; // end of namespace diff --git a/Core/System.cpp b/Core/System.cpp index 4f60c4fc67b6..29189f5551a5 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -779,3 +779,18 @@ bool CreateSysDirectories() { } return true; } + +const char *CoreStateToString(CoreState state) { + switch (state) { + case CORE_RUNNING_CPU: return "RUNNING_CPU"; + case CORE_NEXTFRAME: return "NEXTFRAME"; + case CORE_STEPPING_CPU: return "STEPPING_CPU"; + case CORE_POWERUP: return "POWERUP"; + case CORE_POWERDOWN: return "POWERDOWN"; + case CORE_BOOT_ERROR: return "BOOT_ERROR"; + case CORE_RUNTIME_ERROR: return "RUNTIME_ERROR"; + case CORE_STEPPING_GE: return "STEPPING_GE"; + case CORE_RUNNING_GE: return "RUNNING_GE"; + default: return "N/A"; + } +} diff --git a/Core/System.h b/Core/System.h index e2f75f615614..a3b6c2e68515 100644 --- a/Core/System.h +++ b/Core/System.h @@ -130,6 +130,8 @@ enum CoreState { CORE_RUNNING_GE, }; +const char *CoreStateToString(CoreState state); + extern bool coreCollectDebugStats; extern volatile CoreState coreState; diff --git a/GPU/Debugger/Debugger.cpp b/GPU/Debugger/Debugger.cpp index c89c0b1c7c4b..0e9fc20097de 100644 --- a/GPU/Debugger/Debugger.cpp +++ b/GPU/Debugger/Debugger.cpp @@ -44,6 +44,22 @@ static uint32_t g_skipPcOnce = 0; static std::vector> restrictPrimRanges; static std::string restrictPrimRule; +const char *BreakNextToString(BreakNext next) { + switch (next) { + case BreakNext::NONE: return "NONE,"; + case BreakNext::OP: return "OP"; + case BreakNext::DRAW: return "DRAW"; + case BreakNext::TEX: return "TEX"; + case BreakNext::NONTEX: return "NONTEX"; + case BreakNext::FRAME: return "FRAME"; + case BreakNext::VSYNC: return "VSYNC"; + case BreakNext::PRIM: return "PRIM"; + case BreakNext::CURVE: return "CURVE"; + case BreakNext::COUNT: return "COUNT"; + default: return "N/A"; + } +} + static void Init() { if (!inited) { GPUBreakpoints::Init([](bool flag) { @@ -69,6 +85,10 @@ bool IsActive() { return active; } +BreakNext GetBreakNext() { + return breakNext; +} + void SetBreakNext(BreakNext next) { SetActive(true); breakNext = next; @@ -166,6 +186,7 @@ NotifyResult NotifyCommand(u32 pc) { } g_skipPcOnce = pc; + breakNext = BreakNext::NONE; return NotifyResult::Break; // new. caller will call GPUStepping::EnterStepping(). } diff --git a/GPU/Debugger/Debugger.h b/GPU/Debugger/Debugger.h index c41490ce1697..d7cfb7a7bf9e 100644 --- a/GPU/Debugger/Debugger.h +++ b/GPU/Debugger/Debugger.h @@ -39,6 +39,8 @@ bool IsActive(); void SetBreakNext(BreakNext next); void SetBreakCount(int c, bool relative = false); +BreakNext GetBreakNext(); +const char *BreakNextToString(BreakNext next); enum class NotifyResult { Execute, diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 5ddafafa8e0a..5f425ed00462 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -26,7 +26,7 @@ #include "Core/HLE/sceAudiocodec.h" #include "Core/HLE/sceMp3.h" #include "Core/HLE/AtracCtx.h" - +#include "Core/CoreTiming.h" // Threads window #include "Core/HLE/sceKernelThread.h" @@ -41,7 +41,36 @@ extern bool g_TakeScreenshot; +void DrawSchedulerView(ImConfig &cfg) { + ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("Event Scheduler", &cfg.schedulerOpen)) { + ImGui::End(); + return; + } + s64 ticks = CoreTiming::GetTicks(); + if (ImGui::BeginChild("event_list", ImVec2(300.0f, 0.0))) { + const CoreTiming::Event *event = CoreTiming::GetFirstEvent(); + while (event) { + ImGui::Text("%s (%lld)", CoreTiming::GetEventTypes()[event->type].name, event->time - ticks); + event = event->next; + } + ImGui::EndChild(); + } + ImGui::SameLine(); + if (ImGui::BeginChild("general")) { + ImGui::Text("CoreState: %s", CoreStateToString(coreState)); + ImGui::Text("downcount: %d", currentMIPS->downcount); + ImGui::Text("slicelength: %d", CoreTiming::slicelength); + ImGui::Text("Ticks: %lld", ticks); + ImGui::Text("Clock (MHz): %0.1f", (float)CoreTiming::GetClockFrequencyHz() / 1000000.0f); + ImGui::Text("Global time (us): %lld", CoreTiming::GetGlobalTimeUs()); + ImGui::EndChild(); + } + ImGui::End(); +} + void DrawRegisterView(MIPSDebugInterface *mipsDebug, bool *open) { + ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Registers", open)) { ImGui::End(); return; @@ -814,6 +843,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu ImGui::MenuItem("Registers", nullptr, &cfg_.regsOpen); ImGui::MenuItem("Callstacks", nullptr, &cfg_.callstackOpen); ImGui::MenuItem("Breakpoints", nullptr, &cfg_.breakpointsOpen); + ImGui::MenuItem("Scheduler", nullptr, &cfg_.schedulerOpen); ImGui::EndMenu(); } if (ImGui::BeginMenu("HLE")) { @@ -932,6 +962,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu if (cfg_.geStateOpen) { DrawGeStateWindow(cfg_, gpuDebug); } + + if (cfg_.schedulerOpen) { + DrawSchedulerView(cfg_); + } } void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, CoreState coreState) { @@ -1155,6 +1189,7 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) { sync.Sync("debugStatsOpen", &debugStatsOpen, false); sync.Sync("geDebuggerOpen", &geDebuggerOpen, false); sync.Sync("geStateOpen", &geStateOpen, false); + sync.Sync("schedulerOpen", &schedulerOpen, false); sync.SetSection(ini->GetOrCreateSection("Settings")); sync.Sync("displayLatched", &displayLatched, false); diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index 7566d9fc109c..5ab01e9feddd 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -81,6 +81,7 @@ struct ImConfig { bool debugStatsOpen; bool geDebuggerOpen; bool geStateOpen; + bool schedulerOpen; // HLE explorer settings // bool filterByUsed = true; diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index 1268b7c95b77..e79a583ddb67 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -252,6 +252,12 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) { // TODO: This doesn't work correctly. // GPUDebug::SetBreakNext(GPUDebug::BreakNext::FRAME); //} + + bool disableStepButtons = GPUDebug::GetBreakNext() != GPUDebug::BreakNext::NONE; + + if (disableStepButtons) { + ImGui::BeginDisabled(); + } ImGui::SameLine(); if (ImGui::Button("Tex")) { GPUDebug::SetBreakNext(GPUDebug::BreakNext::TEX); @@ -276,6 +282,9 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) { if (ImGui::Button("Single step")) { GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP); } + if (disableStepButtons) { + ImGui::EndDisabled(); + } // Line break if (ImGui::Button("Goto PC")) { @@ -290,6 +299,11 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, GPUDebugInterface *gpuDebug) { ImGui::EndPopup(); } + // Display any pending step event. + if (GPUDebug::GetBreakNext() != GPUDebug::BreakNext::NONE) { + ImGui::Text("Step pending (waiting for CPU): %s", GPUDebug::BreakNextToString(GPUDebug::GetBreakNext())); + } + // Let's display the current CLUT. // First, let's list any active display lists in the left column.