Merge pull request #19742 from hrydgard/more-debugger-work

Fix more GE debugger bugs, add break-on-count
This commit is contained in:
Henrik Rydgård
2024-12-17 12:59:28 +01:00
committed by GitHub
13 changed files with 273 additions and 188 deletions
+12 -3
View File
@@ -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);
}
}
};
+3 -2
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;
+1
View File
@@ -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_;
+2
View File
@@ -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";
}
}
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+2
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.
+173 -116
View File
@@ -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");
}
+2 -2
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;
+1 -1
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;
+1 -1
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);