Merge pull request #11345 from unknownbrackets/debugger

Expose GE dump recording in WebSocket API
This commit is contained in:
Henrik Rydgård 2018-09-01 22:01:19 +02:00 committed by GitHub
commit 3d1e0e012b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 489 additions and 245 deletions

View File

@ -1357,6 +1357,8 @@ set(GPU_SOURCES
GPU/Common/SplineCommon.h
GPU/Debugger/Breakpoints.cpp
GPU/Debugger/Breakpoints.h
GPU/Debugger/Debugger.cpp
GPU/Debugger/Debugger.h
GPU/Debugger/Record.cpp
GPU/Debugger/Record.h
GPU/Debugger/Stepping.cpp
@ -1430,6 +1432,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/WebSocket/GameSubscriber.h
Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
Core/Debugger/WebSocket/GPUBufferSubscriber.h
Core/Debugger/WebSocket/GPURecordSubscriber.cpp
Core/Debugger/WebSocket/GPURecordSubscriber.h
Core/Debugger/WebSocket/HLESubscriber.cpp
Core/Debugger/WebSocket/HLESubscriber.h
Core/Debugger/WebSocket/LogBroadcaster.cpp

View File

@ -188,6 +188,7 @@
<ClCompile Include="Debugger\WebSocket\GameBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\GameSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\GPUBufferSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\HLESubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp" />
@ -549,6 +550,7 @@
<ClInclude Include="Debugger\WebSocket\GameSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\GPUBufferSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\HLESubscriber.h" />
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\WebSocketUtils.h" />

View File

@ -734,6 +734,9 @@
<ClCompile Include="HLE\sceUsbAcc.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -1361,6 +1364,9 @@
<ClInclude Include="HLE\sceUsbAcc.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View File

@ -53,24 +53,20 @@
#include "Core/Debugger/WebSocket/DisasmSubscriber.h"
#include "Core/Debugger/WebSocket/GameSubscriber.h"
#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h"
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
#include "Core/Debugger/WebSocket/HLESubscriber.h"
#include "Core/Debugger/WebSocket/SteppingSubscriber.h"
typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map);
typedef void (*Subscribershutdown)(void *p);
struct SubscriberInfo {
SubscriberInit init;
Subscribershutdown shutdown;
};
static const std::vector<SubscriberInfo> subscribers({
{ &WebSocketBreakpointInit, nullptr },
{ &WebSocketCPUCoreInit, nullptr },
{ &WebSocketDisasmInit, &WebSocketDisasmShutdown },
{ &WebSocketGameInit, nullptr },
{ &WebSocketGPUBufferInit, nullptr },
{ &WebSocketHLEInit, nullptr },
{ &WebSocketSteppingInit, &WebSocketSteppingShutdown },
typedef DebuggerSubscriber *(*SubscriberInit)(DebuggerEventHandlerMap &map);
static const std::vector<SubscriberInit> subscribers({
&WebSocketBreakpointInit,
&WebSocketCPUCoreInit,
&WebSocketDisasmInit,
&WebSocketGameInit,
&WebSocketGPUBufferInit,
&WebSocketGPURecordInit,
&WebSocketHLEInit,
&WebSocketSteppingInit,
});
// To handle webserver restart, keep track of how many running.
@ -132,10 +128,10 @@ void HandleDebuggerRequest(const http::Request &request) {
SteppingBroadcaster stepping;
std::unordered_map<std::string, DebuggerEventHandler> eventHandlers;
std::vector<void *> subscriberData;
for (auto info : subscribers) {
std::vector<DebuggerSubscriber *> subscriberData;
for (auto init : subscribers) {
std::lock_guard<std::mutex> guard(lifecycleLock);
subscriberData.push_back(info.init(eventHandlers));
subscriberData.push_back(init(eventHandlers));
}
// There's a tradeoff between responsiveness to incoming events, and polling for changes.
@ -178,6 +174,12 @@ void HandleDebuggerRequest(const http::Request &request) {
game.Broadcast(ws);
stepping.Broadcast(ws);
for (size_t i = 0; i < subscribers.size(); ++i) {
if (subscriberData[i]) {
subscriberData[i]->Broadcast(ws);
}
}
if (stopRequested) {
ws->Close(net::WebSocketClose::GOING_AWAY);
}
@ -188,11 +190,7 @@ void HandleDebuggerRequest(const http::Request &request) {
std::lock_guard<std::mutex> guard(lifecycleLock);
for (size_t i = 0; i < subscribers.size(); ++i) {
if (subscribers[i].shutdown) {
subscribers[i].shutdown(subscriberData[i]);
} else {
assert(!subscriberData[i]);
}
delete subscriberData[i];
}
delete ws;

View File

@ -23,7 +23,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/MIPS/MIPSDebugInterface.h"
void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["cpu.breakpoint.add"] = &WebSocketCPUBreakpointAdd;
map["cpu.breakpoint.update"] = &WebSocketCPUBreakpointUpdate;

View File

@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);
void WebSocketCPUBreakpointAdd(DebuggerRequest &req);
void WebSocketCPUBreakpointUpdate(DebuggerRequest &req);

View File

@ -25,7 +25,7 @@
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSDebugInterface.h"
void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["cpu.stepping"] = &WebSocketCPUStepping;
map["cpu.resume"] = &WebSocketCPUResume;

View File

@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map);
void WebSocketCPUStepping(DebuggerRequest &req);
void WebSocketCPUResume(DebuggerRequest &req);

View File

@ -28,11 +28,12 @@
#include "Core/MIPS/MIPSAsm.h"
#include "Core/MIPS/MIPSDebugInterface.h"
struct WebSocketDisasmState {
class WebSocketDisasmState : public DebuggerSubscriber {
public:
WebSocketDisasmState() {
disasm_.setCpu(currentDebugMIPS);
}
~WebSocketDisasmState() {
~WebSocketDisasmState() override {
disasm_.clear();
}
@ -49,7 +50,7 @@ protected:
DisassemblyManager disasm_;
};
void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketDisasmState();
map["memory.base"] = std::bind(&WebSocketDisasmState::Base, p, std::placeholders::_1);
map["memory.disasm"] = std::bind(&WebSocketDisasmState::Disasm, p, std::placeholders::_1);
@ -59,10 +60,6 @@ void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
return p;
}
void WebSocketDisasmShutdown(void *p) {
delete static_cast<WebSocketDisasmState *>(p);
}
static DebugInterface *CPUFromRequest(DebuggerRequest &req) {
if (!req.HasParam("thread"))
return currentDebugMIPS;

View File

@ -19,5 +19,4 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketDisasmInit(DebuggerEventHandlerMap &map);
void WebSocketDisasmShutdown(void *p);
DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map);

View File

@ -28,7 +28,7 @@
#include "Core/Screenshot.h"
#include "GPU/Debugger/Stepping.h"
void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["gpu.buffer.screenshot"] = &WebSocketGPUBufferScreenshot;
map["gpu.buffer.renderColor"] = &WebSocketGPUBufferRenderColor;

View File

@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map);
void WebSocketGPUBufferScreenshot(DebuggerRequest &req);
void WebSocketGPUBufferRenderColor(DebuggerRequest &req);

View File

@ -0,0 +1,107 @@
// Copyright (c) 2018- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "data/base64.h"
#include "Common/FileUtil.h"
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/System.h"
#include "GPU/Debugger/Record.h"
struct WebSocketGPURecordState : public DebuggerSubscriber {
~WebSocketGPURecordState() override;
void Dump(DebuggerRequest &req);
void Broadcast(net::WebSocketServer *ws) override;
protected:
bool pending_ = false;
std::string lastTicket_;
std::string lastFilename_;
};
DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketGPURecordState();
map["gpu.record.dump"] = std::bind(&WebSocketGPURecordState::Dump, p, std::placeholders::_1);
return p;
}
WebSocketGPURecordState::~WebSocketGPURecordState() {
// Clear the callback to hopefully avoid a crash.
if (pending_)
GPURecord::SetCallback(nullptr);
}
// Begin recording (gpu.record.dump)
//
// No parameters.
//
// Response (same event name):
// - uri: data: URI containing debug dump data.
//
// Note: recording may take a moment.
void WebSocketGPURecordState::Dump(DebuggerRequest &req) {
if (!PSP_IsInited())
return req.Fail("CPU not started");
if (!GPURecord::Activate())
return req.Fail("Recording already in progress");
pending_ = true;
GPURecord::SetCallback([=](const std::string &filename) {
lastFilename_ = filename;
pending_ = false;
});
const JsonNode *value = req.data.get("ticket");
lastTicket_ = value ? json_stringify(value) : "";
}
// This handles the asynchronous gpu.record.dump response.
void WebSocketGPURecordState::Broadcast(net::WebSocketServer *ws) {
if (!lastFilename_.empty()) {
FILE *fp = File::OpenCFile(lastFilename_, "rb");
if (!fp) {
lastFilename_.clear();
return;
}
// We write directly to the stream since this is a large chunk of data.
ws->AddFragment(false, R"({"event":"gpu.record.dump")");
if (!lastTicket_.empty()) {
ws->AddFragment(false, R"(,"ticket":)");
ws->AddFragment(false, lastTicket_);
}
ws->AddFragment(false, R"(,"uri":"data:application/octet-stream;base64,)");
// Divisible by 3 for base64 reasons.
const size_t BUF_SIZE = 16383;
std::vector<uint8_t> buf;
buf.resize(BUF_SIZE);
while (!feof(fp)) {
size_t bytes = fread(&buf[0], 1, BUF_SIZE, fp);
ws->AddFragment(false, Base64Encode(&buf[0], bytes));
}
fclose(fp);
ws->AddFragment(true, R"("})");
lastFilename_.clear();
lastTicket_.clear();
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) 2018- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map);

View File

@ -20,7 +20,7 @@
#include "Core/ELF/ParamSFO.h"
#include "Core/System.h"
void *WebSocketGameInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map) {
map["game.status"] = &WebSocketGameStatus;
map["version"] = &WebSocketVersion;

View File

@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketGameInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map);
void WebSocketGameStatus(DebuggerRequest &req);
void WebSocketVersion(DebuggerRequest &req);

View File

@ -27,7 +27,7 @@
#include "Core/MIPS/MIPSStackWalk.h"
#include "Core/HLE/sceKernelThread.h"
void *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
map["hle.thread.list"] = &WebSocketHLEThreadList;
map["hle.thread.wake"] = &WebSocketHLEThreadWake;
map["hle.thread.stop"] = &WebSocketHLEThreadStop;

View File

@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketHLEInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map);
void WebSocketHLEThreadList(DebuggerRequest &req);
void WebSocketHLEThreadWake(DebuggerRequest &req);

View File

@ -28,11 +28,11 @@
using namespace MIPSAnalyst;
struct WebSocketSteppingState {
struct WebSocketSteppingState : public DebuggerSubscriber {
WebSocketSteppingState() {
disasm_.setCpu(currentDebugMIPS);
}
~WebSocketSteppingState() {
~WebSocketSteppingState() override {
disasm_.clear();
}
@ -51,7 +51,7 @@ protected:
DisassemblyManager disasm_;
};
void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketSteppingState();
map["cpu.stepInto"] = std::bind(&WebSocketSteppingState::Into, p, std::placeholders::_1);
map["cpu.stepOver"] = std::bind(&WebSocketSteppingState::Over, p, std::placeholders::_1);
@ -62,10 +62,6 @@ void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
return p;
}
void WebSocketSteppingShutdown(void *p) {
delete static_cast<WebSocketSteppingState *>(p);
}
static DebugInterface *CPUFromRequest(DebuggerRequest &req, uint32_t *threadID = nullptr) {
if (!req.HasParam("thread")) {
if (threadID)

View File

@ -19,5 +19,4 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketSteppingInit(DebuggerEventHandlerMap &map);
void WebSocketSteppingShutdown(void *p);
DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map);

View File

@ -98,5 +98,13 @@ private:
bool responsePartial_ = false;
};
class DebuggerSubscriber {
public:
virtual ~DebuggerSubscriber() {}
// Subscribers can also broadcast if they have simple cases to.
virtual void Broadcast(net::WebSocketServer *ws) {}
};
typedef std::function<void(DebuggerRequest &req)> DebuggerEventHandler;
typedef std::unordered_map<std::string, DebuggerEventHandler> DebuggerEventHandlerMap;

View File

@ -55,6 +55,7 @@
#include "GPU/GPUInterface.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Common/PostShader.h"
#include "GPU/Debugger/Record.h"
struct FrameBufferState {
u32 topaddr;
@ -737,7 +738,7 @@ void __DisplayFlip(int cyclesLate) {
// 4 here means 1 drawn, 4 skipped - so 12 fps minimum.
maxFrameskip = g_Config.iFrameSkip;
}
if (numSkippedFrames >= maxFrameskip) {
if (numSkippedFrames >= maxFrameskip || GPURecord::IsActivePending()) {
skipFrame = false;
}

View File

@ -50,13 +50,6 @@ public:
virtual void SaveSymbolMap() {}
virtual void SetWindowTitle(const char *message) {}
// While debugging is active, it's perfectly fine for these to block.
virtual bool GPUDebuggingActive() { return false; }
virtual void GPUNotifyCommand(u32 pc) {}
virtual void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {}
virtual void GPUNotifyDraw() {}
virtual void GPUNotifyTextureAttachment(u32 addr) {}
virtual bool CanCreateShortcut() {return false;}
virtual bool CreateDesktopShortcut(std::string argumentPath, std::string title) {return false;}

View File

@ -20,7 +20,6 @@
#include "Common/ColorConv.h"
#include "Common/MemoryUtil.h"
#include "Core/Config.h"
#include "Core/Host.h"
#include "Core/Reporting.h"
#include "Core/System.h"
#include "GPU/Common/FramebufferCommon.h"
@ -28,6 +27,7 @@
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/Common/GPUStateUtils.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/GPUState.h"
#include "GPU/GPUInterface.h"
@ -708,7 +708,7 @@ void TextureCacheCommon::AttachFramebufferValid(TexCacheEntry *entry, VirtualFra
entry->maxLevel = 0;
fbTexInfo_[cachekey] = fbInfo;
framebuffer->last_frame_attached = gpuStats.numFlips;
host->GPUNotifyTextureAttachment(entry->addr);
GPUDebug::NotifyTextureAttachment(entry->addr);
} else if (entry->framebuffer == framebuffer) {
framebuffer->last_frame_attached = gpuStats.numFlips;
}
@ -727,7 +727,7 @@ void TextureCacheCommon::AttachFramebufferInvalid(TexCacheEntry *entry, VirtualF
entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE;
entry->maxLevel = 0;
fbTexInfo_[cachekey] = fbInfo;
host->GPUNotifyTextureAttachment(entry->addr);
GPUDebug::NotifyTextureAttachment(entry->addr);
}
}
@ -740,7 +740,7 @@ void TextureCacheCommon::DetachFramebuffer(TexCacheEntry *entry, u32 address, Vi
// Otherwise we never recreate the texture.
entry->hash ^= 1;
fbTexInfo_.erase(cachekey);
host->GPUNotifyTextureAttachment(entry->addr);
GPUDebug::NotifyTextureAttachment(entry->addr);
}
}

View File

@ -22,7 +22,6 @@
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
@ -34,10 +33,10 @@
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Common/SplineCommon.h"
#include "GPU/Common/TransformCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/D3D11/FramebufferManagerD3D11.h"
#include "GPU/D3D11/TextureCacheD3D11.h"
#include "GPU/D3D11/DrawEngineD3D11.h"
@ -690,10 +689,7 @@ rotateVBO:
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
// We only support GPU debugging on Windows, and that's the only use case for this.
host->GPUNotifyDraw();
#endif
GPUDebug::NotifyDraw();
}
void DrawEngineD3D11::TessellationDataTransferD3D11::PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) {

View File

@ -45,7 +45,6 @@
#include "Core/Debugger/Breakpoints.h"
#include "Core/MemMapHelpers.h"
#include "Core/MIPS/MIPS.h"
#include "Core/Host.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Core/System.h"
@ -55,6 +54,7 @@
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/D3D11/ShaderManagerD3D11.h"
#include "GPU/D3D11/GPU_D3D11.h"
#include "GPU/D3D11/FramebufferManagerD3D11.h"
@ -254,7 +254,7 @@ void GPU_D3D11::BeginFrame() {
void GPU_D3D11::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
// TODO: Some games like Spongebob - Yellow Avenger, never change framebuffer, they blit to it.
// So breaking on frames doesn't work. Might want to move this to sceDisplay vsync.
host->GPUNotifyDisplay(framebuf, stride, format);
GPUDebug::NotifyDisplay(framebuf, stride, format);
framebufferManagerD3D11_->SetDisplayFramebuffer(framebuf, stride, format);
}

100
GPU/Debugger/Debugger.cpp Normal file
View File

@ -0,0 +1,100 @@
// Copyright (c) 2018- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "GPU/GPU.h"
#include "GPU/Debugger/Breakpoints.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Debugger/Stepping.h"
namespace GPUDebug {
static bool active = false;
static bool inited = false;
static BreakNext breakNext = BreakNext::NONE;
static void Init() {
if (!inited) {
GPUBreakpoints::Init();
Core_ListenStopRequest(&GPUStepping::ForceUnpause);
inited = true;
}
}
void SetActive(bool flag) {
Init();
active = flag;
if (!active) {
breakNext = BreakNext::NONE;
GPUStepping::ResumeFromStepping();
}
}
bool IsActive() {
return active;
}
void SetBreakNext(BreakNext next) {
SetActive(true);
breakNext = next;
if (next == BreakNext::TEX) {
GPUBreakpoints::AddTextureChangeTempBreakpoint();
} else if (next == BreakNext::PRIM) {
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_PRIM, true);
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true);
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true);
}
GPUStepping::ResumeFromStepping();
}
void NotifyCommand(u32 pc) {
if (!active)
return;
u32 op = Memory::ReadUnchecked_U32(pc);
if (breakNext == BreakNext::OP || GPUBreakpoints::IsBreakpoint(pc, op)) {
GPUBreakpoints::ClearTempBreakpoints();
auto info = gpuDebug->DissassembleOp(pc);
NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str());
GPUStepping::EnterStepping();
}
}
void NotifyDraw() {
if (!active)
return;
if (breakNext == BreakNext::DRAW) {
NOTICE_LOG(G3D, "Waiting at a draw");
GPUStepping::EnterStepping();
}
}
void NotifyDisplay(u32 framebuf, u32 stride, int format) {
if (!active)
return;
if (breakNext == BreakNext::FRAME) {
// This should work fine, start stepping at the first op of the new frame.
breakNext = BreakNext::OP;
}
}
void NotifyTextureAttachment(u32 texaddr) {
if (!active)
return;
}
}

45
GPU/Debugger/Debugger.h Normal file
View File

@ -0,0 +1,45 @@
// Copyright (c) 2018- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/CommonTypes.h"
namespace GPUDebug {
enum class BreakNext {
NONE,
OP,
DRAW,
TEX,
NONTEX,
FRAME,
PRIM,
};
void SetActive(bool flag);
bool IsActive();
void SetBreakNext(BreakNext next);
// While debugging is active, these may block.
void NotifyCommand(u32 pc);
void NotifyDraw();
void NotifyDisplay(u32 framebuf, u32 stride, int format);
void NotifyTextureAttachment(u32 texaddr);
}

View File

@ -48,6 +48,7 @@ static const int VERSION = 2;
static bool active = false;
static bool nextFrame = false;
static bool writePending = false;
static std::function<void(const std::string &)> writeCallback;
enum class CommandType : u8 {
INIT = 0,
@ -409,7 +410,7 @@ static void WriteCompressed(FILE *fp, const void *p, size_t sz) {
delete [] compressed;
}
static void WriteRecording() {
static std::string WriteRecording() {
FlushRegisters();
EmitDisplayBuf();
@ -430,6 +431,8 @@ static void WriteRecording() {
WriteCompressed(fp, pushbuf.data(), bufsz);
fclose(fp);
return filename;
}
static void GetVertDataSizes(int vcount, const void *indices, u32 &vbytes, u32 &ibytes) {
@ -637,8 +640,35 @@ bool IsActive() {
return active;
}
void Activate() {
nextFrame = true;
bool IsActivePending() {
return nextFrame || active;
}
bool Activate() {
if (!nextFrame) {
nextFrame = true;
return true;
}
return false;
}
void SetCallback(const std::function<void(const std::string &)> callback) {
writeCallback = callback;
}
static void FinishRecording() {
// We're done - this was just to write the result out.
std::string filename = WriteRecording();
commands.clear();
pushbuf.clear();
NOTICE_LOG(SYSTEM, "Recording finished");
writePending = false;
active = false;
if (writeCallback)
writeCallback(filename);
writeCallback = nullptr;
}
void NotifyCommand(u32 pc) {
@ -646,14 +676,7 @@ void NotifyCommand(u32 pc) {
return;
}
if (writePending) {
WriteRecording();
commands.clear();
pushbuf.clear();
writePending = false;
// We're done - this was just to write the result out.
NOTICE_LOG(SYSTEM, "Recording finished");
active = false;
FinishRecording();
return;
}
@ -749,12 +772,12 @@ void NotifyUpload(u32 dest, u32 sz) {
}
void NotifyFrame() {
if (active && !writePending) {
if (active && !writePending && !commands.empty()) {
// Delay write until the first command of the next frame, so we get the right display buf.
NOTICE_LOG(SYSTEM, "Recording complete - waiting to get display buffer");
writePending = true;
}
if (nextFrame) {
if (nextFrame && (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) {
NOTICE_LOG(SYSTEM, "Recording starting...");
active = true;
nextFrame = false;

View File

@ -17,13 +17,17 @@
#pragma once
#include <functional>
#include <string>
#include "Common/CommonTypes.h"
namespace GPURecord {
bool IsActive();
void Activate();
bool IsActivePending();
bool Activate();
// Call only if Activate() returns true.
void SetCallback(const std::function<void(const std::string &)> callback);
void NotifyCommand(u32 pc);
void NotifyMemcpy(u32 dest, u32 src, u32 sz);

View File

@ -38,6 +38,7 @@ enum PauseAction {
};
static bool isStepping;
static int stepCounter = 0;
static std::mutex pauseLock;
static std::condition_variable pauseWait;
@ -149,7 +150,7 @@ bool SingleStep() {
return true;
}
bool EnterStepping(std::function<void()> callback) {
bool EnterStepping() {
std::unique_lock<std::mutex> guard(pauseLock);
if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) {
// Shutting down, don't try to step.
@ -170,8 +171,7 @@ bool EnterStepping(std::function<void()> callback) {
pauseAction = PAUSE_BREAK;
}
isStepping = true;
callback();
stepCounter++;
do {
RunPauseAction();
@ -187,6 +187,10 @@ bool IsStepping() {
return isStepping;
}
int GetSteppingCounter() {
return stepCounter;
}
static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) {
if (!isStepping && coreState != CORE_STEPPING) {
return false;

View File

@ -24,12 +24,12 @@
#include "GPU/Common/GPUDebugInterface.h"
namespace GPUStepping {
// Should be called from the GPU thread.
// Begins stepping and calls callback while inside a lock preparing stepping.
// This would be a good place to deliver a message to code that stepping is ready.
bool EnterStepping(std::function<void()> callback);
// Should be called from the emu thread.
// Begins stepping and increments the stepping counter while inside a lock.
bool EnterStepping();
bool SingleStep();
bool IsStepping();
int GetSteppingCounter();
bool GPU_GetOutputFramebuffer(const GPUDebugBuffer *&buffer);
bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type);

View File

@ -20,7 +20,6 @@
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
@ -36,6 +35,7 @@
#include "GPU/Common/TransformCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Directx9/TextureCacheDX9.h"
#include "GPU/Directx9/DrawEngineDX9.h"
#include "GPU/Directx9/ShaderManagerDX9.h"
@ -621,7 +621,7 @@ rotateVBO:
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
host->GPUNotifyDraw();
GPUDebug::NotifyDraw();
}
void DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords)

View File

@ -39,6 +39,7 @@
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Directx9/ShaderManagerDX9.h"
#include "GPU/Directx9/GPU_DX9.h"
#include "GPU/Directx9/FramebufferDX9.h"
@ -230,7 +231,7 @@ void GPU_DX9::BeginFrame() {
}
void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
host->GPUNotifyDisplay(framebuf, stride, format);
GPUDebug::NotifyDisplay(framebuf, stride, format);
framebufferManagerDX9_->SetDisplayFramebuffer(framebuf, stride, format);
}

View File

@ -20,7 +20,6 @@
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
@ -37,6 +36,7 @@
#include "GPU/Common/SplineCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/GLES/FragmentTestCacheGLES.h"
#include "GPU/GLES/StateMappingGLES.h"
#include "GPU/GLES/TextureCacheGLES.h"
@ -648,9 +648,7 @@ rotateVBO:
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
#ifndef MOBILE_DEVICE
host->GPUNotifyDraw();
#endif
GPUDebug::NotifyDraw();
}
bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const {

View File

@ -35,7 +35,7 @@
#include "GPU/ge_constants.h"
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/GLES/ShaderManagerGLES.h"
#include "GPU/GLES/GPU_GLES.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
@ -443,7 +443,7 @@ void GPU_GLES::BeginFrame() {
}
void GPU_GLES::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
host->GPUNotifyDisplay(framebuf, stride, format);
GPUDebug::NotifyDisplay(framebuf, stride, format);
framebufferManagerGL_->SetDisplayFramebuffer(framebuf, stride, format);
}

View File

@ -217,6 +217,7 @@
<ClInclude Include="D3D11\TextureScalerD3D11.h" />
<ClInclude Include="D3D11\VertexShaderGeneratorD3D11.h" />
<ClInclude Include="Debugger\Breakpoints.h" />
<ClInclude Include="Debugger\Debugger.h" />
<ClInclude Include="Debugger\Record.h" />
<ClInclude Include="Debugger\Stepping.h" />
<ClInclude Include="Directx9\DepalettizeShaderDX9.h" />
@ -320,6 +321,7 @@
<ClCompile Include="D3D11\TextureScalerD3D11.cpp" />
<ClCompile Include="D3D11\VertexShaderGeneratorD3D11.cpp" />
<ClCompile Include="Debugger\Breakpoints.cpp" />
<ClCompile Include="Debugger\Debugger.cpp" />
<ClCompile Include="Debugger\Record.cpp" />
<ClCompile Include="Debugger\Stepping.cpp" />
<ClCompile Include="Directx9\DepalettizeShaderDX9.cpp" />

View File

@ -273,6 +273,9 @@
<ClInclude Include="Vulkan\DebugVisVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Debugger\Debugger.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Math3D.cpp">
@ -542,5 +545,8 @@
<ClCompile Include="Vulkan\DebugVisVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Debugger\Debugger.cpp">
<Filter>Debugger</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -28,6 +28,7 @@
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Common/SplineCommon.h"
#include "GPU/Common/TextureCacheCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Debugger/Record.h"
const CommonCommandTableEntry commonCommandTable[] = {
@ -933,7 +934,7 @@ bool GPUCommon::InterpretList(DisplayList &list) {
gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
debugRecording_ = GPURecord::IsActive();
const bool useDebugger = host->GPUDebuggingActive() || debugRecording_;
const bool useDebugger = GPUDebug::IsActive() || debugRecording_;
const bool useFastRunLoop = !dumpThisFrame_ && !useDebugger;
while (gpuState == GPUSTATE_RUNNING) {
{
@ -1035,7 +1036,7 @@ void GPUCommon::SlowRunLoop(DisplayList &list)
const bool dumpThisFrame = dumpThisFrame_;
while (downcount > 0)
{
host->GPUNotifyCommand(list.pc);
GPUDebug::NotifyCommand(list.pc);
GPURecord::NotifyCommand(list.pc);
u32 op = Memory::ReadUnchecked_U32(list.pc);
u32 cmd = op >> 24;
@ -1586,10 +1587,8 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) {
if (!g_Config.bSoftwareSkinning)
vtypeCheckMask = 0xFFFFFFFF;
#ifndef MOBILE_DEVICE
if (debugRecording_ || host->GPUDebuggingActive())
if (debugRecording_ || GPUDebug::IsActive())
goto bail;
#endif
while (src != stall) {
uint32_t data = *src;

View File

@ -23,7 +23,6 @@
#include "Core/Config.h"
#include "Core/ConfigValues.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/Host.h"
#include "Core/MemMap.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/sceGe.h"
@ -39,6 +38,7 @@
#include "GPU/Software/TransformUnit.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Debugger/Record.h"
const int FB_WIDTH = 480;
@ -141,7 +141,7 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for
displayFramebuf_ = (framebuf & 0xFF000000) == 0 ? 0x44000000 | framebuf : framebuf;
displayStride_ = stride;
displayFormat_ = format;
host->GPUNotifyDisplay(framebuf, stride, format);
GPUDebug::NotifyDisplay(framebuf, stride, format);
}
// Copies RGBA8 data from RAM to the currently bound render target.

View File

@ -16,13 +16,12 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/MemoryUtil.h"
#include "Core/Host.h"
#include "Core/Config.h"
#include "GPU/GPUState.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SplineCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Software/TransformUnit.h"
#include "GPU/Software/Clipper.h"
#include "GPU/Software/Lighting.h"
@ -490,7 +489,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy
break;
}
host->GPUNotifyDraw();
GPUDebug::NotifyDraw();
}
// TODO: This probably is not the best interface.

View File

@ -25,7 +25,6 @@
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
@ -44,6 +43,7 @@
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Vulkan/DrawEngineVulkan.h"
#include "GPU/Vulkan/TextureCacheVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
@ -976,7 +976,7 @@ void DrawEngineVulkan::DoFlush() {
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
host->GPUNotifyDraw();
GPUDebug::NotifyDraw();
}
void DrawEngineVulkan::UpdateUBOs(FrameData *frame) {

View File

@ -26,7 +26,6 @@
#include "Core/Config.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/MemMapHelpers.h"
#include "Core/Host.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Core/System.h"
@ -36,7 +35,7 @@
#include "GPU/ge_constants.h"
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
#include "GPU/Vulkan/GPU_Vulkan.h"
#include "GPU/Vulkan/FramebufferVulkan.h"
@ -405,7 +404,7 @@ void GPU_Vulkan::UpdateVsyncInterval(bool force) {
}
void GPU_Vulkan::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
host->GPUNotifyDisplay(framebuf, stride, format);
GPUDebug::NotifyDisplay(framebuf, stride, format);
framebufferManager_->SetDisplayFramebuffer(framebuf, stride, format);
}

View File

@ -1268,6 +1268,12 @@ void DeveloperToolsScreen::CreateViews() {
cpuTests->SetEnabled(TestsAvailable());
#endif
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
allowDebugger->SetEnabledPtr(&canAllowDebugger_);
list->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged);
list->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics")));
list->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLogConfig);
@ -1284,12 +1290,6 @@ void DeveloperToolsScreen::CreateViews() {
createTextureIni->SetEnabled(false);
}
#endif
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
allowDebugger->SetEnabledPtr(&canAllowDebugger_);
}
void DeveloperToolsScreen::onFinish(DialogResult result) {

View File

@ -332,6 +332,7 @@
<ClInclude Include="..\..\GPU\D3D11\TextureScalerD3D11.h" />
<ClInclude Include="..\..\GPU\D3D11\VertexShaderGeneratorD3D11.h" />
<ClInclude Include="..\..\GPU\Debugger\Breakpoints.h" />
<ClInclude Include="..\..\GPU\Debugger\Debugger.h" />
<ClInclude Include="..\..\GPU\Debugger\Record.h" />
<ClInclude Include="..\..\GPU\Debugger\Stepping.h" />
<ClInclude Include="..\..\GPU\Directx9\PixelShaderGeneratorDX9.h" />
@ -390,6 +391,7 @@
<ClCompile Include="..\..\GPU\D3D11\TextureScalerD3D11.cpp" />
<ClCompile Include="..\..\GPU\D3D11\VertexShaderGeneratorD3D11.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Breakpoints.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Debugger.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Record.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Stepping.cpp" />
<ClCompile Include="..\..\GPU\Directx9\PixelShaderGeneratorDX9.cpp" />

View File

@ -150,13 +150,18 @@
<ClCompile Include="..\..\GPU\Debugger\Breakpoints.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="..\..\GPU\Debugger\Debugger.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="..\..\GPU\Debugger\Stepping.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="..\..\GPU\Common\ShaderCommon.cpp">
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="..\..\GPU\Software\Sampler.cpp" />
<ClCompile Include="..\..\GPU\Software\Sampler.cpp">
<Filter>Software</Filter>
</ClInclude>
<ClCompile Include="..\..\GPU\Debugger\Record.cpp">
<Filter>Debugger</Filter>
</ClCompile>
@ -291,10 +296,15 @@
<ClInclude Include="..\..\GPU\Debugger\Breakpoints.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="..\..\GPU\Debugger\Debugger.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="..\..\GPU\Debugger\Stepping.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="..\..\GPU\Software\Sampler.h" />
<ClInclude Include="..\..\GPU\Software\Sampler.h">
<Filter>Software</Filter>
</ClInclude>
<ClInclude Include="..\..\GPU\Debugger\Record.h">
<Filter>Debugger</Filter>
</ClInclude>

View File

@ -31,7 +31,6 @@
#include "Windows/GEDebugger/TabVertices.h"
#include "Windows/W32Util/ShellUtil.h"
#include "Windows/InputBox.h"
#include "Windows/WindowsHost.h"
#include "Windows/MainWindow.h"
#include "Windows/main.h"
#include "GPU/GPUInterface.h"
@ -39,6 +38,7 @@
#include "GPU/Common/GPUStateUtils.h"
#include "GPU/GPUState.h"
#include "GPU/Debugger/Breakpoints.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Debugger/Record.h"
#include "GPU/Debugger/Stepping.h"
#include <windowsx.h>
@ -47,12 +47,9 @@
const int POPUP_SUBMENU_ID_GEDBG_PREVIEW = 10;
using namespace GPUBreakpoints;
using namespace GPUDebug;
using namespace GPUStepping;
static bool attached = false;
static BreakNextType breakNext = BREAK_NONE;
enum PrimaryDisplayType {
PRIMARY_FRAMEBUF,
PRIMARY_DEPTHBUF,
@ -66,9 +63,6 @@ void CGEDebugger::Init() {
CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent)
: Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) {
GPUBreakpoints::Init();
Core_ListenStopRequest(ForceUnpause);
// minimum size = a little more than the default
RECT windowRect;
GetWindowRect(m_hDlg, &windowRect);
@ -129,6 +123,8 @@ CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent)
int h = g_Config.iGEWindowH == -1 ? minHeight_ : g_Config.iGEWindowH;
MoveWindow(m_hDlg,x,y,w,h,FALSE);
SetTimer(m_hDlg, 1, USER_TIMER_MINIMUM, nullptr);
UpdateTextureLevel(textureLevel_);
}
@ -318,6 +314,8 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) {
bool bufferResult = false;
u32 flags = SimpleGLWindow::ALPHA_IGNORE | SimpleGLWindow::RESIZE_SHRINK_CENTER;
SetupPreviews();
primaryBuffer_ = nullptr;
if (showClut_) {
bufferResult = GPU_GetCurrentTexture(primaryBuffer_, textureLevel_);
@ -362,6 +360,8 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) {
void CGEDebugger::UpdateSecondPreview(const GPUgstate &state) {
bool bufferResult = false;
SetupPreviews();
secondBuffer_ = nullptr;
if (showClut_) {
bufferResult = GPU_GetCurrentClut(secondBuffer_);
@ -404,6 +404,8 @@ void CGEDebugger::PrimaryPreviewHover(int x, int y) {
return;
}
SetupPreviews();
wchar_t desc[256] = {0};
if (!primaryWindow->HasTex()) {
@ -626,14 +628,6 @@ void CGEDebugger::SavePosition()
}
}
void CGEDebugger::SetBreakNext(BreakNextType type) {
attached = true;
SetupPreviews();
breakNext = type;
ResumeFromStepping();
}
BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG:
@ -657,9 +651,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
return TRUE;
case WM_CLOSE:
attached = false;
ResumeFromStepping();
breakNext = BREAK_NONE;
GPUDebug::SetActive(false);
Show(false);
return TRUE;
@ -674,6 +666,16 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
}
break;
case WM_TIMER:
if (GPUStepping::IsStepping()) {
static int lastCounter = 0;
if (lastCounter != GPUStepping::GetSteppingCounter()) {
UpdatePreviews();
lastCounter = GPUStepping::GetSteppingCounter();
}
}
break;
case WM_NOTIFY:
switch (wParam)
{
@ -685,7 +687,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
break;
case IDC_GEDBG_FBTABS:
fbTabs->HandleNotify(lParam);
if (attached && gpuDebug != nullptr) {
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
UpdatePreviews();
}
break;
@ -695,32 +697,28 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_GEDBG_STEPDRAW:
SetBreakNext(BREAK_NEXT_DRAW);
SetBreakNext(BreakNext::DRAW);
break;
case IDC_GEDBG_STEP:
SetBreakNext(BREAK_NEXT_OP);
SetBreakNext(BreakNext::OP);
break;
case IDC_GEDBG_STEPTEX:
AddTextureChangeTempBreakpoint();
SetBreakNext(BREAK_NEXT_TEX);
SetBreakNext(BreakNext::TEX);
break;
case IDC_GEDBG_STEPFRAME:
SetBreakNext(BREAK_NEXT_FRAME);
SetBreakNext(BreakNext::FRAME);
break;
case IDC_GEDBG_STEPPRIM:
AddCmdBreakpoint(GE_CMD_PRIM, true);
AddCmdBreakpoint(GE_CMD_BEZIER, true);
AddCmdBreakpoint(GE_CMD_SPLINE, true);
SetBreakNext(BREAK_NEXT_PRIM);
SetBreakNext(BreakNext::PRIM);
break;
case IDC_GEDBG_BREAKTEX:
{
attached = true;
GPUDebug::SetActive(true);
if (!gpuDebug) {
break;
}
@ -739,7 +737,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case IDC_GEDBG_BREAKTARGET:
{
attached = true;
GPUDebug::SetActive(true);
if (!gpuDebug) {
break;
}
@ -758,26 +756,26 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case IDC_GEDBG_TEXLEVELDOWN:
UpdateTextureLevel(textureLevel_ - 1);
if (attached && gpuDebug != nullptr) {
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
UpdatePreviews();
}
break;
case IDC_GEDBG_TEXLEVELUP:
UpdateTextureLevel(textureLevel_ + 1);
if (attached && gpuDebug != nullptr) {
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
UpdatePreviews();
}
break;
case IDC_GEDBG_RESUME:
SetupPreviews();
primaryWindow->Clear();
secondWindow->Clear();
SetDlgItemText(m_hDlg, IDC_GEDBG_FRAMEBUFADDR, L"");
SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L"");
ResumeFromStepping();
breakNext = BREAK_NONE;
SetBreakNext(BreakNext::NONE);
break;
case IDC_GEDBG_RECORD:
@ -785,14 +783,14 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
break;
case IDC_GEDBG_FORCEOPAQUE:
if (attached && gpuDebug != nullptr) {
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
forceOpaque_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_FORCEOPAQUE), BM_GETCHECK, 0, 0) != 0;
UpdatePreviews();
}
break;
case IDC_GEDBG_SHOWCLUT:
if (attached && gpuDebug != nullptr) {
if (GPUDebug::IsActive() && gpuDebug != nullptr) {
showClut_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_SHOWCLUT), BM_GETCHECK, 0, 0) != 0;
UpdatePreviews();
}
@ -800,30 +798,13 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
}
break;
case WM_GEDBG_BREAK_CMD:
{
u32 pc = (u32)wParam;
ClearTempBreakpoints();
auto info = gpuDebug->DissassembleOp(pc);
NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str());
UpdatePreviews();
}
break;
case WM_GEDBG_BREAK_DRAW:
{
NOTICE_LOG(G3D, "Waiting at a draw");
UpdatePreviews();
}
break;
case WM_GEDBG_STEPDISPLAYLIST:
SetBreakNext(BREAK_NEXT_OP);
SetBreakNext(BreakNext::OP);
break;
case WM_GEDBG_TOGGLEPCBREAKPOINT:
{
attached = true;
GPUDebug::SetActive(true);
u32 pc = (u32)wParam;
bool temp;
bool isBreak = IsAddressBreakpoint(pc, temp);
@ -837,7 +818,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case WM_GEDBG_RUNTOWPARAM:
{
attached = true;
GPUDebug::SetActive(true);
u32 pc = (u32)wParam;
AddAddressBreakpoint(pc, true);
SendMessage(m_hDlg,WM_COMMAND,IDC_GEDBG_RESUME,0);
@ -845,9 +826,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
break;
case WM_GEDBG_SETCMDWPARAM:
{
GPU_SetCmdValue((u32)wParam);
}
GPU_SetCmdValue((u32)wParam);
break;
case WM_GEDBG_UPDATE_WATCH:
@ -859,44 +838,3 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
return FALSE;
}
// The below WindowsHost methods are called on the GPU thread.
bool WindowsHost::GPUDebuggingActive() {
return attached;
}
static void DeliverMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
PostMessage(geDebuggerWindow->GetDlgHandle(), msg, wParam, lParam);
}
static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) {
if (attached) {
EnterStepping(std::bind(&DeliverMessage, msg, wParam, lParam));
}
}
void WindowsHost::GPUNotifyCommand(u32 pc) {
u32 op = Memory::ReadUnchecked_U32(pc);
u8 cmd = op >> 24;
if (breakNext == BREAK_NEXT_OP || IsBreakpoint(pc, op)) {
PauseWithMessage(WM_GEDBG_BREAK_CMD, (WPARAM) pc);
}
}
void WindowsHost::GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {
if (breakNext == BREAK_NEXT_FRAME) {
// This should work fine, start stepping at the first op of the new frame.
breakNext = BREAK_NEXT_OP;
}
}
void WindowsHost::GPUNotifyDraw() {
if (breakNext == BREAK_NEXT_DRAW) {
PauseWithMessage(WM_GEDBG_BREAK_DRAW);
}
}
void WindowsHost::GPUNotifyTextureAttachment(u32 addr) {
}

View File

@ -19,31 +19,20 @@
#include "Common/CommonWindows.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Debugger/Debugger.h"
#include "Windows/resource.h"
#include "Windows/W32Util/DialogManager.h"
#include "Windows/W32Util/TabControl.h"
#include "Windows/GEDebugger/SimpleGLWindow.h"
enum {
WM_GEDBG_BREAK_CMD = WM_USER + 200,
WM_GEDBG_BREAK_DRAW,
WM_GEDBG_STEPDISPLAYLIST,
WM_GEDBG_STEPDISPLAYLIST = WM_USER + 200,
WM_GEDBG_TOGGLEPCBREAKPOINT,
WM_GEDBG_RUNTOWPARAM,
WM_GEDBG_SETCMDWPARAM,
WM_GEDBG_UPDATE_WATCH,
};
enum BreakNextType {
BREAK_NONE,
BREAK_NEXT_OP,
BREAK_NEXT_DRAW,
BREAK_NEXT_TEX,
BREAK_NEXT_NONTEX,
BREAK_NEXT_FRAME,
BREAK_NEXT_PRIM,
};
class CtrlDisplayListView;
class TabDisplayLists;
class TabStateFlags;
@ -76,7 +65,6 @@ private:
void HandleRedraw(int which);
void UpdateSize(WORD width, WORD height);
void SavePosition();
void SetBreakNext(BreakNextType type);
void UpdateTextureLevel(int level);
void DescribePrimaryPreview(const GPUgstate &state, wchar_t desc[256]);
void DescribeSecondPreview(const GPUgstate &state, wchar_t desc[256]);

View File

@ -55,11 +55,6 @@ public:
void SaveSymbolMap() override;
void SetWindowTitle(const char *message) override;
bool GPUDebuggingActive() override;
void GPUNotifyCommand(u32 pc) override;
void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) override;
void GPUNotifyDraw() override;
void GPUNotifyTextureAttachment(u32 addr) override;
void ToggleDebugConsoleVisibility() override;
bool CanCreateShortcut() override;

View File

@ -242,6 +242,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/GPU/Common/PostShader.cpp \
$(SRC)/GPU/Common/ShaderUniforms.cpp \
$(SRC)/GPU/Debugger/Breakpoints.cpp \
$(SRC)/GPU/Debugger/Debugger.cpp \
$(SRC)/GPU/Debugger/Record.cpp \
$(SRC)/GPU/Debugger/Stepping.cpp \
$(SRC)/GPU/GLES/FramebufferManagerGLES.cpp \
@ -309,6 +310,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \
$(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/GPURecordSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/HLESubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \
$(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \

View File

@ -168,8 +168,9 @@ SOURCES_CXX += \
$(GPUCOMMONDIR)/PostShader.cpp \
$(COMMONDIR)/ColorConv.cpp \
$(GPUDIR)/Debugger/Breakpoints.cpp \
$(GPUDIR)/Debugger/Stepping.cpp \
$(GPUDIR)/Debugger/Debugger.cpp \
$(GPUDIR)/Debugger/Record.cpp \
$(GPUDIR)/Debugger/Stepping.cpp \
$(GPUDIR)/Common/TextureCacheCommon.cpp \
$(GPUDIR)/Common/TextureScalerCommon.cpp \
$(GPUDIR)/Common/SoftwareTransformCommon.cpp \