mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-11 09:05:38 +00:00
ImDebugger: Add a window to inspect upcoming CoreTiming events
This commit is contained in:
parent
3ca3a797c9
commit
b3346df646
@ -459,6 +459,7 @@
|
||||
<ClInclude Include="Data\Collections\CharQueue.h" />
|
||||
<ClInclude Include="Data\Collections\FixedSizeQueue.h" />
|
||||
<ClInclude Include="Data\Collections\Hashmaps.h" />
|
||||
<ClInclude Include="Data\Collections\LinkedList.h" />
|
||||
<ClInclude Include="Data\Collections\Slice.h" />
|
||||
<ClInclude Include="Data\Collections\ThreadSafeList.h" />
|
||||
<ClInclude Include="Data\Collections\TinySet.h" />
|
||||
|
@ -674,6 +674,9 @@
|
||||
<ClInclude Include="Data\Collections\CharQueue.h">
|
||||
<Filter>Data\Collections</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Data\Collections\LinkedList.h">
|
||||
<Filter>Data\Collections</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
|
6
Common/Data/Collections/LinkedList.h
Normal file
6
Common/Data/Collections/LinkedList.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
template <class T>
|
||||
struct LinkedListItem : public T {
|
||||
LinkedListItem<T> *next;
|
||||
};
|
@ -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 <class T>
|
||||
struct LinkedListItem : public T
|
||||
{
|
||||
LinkedListItem<T> *next;
|
||||
};
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
class PointerWrapSection
|
||||
|
@ -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<EventType> event_types;
|
||||
// Only used during restore.
|
||||
@ -54,14 +48,6 @@ static std::set<int> usedEventTypes;
|
||||
static std::set<int> restoredEventTypes;
|
||||
static int nextEventTypeRestoreId = -1;
|
||||
|
||||
struct BaseEvent {
|
||||
s64 time;
|
||||
u64 userdata;
|
||||
int type;
|
||||
};
|
||||
|
||||
typedef LinkedListItem<BaseEvent> Event;
|
||||
|
||||
Event *first;
|
||||
Event *eventPool = 0;
|
||||
|
||||
@ -120,6 +106,14 @@ u64 GetGlobalTimeUs() {
|
||||
return lastGlobalTimeUs + usSinceLast;
|
||||
}
|
||||
|
||||
const Event *GetFirstEvent() {
|
||||
return first;
|
||||
}
|
||||
|
||||
const std::vector<EventType> &GetEventTypes() {
|
||||
return event_types;
|
||||
}
|
||||
|
||||
Event* GetNewEvent()
|
||||
{
|
||||
if(!eventPool)
|
||||
|
@ -18,7 +18,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<BaseEvent> 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<EventType> &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
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +130,8 @@ enum CoreState {
|
||||
CORE_RUNNING_GE,
|
||||
};
|
||||
|
||||
const char *CoreStateToString(CoreState state);
|
||||
|
||||
extern bool coreCollectDebugStats;
|
||||
|
||||
extern volatile CoreState coreState;
|
||||
|
@ -44,6 +44,22 @@ static uint32_t g_skipPcOnce = 0;
|
||||
static std::vector<std::pair<int, int>> 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().
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -81,6 +81,7 @@ struct ImConfig {
|
||||
bool debugStatsOpen;
|
||||
bool geDebuggerOpen;
|
||||
bool geStateOpen;
|
||||
bool schedulerOpen;
|
||||
|
||||
// HLE explorer settings
|
||||
// bool filterByUsed = true;
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user