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.