Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Debugger/Stepping.cpp
3186 views
1
// Copyright (c) 2013- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <mutex>
19
#include <condition_variable>
20
21
#include "Common/Log.h"
22
#include "Common/Thread/ThreadUtil.h"
23
#include "Core/Core.h"
24
#include "Core/HW/Display.h"
25
#include "GPU/Common/GPUDebugInterface.h"
26
#include "GPU/Debugger/Stepping.h"
27
#include "GPU/GPUState.h"
28
29
namespace GPUStepping {
30
31
enum PauseAction {
32
PAUSE_CONTINUE,
33
PAUSE_BREAK,
34
PAUSE_GETOUTPUTBUF,
35
PAUSE_GETFRAMEBUF,
36
PAUSE_GETDEPTHBUF,
37
PAUSE_GETSTENCILBUF,
38
PAUSE_GETTEX,
39
PAUSE_GETCLUT,
40
PAUSE_SETCMDVALUE,
41
PAUSE_FLUSHDRAW,
42
};
43
44
static bool isStepping;
45
// Number of times we've entered stepping, to detect a resume asynchronously.
46
static int stepCounter = 0;
47
48
static std::mutex pauseLock;
49
static PauseAction pauseAction = PAUSE_CONTINUE;
50
static std::mutex actionLock;
51
static std::condition_variable actionWait;
52
// In case of accidental wakeup.
53
static volatile bool actionComplete;
54
55
// Many things need to run on the GPU thread. For example, reading the framebuffer.
56
// A message system is used to achieve this (temporarily "unpausing" the thread.)
57
// Below are values used to perform actions that return results.
58
59
static bool bufferResult;
60
static GPUDebugFramebufferType bufferType = GPU_DBG_FRAMEBUF_RENDER;
61
static GPUDebugBuffer bufferFrame;
62
static GPUDebugBuffer bufferDepth;
63
static GPUDebugBuffer bufferStencil;
64
static GPUDebugBuffer bufferTex;
65
static GPUDebugBuffer bufferClut;
66
static int bufferLevel;
67
static bool lastWasFramebuffer;
68
static u32 pauseSetCmdValue;
69
70
// This is used only to highlight differences. Should really be owned by the debugger.
71
static GPUgstate lastGState;
72
73
const char *PauseActionToString(PauseAction action) {
74
switch (action) {
75
case PAUSE_CONTINUE: return "CONTINUE";
76
case PAUSE_BREAK: return "BREAK";
77
case PAUSE_GETOUTPUTBUF: return "GETOUTPUTBUF";
78
case PAUSE_GETFRAMEBUF: return "GETFRAMEBUF";
79
case PAUSE_GETDEPTHBUF: return "GETDEPTHBUF";
80
case PAUSE_GETSTENCILBUF: return "GETSTENCILBUF";
81
case PAUSE_GETTEX: return "GETTEX";
82
case PAUSE_GETCLUT: return "GETCLUT";
83
case PAUSE_SETCMDVALUE: return "SETCMDVALUE";
84
case PAUSE_FLUSHDRAW: return "FLUSHDRAW";
85
default: return "N/A";
86
}
87
}
88
89
static void SetPauseAction(PauseAction act, bool waitComplete = true) {
90
pauseLock.lock();
91
std::unique_lock<std::mutex> guard(actionLock);
92
pauseAction = act;
93
pauseLock.unlock();
94
95
// if (coreState == CORE_STEPPING && act != PAUSE_CONTINUE)
96
// Core_UpdateSingleStep();
97
actionComplete = false;
98
}
99
100
static void RunPauseAction() {
101
std::lock_guard<std::mutex> guard(actionLock);
102
if (pauseAction == PAUSE_BREAK) {
103
// Don't notify, just go back, woke up by accident.
104
return;
105
}
106
107
DEBUG_LOG(Log::GeDebugger, "RunPauseAction: %s", PauseActionToString(pauseAction));
108
109
switch (pauseAction) {
110
case PAUSE_BREAK:
111
break;
112
113
case PAUSE_GETOUTPUTBUF:
114
bufferResult = gpuDebug->GetOutputFramebuffer(bufferFrame);
115
break;
116
117
case PAUSE_GETFRAMEBUF:
118
bufferResult = gpuDebug->GetCurrentFramebuffer(bufferFrame, bufferType);
119
break;
120
121
case PAUSE_GETDEPTHBUF:
122
bufferResult = gpuDebug->GetCurrentDepthbuffer(bufferDepth);
123
break;
124
125
case PAUSE_GETSTENCILBUF:
126
bufferResult = gpuDebug->GetCurrentStencilbuffer(bufferStencil);
127
break;
128
129
case PAUSE_GETTEX:
130
bufferResult = gpuDebug->GetCurrentTexture(bufferTex, bufferLevel, &lastWasFramebuffer);
131
break;
132
133
case PAUSE_GETCLUT:
134
bufferResult = gpuDebug->GetCurrentClut(bufferClut);
135
break;
136
137
case PAUSE_SETCMDVALUE:
138
gpuDebug->SetCmdValue(pauseSetCmdValue);
139
break;
140
141
case PAUSE_FLUSHDRAW:
142
gpuDebug->Flush();
143
break;
144
145
default:
146
ERROR_LOG(Log::GeDebugger, "Unsupported pause action, forgot to add it to the switch.");
147
break;
148
}
149
150
actionComplete = true;
151
actionWait.notify_all();
152
153
pauseAction = PAUSE_BREAK;
154
}
155
156
void WaitForPauseAction() {
157
std::unique_lock<std::mutex> guard(actionLock);
158
actionWait.wait(guard);
159
}
160
161
bool ProcessStepping() {
162
_dbg_assert_(gpuDebug);
163
164
std::unique_lock<std::mutex> guard(pauseLock);
165
if (coreState != CORE_STEPPING_GE) {
166
// Not stepping any more, don't try.
167
actionComplete = true;
168
actionWait.notify_all();
169
return false;
170
}
171
172
if (pauseAction == PAUSE_CONTINUE) {
173
// This is fine, can just mean to run to the next breakpoint/event.
174
DEBUG_LOG(Log::GeDebugger, "Continuing...");
175
actionComplete = true;
176
actionWait.notify_all();
177
coreState = CORE_RUNNING_GE;
178
return false;
179
}
180
181
RunPauseAction();
182
return true;
183
}
184
185
bool EnterStepping(CoreState coreState) {
186
_dbg_assert_(gpuDebug);
187
188
std::unique_lock<std::mutex> guard(pauseLock);
189
if (coreState == CORE_STEPPING_GE) {
190
// Already there. Should avoid this happening, I think.
191
return true;
192
}
193
if (coreState != CORE_RUNNING_CPU && coreState != CORE_RUNNING_GE) {
194
// ?? Shutting down, don't try to step.
195
actionComplete = true;
196
actionWait.notify_all();
197
return false;
198
}
199
200
// StartStepping
201
if (lastGState.cmdmem[1] == 0) {
202
lastGState = gstate;
203
// Play it safe so we don't keep resetting.
204
lastGState.cmdmem[1] |= 0x01000000;
205
}
206
207
isStepping = true;
208
stepCounter++;
209
210
// Just to be sure.
211
if (pauseAction == PAUSE_CONTINUE) {
212
pauseAction = PAUSE_BREAK;
213
}
214
215
::coreState = CORE_STEPPING_GE;
216
return true;
217
}
218
219
void ResumeFromStepping() {
220
lastGState = gstate;
221
isStepping = false;
222
SetPauseAction(PAUSE_CONTINUE, false);
223
}
224
225
bool IsStepping() {
226
return isStepping;
227
}
228
229
int GetSteppingCounter() {
230
return stepCounter;
231
}
232
233
// NOTE: This can't be called on the EmuThread!
234
static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) {
235
if (!isStepping && coreState != CORE_STEPPING_CPU) {
236
return false;
237
}
238
239
_dbg_assert_(strcmp(GetCurrentThreadName(), "EmuThread") != 0);
240
241
SetPauseAction(type);
242
WaitForPauseAction();
243
buffer = &resultBuffer;
244
return bufferResult;
245
}
246
247
bool GPU_GetOutputFramebuffer(const GPUDebugBuffer *&buffer) {
248
return GetBuffer(buffer, PAUSE_GETOUTPUTBUF, bufferFrame);
249
}
250
251
bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type) {
252
bufferType = type;
253
return GetBuffer(buffer, PAUSE_GETFRAMEBUF, bufferFrame);
254
}
255
256
bool GPU_GetCurrentDepthbuffer(const GPUDebugBuffer *&buffer) {
257
return GetBuffer(buffer, PAUSE_GETDEPTHBUF, bufferDepth);
258
}
259
260
bool GPU_GetCurrentStencilbuffer(const GPUDebugBuffer *&buffer) {
261
return GetBuffer(buffer, PAUSE_GETSTENCILBUF, bufferStencil);
262
}
263
264
bool GPU_GetCurrentTexture(const GPUDebugBuffer *&buffer, int level, bool *isFramebuffer) {
265
bufferLevel = level;
266
bool result = GetBuffer(buffer, PAUSE_GETTEX, bufferTex);
267
*isFramebuffer = lastWasFramebuffer;
268
return result;
269
}
270
271
bool GPU_GetCurrentClut(const GPUDebugBuffer *&buffer) {
272
return GetBuffer(buffer, PAUSE_GETCLUT, bufferClut);
273
}
274
275
bool GPU_SetCmdValue(u32 op) {
276
if (!isStepping && coreState != CORE_STEPPING_CPU) {
277
return false;
278
}
279
280
pauseSetCmdValue = op;
281
SetPauseAction(PAUSE_SETCMDVALUE);
282
return true;
283
}
284
285
bool GPU_FlushDrawing() {
286
if (!isStepping && coreState != CORE_STEPPING_CPU) {
287
return false;
288
}
289
290
SetPauseAction(PAUSE_FLUSHDRAW);
291
return true;
292
}
293
294
const GPUgstate &LastState() {
295
return lastGState;
296
}
297
298
} // namespace
299
300