mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Merge pull request #19980 from hrydgard/ge-debugger-work
ImDebugger: Add zoom functionality, file export
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -30,5 +30,6 @@ enum class ReplayResult {
|
||||
|
||||
void WriteRunDumpCode(u32 addr);
|
||||
ReplayResult RunMountedReplay(const std::string &filename);
|
||||
void ReplayShutdown();
|
||||
|
||||
} // namespace GPURecord
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user