mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Merge pull request #19742 from hrydgard/more-debugger-work
Fix more GE debugger bugs, add break-on-count
This commit is contained in:
+12
-3
@@ -145,7 +145,8 @@ public:
|
||||
|
||||
// Hm. This might be really tricky to get to behave the same in both modes. Here we are in __KernelReschedule, CoreTiming::Advance, ProcessEvents, GeExecuteInterrupt, ... .... __RunOnePendingInterrupt
|
||||
// But not sure how much it will matter. The test pause2 hits here.
|
||||
gpu->ProcessDLQueue();
|
||||
DLResult result = gpu->ProcessDLQueue();
|
||||
_dbg_assert_(result != DLResult::DebugBreak);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -184,8 +185,16 @@ public:
|
||||
|
||||
// TODO: This is called from __KernelReturnFromInterrupt which does a bunch of stuff afterwards.
|
||||
// Using hleSplitSyscallOverGe here breaks the gpu/signals/suspend.prx test, for that reason.
|
||||
// So we just process inline and sacrifice debuggability a little.
|
||||
gpu->ProcessDLQueue();
|
||||
// However, it's still useful when debugging, and I believe the scheduling difference is quite insignificant -
|
||||
// we are already being extremely inaccurate by blasting the full display list here instead of running
|
||||
// it in the background in parallel with the CPU.
|
||||
// So, when debugging is active, we'll just use hleSplitSyscallOverGe.
|
||||
if (gpu->ShouldSplitOverGe()) {
|
||||
hleSplitSyscallOverGe();
|
||||
} else {
|
||||
DLResult result = gpu->ProcessDLQueue();
|
||||
_dbg_assert_(result != DLResult::DebugBreak);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -248,9 +248,10 @@ public:
|
||||
virtual void ClearBreakNext() = 0;
|
||||
virtual void SetBreakNext(GPUDebug::BreakNext next) = 0 ;
|
||||
virtual void SetBreakCount(int c, bool relative = false) = 0 ;
|
||||
virtual GPUDebug::BreakNext GetBreakNext() = 0 ;
|
||||
virtual GPUDebug::BreakNext GetBreakNext() const = 0;
|
||||
virtual int GetBreakCount() const = 0;
|
||||
virtual bool SetRestrictPrims(std::string_view rule) = 0 ;
|
||||
virtual const char *GetRestrictPrims() = 0 ;
|
||||
virtual std::string_view GetRestrictPrims() = 0;
|
||||
|
||||
virtual GPURecord::Recorder *GetRecorder() = 0;
|
||||
virtual GPUBreakpoints *GetBreakpoints() = 0;
|
||||
|
||||
@@ -193,6 +193,7 @@ void IndexGenerator::AddStrip(int numVerts, int indexOffset, bool clockwise) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// God of War uses this for text. Otherwise rare, not much reason to optimize.
|
||||
void IndexGenerator::AddFan(int numVerts, int indexOffset, bool clockwise) {
|
||||
const int numTris = numVerts - 2;
|
||||
u16 *outInds = inds_;
|
||||
|
||||
@@ -36,7 +36,9 @@ const char *BreakNextToString(BreakNext next) {
|
||||
case BreakNext::VSYNC: return "VSYNC";
|
||||
case BreakNext::PRIM: return "PRIM";
|
||||
case BreakNext::CURVE: return "CURVE";
|
||||
case BreakNext::BLOCK_TRANSFER: return "BLOCK_TRANSFER";
|
||||
case BreakNext::COUNT: return "COUNT";
|
||||
case BreakNext::DEBUG_RUN: return "DEBUG_RUN";
|
||||
default: return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ enum class BreakNext {
|
||||
VSYNC,
|
||||
PRIM,
|
||||
CURVE,
|
||||
BLOCK_TRANSFER,
|
||||
DEBUG_RUN, // This is just running as normal, but with debug instrumentation.
|
||||
COUNT,
|
||||
};
|
||||
|
||||
|
||||
+53
-49
@@ -621,9 +621,9 @@ void GPUCommon::PSPFrame() {
|
||||
dumpThisFrame_ = false;
|
||||
}
|
||||
|
||||
if (breakNext == GPUDebug::BreakNext::VSYNC) {
|
||||
if (breakNext_ == GPUDebug::BreakNext::VSYNC) {
|
||||
// Just start stepping as soon as we can once the vblank finishes.
|
||||
breakNext = GPUDebug::BreakNext::OP;
|
||||
breakNext_ = GPUDebug::BreakNext::OP;
|
||||
}
|
||||
recorder_.NotifyBeginFrame();
|
||||
}
|
||||
@@ -2008,22 +2008,18 @@ bool GPUCommon::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
}
|
||||
|
||||
bool GPUCommon::NeedsSlowInterpreter() const {
|
||||
return breakNext != GPUDebug::BreakNext::NONE;
|
||||
return breakNext_ != GPUDebug::BreakNext::NONE;
|
||||
}
|
||||
|
||||
void GPUCommon::ClearBreakNext() {
|
||||
breakNext = GPUDebug::BreakNext::NONE;
|
||||
breakAtCount = -1;
|
||||
breakNext_ = GPUDebug::BreakNext::NONE;
|
||||
breakAtCount_ = -1;
|
||||
GPUStepping::ResumeFromStepping();
|
||||
}
|
||||
|
||||
GPUDebug::BreakNext GPUCommon::GetBreakNext() {
|
||||
return breakNext;
|
||||
}
|
||||
|
||||
void GPUCommon::SetBreakNext(GPUDebug::BreakNext next) {
|
||||
breakNext = next;
|
||||
breakAtCount = -1;
|
||||
breakNext_ = next;
|
||||
breakAtCount_ = -1;
|
||||
switch (next) {
|
||||
case GPUDebug::BreakNext::TEX:
|
||||
breakpoints_.AddTextureChangeTempBreakpoint();
|
||||
@@ -2034,6 +2030,7 @@ void GPUCommon::SetBreakNext(GPUDebug::BreakNext next) {
|
||||
breakpoints_.AddCmdBreakpoint(GE_CMD_BEZIER, true);
|
||||
breakpoints_.AddCmdBreakpoint(GE_CMD_SPLINE, true);
|
||||
breakpoints_.AddCmdBreakpoint(GE_CMD_VAP, true);
|
||||
breakpoints_.AddCmdBreakpoint(GE_CMD_TRANSFERSTART, true); // We count block transfers as prims, too.
|
||||
break;
|
||||
case GPUDebug::BreakNext::CURVE:
|
||||
breakpoints_.AddCmdBreakpoint(GE_CMD_BEZIER, true);
|
||||
@@ -2044,6 +2041,9 @@ void GPUCommon::SetBreakNext(GPUDebug::BreakNext next) {
|
||||
// This will take us to the following actual draw.
|
||||
primAfterDraw_ = true;
|
||||
break;
|
||||
case GPUDebug::BreakNext::BLOCK_TRANSFER:
|
||||
breakpoints_.AddCmdBreakpoint(GE_CMD_TRANSFERSTART, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2055,9 +2055,9 @@ void GPUCommon::SetBreakNext(GPUDebug::BreakNext next) {
|
||||
|
||||
void GPUCommon::SetBreakCount(int c, bool relative) {
|
||||
if (relative) {
|
||||
breakAtCount = primsThisFrame + c;
|
||||
breakAtCount_ = primsThisFrame_ + c;
|
||||
} else {
|
||||
breakAtCount = c;
|
||||
breakAtCount_ = c;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2066,20 +2066,25 @@ GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoi
|
||||
|
||||
u32 op = Memory::ReadUnchecked_U32(pc);
|
||||
u32 cmd = op >> 24;
|
||||
if (thisFlipNum != gpuStats.numFlips) {
|
||||
primsLastFrame = primsThisFrame;
|
||||
primsThisFrame = 0;
|
||||
thisFlipNum = gpuStats.numFlips;
|
||||
if (thisFlipNum_ != gpuStats.numFlips) {
|
||||
primsLastFrame_ = primsThisFrame_;
|
||||
primsThisFrame_ = 0;
|
||||
thisFlipNum_ = gpuStats.numFlips;
|
||||
}
|
||||
|
||||
bool process = true;
|
||||
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE || cmd == GE_CMD_VAP) {
|
||||
primsThisFrame++;
|
||||
bool isPrim = false;
|
||||
|
||||
if (!restrictPrimRanges.empty()) {
|
||||
bool process = true; // Process is only for the restrictPrimRanges functionality
|
||||
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE || cmd == GE_CMD_VAP || cmd == GE_CMD_TRANSFERSTART) { // VAP is immediate mode prims.
|
||||
isPrim = true;
|
||||
primsThisFrame_++;
|
||||
|
||||
// TODO: Should restricted prim ranges also avoid breakpoints?
|
||||
|
||||
if (!restrictPrimRanges_.empty()) {
|
||||
process = false;
|
||||
for (const auto &range : restrictPrimRanges) {
|
||||
if (primsThisFrame >= range.first && primsThisFrame <= range.second) {
|
||||
for (const auto &range : restrictPrimRanges_) {
|
||||
if ((primsThisFrame_ + 1) >= range.first && (primsThisFrame_ + 1) <= range.second) {
|
||||
process = true;
|
||||
break;
|
||||
}
|
||||
@@ -2087,27 +2092,29 @@ GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoi
|
||||
}
|
||||
}
|
||||
|
||||
bool isBreakpoint = false;
|
||||
if (breakNext == BreakNext::OP) {
|
||||
isBreakpoint = true;
|
||||
} else if (breakNext == BreakNext::COUNT) {
|
||||
isBreakpoint = primsThisFrame == breakAtCount;
|
||||
bool debugBreak = false;
|
||||
if (breakNext_ == BreakNext::OP) {
|
||||
debugBreak = true;
|
||||
} else if (breakNext_ == BreakNext::COUNT) {
|
||||
debugBreak = primsThisFrame_ == breakAtCount_;
|
||||
} else if (breakpoints->HasBreakpoints()) {
|
||||
isBreakpoint = breakpoints->IsBreakpoint(pc, op);
|
||||
debugBreak = breakpoints->IsBreakpoint(pc, op);
|
||||
}
|
||||
|
||||
if (isBreakpoint && pc == g_skipPcOnce) {
|
||||
INFO_LOG(Log::GeDebugger, "Skipping GE break at %08x (last break was here)", g_skipPcOnce);
|
||||
g_skipPcOnce = 0;
|
||||
if (debugBreak && pc == skipPcOnce_) {
|
||||
INFO_LOG(Log::GeDebugger, "Skipping GE break at %08x (last break was here)", skipPcOnce_);
|
||||
skipPcOnce_ = 0;
|
||||
if (isPrim)
|
||||
primsThisFrame_--; // Compensate for the wrong increment above - we didn't run anything.
|
||||
return process ? NotifyResult::Execute : NotifyResult::Skip;
|
||||
}
|
||||
g_skipPcOnce = 0;
|
||||
skipPcOnce_ = 0;
|
||||
|
||||
if (isBreakpoint) {
|
||||
if (debugBreak) {
|
||||
breakpoints->ClearTempBreakpoints();
|
||||
|
||||
if (coreState == CORE_POWERDOWN) {
|
||||
breakNext = BreakNext::NONE;
|
||||
breakNext_ = BreakNext::NONE;
|
||||
return process ? NotifyResult::Execute : NotifyResult::Skip;
|
||||
}
|
||||
|
||||
@@ -2115,9 +2122,10 @@ GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoi
|
||||
auto info = DisassembleOp(pc, op);
|
||||
NOTICE_LOG(Log::GeDebugger, "Waiting at %08x, %s", pc, info.desc.c_str());
|
||||
|
||||
g_skipPcOnce = pc;
|
||||
breakNext = BreakNext::NONE;
|
||||
return NotifyResult::Break; // new. caller will call GPUStepping::EnterStepping().
|
||||
skipPcOnce_ = pc;
|
||||
breakNext_ = BreakNext::NONE;
|
||||
// Not incrementing the prim counter!
|
||||
return NotifyResult::Break; // caller will call GPUStepping::EnterStepping().
|
||||
}
|
||||
|
||||
return process ? NotifyResult::Execute : NotifyResult::Skip;
|
||||
@@ -2125,7 +2133,7 @@ GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoi
|
||||
|
||||
void GPUCommon::NotifyFlush() {
|
||||
using namespace GPUDebug;
|
||||
if (breakNext == BreakNext::DRAW && !GPUStepping::IsStepping()) {
|
||||
if (breakNext_ == BreakNext::DRAW && !GPUStepping::IsStepping()) {
|
||||
// Break on the first PRIM after a flush.
|
||||
if (primAfterDraw_) {
|
||||
NOTICE_LOG(Log::GeDebugger, "Flush detected, breaking at next PRIM");
|
||||
@@ -2138,28 +2146,24 @@ void GPUCommon::NotifyFlush() {
|
||||
|
||||
void GPUCommon::NotifyDisplay(u32 framebuf, u32 stride, int format) {
|
||||
using namespace GPUDebug;
|
||||
if (breakNext == BreakNext::FRAME) {
|
||||
if (breakNext_ == BreakNext::FRAME) {
|
||||
// Start stepping at the first op of the new frame.
|
||||
breakNext = BreakNext::OP;
|
||||
breakNext_ = BreakNext::OP;
|
||||
}
|
||||
recorder_.NotifyDisplay(framebuf, stride, format);
|
||||
}
|
||||
|
||||
bool GPUCommon::SetRestrictPrims(std::string_view rule) {
|
||||
if (rule.empty() || rule == "*") {
|
||||
restrictPrimRanges.clear();
|
||||
restrictPrimRule.clear();
|
||||
restrictPrimRanges_.clear();
|
||||
restrictPrimRule_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GPUDebug::ParsePrimRanges(rule, &restrictPrimRanges)) {
|
||||
restrictPrimRule = rule;
|
||||
if (GPUDebug::ParsePrimRanges(rule, &restrictPrimRanges_)) {
|
||||
restrictPrimRule_ = rule;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char *GPUCommon::GetRestrictPrims() {
|
||||
return restrictPrimRule.c_str();
|
||||
}
|
||||
|
||||
+19
-12
@@ -388,15 +388,22 @@ public:
|
||||
void ClearBreakNext() override;
|
||||
void SetBreakNext(GPUDebug::BreakNext next) override;
|
||||
void SetBreakCount(int c, bool relative = false) override;
|
||||
GPUDebug::BreakNext GetBreakNext() override;
|
||||
GPUDebug::BreakNext GetBreakNext() const override {
|
||||
return breakNext_;
|
||||
}
|
||||
int GetBreakCount() const override {
|
||||
return breakAtCount_;
|
||||
}
|
||||
bool SetRestrictPrims(std::string_view rule) override;
|
||||
const char *GetRestrictPrims() override;
|
||||
std::string_view GetRestrictPrims() override {
|
||||
return restrictPrimRule_;
|
||||
}
|
||||
|
||||
int PrimsThisFrame() const override {
|
||||
return primsThisFrame;
|
||||
return primsThisFrame_;
|
||||
}
|
||||
int PrimsLastFrame() const override {
|
||||
return primsLastFrame;
|
||||
return primsLastFrame_;
|
||||
}
|
||||
|
||||
void NotifyFlush();
|
||||
@@ -536,19 +543,19 @@ protected:
|
||||
GPURecord::Recorder recorder_;
|
||||
GPUBreakpoints breakpoints_;
|
||||
|
||||
GPUDebug::BreakNext breakNext = GPUDebug::BreakNext::NONE;
|
||||
int breakAtCount = -1;
|
||||
GPUDebug::BreakNext breakNext_ = GPUDebug::BreakNext::NONE;
|
||||
int breakAtCount_ = -1;
|
||||
|
||||
int primsLastFrame = 0;
|
||||
int primsThisFrame = 0;
|
||||
int thisFlipNum = 0;
|
||||
int primsLastFrame_ = 0;
|
||||
int primsThisFrame_ = 0;
|
||||
int thisFlipNum_ = 0;
|
||||
|
||||
bool primAfterDraw_ = false;
|
||||
|
||||
uint32_t g_skipPcOnce = 0;
|
||||
uint32_t skipPcOnce_ = 0;
|
||||
|
||||
std::vector<std::pair<int, int>> restrictPrimRanges;
|
||||
std::string restrictPrimRule;
|
||||
std::vector<std::pair<int, int>> restrictPrimRanges_;
|
||||
std::string restrictPrimRule_;
|
||||
|
||||
private:
|
||||
void DoExecuteCall(u32 target);
|
||||
|
||||
+2
-2
@@ -1067,8 +1067,8 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
|
||||
|
||||
uint32_t vtypeCheckMask = g_Config.bSoftwareSkinning ? (~GE_VTYPE_WEIGHTCOUNT_MASK) : 0xFFFFFFFF;
|
||||
|
||||
if (debugRecording_)
|
||||
goto bail;
|
||||
if (!useFastRunLoop_)
|
||||
goto bail; // we're either recording or stepping.
|
||||
|
||||
while (src != stall) {
|
||||
uint32_t data = *src;
|
||||
|
||||
@@ -151,6 +151,8 @@ struct ImConfig {
|
||||
int selectedMemCheck = -1;
|
||||
uint64_t selectedTexAddr = 0;
|
||||
|
||||
int breakCount = 0;
|
||||
|
||||
bool displayLatched = false;
|
||||
|
||||
// We use a separate ini file from the main PPSSPP config.
|
||||
|
||||
+173
-116
@@ -751,7 +751,19 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
|
||||
|
||||
ImGui::BeginDisabled(coreState != CORE_STEPPING_GE);
|
||||
if (ImGui::Button("Run/Resume")) {
|
||||
Core_Resume();
|
||||
// Core_Resume();
|
||||
gpuDebug->SetBreakNext(GPUDebug::BreakNext::DEBUG_RUN);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("...")) {
|
||||
ImGui::OpenPopup("dotdotdot");
|
||||
}
|
||||
if (ImGui::BeginPopup("dotdotdot")) {
|
||||
if (ImGui::MenuItem("RunFast")) {
|
||||
gpuDebug->ClearBreakNext();
|
||||
Core_Resume();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
@@ -762,7 +774,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
|
||||
// GPUDebug::SetBreakNext(GPUDebug::BreakNext::FRAME);
|
||||
//}
|
||||
|
||||
bool disableStepButtons = gpuDebug->GetBreakNext() != GPUDebug::BreakNext::NONE;
|
||||
bool disableStepButtons = gpuDebug->GetBreakNext() != GPUDebug::BreakNext::NONE && gpuDebug->GetBreakNext() != GPUDebug::BreakNext::DEBUG_RUN;
|
||||
|
||||
if (disableStepButtons) {
|
||||
ImGui::BeginDisabled();
|
||||
@@ -784,6 +796,10 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
|
||||
gpuDebug->SetBreakNext(GPUDebug::BreakNext::DRAW);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Block xfer")) {
|
||||
gpuDebug->SetBreakNext(GPUDebug::BreakNext::BLOCK_TRANSFER);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Curve")) {
|
||||
gpuDebug->SetBreakNext(GPUDebug::BreakNext::CURVE);
|
||||
}
|
||||
@@ -795,18 +811,36 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
// TODO: this doesn't seem very accurate? strange double-increments when stepping.
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%d/%d", gpuDebug->PrimsThisFrame(), gpuDebug->PrimsLastFrame());
|
||||
|
||||
// TODO: Break on count!
|
||||
if (disableStepButtons) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(160.0f);
|
||||
ImGui::InputInt("Number", &cfg.breakCount);
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Break on #")) {
|
||||
gpuDebug->SetBreakNext(GPUDebug::BreakNext::COUNT);
|
||||
gpuDebug->SetBreakCount(cfg.breakCount);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Step by")) {
|
||||
gpuDebug->SetBreakNext(GPUDebug::BreakNext::COUNT);
|
||||
gpuDebug->SetBreakCount(cfg.breakCount, true); // relative
|
||||
}
|
||||
if (disableStepButtons) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
// Line break
|
||||
if (ImGui::Button("Goto PC")) {
|
||||
disasmView_.GotoPC();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Settings")) {
|
||||
if (ImGui::Button("Settings")) {
|
||||
ImGui::OpenPopup("disSettings");
|
||||
}
|
||||
if (ImGui::BeginPopup("disSettings")) {
|
||||
@@ -815,13 +849,17 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
|
||||
}
|
||||
|
||||
// Display any pending step event.
|
||||
if (gpuDebug->GetBreakNext() != GPUDebug::BreakNext::NONE) {
|
||||
if (gpuDebug->GetBreakNext() != GPUDebug::BreakNext::NONE && gpuDebug->GetBreakNext() != GPUDebug::BreakNext::DEBUG_RUN) {
|
||||
if (showBannerInFrames_ > 0) {
|
||||
showBannerInFrames_--;
|
||||
}
|
||||
if (showBannerInFrames_ == 0) {
|
||||
ImGui::Text("Step pending (waiting for CPU): %s", GPUDebug::BreakNextToString(gpuDebug->GetBreakNext()));
|
||||
ImGui::SameLine();
|
||||
if (gpuDebug->GetBreakNext() == GPUDebug::BreakNext::COUNT) {
|
||||
ImGui::Text("(%d)", gpuDebug->GetBreakCount());
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (ImGui::Button("Cancel step")) {
|
||||
gpuDebug->ClearBreakNext();
|
||||
}
|
||||
@@ -870,20 +908,30 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
|
||||
|
||||
u32 op = 0;
|
||||
DisplayList list;
|
||||
bool isOnBlockTransfer = false;
|
||||
if (gpuDebug->GetCurrentDisplayList(list)) {
|
||||
op = Memory::Read_U32(list.pc);
|
||||
|
||||
// TODO: Also add support for block transfer previews!
|
||||
|
||||
bool isOnPrim = (op >> 24) == GE_CMD_PRIM;
|
||||
if (isOnPrim) {
|
||||
bool isOnPrim = false;
|
||||
switch (op >> 24) {
|
||||
case GE_CMD_PRIM:
|
||||
isOnPrim = true;
|
||||
if (reloadPreview_) {
|
||||
GetPrimPreview(op, previewPrim_, previewVertices_, previewIndices_, previewCount_);
|
||||
reloadPreview_ = false;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
case GE_CMD_TRANSFERSTART:
|
||||
isOnBlockTransfer = true;
|
||||
break;
|
||||
default:
|
||||
// Disable the current preview.
|
||||
previewCount_ = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImGui::BeginChild("texture/fb view"); // Leave room for 1 line below us
|
||||
@@ -891,125 +939,134 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
|
||||
ImDrawList *drawList = ImGui::GetWindowDrawList();
|
||||
|
||||
if (coreState == CORE_STEPPING_GE) {
|
||||
VirtualFramebuffer *vfb = rbViewer_.vfb;
|
||||
if (vfb) {
|
||||
if (vfb->fbo) {
|
||||
ImGui::Text("Framebuffer: %s", vfb->fbo->Tag());
|
||||
} else {
|
||||
ImGui::Text("Framebuffer");
|
||||
if (isOnBlockTransfer) {
|
||||
ImGui::Text("Block transfer! Proper preview coming in the future.\n");
|
||||
ImGui::Text("%08x -> %08x, %d bpp (strides: %d, %d)", gstate.getTransferSrcAddress(), gstate.getTransferDstAddress(), gstate.getTransferBpp(), gstate.getTransferSrcStride(), gstate.getTransferDstStride());
|
||||
ImGui::Text("%dx%d pixels", gstate.getTransferWidth(), gstate.getTransferHeight());
|
||||
ImGui::Text("Src pos: %d, %d", gstate.getTransferSrcX(), gstate.getTransferSrcY());
|
||||
ImGui::Text("Dst pos: %d, %d", gstate.getTransferDstX(), gstate.getTransferDstY());
|
||||
ImGui::Text("Total bytes to transfer: %d", gstate.getTransferWidth() * gstate.getTransferHeight() * gstate.getTransferBpp());
|
||||
} else {
|
||||
// Visualize prim by default (even if we're not directly on a prim instruction).
|
||||
VirtualFramebuffer *vfb = rbViewer_.vfb;
|
||||
if (vfb) {
|
||||
if (vfb->fbo) {
|
||||
ImGui::Text("Framebuffer: %s", vfb->fbo->Tag());
|
||||
} else {
|
||||
ImGui::Text("Framebuffer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use selectable instead of tab bar so we can get events (haven't figured that out).
|
||||
static const Draw::Aspect aspects[3] = { Draw::Aspect::COLOR_BIT, Draw::Aspect::DEPTH_BIT, Draw::Aspect::STENCIL_BIT, };
|
||||
static const char *const aspectNames[3] = { "Color", "Depth", "Stencil" };
|
||||
for (int i = 0; i < ARRAY_SIZE(aspects); i++) {
|
||||
if (i != 0)
|
||||
// Use selectable instead of tab bar so we can get events (haven't figured that out).
|
||||
static const Draw::Aspect aspects[3] = { Draw::Aspect::COLOR_BIT, Draw::Aspect::DEPTH_BIT, Draw::Aspect::STENCIL_BIT, };
|
||||
static const char *const aspectNames[3] = { "Color", "Depth", "Stencil" };
|
||||
for (int i = 0; i < ARRAY_SIZE(aspects); i++) {
|
||||
if (i != 0)
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable(aspectNames[i], aspects[i] == selectedAspect_, 0, ImVec2(120.0f, 0.0f))) {
|
||||
selectedAspect_ = aspects[i];
|
||||
NotifyStep();
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedAspect_ == Draw::Aspect::DEPTH_BIT) {
|
||||
float minimum = 0.5f;
|
||||
float maximum = 256.0f;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable(aspectNames[i], aspects[i] == selectedAspect_, 0, ImVec2(120.0f, 0.0f))) {
|
||||
selectedAspect_ = aspects[i];
|
||||
NotifyStep();
|
||||
ImGui::SetNextItemWidth(200.0f);
|
||||
if (ImGui::DragFloat("Z scale", &rbViewer_.scale, 1.0f, 0.5f, 256.0f, "%0.2f", ImGuiSliderFlags_Logarithmic)) {
|
||||
rbViewer_.Snapshot();
|
||||
swViewer_.Snapshot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedAspect_ == Draw::Aspect::DEPTH_BIT) {
|
||||
float minimum = 0.5f;
|
||||
float maximum = 256.0f;
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(200.0f);
|
||||
if (ImGui::DragFloat("Z scale", &rbViewer_.scale, 1.0f, 0.5f, 256.0f, "%0.2f", ImGuiSliderFlags_Logarithmic)) {
|
||||
rbViewer_.Snapshot();
|
||||
swViewer_.Snapshot();
|
||||
}
|
||||
}
|
||||
|
||||
const ImVec2 p0 = ImGui::GetCursorScreenPos();
|
||||
ImVec2 p1;
|
||||
float scale = 1.0f;
|
||||
if (vfb && vfb->fbo) {
|
||||
scale = vfb->renderScaleFactor;
|
||||
p1 = ImVec2(p0.x + vfb->fbo->Width(), p0.y + vfb->fbo->Height());
|
||||
} else {
|
||||
// Guess
|
||||
p1 = ImVec2(p0.x + swViewer_.width, p0.y + swViewer_.height);
|
||||
}
|
||||
|
||||
// Draw border and background color
|
||||
drawList->PushClipRect(p0, p1, true);
|
||||
|
||||
PixelLookup *lookup = nullptr;
|
||||
if (vfb) {
|
||||
rbViewer_.Draw(gpuDebug, draw);
|
||||
lookup = &rbViewer_;
|
||||
// ImTextureID texId = ImGui_ImplThin3d_AddFBAsTextureTemp(vfb->fbo, Draw::Aspect::COLOR_BIT, ImGuiPipeline::TexturedOpaque);
|
||||
// ImGui::Image(texId, ImVec2(vfb->width, vfb->height));
|
||||
} else {
|
||||
swViewer_.Draw(gpuDebug, draw);
|
||||
lookup = &swViewer_;
|
||||
}
|
||||
|
||||
// Draw vertex preview on top!
|
||||
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, false, scale, scale);
|
||||
|
||||
drawList->PopClipRect();
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
int x = (int)(ImGui::GetMousePos().x - p0.x);
|
||||
int y = (int)(ImGui::GetMousePos().y - p0.y);
|
||||
char temp[128];
|
||||
if (lookup->FormatValueAt(temp, sizeof(temp), x, y)) {
|
||||
ImGui::Text("(%d, %d): %s", x, y, temp);
|
||||
const ImVec2 p0 = ImGui::GetCursorScreenPos();
|
||||
ImVec2 p1;
|
||||
float scale = 1.0f;
|
||||
if (vfb && vfb->fbo) {
|
||||
scale = vfb->renderScaleFactor;
|
||||
p1 = ImVec2(p0.x + vfb->fbo->Width(), p0.y + vfb->fbo->Height());
|
||||
} else {
|
||||
ImGui::Text("%d, %d: N/A");
|
||||
// Guess
|
||||
p1 = ImVec2(p0.x + swViewer_.width, p0.y + swViewer_.height);
|
||||
}
|
||||
} else {
|
||||
ImGui::TextUnformatted("(no pixel hovered)");
|
||||
}
|
||||
|
||||
if (vfb && vfb->fbo) {
|
||||
ImGui::Text("VFB %dx%d (emulated: %dx%d)", vfb->width, vfb->height, vfb->fbo->Width(), vfb->fbo->Height());
|
||||
} else {
|
||||
// Use the swViewer_!
|
||||
ImGui::Text("Raw FB: %08x (%s)", gstate.getFrameBufRawAddress(), GeBufferFormatToString(gstate.FrameBufFormat()));
|
||||
}
|
||||
// Draw border and background color
|
||||
drawList->PushClipRect(p0, p1, true);
|
||||
|
||||
if (gstate.isModeClear()) {
|
||||
ImGui::Text("(clear mode - texturing not used)");
|
||||
} else if (!gstate.isTextureMapEnabled()) {
|
||||
ImGui::Text("(texturing not enabled");
|
||||
} else {
|
||||
TextureCacheCommon *texcache = gpuDebug->GetTextureCacheCommon();
|
||||
TexCacheEntry *tex = texcache ? texcache->SetTexture() : nullptr;
|
||||
if (tex) {
|
||||
ImGui::Text("Texture: ");
|
||||
texcache->ApplyTexture();
|
||||
|
||||
void *nativeView = texcache->GetNativeTextureView(tex, true);
|
||||
ImTextureID texId = ImGui_ImplThin3d_AddNativeTextureTemp(nativeView);
|
||||
|
||||
float texW = dimWidth(tex->dim);
|
||||
float texH = dimHeight(tex->dim);
|
||||
|
||||
const ImVec2 p0 = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 sz = ImGui::GetContentRegionAvail();
|
||||
const ImVec2 p1 = ImVec2(p0.x + texW, p0.y + texH);
|
||||
|
||||
// Draw border and background color
|
||||
drawList->PushClipRect(p0, p1, true);
|
||||
|
||||
ImGui::Image(texId, ImVec2(texW, texH));
|
||||
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, true, texW, texH);
|
||||
|
||||
drawList->PopClipRect();
|
||||
PixelLookup *lookup = nullptr;
|
||||
if (vfb) {
|
||||
rbViewer_.Draw(gpuDebug, draw);
|
||||
lookup = &rbViewer_;
|
||||
// ImTextureID texId = ImGui_ImplThin3d_AddFBAsTextureTemp(vfb->fbo, Draw::Aspect::COLOR_BIT, ImGuiPipeline::TexturedOpaque);
|
||||
// ImGui::Image(texId, ImVec2(vfb->width, vfb->height));
|
||||
} else {
|
||||
ImGui::Text("(no valid texture bound)");
|
||||
// In software mode, we should just decode the texture here.
|
||||
// TODO: List some of the texture params here.
|
||||
swViewer_.Draw(gpuDebug, draw);
|
||||
lookup = &swViewer_;
|
||||
}
|
||||
|
||||
// Draw vertex preview on top!
|
||||
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, false, scale, scale);
|
||||
|
||||
drawList->PopClipRect();
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
int x = (int)(ImGui::GetMousePos().x - p0.x);
|
||||
int y = (int)(ImGui::GetMousePos().y - p0.y);
|
||||
char temp[128];
|
||||
if (lookup->FormatValueAt(temp, sizeof(temp), x, y)) {
|
||||
ImGui::Text("(%d, %d): %s", x, y, temp);
|
||||
} else {
|
||||
ImGui::Text("%d, %d: N/A");
|
||||
}
|
||||
} else {
|
||||
ImGui::TextUnformatted("(no pixel hovered)");
|
||||
}
|
||||
|
||||
if (vfb && vfb->fbo) {
|
||||
ImGui::Text("VFB %dx%d (emulated: %dx%d)", vfb->width, vfb->height, vfb->fbo->Width(), vfb->fbo->Height());
|
||||
} else {
|
||||
// Use the swViewer_!
|
||||
ImGui::Text("Raw FB: %08x (%s)", gstate.getFrameBufRawAddress(), GeBufferFormatToString(gstate.FrameBufFormat()));
|
||||
}
|
||||
|
||||
if (gstate.isModeClear()) {
|
||||
ImGui::Text("(clear mode - texturing not used)");
|
||||
} else if (!gstate.isTextureMapEnabled()) {
|
||||
ImGui::Text("(texturing not enabled");
|
||||
} else {
|
||||
TextureCacheCommon *texcache = gpuDebug->GetTextureCacheCommon();
|
||||
TexCacheEntry *tex = texcache ? texcache->SetTexture() : nullptr;
|
||||
if (tex) {
|
||||
ImGui::Text("Texture: ");
|
||||
texcache->ApplyTexture();
|
||||
|
||||
void *nativeView = texcache->GetNativeTextureView(tex, true);
|
||||
ImTextureID texId = ImGui_ImplThin3d_AddNativeTextureTemp(nativeView);
|
||||
|
||||
float texW = dimWidth(tex->dim);
|
||||
float texH = dimHeight(tex->dim);
|
||||
|
||||
const ImVec2 p0 = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 sz = ImGui::GetContentRegionAvail();
|
||||
const ImVec2 p1 = ImVec2(p0.x + texW, p0.y + texH);
|
||||
|
||||
// Draw border and background color
|
||||
drawList->PushClipRect(p0, p1, true);
|
||||
|
||||
ImGui::Image(texId, ImVec2(texW, texH));
|
||||
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, true, texW, texH);
|
||||
|
||||
drawList->PopClipRect();
|
||||
} else {
|
||||
ImGui::Text("(no valid texture bound)");
|
||||
// In software mode, we should just decode the texture here.
|
||||
// TODO: List some of the texture params here.
|
||||
}
|
||||
}
|
||||
|
||||
// Let's display the current CLUT.
|
||||
}
|
||||
|
||||
// Let's display the current CLUT.
|
||||
|
||||
} else {
|
||||
ImGui::Text("Click the buttons above (Tex, etc) to stop");
|
||||
}
|
||||
|
||||
@@ -1116,8 +1116,8 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
case IDC_GEDBG_SETPRIMFILTER:
|
||||
{
|
||||
std::string value = gpuDebug->GetRestrictPrims();
|
||||
if (InputBox_GetString(GetModuleHandle(NULL), m_hDlg, L"Prim counter ranges", value, value)) {
|
||||
std::string value;
|
||||
if (InputBox_GetString(GetModuleHandle(NULL), m_hDlg, L"Prim counter ranges", gpuDebug->GetRestrictPrims(), value)) {
|
||||
gpuDebug->SetRestrictPrims(value.c_str());
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -59,7 +59,7 @@ static INT_PTR CALLBACK InputBoxFunc(HWND hDlg, UINT message, WPARAM wParam, LPA
|
||||
}
|
||||
}
|
||||
|
||||
bool InputBox_GetString(HINSTANCE hInst, HWND hParent, const wchar_t *title, const std::string &defaultValue, std::string &outvalue, InputBoxFlags flags) {
|
||||
bool InputBox_GetString(HINSTANCE hInst, HWND hParent, const wchar_t *title, std::string_view defaultValue, std::string &outvalue, InputBoxFlags flags) {
|
||||
const wchar_t *defaultTitle = L"Input value";
|
||||
|
||||
g_params.defaultSelected = flags & InputBoxFlags::Selected;
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ enum class InputBoxFlags {
|
||||
ENUM_CLASS_BITOPS(InputBoxFlags);
|
||||
|
||||
// All I/O is in UTF-8
|
||||
bool InputBox_GetString(HINSTANCE hInst, HWND hParent, const wchar_t *title, const std::string &defaultvalue, std::string &outvalue, InputBoxFlags flags = InputBoxFlags::Default);
|
||||
bool InputBox_GetString(HINSTANCE hInst, HWND hParent, const wchar_t *title, std::string_view defaultvalue, std::string &outvalue, InputBoxFlags flags = InputBoxFlags::Default);
|
||||
bool InputBox_GetHex(HINSTANCE hInst, HWND hParent, const wchar_t *title, u32 defaultvalue, u32 &outvalue);
|
||||
|
||||
bool UserPasswordBox_GetStrings(HINSTANCE hInst, HWND hParent, const wchar_t *title, std::string *username, std::string *password);
|
||||
|
||||
Reference in New Issue
Block a user