Merge pull request #19980 from hrydgard/ge-debugger-work

ImDebugger: Add zoom functionality, file export
This commit is contained in:
Henrik Rydgård
2025-02-14 09:48:52 -06:00
committed by GitHub
9 changed files with 70 additions and 78 deletions
-8
View File
@@ -71,7 +71,6 @@ static std::mutex m_hInactiveMutex;
static int steppingCounter = 0;
static std::set<CoreLifecycleFunc> lifecycleFuncs;
static std::set<CoreStopRequestFunc> stopFuncs;
// This can be read and written from ANYWHERE.
volatile CoreState coreState = CORE_STEPPING_CPU;
@@ -106,16 +105,9 @@ void Core_NotifyLifecycle(CoreLifecycle stage) {
}
}
void Core_ListenStopRequest(CoreStopRequestFunc func) {
stopFuncs.insert(func);
}
void Core_Stop() {
Core_ResetException();
Core_UpdateState(CORE_POWERDOWN);
for (auto func : stopFuncs) {
func();
}
}
void Core_UpdateState(CoreState newState) {
-4
View File
@@ -107,10 +107,6 @@ typedef void (* CoreLifecycleFunc)(CoreLifecycle stage);
void Core_ListenLifecycle(CoreLifecycleFunc func);
void Core_NotifyLifecycle(CoreLifecycle stage);
// Callback is executed on requesting thread.
typedef void (* CoreStopRequestFunc)();
void Core_ListenStopRequest(CoreStopRequestFunc callback);
bool Core_IsStepping();
bool Core_IsActive();
+5 -20
View File
@@ -289,27 +289,7 @@ int HTTPRequest::sendRequest(u32 postDataPtr, u32 postDataSize) {
return 0;
}
static void __HttpNotifyLifecycle(CoreLifecycle stage) {
if (stage == CoreLifecycle::STOPPING) {
for (const auto& it : httpObjects) {
if (it->className() == name_HTTPRequest)
(static_cast<HTTPRequest*>(it.get()))->abortRequest();
}
}
}
static void __HttpRequestStop() {
// This can happen from a separate thread.
std::lock_guard<std::mutex> guard(httpLock);
for (const auto& it : httpObjects) {
if (it->className() == name_HTTPRequest)
(static_cast<HTTPRequest*>(it.get()))->abortRequest();
}
}
void __HttpInit() {
Core_ListenLifecycle(&__HttpNotifyLifecycle);
Core_ListenStopRequest(&__HttpRequestStop);
}
void __HttpShutdown() {
@@ -317,6 +297,11 @@ void __HttpShutdown() {
httpInited = false;
httpsInited = false;
httpCacheInited = false;
for (const auto& it : httpObjects) {
if (it->className() == name_HTTPRequest)
(static_cast<HTTPRequest*>(it.get()))->abortRequest();
}
httpObjects.clear();
}
-12
View File
@@ -822,16 +822,6 @@ static bool ReadCompressed(u32 fp, void *dest, size_t sz, uint32_t version) {
return real_size == sz;
}
static void ReplayStop() {
_dbg_assert_(!replayThread.joinable());
// This can happen from a separate thread.
lastExecFilename.clear();
lastExecCommands.clear();
lastExecPushbuf.clear();
lastExecVersion = 0;
}
static u32 LoadReplay(const std::string &filename) {
PROFILE_THIS_SCOPE("ReplayLoad");
u32 fp = pspFileSystem.OpenFile(filename, FILEACCESS_READ);
@@ -911,8 +901,6 @@ void WriteRunDumpCode(u32 codeStart) {
ReplayResult RunMountedReplay(const std::string &filename) {
_assert_msg_(!gpuDebug->GetRecorder()->IsActivePending(), "Cannot run replay while recording.");
Core_ListenStopRequest(&ReplayStop);
uint32_t version = lastExecVersion;
if (lastExecFilename != filename) {
// Does this ever happen? Can the filename change, without going through core shutdown/startup?
+1
View File
@@ -30,5 +30,6 @@ enum class ReplayResult {
void WriteRunDumpCode(u32 addr);
ReplayResult RunMountedReplay(const std::string &filename);
void ReplayShutdown();
} // namespace GPURecord
+30 -4
View File
@@ -4,6 +4,7 @@
#include "ext/imgui/imgui_internal.h"
#include "Common/StringUtils.h"
#include "Common/File/FileUtil.h"
#include "Common/Data/Format/IniFile.h"
#include "Core/Config.h"
#include "Core/System.h"
@@ -403,17 +404,40 @@ void DrawThreadView(ImConfig &cfg, ImControl &control) {
}
// TODO: Add popup menu, export file, export dir, etc...
static void RecurseFileSystem(IFileSystem *fs, std::string path) {
static void RecurseFileSystem(IFileSystem *fs, std::string path, RequesterToken token) {
std::vector<PSPFileInfo> fileInfo = fs->GetDirListing(path);
for (auto &file : fileInfo) {
if (file.type == FileType::FILETYPE_DIRECTORY) {
if (file.name != "." && file.name != ".." && ImGui::TreeNode(file.name.c_str())) {
std::string fpath = path + "/" + file.name;
RecurseFileSystem(fs, fpath);
RecurseFileSystem(fs, fpath, token);
ImGui::TreePop();
}
} else {
ImGui::TextUnformatted(file.name.c_str());
ImGui::Selectable(file.name.c_str());
if (ImGui::BeginPopupContextItem()) {
if (ImGui::MenuItem("Copy Path")) {
System_CopyStringToClipboard(path + "/" + file.name);
}
if (ImGui::MenuItem("Save file...")) {
std::string fullPath = path + "/" + file.name;
int size = file.size;
// save dialog
System_BrowseForFileSave(token, "Save file", file.name, BrowseFileType::ANY, [fullPath, fs, size](const char *responseString, int) {
int fd = fs->OpenFile(fullPath, FILEACCESS_READ);
if (fd >= 0) {
std::string data;
data.resize(size);
fs->ReadFile(fd, (u8 *)data.data(), size);
fs->CloseFile(fd);
Path dest(responseString);
File::WriteDataToFile(false, data.data(), data.size(), dest);
}
});
}
// your popup code
ImGui::EndPopup();
}
}
}
}
@@ -433,7 +457,7 @@ static void DrawFilesystemBrowser(ImConfig &cfg) {
snprintf(fsTitle, sizeof(fsTitle), "%s - %s", fs.prefix.c_str(), desc);
if (ImGui::TreeNode(fsTitle)) {
auto system = fs.system;
RecurseFileSystem(system.get(), path);
RecurseFileSystem(system.get(), path, cfg.requesterToken);
ImGui::TreePop();
}
}
@@ -1764,6 +1788,8 @@ Path ImDebugger::ConfigPath() {
// But, I don't really want Core to know about the ImDebugger..
void ImConfig::LoadConfig(const Path &iniFile) {
requesterToken = g_requestManager.GenerateRequesterToken();
IniFile ini;
ini.Load(iniFile); // Ignore return value, might not exist yet. In that case we'll end up loading defaults.
SyncConfig(&ini, false);
+1
View File
@@ -164,6 +164,7 @@ struct ImConfig {
int breakCount = 0;
bool displayLatched = false;
int requesterToken;
// We use a separate ini file from the main PPSSPP config.
void LoadConfig(const Path &iniFile);
+30 -28
View File
@@ -174,7 +174,7 @@ void ImGePixelViewerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInte
ImGui::SameLine();
if (ImGui::BeginChild("right")) {
ImVec2 p0 = ImGui::GetCursorScreenPos();
viewer_.Draw(gpuDebug, draw);
viewer_.Draw(gpuDebug, draw, 1.0f);
if (ImGui::IsItemHovered()) {
int x = (int)(ImGui::GetMousePos().x - p0.x);
int y = (int)(ImGui::GetMousePos().y - p0.y);
@@ -197,7 +197,7 @@ ImGePixelViewer::~ImGePixelViewer() {
texture_->Release();
}
bool ImGePixelViewer::Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *draw) {
bool ImGePixelViewer::Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *draw, float zoom) {
if (dirty_) {
UpdateTexture(draw);
dirty_ = false;
@@ -206,7 +206,7 @@ bool ImGePixelViewer::Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *draw)
if (Memory::IsValid4AlignedAddress(addr)) {
if (texture_) {
ImTextureID texId = ImGui_ImplThin3d_AddTextureTemp(texture_, useAlpha ? ImGuiPipeline::TexturedAlphaBlend : ImGuiPipeline::TexturedOpaque);
ImGui::Image(texId, ImVec2((float)width, (float)height));
ImGui::Image(texId, ImVec2((float)width * zoom, (float)height * zoom));
return true;
} else {
ImGui::Text("(invalid params: %dx%d, %08x)", width, height, addr);
@@ -375,7 +375,7 @@ ImGeReadbackViewer::~ImGeReadbackViewer() {
delete[] data_;
}
bool ImGeReadbackViewer::Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *draw) {
bool ImGeReadbackViewer::Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *draw, float zoom) {
FramebufferManagerCommon *fbmanager = gpuDebug->GetFramebufferManagerCommon();
if (!vfb || !vfb->fbo || !fbmanager) {
ImGui::TextUnformatted("(N/A)");
@@ -454,7 +454,7 @@ bool ImGeReadbackViewer::Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *dr
} else {
texId = ImGui_ImplThin3d_AddFBAsTextureTemp(vfb->fbo, Draw::Aspect::COLOR_BIT, ImGuiPipeline::TexturedOpaque);
}
ImGui::Image(texId, ImVec2((float)vfb->fbo->Width(), (float)vfb->fbo->Height()));
ImGui::Image(texId, ImVec2((float)vfb->fbo->Width() * zoom, (float)vfb->fbo->Height() * zoom));
return true;
}
@@ -857,7 +857,6 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
ImGui::EndDisabled();
}
ImGui::SameLine();
ImGui::Text("%d/%d", gpuDebug->PrimsThisFrame(), gpuDebug->PrimsLastFrame());
if (disableStepButtons) {
@@ -881,26 +880,13 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
ImGui::EndDisabled();
}
// Line break
if (ImGui::Button("Goto PC")) {
disasmView_.GotoPC();
}
ImGui::SameLine();
if (ImGui::Button("Settings")) {
ImGui::OpenPopup("disSettings");
}
if (ImGui::BeginPopup("disSettings")) {
ImGui::Checkbox("Follow PC", &disasmView_.followPC_);
ImGui::EndPopup();
}
// Display any pending step event.
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::Text("Step pending: %s", GPUDebug::BreakNextToString(gpuDebug->GetBreakNext()));
ImGui::SameLine();
if (gpuDebug->GetBreakNext() == GPUDebug::BreakNext::COUNT) {
ImGui::Text("(%d)", gpuDebug->GetBreakCount());
@@ -914,6 +900,19 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
showBannerInFrames_ = 2;
}
// Line break
if (ImGui::Button("Goto PC")) {
disasmView_.GotoPC();
}
ImGui::SameLine();
if (ImGui::Button("Settings")) {
ImGui::OpenPopup("disSettings");
}
if (ImGui::BeginPopup("disSettings")) {
ImGui::Checkbox("Follow PC", &disasmView_.followPC_);
ImGui::EndPopup();
}
// First, let's list any active display lists in the left column, on top of the disassembly.
ImGui::BeginChild("left pane", ImVec2(400, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);
@@ -980,7 +979,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
}
ImGui::BeginChild("texture/fb view"); // Leave room for 1 line below us
ImGui::BeginChild("texture/fb view");
ImDrawList *drawList = ImGui::GetWindowDrawList();
@@ -1001,7 +1000,10 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
} else {
ImGui::Text("Framebuffer");
}
ImGui::SameLine();
}
ImGui::SetNextItemWidth(200.0f);
ImGui::SliderFloat("Zoom", &previewZoom_, 0.125f, 2.f, "%.3f", ImGuiSliderFlags_Logarithmic);
// 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, };
@@ -1020,7 +1022,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
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)) {
if (ImGui::DragFloat("Z value scale", &rbViewer_.scale, 1.0f, 0.5f, 256.0f, "%0.2f", ImGuiSliderFlags_Logarithmic)) {
rbViewer_.Snapshot();
swViewer_.Snapshot();
}
@@ -1031,7 +1033,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
float scale = 1.0f;
if (vfb && vfb->fbo) {
scale = vfb->renderScaleFactor;
p1 = ImVec2(p0.x + vfb->fbo->Width(), p0.y + vfb->fbo->Height());
p1 = ImVec2(p0.x + vfb->fbo->Width() * previewZoom_, p0.y + vfb->fbo->Height() * previewZoom_);
} else {
// Guess
p1 = ImVec2(p0.x + swViewer_.width, p0.y + swViewer_.height);
@@ -1042,23 +1044,23 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
PixelLookup *lookup = nullptr;
if (vfb) {
rbViewer_.Draw(gpuDebug, draw);
rbViewer_.Draw(gpuDebug, draw, previewZoom_);
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);
swViewer_.Draw(gpuDebug, draw, previewZoom_);
lookup = &swViewer_;
}
// Draw vertex preview on top!
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, false, scale, scale);
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, false, scale * previewZoom_, scale * previewZoom_);
drawList->PopClipRect();
if (ImGui::IsItemHovered()) {
int x = (int)(ImGui::GetMousePos().x - p0.x);
int y = (int)(ImGui::GetMousePos().y - p0.y);
int x = (int)(ImGui::GetMousePos().x - p0.x) * previewZoom_;
int y = (int)(ImGui::GetMousePos().y - p0.y) * previewZoom_;
char temp[128];
if (lookup->FormatValueAt(temp, sizeof(temp), x, y)) {
ImGui::Text("(%d, %d): %s", x, y, temp);
+3 -2
View File
@@ -65,7 +65,7 @@ public:
struct ImGePixelViewer : public PixelLookup {
~ImGePixelViewer();
bool Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *draw);
bool Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *draw, float zoom);
void Snapshot() {
dirty_ = true;
}
@@ -90,7 +90,7 @@ private:
struct ImGeReadbackViewer : public PixelLookup {
ImGeReadbackViewer();
~ImGeReadbackViewer();
bool Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *);
bool Draw(GPUDebugInterface *gpuDebug, Draw::DrawContext *draw, float zoom);
void Snapshot() {
dirty_ = true;
}
@@ -152,4 +152,5 @@ private:
std::vector<GPUDebugVertex> previewVertices_;
int previewCount_ = 0;
Draw::Aspect selectedAspect_;
float previewZoom_ = 1.0f;
};