Add break-on-count (with rudimentary UI), break on block transfer

This commit is contained in:
Henrik Rydgård 2024-12-17 11:12:46 +01:00
parent 3a97479116
commit db13c09c41
10 changed files with 67 additions and 32 deletions

View file

@ -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;

View file

@ -36,6 +36,7 @@ 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";
default: return "N/A";
}

View file

@ -34,6 +34,7 @@ enum class BreakNext {
VSYNC,
PRIM,
CURVE,
BLOCK_TRANSFER,
COUNT,
};

View file

@ -2017,10 +2017,6 @@ void GPUCommon::ClearBreakNext() {
GPUStepping::ResumeFromStepping();
}
GPUDebug::BreakNext GPUCommon::GetBreakNext() {
return breakNext_;
}
void GPUCommon::SetBreakNext(GPUDebug::BreakNext next) {
breakNext_ = next;
breakAtCount_ = -1;
@ -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;
}
@ -2075,14 +2075,14 @@ GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoi
bool isPrim = false;
bool process = true;
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE || cmd == GE_CMD_VAP) { // VAP is immediate mode prims.
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;
// TODO: Should restricted prim ranges also avoid breakpoints?
if (!restrictPrimRanges.empty()) {
if (!restrictPrimRanges_.empty()) {
process = false;
for (const auto &range : restrictPrimRanges) {
for (const auto &range : restrictPrimRanges_) {
if ((primsThisFrame_ + 1) >= range.first && (primsThisFrame_ + 1) <= range.second) {
process = true;
break;
@ -2100,12 +2100,12 @@ GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoi
debugBreak = breakpoints->IsBreakpoint(pc, op);
}
if (debugBreak && 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;
goto bail;
}
g_skipPcOnce = 0;
skipPcOnce_ = 0;
if (debugBreak) {
breakpoints->ClearTempBreakpoints();
@ -2119,7 +2119,7 @@ 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;
skipPcOnce_ = pc;
breakNext_ = BreakNext::NONE;
// Not incrementing the prim counter!
return NotifyResult::Break; // caller will call GPUStepping::EnterStepping().
@ -2159,19 +2159,15 @@ void GPUCommon::NotifyDisplay(u32 framebuf, u32 stride, int 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();
}

View file

@ -388,9 +388,16 @@ 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_;
@ -545,10 +552,10 @@ protected:
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);

View file

@ -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.

View file

@ -784,6 +784,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);
}
@ -798,7 +802,26 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
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")) {
@ -821,6 +844,10 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
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();
}

View file

@ -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;

View file

@ -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;

View file

@ -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);