CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Debugger/Stepping.cpp
Views: 1401
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 "Core/Core.h"
23
#include "GPU/Common/GPUDebugInterface.h"
24
#include "GPU/Debugger/Stepping.h"
25
#include "GPU/GPUState.h"
26
27
namespace GPUStepping {
28
29
enum PauseAction {
30
PAUSE_CONTINUE,
31
PAUSE_BREAK,
32
PAUSE_GETOUTPUTBUF,
33
PAUSE_GETFRAMEBUF,
34
PAUSE_GETDEPTHBUF,
35
PAUSE_GETSTENCILBUF,
36
PAUSE_GETTEX,
37
PAUSE_GETCLUT,
38
PAUSE_SETCMDVALUE,
39
PAUSE_FLUSHDRAW,
40
};
41
42
static bool isStepping;
43
// Number of times we've entered stepping, to detect a resume asynchronously.
44
static int stepCounter = 0;
45
46
static std::mutex pauseLock;
47
static std::condition_variable pauseWait;
48
static PauseAction pauseAction = PAUSE_CONTINUE;
49
static std::mutex actionLock;
50
static std::condition_variable actionWait;
51
// In case of accidental wakeup.
52
static volatile bool actionComplete;
53
54
// Many things need to run on the GPU thread. For example, reading the framebuffer.
55
// A message system is used to achieve this (temporarily "unpausing" the thread.)
56
// Below are values used to perform actions that return results.
57
58
static bool bufferResult;
59
static GPUDebugFramebufferType bufferType = GPU_DBG_FRAMEBUF_RENDER;
60
static GPUDebugBuffer bufferFrame;
61
static GPUDebugBuffer bufferDepth;
62
static GPUDebugBuffer bufferStencil;
63
static GPUDebugBuffer bufferTex;
64
static GPUDebugBuffer bufferClut;
65
static int bufferLevel;
66
static bool lastWasFramebuffer;
67
static u32 pauseSetCmdValue;
68
69
static GPUgstate lastGState;
70
71
static void SetPauseAction(PauseAction act, bool waitComplete = true) {
72
pauseLock.lock();
73
std::unique_lock<std::mutex> guard(actionLock);
74
pauseAction = act;
75
pauseLock.unlock();
76
77
if (coreState == CORE_STEPPING && act != PAUSE_CONTINUE)
78
Core_UpdateSingleStep();
79
80
actionComplete = false;
81
pauseWait.notify_all();
82
while (waitComplete && !actionComplete) {
83
actionWait.wait(guard);
84
}
85
}
86
87
static void RunPauseAction() {
88
std::lock_guard<std::mutex> guard(actionLock);
89
90
switch (pauseAction) {
91
case PAUSE_CONTINUE:
92
// Don't notify, just go back, woke up by accident.
93
return;
94
95
case PAUSE_BREAK:
96
break;
97
98
case PAUSE_GETOUTPUTBUF:
99
bufferResult = gpuDebug->GetOutputFramebuffer(bufferFrame);
100
break;
101
102
case PAUSE_GETFRAMEBUF:
103
bufferResult = gpuDebug->GetCurrentFramebuffer(bufferFrame, bufferType);
104
break;
105
106
case PAUSE_GETDEPTHBUF:
107
bufferResult = gpuDebug->GetCurrentDepthbuffer(bufferDepth);
108
break;
109
110
case PAUSE_GETSTENCILBUF:
111
bufferResult = gpuDebug->GetCurrentStencilbuffer(bufferStencil);
112
break;
113
114
case PAUSE_GETTEX:
115
bufferResult = gpuDebug->GetCurrentTexture(bufferTex, bufferLevel, &lastWasFramebuffer);
116
break;
117
118
case PAUSE_GETCLUT:
119
bufferResult = gpuDebug->GetCurrentClut(bufferClut);
120
break;
121
122
case PAUSE_SETCMDVALUE:
123
gpuDebug->SetCmdValue(pauseSetCmdValue);
124
break;
125
126
case PAUSE_FLUSHDRAW:
127
gpuDebug->DispatchFlush();
128
break;
129
130
default:
131
ERROR_LOG(Log::G3D, "Unsupported pause action, forgot to add it to the switch.");
132
}
133
134
actionComplete = true;
135
actionWait.notify_all();
136
pauseAction = PAUSE_BREAK;
137
}
138
139
static void StartStepping() {
140
if (lastGState.cmdmem[1] == 0) {
141
lastGState = gstate;
142
// Play it safe so we don't keep resetting.
143
lastGState.cmdmem[1] |= 0x01000000;
144
}
145
gpuDebug->NotifySteppingEnter();
146
isStepping = true;
147
}
148
149
static void StopStepping() {
150
gpuDebug->NotifySteppingExit();
151
lastGState = gstate;
152
isStepping = false;
153
}
154
155
bool SingleStep() {
156
std::unique_lock<std::mutex> guard(pauseLock);
157
if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && coreState != CORE_STEPPING) {
158
// Shutting down, don't try to step.
159
actionComplete = true;
160
actionWait.notify_all();
161
return false;
162
}
163
if (!gpuDebug || pauseAction == PAUSE_CONTINUE) {
164
actionComplete = true;
165
actionWait.notify_all();
166
return false;
167
}
168
169
StartStepping();
170
RunPauseAction();
171
StopStepping();
172
return true;
173
}
174
175
bool EnterStepping() {
176
std::unique_lock<std::mutex> guard(pauseLock);
177
if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && coreState != CORE_STEPPING) {
178
// Shutting down, don't try to step.
179
actionComplete = true;
180
actionWait.notify_all();
181
return false;
182
}
183
if (!gpuDebug) {
184
actionComplete = true;
185
actionWait.notify_all();
186
return false;
187
}
188
189
StartStepping();
190
191
// Just to be sure.
192
if (pauseAction == PAUSE_CONTINUE) {
193
pauseAction = PAUSE_BREAK;
194
}
195
stepCounter++;
196
197
do {
198
RunPauseAction();
199
pauseWait.wait(guard);
200
} while (pauseAction != PAUSE_CONTINUE);
201
202
StopStepping();
203
return true;
204
}
205
206
bool IsStepping() {
207
return isStepping;
208
}
209
210
int GetSteppingCounter() {
211
return stepCounter;
212
}
213
214
static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) {
215
if (!isStepping && coreState != CORE_STEPPING) {
216
return false;
217
}
218
219
SetPauseAction(type);
220
buffer = &resultBuffer;
221
return bufferResult;
222
}
223
224
bool GPU_GetOutputFramebuffer(const GPUDebugBuffer *&buffer) {
225
return GetBuffer(buffer, PAUSE_GETOUTPUTBUF, bufferFrame);
226
}
227
228
bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type) {
229
bufferType = type;
230
return GetBuffer(buffer, PAUSE_GETFRAMEBUF, bufferFrame);
231
}
232
233
bool GPU_GetCurrentDepthbuffer(const GPUDebugBuffer *&buffer) {
234
return GetBuffer(buffer, PAUSE_GETDEPTHBUF, bufferDepth);
235
}
236
237
bool GPU_GetCurrentStencilbuffer(const GPUDebugBuffer *&buffer) {
238
return GetBuffer(buffer, PAUSE_GETSTENCILBUF, bufferStencil);
239
}
240
241
bool GPU_GetCurrentTexture(const GPUDebugBuffer *&buffer, int level, bool *isFramebuffer) {
242
bufferLevel = level;
243
bool result = GetBuffer(buffer, PAUSE_GETTEX, bufferTex);
244
*isFramebuffer = lastWasFramebuffer;
245
return result;
246
}
247
248
bool GPU_GetCurrentClut(const GPUDebugBuffer *&buffer) {
249
return GetBuffer(buffer, PAUSE_GETCLUT, bufferClut);
250
}
251
252
bool GPU_SetCmdValue(u32 op) {
253
if (!isStepping && coreState != CORE_STEPPING) {
254
return false;
255
}
256
257
pauseSetCmdValue = op;
258
SetPauseAction(PAUSE_SETCMDVALUE);
259
return true;
260
}
261
262
bool GPU_FlushDrawing() {
263
if (!isStepping && coreState != CORE_STEPPING) {
264
return false;
265
}
266
267
SetPauseAction(PAUSE_FLUSHDRAW);
268
return true;
269
}
270
271
void ResumeFromStepping() {
272
SetPauseAction(PAUSE_CONTINUE, false);
273
}
274
275
void ForceUnpause() {
276
SetPauseAction(PAUSE_CONTINUE, false);
277
actionComplete = true;
278
actionWait.notify_all();
279
}
280
281
GPUgstate LastState() {
282
return lastGState;
283
}
284
285
} // namespace
286
287