Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/GPUCommon.cpp
5654 views
1
#include "ppsspp_config.h"
2
3
#include <algorithm> // std::remove
4
5
#include "Common/Profiler/Profiler.h"
6
7
#include "Common/GraphicsContext.h"
8
#include "Common/LogReporting.h"
9
#include "Common/Math/SIMDHeaders.h"
10
#include "Common/Serialize/Serializer.h"
11
#include "Common/Serialize/SerializeFuncs.h"
12
#include "Common/Serialize/SerializeList.h"
13
#include "Common/TimeUtil.h"
14
#include "GPU/GeDisasm.h"
15
#include "GPU/GPU.h"
16
#include "GPU/GPUCommon.h"
17
#include "GPU/GPUState.h"
18
#include "Core/Config.h"
19
#include "Core/Core.h"
20
#include "Core/CoreTiming.h"
21
#include "Core/Debugger/MemBlockInfo.h"
22
#include "Core/MemMap.h"
23
#include "Core/Reporting.h"
24
#include "Core/HLE/HLE.h"
25
#include "Core/HLE/ErrorCodes.h"
26
#include "Core/HLE/sceKernelMemory.h"
27
#include "Core/HLE/sceKernelInterrupt.h"
28
#include "Core/HLE/sceGe.h"
29
#include "Core/Util/PPGeDraw.h"
30
#include "Core/MemMapHelpers.h"
31
#include "GPU/Common/DrawEngineCommon.h"
32
#include "GPU/Common/FramebufferManagerCommon.h"
33
#include "GPU/Common/TextureCacheCommon.h"
34
#include "GPU/Common/SoftwareTransformCommon.h"
35
#include "GPU/Debugger/Debugger.h"
36
#include "GPU/Debugger/Record.h"
37
#include "GPU/Debugger/Stepping.h"
38
39
bool __KernelIsDispatchEnabled();
40
41
void GPUCommon::Flush() {
42
drawEngineCommon_->Flush();
43
}
44
45
GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) :
46
gfxCtx_(gfxCtx),
47
draw_(draw)
48
{
49
// This assert failed on GCC x86 32-bit (but not MSVC 32-bit!) before adding the
50
// "padding" field at the end. This is important for save state compatibility.
51
// The compiler was not rounding the struct size up to an 8 byte boundary, which
52
// you'd expect due to the int64 field, but the Linux ABI apparently does not require that.
53
static_assert(sizeof(DisplayList) == 456, "Bad DisplayList size");
54
55
Reinitialize();
56
gstate.Reset();
57
gstate_c.Reset();
58
gpuStats.Reset();
59
60
PPGeSetDrawContext(draw);
61
ResetMatrices();
62
}
63
64
void GPUCommon::BeginHostFrame(const DisplayLayoutConfig &config) {
65
ReapplyGfxState();
66
67
// TODO: Assume config may have changed - maybe move to resize.
68
gstate_c.Dirty(DIRTY_ALL);
69
70
UpdateCmdInfo();
71
72
UpdateMSAALevel(draw_);
73
CheckConfigChanged(config);
74
CheckDisplayResized();
75
CheckRenderResized(config);
76
}
77
78
void GPUCommon::EndHostFrame() {
79
// Probably not necessary.
80
if (draw_) {
81
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
82
}
83
}
84
85
void GPUCommon::Reinitialize() {
86
memset(dls, 0, sizeof(dls));
87
for (int i = 0; i < DisplayListMaxCount; ++i) {
88
dls[i].state = PSP_GE_DL_STATE_NONE;
89
dls[i].waitUntilTicks = 0;
90
}
91
92
nextListID = 0;
93
currentList = nullptr;
94
isbreak = false;
95
drawCompleteTicks = 0;
96
busyTicks = 0;
97
interruptsEnabled_ = true;
98
99
if (textureCache_)
100
textureCache_->Clear(true);
101
if (framebufferManager_)
102
framebufferManager_->DestroyAllFBOs();
103
}
104
105
int GPUCommon::EstimatePerVertexCost() {
106
// TODO: This is transform cost, also account for rasterization cost somehow... although it probably
107
// runs in parallel with transform.
108
109
// Also, this is all pure guesswork. If we can find a way to do measurements, that would be great.
110
111
// GTA wants a low value to run smooth, GoW wants a high value (otherwise it thinks things
112
// went too fast and starts doing all the work over again).
113
114
int cost = 20;
115
if (gstate.isLightingEnabled()) {
116
cost += 10;
117
118
for (int i = 0; i < 4; i++) {
119
if (gstate.isLightChanEnabled(i))
120
cost += 7;
121
}
122
}
123
124
if (gstate.getUVGenMode() != GE_TEXMAP_TEXTURE_COORDS) {
125
cost += 20;
126
}
127
int morphCount = gstate.getNumMorphWeights();
128
if (morphCount > 1) {
129
cost += 5 * morphCount;
130
}
131
return cost;
132
}
133
134
void GPUCommon::PopDLQueue() {
135
if(!dlQueue.empty()) {
136
dlQueue.pop_front();
137
if(!dlQueue.empty()) {
138
bool running = currentList->state == PSP_GE_DL_STATE_RUNNING;
139
currentList = &dls[dlQueue.front()];
140
if (running)
141
currentList->state = PSP_GE_DL_STATE_RUNNING;
142
} else {
143
currentList = nullptr;
144
}
145
}
146
}
147
148
bool GPUCommon::BusyDrawing() {
149
u32 state = DrawSync(1);
150
if (state == PSP_GE_LIST_DRAWING || state == PSP_GE_LIST_STALLING) {
151
if (currentList && currentList->state != PSP_GE_DL_STATE_PAUSED) {
152
return true;
153
}
154
}
155
return false;
156
}
157
158
void GPUCommon::NotifyConfigChanged() {
159
configChanged_ = true;
160
}
161
162
void GPUCommon::NotifyRenderResized(const DisplayLayoutConfig &config) {
163
renderResized_ = true;
164
}
165
166
void GPUCommon::NotifyDisplayResized() {
167
displayResized_ = true;
168
}
169
170
void GPUCommon::DumpNextFrame() {
171
dumpNextFrame_ = true;
172
}
173
174
u32 GPUCommon::DrawSync(int mode) {
175
gpuStats.numDrawSyncs++;
176
177
if (mode < 0 || mode > 1)
178
return SCE_KERNEL_ERROR_INVALID_MODE;
179
180
if (mode == 0) {
181
if (!__KernelIsDispatchEnabled()) {
182
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
183
}
184
if (__IsInInterrupt()) {
185
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
186
}
187
188
if (drawCompleteTicks > CoreTiming::GetTicks()) {
189
__GeWaitCurrentThread(GPU_SYNC_DRAW, 1, "GeDrawSync");
190
} else {
191
for (int i = 0; i < DisplayListMaxCount; ++i) {
192
if (dls[i].state == PSP_GE_DL_STATE_COMPLETED) {
193
dls[i].state = PSP_GE_DL_STATE_NONE;
194
}
195
}
196
}
197
return 0;
198
}
199
200
// If there's no current list, it must be complete.
201
DisplayList *top = NULL;
202
for (int i : dlQueue) {
203
if (dls[i].state != PSP_GE_DL_STATE_COMPLETED) {
204
top = &dls[i];
205
break;
206
}
207
}
208
if (!top || top->state == PSP_GE_DL_STATE_COMPLETED)
209
return PSP_GE_LIST_COMPLETED;
210
211
if (currentList->pc == currentList->stall)
212
return PSP_GE_LIST_STALLING;
213
214
return PSP_GE_LIST_DRAWING;
215
}
216
217
void GPUCommon::CheckDrawSync() {
218
if (dlQueue.empty()) {
219
for (int i = 0; i < DisplayListMaxCount; ++i)
220
dls[i].state = PSP_GE_DL_STATE_NONE;
221
}
222
}
223
224
int GPUCommon::ListSync(int listid, int mode) {
225
gpuStats.numListSyncs++;
226
227
if (listid < 0 || listid >= DisplayListMaxCount)
228
return SCE_KERNEL_ERROR_INVALID_ID;
229
230
if (mode < 0 || mode > 1)
231
return SCE_KERNEL_ERROR_INVALID_MODE;
232
233
DisplayList& dl = dls[listid];
234
if (mode == 1) {
235
switch (dl.state) {
236
case PSP_GE_DL_STATE_QUEUED:
237
if (dl.interrupted)
238
return PSP_GE_LIST_PAUSED;
239
return PSP_GE_LIST_QUEUED;
240
241
case PSP_GE_DL_STATE_RUNNING:
242
if (dl.pc == dl.stall)
243
return PSP_GE_LIST_STALLING;
244
return PSP_GE_LIST_DRAWING;
245
246
case PSP_GE_DL_STATE_COMPLETED:
247
return PSP_GE_LIST_COMPLETED;
248
249
case PSP_GE_DL_STATE_PAUSED:
250
return PSP_GE_LIST_PAUSED;
251
252
default:
253
return SCE_KERNEL_ERROR_INVALID_ID;
254
}
255
}
256
257
if (!__KernelIsDispatchEnabled()) {
258
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
259
}
260
if (__IsInInterrupt()) {
261
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
262
}
263
264
if (dl.waitUntilTicks > CoreTiming::GetTicks()) {
265
__GeWaitCurrentThread(GPU_SYNC_LIST, listid, "GeListSync");
266
}
267
return PSP_GE_LIST_COMPLETED;
268
}
269
270
int GPUCommon::GetStack(int index, u32 stackPtr) {
271
if (!currentList) {
272
// Seems like it doesn't return an error code?
273
return 0;
274
}
275
276
if (currentList->stackptr <= index) {
277
return SCE_KERNEL_ERROR_INVALID_INDEX;
278
}
279
280
if (index >= 0) {
281
auto stack = PSPPointer<u32_le>::Create(stackPtr);
282
if (stack.IsValid()) {
283
auto entry = currentList->stack[index];
284
// Not really sure what most of these values are.
285
stack[0] = 0;
286
stack[1] = entry.pc + 4;
287
stack[2] = entry.offsetAddr;
288
stack[7] = entry.baseAddr;
289
}
290
}
291
292
return currentList->stackptr;
293
}
294
295
static void CopyMatrix24(u32_le *result, const float *mtx, u32 count, u32 cmdbits) {
296
// Screams out for simple SIMD, but probably not called often enough to be worth it.
297
for (u32 i = 0; i < count; ++i) {
298
result[i] = toFloat24(mtx[i]) | cmdbits;
299
}
300
}
301
302
bool GPUCommon::GetMatrix24(GEMatrixType type, u32_le *result, u32 cmdbits) {
303
switch (type) {
304
case GE_MTX_BONE0:
305
case GE_MTX_BONE1:
306
case GE_MTX_BONE2:
307
case GE_MTX_BONE3:
308
case GE_MTX_BONE4:
309
case GE_MTX_BONE5:
310
case GE_MTX_BONE6:
311
case GE_MTX_BONE7:
312
CopyMatrix24(result, gstate.boneMatrix + (type - GE_MTX_BONE0) * 12, 12, cmdbits);
313
break;
314
case GE_MTX_TEXGEN:
315
CopyMatrix24(result, gstate.tgenMatrix, 12, cmdbits);
316
break;
317
case GE_MTX_WORLD:
318
CopyMatrix24(result, gstate.worldMatrix, 12, cmdbits);
319
break;
320
case GE_MTX_VIEW:
321
CopyMatrix24(result, gstate.viewMatrix, 12, cmdbits);
322
break;
323
case GE_MTX_PROJECTION:
324
CopyMatrix24(result, gstate.projMatrix, 16, cmdbits);
325
break;
326
default:
327
return false;
328
}
329
return true;
330
}
331
332
void GPUCommon::ResetMatrices() {
333
// This means we restored a context, so update the visible matrix data.
334
for (size_t i = 0; i < ARRAY_SIZE(gstate.boneMatrix); ++i)
335
matrixVisible.bone[i] = toFloat24(gstate.boneMatrix[i]);
336
for (size_t i = 0; i < ARRAY_SIZE(gstate.worldMatrix); ++i)
337
matrixVisible.world[i] = toFloat24(gstate.worldMatrix[i]);
338
for (size_t i = 0; i < ARRAY_SIZE(gstate.viewMatrix); ++i)
339
matrixVisible.view[i] = toFloat24(gstate.viewMatrix[i]);
340
for (size_t i = 0; i < ARRAY_SIZE(gstate.projMatrix); ++i)
341
matrixVisible.proj[i] = toFloat24(gstate.projMatrix[i]);
342
for (size_t i = 0; i < ARRAY_SIZE(gstate.tgenMatrix); ++i)
343
matrixVisible.tgen[i] = toFloat24(gstate.tgenMatrix[i]);
344
345
// Assume all the matrices changed, so dirty things related to them.
346
gstate_c.Dirty(DIRTY_WORLDMATRIX | DIRTY_VIEWMATRIX | DIRTY_PROJMATRIX | DIRTY_TEXMATRIX | DIRTY_FRAGMENTSHADER_STATE | DIRTY_BONE_UNIFORMS);
347
}
348
349
u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointer<PspGeListArgs> args, bool head, bool *runList) {
350
*runList = false;
351
352
// TODO Check the stack values in missing arg and ajust the stack depth
353
354
// Check alignment
355
// TODO Check the context and stack alignement too
356
if (((listpc | stall) & 3) != 0 || !Memory::IsValidAddress(listpc)) {
357
ERROR_LOG_REPORT(Log::G3D, "sceGeListEnqueue: invalid address %08x", listpc);
358
return SCE_KERNEL_ERROR_INVALID_POINTER;
359
}
360
361
// If args->size is below 16, it's the old struct without stack info.
362
if (args.IsValid() && args->size >= 16 && args->numStacks >= 256) {
363
ERROR_LOG(Log::G3D, "invalid stack depth %d", args->numStacks);
364
return SCE_KERNEL_ERROR_INVALID_SIZE;
365
}
366
367
int id = -1;
368
u64 currentTicks = CoreTiming::GetTicks();
369
u32 stackAddr = args.IsValid() && args->size >= 16 ? (u32)args->stackAddr : 0;
370
// Check compatibility
371
if (sceKernelGetCompiledSdkVersion() > 0x01FFFFFF) {
372
//numStacks = 0;
373
//stack = NULL;
374
for (int i = 0; i < DisplayListMaxCount; ++i) {
375
if (dls[i].state != PSP_GE_DL_STATE_NONE && dls[i].state != PSP_GE_DL_STATE_COMPLETED) {
376
// Logically, if the CPU has not interrupted yet, it hasn't seen the latest pc either.
377
// Exit enqueues right after an END, which fails without ignoring pendingInterrupt lists.
378
if (dls[i].pc == listpc && !dls[i].pendingInterrupt) {
379
ERROR_LOG(Log::G3D, "sceGeListEnqueue: can't enqueue, list address %08X already used", listpc);
380
return 0x80000021;
381
} else if (stackAddr != 0 && dls[i].stackAddr == stackAddr && !dls[i].pendingInterrupt) {
382
ERROR_LOG(Log::G3D, "sceGeListEnqueue: can't enqueue, stack address %08X already used", stackAddr);
383
if (!PSP_CoreParameter().compat.flags().IgnoreEnqueue) {
384
return 0x80000021;
385
}
386
}
387
}
388
}
389
}
390
// TODO Check if list stack dls[i].stack already used then return 0x80000021 as above
391
392
for (int i = 0; i < DisplayListMaxCount; ++i) {
393
int possibleID = (i + nextListID) % DisplayListMaxCount;
394
auto possibleList = dls[possibleID];
395
if (possibleList.pendingInterrupt) {
396
continue;
397
}
398
399
if (possibleList.state == PSP_GE_DL_STATE_NONE) {
400
id = possibleID;
401
break;
402
}
403
if (possibleList.state == PSP_GE_DL_STATE_COMPLETED && possibleList.waitUntilTicks < currentTicks) {
404
id = possibleID;
405
}
406
}
407
if (id < 0) {
408
ERROR_LOG_REPORT(Log::G3D, "No DL ID available to enqueue");
409
for (int i : dlQueue) {
410
DisplayList &dl = dls[i];
411
DEBUG_LOG(Log::G3D, "DisplayList %d status %d pc %08x stall %08x", i, dl.state, dl.pc, dl.stall);
412
}
413
return SCE_KERNEL_ERROR_OUT_OF_MEMORY;
414
}
415
nextListID = id + 1;
416
417
DisplayList &dl = dls[id];
418
dl.id = id;
419
dl.startpc = listpc & 0x0FFFFFFF;
420
dl.pc = listpc & 0x0FFFFFFF;
421
dl.stall = stall & 0x0FFFFFFF;
422
dl.subIntrBase = std::max(subIntrBase, -1);
423
dl.stackptr = 0;
424
dl.signal = PSP_GE_SIGNAL_NONE;
425
dl.interrupted = false;
426
dl.waitUntilTicks = (u64)-1;
427
dl.interruptsEnabled = interruptsEnabled_;
428
dl.started = false;
429
dl.offsetAddr = 0;
430
dl.bboxResult = false;
431
dl.stackAddr = stackAddr;
432
433
if (args.IsValid() && args->context.IsValid())
434
dl.context = args->context;
435
else
436
dl.context = 0;
437
438
if (head) {
439
if (currentList) {
440
if (currentList->state != PSP_GE_DL_STATE_PAUSED)
441
return SCE_KERNEL_ERROR_INVALID_VALUE;
442
currentList->state = PSP_GE_DL_STATE_QUEUED;
443
// Make sure we clear the signal so we don't try to pause it again.
444
currentList->signal = PSP_GE_SIGNAL_NONE;
445
}
446
447
dl.state = PSP_GE_DL_STATE_PAUSED;
448
449
currentList = &dl;
450
dlQueue.push_front(id);
451
} else if (currentList) {
452
dl.state = PSP_GE_DL_STATE_QUEUED;
453
dlQueue.push_back(id);
454
} else {
455
dl.state = PSP_GE_DL_STATE_RUNNING;
456
currentList = &dl;
457
dlQueue.push_front(id);
458
459
drawCompleteTicks = (u64)-1;
460
461
// TODO save context when starting the list if param is set
462
// LATER: Wait, what? Please explain.
463
*runList = true;
464
}
465
return id;
466
}
467
468
u32 GPUCommon::DequeueList(int listid) {
469
if (listid < 0 || listid >= DisplayListMaxCount || dls[listid].state == PSP_GE_DL_STATE_NONE)
470
return SCE_KERNEL_ERROR_INVALID_ID;
471
472
auto &dl = dls[listid];
473
if (dl.started)
474
return SCE_KERNEL_ERROR_BUSY;
475
476
dl.state = PSP_GE_DL_STATE_NONE;
477
478
if (listid == dlQueue.front())
479
PopDLQueue();
480
else
481
dlQueue.remove(listid);
482
483
dl.waitUntilTicks = 0;
484
__GeTriggerWait(GPU_SYNC_LIST, listid);
485
486
CheckDrawSync();
487
return 0;
488
}
489
490
u32 GPUCommon::UpdateStall(int listid, u32 newstall, bool *runList) {
491
*runList = false;
492
if (listid < 0 || listid >= DisplayListMaxCount || dls[listid].state == PSP_GE_DL_STATE_NONE)
493
return SCE_KERNEL_ERROR_INVALID_ID;
494
auto &dl = dls[listid];
495
if (dl.state == PSP_GE_DL_STATE_COMPLETED)
496
return SCE_KERNEL_ERROR_ALREADY;
497
498
dl.stall = newstall & 0x0FFFFFFF;
499
500
*runList = true;
501
return 0;
502
}
503
504
u32 GPUCommon::Continue(bool *runList) {
505
*runList = false;
506
if (!currentList)
507
return 0;
508
509
if (currentList->state == PSP_GE_DL_STATE_PAUSED)
510
{
511
if (!isbreak) {
512
// TODO: Supposedly this returns SCE_KERNEL_ERROR_BUSY in some case, previously it had
513
// currentList->signal == PSP_GE_SIGNAL_HANDLER_PAUSE, but it doesn't reproduce.
514
515
currentList->state = PSP_GE_DL_STATE_RUNNING;
516
currentList->signal = PSP_GE_SIGNAL_NONE;
517
518
// TODO Restore context of DL is necessary
519
// TODO Restore BASE
520
521
// We have a list now, so it's not complete.
522
drawCompleteTicks = (u64)-1;
523
} else {
524
currentList->state = PSP_GE_DL_STATE_QUEUED;
525
currentList->signal = PSP_GE_SIGNAL_NONE;
526
}
527
}
528
else if (currentList->state == PSP_GE_DL_STATE_RUNNING)
529
{
530
if (sceKernelGetCompiledSdkVersion() >= 0x02000000)
531
return 0x80000020;
532
return -1;
533
}
534
else
535
{
536
if (sceKernelGetCompiledSdkVersion() >= 0x02000000)
537
return 0x80000004; // matches SCE_KERNEL_ERROR_BAD_ARGUMENT but doesn't really seem like it. Maybe that error code is more general.
538
return -1;
539
}
540
541
*runList = true;
542
return 0;
543
}
544
545
u32 GPUCommon::Break(int mode) {
546
if (mode < 0 || mode > 1)
547
return SCE_KERNEL_ERROR_INVALID_MODE;
548
549
if (!currentList)
550
return SCE_KERNEL_ERROR_ALREADY;
551
552
if (mode == 1)
553
{
554
// Clear the queue
555
dlQueue.clear();
556
for (int i = 0; i < DisplayListMaxCount; ++i)
557
{
558
dls[i].state = PSP_GE_DL_STATE_NONE;
559
dls[i].signal = PSP_GE_SIGNAL_NONE;
560
}
561
562
nextListID = 0;
563
currentList = NULL;
564
return 0;
565
}
566
567
if (currentList->state == PSP_GE_DL_STATE_NONE || currentList->state == PSP_GE_DL_STATE_COMPLETED)
568
{
569
if (sceKernelGetCompiledSdkVersion() >= 0x02000000)
570
return 0x80000004;
571
return -1;
572
}
573
574
if (currentList->state == PSP_GE_DL_STATE_PAUSED)
575
{
576
if (sceKernelGetCompiledSdkVersion() > 0x02000010)
577
{
578
if (currentList->signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
579
{
580
ERROR_LOG_REPORT(Log::G3D, "sceGeBreak: can't break signal-pausing list");
581
}
582
else
583
return SCE_KERNEL_ERROR_ALREADY;
584
}
585
return SCE_KERNEL_ERROR_BUSY;
586
}
587
588
if (currentList->state == PSP_GE_DL_STATE_QUEUED)
589
{
590
currentList->state = PSP_GE_DL_STATE_PAUSED;
591
return currentList->id;
592
}
593
594
// TODO Save BASE
595
// TODO Adjust pc to be just before SIGNAL/END
596
597
// TODO: Is this right?
598
if (currentList->signal == PSP_GE_SIGNAL_SYNC)
599
currentList->pc += 8;
600
601
currentList->interrupted = true;
602
currentList->state = PSP_GE_DL_STATE_PAUSED;
603
currentList->signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
604
isbreak = true;
605
606
return currentList->id;
607
}
608
609
void GPUCommon::PSPFrame() {
610
immCount_ = 0;
611
if (dumpNextFrame_) {
612
NOTICE_LOG(Log::G3D, "DUMPING THIS FRAME");
613
dumpThisFrame_ = true;
614
dumpNextFrame_ = false;
615
} else if (dumpThisFrame_) {
616
dumpThisFrame_ = false;
617
}
618
619
if (breakNext_ == GPUDebug::BreakNext::VSYNC) {
620
// Just start stepping as soon as we can once the vblank finishes.
621
breakNext_ = GPUDebug::BreakNext::OP;
622
}
623
recorder_.NotifyBeginFrame();
624
}
625
626
// Returns false on breakpoint.
627
bool GPUCommon::SlowRunLoop(DisplayList &list) {
628
const bool dumpThisFrame = dumpThisFrame_;
629
while (downcount > 0) {
630
GPUDebug::NotifyResult result = NotifyCommand(list.pc, &breakpoints_);
631
if (result == GPUDebug::NotifyResult::Break) {
632
return false;
633
}
634
635
recorder_.NotifyCommand(list.pc);
636
u32 op = Memory::ReadUnchecked_U32(list.pc);
637
u32 cmd = op >> 24;
638
639
u32 diff = op ^ gstate.cmdmem[cmd];
640
PreExecuteOp(op, diff);
641
if (dumpThisFrame) {
642
char temp[256];
643
u32 prev;
644
if (Memory::IsValidAddress(list.pc - 4)) {
645
prev = Memory::ReadUnchecked_U32(list.pc - 4);
646
} else {
647
prev = 0;
648
}
649
GeDisassembleOp(list.pc, op, prev, temp, 256);
650
NOTICE_LOG(Log::G3D, "%08x: %s", op, temp);
651
}
652
gstate.cmdmem[cmd] = op;
653
654
ExecuteOp(op, diff);
655
656
list.pc += 4;
657
--downcount;
658
}
659
return true;
660
}
661
662
// The newPC parameter is used for jumps, we don't count cycles between.
663
void GPUCommon::UpdatePC(u32 currentPC, u32 newPC) {
664
// Rough estimate, 2 CPU ticks (it's double the clock rate) per GPU instruction.
665
u32 executed = (currentPC - cycleLastPC) / 4;
666
cyclesExecuted += 2 * executed;
667
cycleLastPC = newPC;
668
669
// Exit the runloop and recalculate things. This happens a lot in some games.
670
if (currentList)
671
downcount = currentList->stall == 0 ? 0x0FFFFFFF : (currentList->stall - newPC) / 4;
672
else
673
downcount = 0;
674
}
675
676
void GPUCommon::ReapplyGfxState() {
677
// The commands are embedded in the command memory so we can just reexecute the words. Convenient.
678
// To be safe we pass 0xFFFFFFFF as the diff.
679
680
// TODO: Consider whether any of this should really be done. We might be able to get all the way
681
// by simplying dirtying the appropriate gstate_c dirty flags.
682
683
for (int i = GE_CMD_VERTEXTYPE; i < GE_CMD_BONEMATRIXNUMBER; i++) {
684
if (i != GE_CMD_ORIGIN && i != GE_CMD_OFFSETADDR) {
685
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
686
}
687
}
688
689
// Can't write to bonematrixnumber here
690
691
for (int i = GE_CMD_MORPHWEIGHT0; i <= GE_CMD_PATCHFACING; i++) {
692
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
693
}
694
695
// There are a few here in the middle that we shouldn't execute...
696
697
// 0x42 to 0xEA
698
for (int i = GE_CMD_VIEWPORTXSCALE; i < GE_CMD_TRANSFERSTART; i++) {
699
switch (i) {
700
case GE_CMD_TEXSYNC:
701
case GE_CMD_TEXFLUSH:
702
break;
703
default:
704
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
705
break;
706
}
707
}
708
709
// Let's just skip the transfer size stuff, it's just values.
710
}
711
712
uint32_t GPUCommon::SetAddrTranslation(uint32_t value) {
713
std::swap(edramTranslation_, value);
714
return value;
715
}
716
717
uint32_t GPUCommon::GetAddrTranslation() {
718
return edramTranslation_;
719
}
720
721
inline void GPUCommon::UpdateState(GPURunState state) {
722
gpuState = state;
723
if (state != GPUSTATE_RUNNING)
724
downcount = 0;
725
}
726
727
// This is now called when coreState == CORE_RUNNING_GE, in addition to from the various sceGe commands.
728
DLResult GPUCommon::ProcessDLQueue() {
729
if (!resumingFromDebugBreak_) {
730
startingTicks = CoreTiming::GetTicks();
731
cyclesExecuted = 0;
732
733
// ?? Seems to be correct behaviour to process the list anyway?
734
if (startingTicks < busyTicks) {
735
DEBUG_LOG(Log::G3D, "Can't execute a list yet, still busy for %lld ticks", busyTicks - startingTicks);
736
//return;
737
}
738
}
739
740
TimeCollector collectStat(&gpuStats.msProcessingDisplayLists, coreCollectDebugStats);
741
742
auto GetNextListIndex = [&]() -> int {
743
if (dlQueue.empty())
744
return -1;
745
return dlQueue.front();
746
};
747
748
for (int listIndex = GetNextListIndex(); listIndex != -1; listIndex = GetNextListIndex()) {
749
DisplayList &list = dls[listIndex];
750
751
if (list.state == PSP_GE_DL_STATE_PAUSED) {
752
return DLResult::Done;
753
}
754
755
// Temporary workaround for Crazy Taxi, see #19894
756
if (list.state == PSP_GE_DL_STATE_NONE) {
757
WARN_LOG(Log::G3D, "Discarding display list with state NONE (pc=%08x). This is odd.", list.pc);
758
dlQueue.erase(std::remove(dlQueue.begin(), dlQueue.end(), listIndex), dlQueue.end());
759
return DLResult::Done;
760
}
761
762
DEBUG_LOG(Log::G3D, "%s DL execution at %08x - stall = %08x (startingTicks=%lld)",
763
list.pc == list.startpc ? "Starting" : "Resuming", list.pc, list.stall, startingTicks);
764
765
if (!resumingFromDebugBreak_) {
766
// TODO: Need to be careful when *resuming* a list (when it wasn't from a stall...)
767
currentList = &list;
768
769
if (!list.started && list.context.IsValid()) {
770
gstate.Save(list.context);
771
}
772
list.started = true;
773
774
gstate_c.offsetAddr = list.offsetAddr;
775
776
if (!Memory::IsValidAddress(list.pc)) {
777
ERROR_LOG(Log::G3D, "DL PC = %08x WTF!!!!", list.pc);
778
return DLResult::Done;
779
}
780
781
cycleLastPC = list.pc;
782
cyclesExecuted += 60;
783
downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
784
list.state = PSP_GE_DL_STATE_RUNNING;
785
list.interrupted = false;
786
787
gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
788
789
// To enable breakpoints, we don't do fast matrix loads while debugger active.
790
debugRecording_ = recorder_.IsActive();
791
useFastRunLoop_ = !(dumpThisFrame_ || debugRecording_ || NeedsSlowInterpreter() || breakpoints_.HasBreakpoints());
792
} else {
793
resumingFromDebugBreak_ = false;
794
// The bottom part of the gpuState loop below, that wasn't executed
795
// when we bailed.
796
downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
797
if (gpuState == GPUSTATE_STALL && list.pc != list.stall) {
798
// Unstalled (Can this happen?)
799
gpuState = GPUSTATE_RUNNING;
800
}
801
// Proceed...
802
}
803
804
const bool useFastRunLoop = useFastRunLoop_;
805
806
while (gpuState == GPUSTATE_RUNNING) {
807
if (list.pc == list.stall) {
808
gpuState = GPUSTATE_STALL;
809
downcount = 0;
810
}
811
812
if (useFastRunLoop) {
813
// When no Ge debugger is active, we go full speed.
814
FastRunLoop(list);
815
} else {
816
// When a Ge debugger is active (or similar), we do more checking.
817
if (!SlowRunLoop(list)) {
818
// Hit a breakpoint, so we set the state and bail. We can resume later.
819
// TODO: Cycle counting might need some more care?
820
FinishDeferred();
821
_dbg_assert_(!recorder_.IsActive());
822
823
resumingFromDebugBreak_ = true;
824
return DLResult::DebugBreak;
825
}
826
}
827
828
downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
829
if (gpuState == GPUSTATE_STALL && list.pc != list.stall) {
830
// Unstalled (Can this happen?)
831
gpuState = GPUSTATE_RUNNING;
832
}
833
}
834
835
FinishDeferred();
836
if (debugRecording_)
837
recorder_.NotifyCPU();
838
839
// We haven't run the op at list.pc, so it shouldn't count.
840
if (cycleLastPC != list.pc) {
841
UpdatePC(list.pc - 4, list.pc);
842
}
843
844
list.offsetAddr = gstate_c.offsetAddr;
845
846
switch (gpuState) {
847
case GPUSTATE_DONE:
848
case GPUSTATE_ERROR:
849
// don't do anything - though dunno about error...
850
break;
851
case GPUSTATE_STALL:
852
// Resume work on this same display list later.
853
return DLResult::Done;
854
default:
855
return DLResult::Error;
856
}
857
858
// Some other list could've taken the spot while we dilly-dallied around, so we need the check.
859
// Yes, this does happen.
860
if (list.state != PSP_GE_DL_STATE_QUEUED) {
861
// At the end, we can remove it from the queue and continue.
862
dlQueue.erase(std::remove(dlQueue.begin(), dlQueue.end(), listIndex), dlQueue.end());
863
}
864
}
865
866
currentList = nullptr;
867
868
if (coreCollectDebugStats) {
869
gpuStats.otherGPUCycles += cyclesExecuted;
870
}
871
872
drawCompleteTicks = startingTicks + cyclesExecuted;
873
busyTicks = std::max(busyTicks, drawCompleteTicks);
874
875
__GeTriggerSync(GPU_SYNC_DRAW, 1, drawCompleteTicks);
876
// Since the event is in CoreTiming, we're in sync. Just set 0 now.
877
return DLResult::Done;
878
}
879
880
bool GPUCommon::ShouldSplitOverGe() const {
881
// Check for debugger active.
882
// We only need to do this if we want to be able to step through Ge display lists using the Ge debuggers.
883
return NeedsSlowInterpreter() || breakpoints_.HasBreakpoints();
884
}
885
886
void GPUCommon::Execute_OffsetAddr(u32 op, u32 diff) {
887
gstate_c.offsetAddr = op << 8;
888
}
889
890
void GPUCommon::Execute_Vaddr(u32 op, u32 diff) {
891
gstate_c.vertexAddr = gstate_c.getRelativeAddress(op & 0x00FFFFFF);
892
}
893
894
void GPUCommon::Execute_Iaddr(u32 op, u32 diff) {
895
gstate_c.indexAddr = gstate_c.getRelativeAddress(op & 0x00FFFFFF);
896
}
897
898
void GPUCommon::Execute_Origin(u32 op, u32 diff) {
899
if (currentList)
900
gstate_c.offsetAddr = currentList->pc;
901
}
902
903
void GPUCommon::Execute_Jump(u32 op, u32 diff) {
904
const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC);
905
if (!Memory::IsValidAddress(target)) {
906
ERROR_LOG(Log::G3D, "JUMP to illegal address %08x - ignoring! data=%06x", target, op & 0x00FFFFFF);
907
UpdateState(GPUSTATE_ERROR);
908
return;
909
}
910
UpdatePC(currentList->pc, target - 4);
911
currentList->pc = target - 4; // pc will be increased after we return, counteract that
912
}
913
914
void GPUCommon::Execute_BJump(u32 op, u32 diff) {
915
if (!currentList->bboxResult) {
916
// bounding box jump.
917
const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC);
918
gpuStats.numBBOXJumps++;
919
if (Memory::IsValidAddress(target)) {
920
UpdatePC(currentList->pc, target - 4);
921
currentList->pc = target - 4; // pc will be increased after we return, counteract that
922
} else {
923
ERROR_LOG(Log::G3D, "BJUMP to illegal address %08x - ignoring! data=%06x", target, op & 0x00FFFFFF);
924
UpdateState(GPUSTATE_ERROR);
925
}
926
}
927
}
928
929
void GPUCommon::Execute_Call(u32 op, u32 diff) {
930
PROFILE_THIS_SCOPE("gpu_call");
931
932
const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC);
933
if (!Memory::IsValidAddress(target)) {
934
ERROR_LOG(Log::G3D, "CALL to illegal address %08x - ignoring! data=%06x", target, op & 0x00FFFFFF);
935
if (g_Config.bIgnoreBadMemAccess) {
936
return;
937
}
938
UpdateState(GPUSTATE_ERROR);
939
return;
940
}
941
DoExecuteCall(target);
942
}
943
944
void GPUCommon::DoExecuteCall(u32 target) {
945
// Local variable for better codegen
946
DisplayList *currentList = this->currentList;
947
948
// Bone matrix optimization - many games will CALL a bone matrix (!).
949
// We don't optimize during recording or debugging - so the matrix data gets recorded.
950
if (useFastRunLoop_ && Memory::IsValidRange(target, 13 * 4) && (Memory::ReadUnchecked_U32(target) >> 24) == GE_CMD_BONEMATRIXDATA) {
951
// Check for the end
952
if ((Memory::ReadUnchecked_U32(target + 11 * 4) >> 24) == GE_CMD_BONEMATRIXDATA &&
953
(Memory::ReadUnchecked_U32(target + 12 * 4) >> 24) == GE_CMD_RET &&
954
(gstate.boneMatrixNumber & 0x00FFFFFF) <= 96 - 12) {
955
// Yep, pretty sure this is a bone matrix call. Double check stall first.
956
if (target > currentList->stall || target + 12 * 4 < currentList->stall) {
957
FastLoadBoneMatrix(target);
958
return;
959
}
960
}
961
}
962
963
if (currentList->stackptr == ARRAY_SIZE(currentList->stack)) {
964
ERROR_LOG(Log::G3D, "CALL: Stack full!");
965
// TODO: UpdateState(GPUSTATE_ERROR) ?
966
} else {
967
auto &stackEntry = currentList->stack[currentList->stackptr++];
968
stackEntry.pc = currentList->pc + 4;
969
stackEntry.offsetAddr = gstate_c.offsetAddr;
970
// The base address is NOT saved/restored for a regular call.
971
UpdatePC(currentList->pc, target - 4);
972
currentList->pc = target - 4; // pc will be increased after we return, counteract that
973
}
974
}
975
976
void GPUCommon::Execute_Ret(u32 op, u32 diff) {
977
// Local variable for better codegen
978
DisplayList *currentList = this->currentList;
979
if (currentList->stackptr == 0) {
980
DEBUG_LOG(Log::G3D, "RET: Stack empty!");
981
} else {
982
auto &stackEntry = currentList->stack[--currentList->stackptr];
983
gstate_c.offsetAddr = stackEntry.offsetAddr;
984
// We always clear the top (uncached/etc.) bits
985
const u32 target = stackEntry.pc & 0x0FFFFFFF;
986
UpdatePC(currentList->pc, target - 4);
987
currentList->pc = target - 4;
988
#ifdef _DEBUG
989
if (!Memory::IsValidAddress(currentList->pc)) {
990
ERROR_LOG_REPORT(Log::G3D, "Invalid DL PC %08x on return", currentList->pc);
991
UpdateState(GPUSTATE_ERROR);
992
}
993
#endif
994
}
995
}
996
997
void GPUCommon::Execute_End(u32 op, u32 diff) {
998
if (flushOnParams_) {
999
drawEngineCommon_->FlushQueuedDepth();
1000
Flush();
1001
}
1002
1003
const u32 prev = Memory::ReadUnchecked_U32(currentList->pc - 4);
1004
UpdatePC(currentList->pc, currentList->pc);
1005
// Count in a few extra cycles on END.
1006
cyclesExecuted += 60;
1007
1008
switch (prev >> 24) {
1009
case GE_CMD_SIGNAL:
1010
{
1011
// TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935#
1012
SignalBehavior behaviour = static_cast<SignalBehavior>((prev >> 16) & 0xFF);
1013
const int signal = prev & 0xFFFF;
1014
const int enddata = op & 0xFFFF;
1015
bool trigger = true;
1016
currentList->subIntrToken = signal;
1017
1018
switch (behaviour) {
1019
case PSP_GE_SIGNAL_HANDLER_SUSPEND:
1020
// Suspend the list, and call the signal handler. When it's done, resume.
1021
// Before sdkver 0x02000010, listsync should return paused.
1022
if (sceKernelGetCompiledSdkVersion() <= 0x02000010)
1023
currentList->state = PSP_GE_DL_STATE_PAUSED;
1024
currentList->signal = behaviour;
1025
DEBUG_LOG(Log::G3D, "Signal with wait. signal/end: %04x %04x", signal, enddata);
1026
break;
1027
case PSP_GE_SIGNAL_HANDLER_CONTINUE:
1028
// Resume the list right away, then call the handler.
1029
currentList->signal = behaviour;
1030
DEBUG_LOG(Log::G3D, "Signal without wait. signal/end: %04x %04x", signal, enddata);
1031
break;
1032
case PSP_GE_SIGNAL_HANDLER_PAUSE:
1033
// Pause the list instead of ending at the next FINISH.
1034
// Call the handler with the PAUSE signal value at that FINISH.
1035
// Technically, this ought to trigger an interrupt, but it won't do anything.
1036
// But right now, signal is always reset by interrupts, so that causes pause to not work.
1037
trigger = false;
1038
currentList->signal = behaviour;
1039
DEBUG_LOG(Log::G3D, "Signal with Pause. signal/end: %04x %04x", signal, enddata);
1040
break;
1041
case PSP_GE_SIGNAL_SYNC:
1042
// Acts as a memory barrier, never calls any user code.
1043
// Technically, this ought to trigger an interrupt, but it won't do anything.
1044
// Triggering here can cause incorrect rescheduling, which breaks 3rd Birthday.
1045
// However, this is likely a bug in how GE signal interrupts are handled.
1046
trigger = false;
1047
currentList->signal = behaviour;
1048
DEBUG_LOG(Log::G3D, "Signal with Sync. signal/end: %04x %04x", signal, enddata);
1049
break;
1050
case PSP_GE_SIGNAL_JUMP:
1051
case PSP_GE_SIGNAL_RJUMP:
1052
case PSP_GE_SIGNAL_OJUMP:
1053
{
1054
trigger = false;
1055
currentList->signal = behaviour;
1056
// pc will be increased after we return, counteract that.
1057
u32 target = (((signal << 16) | enddata) & 0xFFFFFFFC) - 4;
1058
const char *targetType = "absolute";
1059
if (behaviour == PSP_GE_SIGNAL_RJUMP) {
1060
target += currentList->pc - 4;
1061
targetType = "relative";
1062
} else if (behaviour == PSP_GE_SIGNAL_OJUMP) {
1063
target = gstate_c.getRelativeAddress(target);
1064
targetType = "origin";
1065
}
1066
1067
if (!Memory::IsValidAddress(target)) {
1068
ERROR_LOG_REPORT(Log::G3D, "Signal with Jump (%s): bad address. signal/end: %04x %04x", targetType, signal, enddata);
1069
UpdateState(GPUSTATE_ERROR);
1070
} else {
1071
UpdatePC(currentList->pc, target);
1072
currentList->pc = target;
1073
DEBUG_LOG(Log::G3D, "Signal with Jump (%s). signal/end: %04x %04x", targetType, signal, enddata);
1074
}
1075
}
1076
break;
1077
case PSP_GE_SIGNAL_CALL:
1078
case PSP_GE_SIGNAL_RCALL:
1079
case PSP_GE_SIGNAL_OCALL:
1080
{
1081
trigger = false;
1082
currentList->signal = behaviour;
1083
// pc will be increased after we return, counteract that.
1084
u32 target = (((signal << 16) | enddata) & 0xFFFFFFFC) - 4;
1085
const char *targetType = "absolute";
1086
if (behaviour == PSP_GE_SIGNAL_RCALL) {
1087
target += currentList->pc - 4;
1088
targetType = "relative";
1089
} else if (behaviour == PSP_GE_SIGNAL_OCALL) {
1090
target = gstate_c.getRelativeAddress(target);
1091
targetType = "origin";
1092
}
1093
1094
if (currentList->stackptr == ARRAY_SIZE(currentList->stack)) {
1095
ERROR_LOG_REPORT(Log::G3D, "Signal with Call (%s): stack full. signal/end: %04x %04x", targetType, signal, enddata);
1096
} else if (!Memory::IsValidAddress(target)) {
1097
ERROR_LOG_REPORT(Log::G3D, "Signal with Call (%s): bad address. signal/end: %04x %04x", targetType, signal, enddata);
1098
UpdateState(GPUSTATE_ERROR);
1099
} else {
1100
// TODO: This might save/restore other state...
1101
auto &stackEntry = currentList->stack[currentList->stackptr++];
1102
stackEntry.pc = currentList->pc;
1103
stackEntry.offsetAddr = gstate_c.offsetAddr;
1104
stackEntry.baseAddr = gstate.base;
1105
UpdatePC(currentList->pc, target);
1106
currentList->pc = target;
1107
DEBUG_LOG(Log::G3D, "Signal with Call (%s). signal/end: %04x %04x", targetType, signal, enddata);
1108
}
1109
}
1110
break;
1111
case PSP_GE_SIGNAL_RET:
1112
{
1113
trigger = false;
1114
currentList->signal = behaviour;
1115
if (currentList->stackptr == 0) {
1116
ERROR_LOG_REPORT(Log::G3D, "Signal with Return: stack empty. signal/end: %04x %04x", signal, enddata);
1117
} else {
1118
// TODO: This might save/restore other state...
1119
auto &stackEntry = currentList->stack[--currentList->stackptr];
1120
gstate_c.offsetAddr = stackEntry.offsetAddr;
1121
gstate.base = stackEntry.baseAddr;
1122
UpdatePC(currentList->pc, stackEntry.pc);
1123
currentList->pc = stackEntry.pc;
1124
DEBUG_LOG(Log::G3D, "Signal with Return. signal/end: %04x %04x", signal, enddata);
1125
}
1126
}
1127
break;
1128
default:
1129
ERROR_LOG_REPORT(Log::G3D, "UNKNOWN Signal UNIMPLEMENTED %i ! signal/end: %04x %04x", behaviour, signal, enddata);
1130
break;
1131
}
1132
// TODO: Technically, jump/call/ret should generate an interrupt, but before the pc change maybe?
1133
if (currentList->interruptsEnabled && trigger) {
1134
if (__GeTriggerInterrupt(currentList->id, currentList->pc, startingTicks + cyclesExecuted)) {
1135
currentList->pendingInterrupt = true;
1136
UpdateState(GPUSTATE_INTERRUPT);
1137
}
1138
}
1139
}
1140
break;
1141
case GE_CMD_FINISH:
1142
switch (currentList->signal) {
1143
case PSP_GE_SIGNAL_HANDLER_PAUSE:
1144
currentList->state = PSP_GE_DL_STATE_PAUSED;
1145
if (currentList->interruptsEnabled) {
1146
if (__GeTriggerInterrupt(currentList->id, currentList->pc, startingTicks + cyclesExecuted)) {
1147
currentList->pendingInterrupt = true;
1148
UpdateState(GPUSTATE_INTERRUPT);
1149
}
1150
}
1151
break;
1152
1153
case PSP_GE_SIGNAL_SYNC:
1154
currentList->signal = PSP_GE_SIGNAL_NONE;
1155
// TODO: Technically this should still cause an interrupt. Probably for memory sync.
1156
break;
1157
1158
default:
1159
FlushImm();
1160
currentList->subIntrToken = prev & 0xFFFF;
1161
UpdateState(GPUSTATE_DONE);
1162
// Since we marked done, we have to restore the context now before the next list runs.
1163
if (currentList->started && currentList->context.IsValid()) {
1164
gstate.Restore(currentList->context);
1165
ReapplyGfxState();
1166
// Don't restore the context again.
1167
currentList->started = false;
1168
}
1169
1170
if (currentList->interruptsEnabled && __GeTriggerInterrupt(currentList->id, currentList->pc, startingTicks + cyclesExecuted)) {
1171
currentList->pendingInterrupt = true;
1172
} else {
1173
currentList->state = PSP_GE_DL_STATE_COMPLETED;
1174
currentList->waitUntilTicks = startingTicks + cyclesExecuted;
1175
busyTicks = std::max(busyTicks, currentList->waitUntilTicks);
1176
__GeTriggerSync(GPU_SYNC_LIST, currentList->id, currentList->waitUntilTicks);
1177
}
1178
break;
1179
}
1180
break;
1181
default:
1182
DEBUG_LOG(Log::G3D, "END: Not finished: %06x", prev & 0xFFFFFF);
1183
break;
1184
}
1185
}
1186
1187
void GPUCommon::Execute_BoundingBox(u32 op, u32 diff) {
1188
// Just resetting, nothing to check bounds for.
1189
const u32 count = op & 0xFFFF;
1190
if (count == 0) {
1191
currentList->bboxResult = false;
1192
return;
1193
}
1194
1195
// Approximate based on timings of several counts on a PSP.
1196
cyclesExecuted += count * 22;
1197
1198
const u32 vertType = gstate.vertType;
1199
1200
const bool useInds = (vertType & GE_VTYPE_IDX_MASK) != 0;
1201
const VertexDecoder *dec = drawEngineCommon_->GetVertexDecoder(vertType);
1202
int bytesRead = (useInds ? 1 : dec->VertexSize()) * count;
1203
1204
if (!Memory::IsValidRange(gstate_c.vertexAddr, bytesRead)) {
1205
ERROR_LOG_REPORT_ONCE(boundingbox, Log::G3D, "Bad bounding box data: %06x", count);
1206
// Data seems invalid. Let's assume the box test passed.
1207
currentList->bboxResult = true;
1208
return;
1209
}
1210
const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); // we checked the range above.
1211
1212
const void *inds = nullptr;
1213
if (useInds) {
1214
const int indexSizeShift = ((vertType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT) - 1;
1215
if (!Memory::IsValidRange(gstate_c.indexAddr, count << indexSizeShift)) {
1216
ERROR_LOG_REPORT_ONCE(boundingboxInds, Log::G3D, "Invalid inds in bounding box check");
1217
currentList->bboxResult = true;
1218
return;
1219
}
1220
inds = Memory::GetPointerUnchecked(gstate_c.indexAddr);
1221
}
1222
1223
// Test if the bounding box is within the drawing region.
1224
// The PSP only seems to vary the result based on a single range of 0x100.
1225
if (count > 0x200) {
1226
// The second to last set of 0x100 is checked (even for odd counts.)
1227
size_t skipSize = (count - 0x200) * dec->VertexSize();
1228
currentList->bboxResult = drawEngineCommon_->TestBoundingBox((const uint8_t *)control_points + skipSize, inds, 0x100, dec, vertType);
1229
} else if (count > 0x100) {
1230
int checkSize = count - 0x100;
1231
currentList->bboxResult = drawEngineCommon_->TestBoundingBox(control_points, inds, checkSize, dec, vertType);
1232
} else {
1233
currentList->bboxResult = drawEngineCommon_->TestBoundingBox(control_points, inds, count, dec, vertType);
1234
}
1235
AdvanceVerts(gstate.vertType, count, bytesRead);
1236
}
1237
1238
void GPUCommon::Execute_MorphWeight(u32 op, u32 diff) {
1239
gstate_c.morphWeights[(op >> 24) - GE_CMD_MORPHWEIGHT0] = getFloat24(op);
1240
}
1241
1242
void GPUCommon::Execute_ImmVertexAlphaPrim(u32 op, u32 diff) {
1243
// Safety check.
1244
if (immCount_ >= MAX_IMMBUFFER_SIZE) {
1245
// Only print once for each overrun.
1246
if (immCount_ == MAX_IMMBUFFER_SIZE) {
1247
ERROR_LOG_REPORT_ONCE(exceed_imm_buffer, Log::G3D, "Exceeded immediate draw buffer size. gstate.imm_ap=%06x , prim=%d", gstate.imm_ap & 0xFFFFFF, (int)immPrim_);
1248
}
1249
if (immCount_ < 0x7fffffff) // Paranoia :)
1250
immCount_++;
1251
return;
1252
}
1253
1254
const int prim = (op >> 8) & 0x7;
1255
if (prim != GE_PRIM_KEEP_PREVIOUS) {
1256
// Flush before changing the prim type. Only continue can be used to continue a prim.
1257
FlushImm();
1258
}
1259
1260
TransformedVertex &v = immBuffer_[immCount_++];
1261
1262
// ThrillVille does a clear with this, additional parameters found via tests.
1263
// The current vtype affects how the coordinate is processed.
1264
if (gstate.isModeThrough()) {
1265
v.x = ((int)(gstate.imm_vscx & 0xFFFF) - 0x8000) / 16.0f;
1266
v.y = ((int)(gstate.imm_vscy & 0xFFFF) - 0x8000) / 16.0f;
1267
} else {
1268
int offsetX = gstate.getOffsetX16();
1269
int offsetY = gstate.getOffsetY16();
1270
v.x = ((int)(gstate.imm_vscx & 0xFFFF) - offsetX) / 16.0f;
1271
v.y = ((int)(gstate.imm_vscy & 0xFFFF) - offsetY) / 16.0f;
1272
}
1273
v.z = gstate.imm_vscz & 0xFFFF;
1274
v.pos_w = 1.0f;
1275
v.u = getFloat24(gstate.imm_vtcs);
1276
v.v = getFloat24(gstate.imm_vtct);
1277
v.uv_w = getFloat24(gstate.imm_vtcq);
1278
v.color0_32 = (gstate.imm_cv & 0xFFFFFF) | (gstate.imm_ap << 24);
1279
// TODO: When !gstate.isModeThrough(), direct fog coefficient (0 = entirely fog), ignore fog flag (also GE_IMM_FOG.)
1280
v.fog = (gstate.imm_fc & 0xFF) / 255.0f;
1281
// TODO: Apply if gstate.isUsingSecondaryColor() && !gstate.isModeThrough(), ignore lighting flag.
1282
v.color1_32 = gstate.imm_scv & 0xFFFFFF;
1283
if (prim != GE_PRIM_KEEP_PREVIOUS) {
1284
immPrim_ = (GEPrimitiveType)prim;
1285
// Flags seem to only be respected from the first prim.
1286
immFlags_ = op & 0x00FFF800;
1287
immFirstSent_ = false;
1288
} else if (prim == GE_PRIM_KEEP_PREVIOUS && immPrim_ != GE_PRIM_INVALID) {
1289
static constexpr int flushPrimCount[] = { 1, 2, 0, 3, 0, 0, 2, 0 };
1290
// Instead of finding a proper point to flush, we just emit prims when we can.
1291
if (immCount_ == flushPrimCount[immPrim_ & 7])
1292
FlushImm();
1293
} else {
1294
ERROR_LOG_REPORT_ONCE(imm_draw_prim, Log::G3D, "Immediate draw: Unexpected primitive %d at count %d", prim, immCount_);
1295
}
1296
}
1297
1298
void GPUCommon::FlushImm() {
1299
if (immCount_ == 0 || immPrim_ == GE_PRIM_INVALID)
1300
return;
1301
1302
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
1303
// No idea how many cycles to skip, heh.
1304
immCount_ = 0;
1305
return;
1306
}
1307
1308
// Ignore immediate point primitives with a X/Y of 0.
1309
// These are accidentally created when games clear the graphics state.
1310
if (immCount_ == 1 && immPrim_ == GE_PRIM_POINTS && immBuffer_[0].x == 0 && immBuffer_[0].y == 0 && immBuffer_[0].z == 0 && immBuffer_[0].color0_32 == 0) {
1311
immCount_ = 0;
1312
return;
1313
}
1314
1315
SetDrawType(DRAW_PRIM, immPrim_);
1316
1317
gstate_c.UpdateUVScaleOffset();
1318
1319
VirtualFramebuffer *vfb = nullptr;
1320
if (framebufferManager_) {
1321
bool changed;
1322
vfb = framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason, &changed);
1323
}
1324
if (vfb) {
1325
CheckDepthUsage(vfb);
1326
}
1327
1328
bool antialias = (immFlags_ & GE_IMM_ANTIALIAS) != 0;
1329
bool prevAntialias = gstate.isAntiAliasEnabled();
1330
bool shading = (immFlags_ & GE_IMM_SHADING) != 0;
1331
bool prevShading = gstate.getShadeMode() == GE_SHADE_GOURAUD;
1332
bool cullEnable = (immFlags_ & GE_IMM_CULLENABLE) != 0;
1333
bool prevCullEnable = gstate.isCullEnabled();
1334
int cullMode = (immFlags_ & GE_IMM_CULLFACE) != 0 ? 1 : 0;
1335
bool texturing = (immFlags_ & GE_IMM_TEXTURE) != 0;
1336
bool prevTexturing = gstate.isTextureMapEnabled();
1337
bool fog = (immFlags_ & GE_IMM_FOG) != 0;
1338
bool prevFog = gstate.isFogEnabled();
1339
bool dither = (immFlags_ & GE_IMM_DITHER) != 0;
1340
bool prevDither = gstate.isDitherEnabled();
1341
1342
if ((immFlags_ & GE_IMM_CLIPMASK) != 0) {
1343
WARN_LOG_REPORT_ONCE(geimmclipvalue, Log::G3D, "Imm vertex used clip value, flags=%06x", immFlags_);
1344
}
1345
1346
bool changed = texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither;
1347
changed = changed || prevShading != shading || prevFog != fog;
1348
if (changed) {
1349
Flush();
1350
gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)antialias;
1351
gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)shading;
1352
gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)cullEnable;
1353
gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)texturing;
1354
gstate.fogEnable = (GE_CMD_FOGENABLE << 24) | (int)fog;
1355
gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)dither;
1356
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_UVSCALEOFFSET | DIRTY_CULLRANGE);
1357
}
1358
1359
drawEngineCommon_->DispatchSubmitImm(immPrim_, immBuffer_, immCount_, cullMode, immFirstSent_);
1360
immCount_ = 0;
1361
immFirstSent_ = true;
1362
1363
if (changed) {
1364
Flush();
1365
gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)prevAntialias;
1366
gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)prevShading;
1367
gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)prevCullEnable;
1368
gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)prevTexturing;
1369
gstate.fogEnable = (GE_CMD_FOGENABLE << 24) | (int)prevFog;
1370
gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)prevDither;
1371
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_UVSCALEOFFSET | DIRTY_CULLRANGE);
1372
}
1373
}
1374
1375
void GPUCommon::Execute_Unknown(u32 op, u32 diff) {
1376
// Do nothing. We used to report here, but we're confident we have them all so no need to report unknown.
1377
}
1378
1379
void GPUCommon::FastLoadBoneMatrix(u32 target) {
1380
const u32 num = gstate.boneMatrixNumber & 0x7F;
1381
_dbg_assert_msg_(num + 12 <= 96, "FastLoadBoneMatrix would corrupt memory");
1382
const u32 mtxNum = num / 12;
1383
u32 uniformsToDirty = DIRTY_BONEMATRIX0 << mtxNum;
1384
if (num != 12 * mtxNum) {
1385
uniformsToDirty |= DIRTY_BONEMATRIX0 << ((mtxNum + 1) & 7);
1386
}
1387
1388
if (!g_Config.bSoftwareSkinning) {
1389
if (flushOnParams_) {
1390
Flush();
1391
}
1392
gstate_c.Dirty(uniformsToDirty);
1393
} else {
1394
gstate_c.deferredVertTypeDirty |= uniformsToDirty;
1395
}
1396
gstate.FastLoadBoneMatrix(target);
1397
1398
cyclesExecuted += 2 * 14; // one to reset the counter, 12 to load the matrix, and a return.
1399
1400
if (coreCollectDebugStats) {
1401
gpuStats.otherGPUCycles += 2 * 14;
1402
}
1403
}
1404
1405
struct DisplayList_v1 {
1406
int id;
1407
u32 startpc;
1408
u32 pc;
1409
u32 stall;
1410
DisplayListState state;
1411
SignalBehavior signal;
1412
int subIntrBase;
1413
u16 subIntrToken;
1414
DisplayListStackEntry stack[32];
1415
int stackptr;
1416
bool interrupted;
1417
u64 waitUntilTicks;
1418
bool interruptsEnabled;
1419
bool pendingInterrupt;
1420
bool started;
1421
size_t contextPtr;
1422
u32 offsetAddr;
1423
bool bboxResult;
1424
};
1425
1426
struct DisplayList_v2 {
1427
int id;
1428
u32 startpc;
1429
u32 pc;
1430
u32 stall;
1431
DisplayListState state;
1432
SignalBehavior signal;
1433
int subIntrBase;
1434
u16 subIntrToken;
1435
DisplayListStackEntry stack[32];
1436
int stackptr;
1437
bool interrupted;
1438
u64 waitUntilTicks;
1439
bool interruptsEnabled;
1440
bool pendingInterrupt;
1441
bool started;
1442
PSPPointer<u32_le> context;
1443
u32 offsetAddr;
1444
bool bboxResult;
1445
};
1446
1447
void GPUCommon::DoState(PointerWrap &p) {
1448
auto s = p.Section("GPUCommon", 1, 6);
1449
if (!s)
1450
return;
1451
1452
Do<int>(p, dlQueue);
1453
if (s >= 4) {
1454
DoArray(p, dls, ARRAY_SIZE(dls));
1455
} else if (s >= 3) {
1456
// This may have been saved with or without padding, depending on platform.
1457
// We need to upconvert it to our consistently-padded struct.
1458
static const size_t DisplayList_v3_size = 452;
1459
static const size_t DisplayList_v4_size = 456;
1460
static_assert(DisplayList_v4_size == sizeof(DisplayList), "Make sure to change here when updating DisplayList");
1461
1462
p.DoVoid(&dls[0], DisplayList_v3_size);
1463
dls[0].padding = 0;
1464
1465
const u8 *savedPtr = *p.GetPPtr();
1466
const u32 *savedPtr32 = (const u32 *)savedPtr;
1467
// Here's the trick: the first member (id) is always the same as the index.
1468
// The second member (startpc) is always an address, or 0, never 1. So we can see the padding.
1469
const bool hasPadding = savedPtr32[1] == 1;
1470
if (hasPadding) {
1471
u32 padding;
1472
Do(p, padding);
1473
}
1474
1475
for (size_t i = 1; i < ARRAY_SIZE(dls); ++i) {
1476
p.DoVoid(&dls[i], DisplayList_v3_size);
1477
dls[i].padding = 0;
1478
if (hasPadding) {
1479
u32 padding;
1480
Do(p, padding);
1481
}
1482
}
1483
} else if (s >= 2) {
1484
for (size_t i = 0; i < ARRAY_SIZE(dls); ++i) {
1485
DisplayList_v2 oldDL;
1486
Do(p, oldDL);
1487
// Copy over everything except the last, new member (stackAddr.)
1488
memcpy(&dls[i], &oldDL, sizeof(DisplayList_v2));
1489
dls[i].stackAddr = 0;
1490
}
1491
} else {
1492
// Can only be in read mode here.
1493
for (size_t i = 0; i < ARRAY_SIZE(dls); ++i) {
1494
DisplayList_v1 oldDL;
1495
Do(p, oldDL);
1496
// On 32-bit, they're the same, on 64-bit oldDL is bigger.
1497
memcpy(&dls[i], &oldDL, sizeof(DisplayList_v1));
1498
// Fix the other fields. Let's hope context wasn't important, it was a pointer.
1499
dls[i].context = 0;
1500
dls[i].offsetAddr = oldDL.offsetAddr;
1501
dls[i].bboxResult = oldDL.bboxResult;
1502
dls[i].stackAddr = 0;
1503
}
1504
}
1505
int currentID = 0;
1506
if (currentList != nullptr) {
1507
currentID = (int)(currentList - &dls[0]);
1508
}
1509
Do(p, currentID);
1510
if (currentID == 0) {
1511
currentList = nullptr;
1512
} else {
1513
currentList = &dls[currentID];
1514
}
1515
Do(p, interruptRunning);
1516
Do(p, gpuState);
1517
Do(p, isbreak);
1518
Do(p, drawCompleteTicks);
1519
Do(p, busyTicks);
1520
1521
if (s >= 5) {
1522
Do(p, matrixVisible.all);
1523
}
1524
if (s >= 6) {
1525
Do(p, edramTranslation_);
1526
}
1527
}
1528
1529
void GPUCommon::InterruptStart(int listid) {
1530
interruptRunning = true;
1531
}
1532
1533
void GPUCommon::InterruptEnd(int listid) {
1534
interruptRunning = false;
1535
isbreak = false;
1536
1537
DisplayList &dl = dls[listid];
1538
dl.pendingInterrupt = false;
1539
// TODO: Unless the signal handler could change it?
1540
if (dl.state == PSP_GE_DL_STATE_COMPLETED || dl.state == PSP_GE_DL_STATE_NONE) {
1541
if (dl.started && dl.context.IsValid()) {
1542
gstate.Restore(dl.context);
1543
ReapplyGfxState();
1544
}
1545
dl.waitUntilTicks = 0;
1546
__GeTriggerWait(GPU_SYNC_LIST, listid);
1547
1548
// Make sure the list isn't still queued since it's now completed.
1549
if (!dlQueue.empty()) {
1550
if (listid == dlQueue.front())
1551
PopDLQueue();
1552
else
1553
dlQueue.remove(listid);
1554
}
1555
}
1556
}
1557
1558
// TODO: Maybe cleaner to keep this in GE and trigger the clear directly?
1559
void GPUCommon::SyncEnd(GPUSyncType waitType, int listid, bool wokeThreads) {
1560
if (waitType == GPU_SYNC_DRAW && wokeThreads)
1561
{
1562
for (int i = 0; i < DisplayListMaxCount; ++i) {
1563
if (dls[i].state == PSP_GE_DL_STATE_COMPLETED) {
1564
dls[i].state = PSP_GE_DL_STATE_NONE;
1565
}
1566
}
1567
}
1568
}
1569
1570
bool GPUCommon::GetCurrentDisplayList(DisplayList &list) {
1571
if (!currentList) {
1572
return false;
1573
}
1574
list = *currentList;
1575
return true;
1576
}
1577
1578
int GPUCommon::GetCurrentPrimCount() {
1579
DisplayList list;
1580
if (GetCurrentDisplayList(list)) {
1581
u32 cmd = Memory::Read_U32(list.pc);
1582
if ((cmd >> 24) == GE_CMD_PRIM || (cmd >> 24) == GE_CMD_BOUNDINGBOX) {
1583
return cmd & 0xFFFF;
1584
} else if ((cmd >> 24) == GE_CMD_BEZIER || (cmd >> 24) == GE_CMD_SPLINE) {
1585
u32 u = (cmd & 0x00FF) >> 0;
1586
u32 v = (cmd & 0xFF00) >> 8;
1587
return u * v;
1588
}
1589
return true;
1590
} else {
1591
// Current prim value.
1592
return gstate.cmdmem[GE_CMD_PRIM] & 0xFFFF;
1593
}
1594
}
1595
1596
std::vector<DisplayList> GPUCommon::ActiveDisplayLists() {
1597
std::vector<DisplayList> result;
1598
1599
for (int it : dlQueue) {
1600
result.push_back(dls[it]);
1601
}
1602
1603
return result;
1604
}
1605
1606
void GPUCommon::ResetListPC(int listID, u32 pc) {
1607
if (listID < 0 || listID >= DisplayListMaxCount) {
1608
_dbg_assert_msg_(false, "listID out of range: %d", listID);
1609
return;
1610
}
1611
1612
Reporting::NotifyDebugger();
1613
dls[listID].pc = pc;
1614
downcount = 0;
1615
}
1616
1617
void GPUCommon::ResetListStall(int listID, u32 stall) {
1618
if (listID < 0 || listID >= DisplayListMaxCount) {
1619
_dbg_assert_msg_(false, "listID out of range: %d", listID);
1620
return;
1621
}
1622
1623
Reporting::NotifyDebugger();
1624
dls[listID].stall = stall;
1625
downcount = 0;
1626
}
1627
1628
void GPUCommon::ResetListState(int listID, DisplayListState state) {
1629
if (listID < 0 || listID >= DisplayListMaxCount) {
1630
_dbg_assert_msg_(false, "listID out of range: %d", listID);
1631
return;
1632
}
1633
1634
Reporting::NotifyDebugger();
1635
dls[listID].state = state;
1636
downcount = 0;
1637
}
1638
1639
GPUDebugOp GPUCommon::DisassembleOp(u32 pc, u32 op) {
1640
char buffer[1024];
1641
u32 prev = Memory::IsValidAddress(pc - 4) ? Memory::ReadUnchecked_U32(pc - 4) : 0;
1642
GeDisassembleOp(pc, op, prev, buffer, sizeof(buffer));
1643
1644
GPUDebugOp info;
1645
info.pc = pc;
1646
info.cmd = op >> 24;
1647
info.op = op;
1648
info.desc = buffer;
1649
return info;
1650
}
1651
1652
std::vector<GPUDebugOp> GPUCommon::DisassembleOpRange(u32 startpc, u32 endpc) {
1653
char buffer[1024];
1654
std::vector<GPUDebugOp> result;
1655
GPUDebugOp info;
1656
1657
// Don't trigger a pause.
1658
u32 prev = Memory::IsValidAddress(startpc - 4) ? Memory::Read_U32(startpc - 4) : 0;
1659
result.reserve((endpc - startpc) / 4);
1660
for (u32 pc = startpc; pc < endpc; pc += 4) {
1661
u32 op = Memory::IsValidAddress(pc) ? Memory::Read_U32(pc) : 0;
1662
GeDisassembleOp(pc, op, prev, buffer, sizeof(buffer));
1663
prev = op;
1664
1665
info.pc = pc;
1666
info.cmd = op >> 24;
1667
info.op = op;
1668
info.desc = buffer;
1669
result.push_back(info);
1670
}
1671
return result;
1672
}
1673
1674
u32 GPUCommon::GetRelativeAddress(u32 data) {
1675
return gstate_c.getRelativeAddress(data);
1676
}
1677
1678
u32 GPUCommon::GetVertexAddress() {
1679
return gstate_c.vertexAddr;
1680
}
1681
1682
u32 GPUCommon::GetIndexAddress() {
1683
return gstate_c.indexAddr;
1684
}
1685
1686
const GPUgstate &GPUCommon::GetGState() {
1687
return gstate;
1688
}
1689
1690
void GPUCommon::SetCmdValue(u32 op) {
1691
u32 cmd = op >> 24;
1692
u32 diff = op ^ gstate.cmdmem[cmd];
1693
1694
Reporting::NotifyDebugger();
1695
PreExecuteOp(op, diff);
1696
gstate.cmdmem[cmd] = op;
1697
ExecuteOp(op, diff);
1698
downcount = 0;
1699
}
1700
1701
void GPUCommon::DoBlockTransfer(u32 skipDrawReason) {
1702
u32 srcBasePtr = gstate.getTransferSrcAddress();
1703
u32 srcStride = gstate.getTransferSrcStride();
1704
1705
u32 dstBasePtr = gstate.getTransferDstAddress();
1706
u32 dstStride = gstate.getTransferDstStride();
1707
1708
int srcX = gstate.getTransferSrcX();
1709
int srcY = gstate.getTransferSrcY();
1710
1711
int dstX = gstate.getTransferDstX();
1712
int dstY = gstate.getTransferDstY();
1713
1714
int width = gstate.getTransferWidth();
1715
int height = gstate.getTransferHeight();
1716
1717
int bpp = gstate.getTransferBpp();
1718
1719
DEBUG_LOG(Log::G3D, "Block transfer: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY);
1720
gpuStats.numBlockTransfers++;
1721
1722
// For VRAM, we wrap around when outside valid memory (mirrors still work.)
1723
if ((srcBasePtr & 0x04800000) == 0x04800000)
1724
srcBasePtr &= ~0x00800000;
1725
if ((dstBasePtr & 0x04800000) == 0x04800000)
1726
dstBasePtr &= ~0x00800000;
1727
1728
// Use height less one to account for width, which can be greater or less than stride, and then add it on for the last line.
1729
// NOTE: The sizes are only used for validity checks and memory info tracking.
1730
const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp;
1731
const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp;
1732
const uint32_t srcSize = ((height - 1) * srcStride) + width * bpp;
1733
const uint32_t dstSize = ((height - 1) * dstStride) + width * bpp;
1734
1735
bool srcDstOverlap = src + srcSize > dst && dst + dstSize > src;
1736
bool srcValid = Memory::IsValidRange(src, srcSize);
1737
bool dstValid = Memory::IsValidRange(dst, dstSize);
1738
bool srcWraps = Memory::IsVRAMAddress(srcBasePtr) && !srcValid;
1739
bool dstWraps = Memory::IsVRAMAddress(dstBasePtr) && !dstValid;
1740
1741
char tag[128];
1742
size_t tagSize = 0;
1743
1744
// Tell the framebuffer manager to take action if possible. If it does the entire thing, let's just return.
1745
if (!framebufferManager_ || !framebufferManager_->NotifyBlockTransferBefore(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason)) {
1746
// Do the copy! (Hm, if we detect a drawn video frame (see below) then we could maybe skip this?)
1747
// Can use GetPointerUnchecked because we checked the addresses above. We could also avoid them
1748
// entirely by walking a couple of pointers...
1749
1750
// Simple case: just a straight copy, no overlap or wrapping.
1751
if (srcStride == dstStride && (u32)width == srcStride && !srcDstOverlap && srcValid && dstValid) {
1752
u32 srcLineStartAddr = srcBasePtr + (srcY * srcStride + srcX) * bpp;
1753
u32 dstLineStartAddr = dstBasePtr + (dstY * dstStride + dstX) * bpp;
1754
u32 bytesToCopy = width * height * bpp;
1755
1756
const u8 *srcp = Memory::GetPointer(srcLineStartAddr);
1757
u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr);
1758
memcpy(dstp, srcp, bytesToCopy);
1759
1760
if (MemBlockInfoDetailed(bytesToCopy)) {
1761
NotifyMemInfoCopy(dst, src, bytesToCopy, "GPUBlockTransfer/");
1762
}
1763
} else if ((srcDstOverlap || srcWraps || dstWraps) && (srcValid || srcWraps) && (dstValid || dstWraps)) {
1764
// This path means we have either src/dst overlap, OR one or both of src and dst wrap.
1765
// This should be uncommon so it's the slowest path.
1766
u32 bytesToCopy = width * bpp;
1767
bool notifyDetail = MemBlockInfoDetailed(srcWraps || dstWraps ? 64 : bytesToCopy);
1768
bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize);
1769
if (notifyDetail || notifyAll) {
1770
tagSize = FormatMemWriteTagAt(tag, sizeof(tag), "GPUBlockTransfer/", src, srcSize);
1771
}
1772
1773
auto notifyingMemmove = [&](u32 d, u32 s, u32 sz) {
1774
const u8 *srcp = Memory::GetPointer(s);
1775
u8 *dstp = Memory::GetPointerWrite(d);
1776
memmove(dstp, srcp, sz);
1777
1778
if (notifyDetail) {
1779
NotifyMemInfo(MemBlockFlags::READ, s, sz, tag, tagSize);
1780
NotifyMemInfo(MemBlockFlags::WRITE, d, sz, tag, tagSize);
1781
}
1782
};
1783
1784
for (int y = 0; y < height; y++) {
1785
u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp;
1786
u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp;
1787
// If we already passed a wrap, we can use the quicker path.
1788
if ((srcLineStartAddr & 0x04800000) == 0x04800000)
1789
srcLineStartAddr &= ~0x00800000;
1790
if ((dstLineStartAddr & 0x04800000) == 0x04800000)
1791
dstLineStartAddr &= ~0x00800000;
1792
// These flags mean there's a wrap inside this line.
1793
bool srcLineWrap = !Memory::IsValidRange(srcLineStartAddr, bytesToCopy);
1794
bool dstLineWrap = !Memory::IsValidRange(dstLineStartAddr, bytesToCopy);
1795
1796
if (!srcLineWrap && !dstLineWrap) {
1797
const u8 *srcp = Memory::GetPointer(srcLineStartAddr);
1798
u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr);
1799
for (u32 i = 0; i < bytesToCopy; i += 64) {
1800
u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64;
1801
memmove(dstp + i, srcp + i, chunk);
1802
}
1803
1804
// If we're tracking detail, it's useful to have the gaps illustrated properly.
1805
if (notifyDetail) {
1806
NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag, tagSize);
1807
NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag, tagSize);
1808
}
1809
} else {
1810
// We can wrap at any point, so along with overlap this gets a bit complicated.
1811
// We're just going to do this the slow and easy way.
1812
u32 srcLinePos = srcLineStartAddr;
1813
u32 dstLinePos = dstLineStartAddr;
1814
for (u32 i = 0; i < bytesToCopy; i += 64) {
1815
u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64;
1816
u32 srcValid = Memory::ClampValidSizeAt(srcLinePos, chunk);
1817
u32 dstValid = Memory::ClampValidSizeAt(dstLinePos, chunk);
1818
1819
// First chunk, for which both are valid.
1820
u32 bothSize = std::min(srcValid, dstValid);
1821
if (bothSize != 0)
1822
notifyingMemmove(dstLinePos, srcLinePos, bothSize);
1823
1824
// Now, whichever side has more valid (or the rest, if only one side must wrap.)
1825
u32 exclusiveSize = std::max(srcValid, dstValid) - bothSize;
1826
if (exclusiveSize != 0 && srcValid >= dstValid) {
1827
notifyingMemmove(PSP_GetVidMemBase(), srcLineStartAddr + bothSize, exclusiveSize);
1828
} else if (exclusiveSize != 0 && srcValid < dstValid) {
1829
notifyingMemmove(dstLineStartAddr + bothSize, PSP_GetVidMemBase(), exclusiveSize);
1830
}
1831
1832
// Finally, if both src and dst wrapped, that portion.
1833
u32 wrappedSize = chunk - bothSize - exclusiveSize;
1834
if (wrappedSize != 0 && srcValid >= dstValid) {
1835
notifyingMemmove(PSP_GetVidMemBase() + exclusiveSize, PSP_GetVidMemBase(), wrappedSize);
1836
} else if (wrappedSize != 0 && srcValid < dstValid) {
1837
notifyingMemmove(PSP_GetVidMemBase(), PSP_GetVidMemBase() + exclusiveSize, wrappedSize);
1838
}
1839
1840
srcLinePos += chunk;
1841
dstLinePos += chunk;
1842
if ((srcLinePos & 0x04800000) == 0x04800000)
1843
srcLinePos &= ~0x00800000;
1844
if ((dstLinePos & 0x04800000) == 0x04800000)
1845
dstLinePos &= ~0x00800000;
1846
}
1847
}
1848
}
1849
1850
if (notifyAll) {
1851
if (srcWraps) {
1852
u32 validSize = Memory::ClampValidSizeAt(src, srcSize);
1853
NotifyMemInfo(MemBlockFlags::READ, src, validSize, tag, tagSize);
1854
NotifyMemInfo(MemBlockFlags::READ, PSP_GetVidMemBase(), srcSize - validSize, tag, tagSize);
1855
} else {
1856
NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag, tagSize);
1857
}
1858
if (dstWraps) {
1859
u32 validSize = Memory::ClampValidSizeAt(dst, dstSize);
1860
NotifyMemInfo(MemBlockFlags::WRITE, dst, validSize, tag, tagSize);
1861
NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetVidMemBase(), dstSize - validSize, tag, tagSize);
1862
} else {
1863
NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag, tagSize);
1864
}
1865
}
1866
} else if (srcValid && dstValid) {
1867
u32 bytesToCopy = width * bpp;
1868
bool notifyDetail = MemBlockInfoDetailed(bytesToCopy);
1869
bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize);
1870
if (notifyDetail || notifyAll) {
1871
tagSize = FormatMemWriteTagAt(tag, sizeof(tag), "GPUBlockTransfer/", src, srcSize);
1872
}
1873
1874
for (int y = 0; y < height; y++) {
1875
u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp;
1876
u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp;
1877
1878
const u8 *srcp = Memory::GetPointer(srcLineStartAddr);
1879
u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr);
1880
memcpy(dstp, srcp, bytesToCopy);
1881
1882
// If we're tracking detail, it's useful to have the gaps illustrated properly.
1883
if (notifyDetail) {
1884
NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag, tagSize);
1885
NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag, tagSize);
1886
}
1887
}
1888
1889
if (notifyAll) {
1890
NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag, tagSize);
1891
NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag, tagSize);
1892
}
1893
} else {
1894
// This seems to cause the GE to require a break/reset on a PSP.
1895
// TODO: Handle that and figure out which bytes are still copied?
1896
ERROR_LOG_REPORT_ONCE(invalidtransfer, Log::G3D, "Block transfer invalid: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY);
1897
}
1898
1899
if (framebufferManager_) {
1900
// Fixes Gran Turismo's funky text issue, since it overwrites the current texture.
1901
textureCache_->Invalidate(dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, GPU_INVALIDATE_HINT);
1902
framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason);
1903
}
1904
}
1905
1906
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
1907
cyclesExecuted += ((height * width * bpp) * 16) / 10;
1908
}
1909
1910
bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags) {
1911
if (size == 0) {
1912
_dbg_assert_msg_(false, "Zero-sized PerformMemoryCopy: %08x -> %08x, size %d (flag: %d)", src, dest, size, (int)flags);
1913
return false;
1914
}
1915
1916
// If dest is not a valid address, just ignore.
1917
if (!Memory::IsValidAddress(dest)) {
1918
_dbg_assert_msg_(false, "Invalid address for PerformMemorySet: %08x, size %d", dest, size);
1919
return false;
1920
}
1921
1922
// Check for invalid memory range. Should we reject? For now, let's clamp it.
1923
if (Memory::ClampValidSizeAt(dest, size) < (u32)size) {
1924
ERROR_LOG_REPORT_ONCE(invalidmemset, Log::G3D, "PerformMemorySet with invalid range: %08x, size %d", dest, size);
1925
size = Memory::ClampValidSizeAt(dest, size);
1926
}
1927
1928
// Track stray copies of a framebuffer in RAM. MotoGP does this.
1929
if (framebufferManager_->MayIntersectFramebufferColor(src) || framebufferManager_->MayIntersectFramebufferColor(dest)) {
1930
if (!framebufferManager_->NotifyFramebufferCopy(src, dest, size, flags, gstate_c.skipDrawReason)) {
1931
// We use matching values in PerformReadbackToMemory/PerformWriteColorFromMemory.
1932
// Since they're identical we don't need to copy.
1933
if (dest != src) {
1934
if (Memory::IsValidRange(dest, size) && Memory::IsValidRange(src, size)) {
1935
memcpy(Memory::GetPointerWriteUnchecked(dest), Memory::GetPointerUnchecked(src), size);
1936
}
1937
if (MemBlockInfoDetailed(size)) {
1938
NotifyMemInfoCopy(dest, src, size, "GPUMemcpy/");
1939
}
1940
}
1941
}
1942
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
1943
return true;
1944
}
1945
1946
if (MemBlockInfoDetailed(size)) {
1947
NotifyMemInfoCopy(dest, src, size, "GPUMemcpy/");
1948
}
1949
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
1950
if (!(flags & GPUCopyFlag::DEBUG_NOTIFIED))
1951
recorder_.NotifyMemcpy(dest, src, size);
1952
return false;
1953
}
1954
1955
bool GPUCommon::PerformMemorySet(u32 dest, u8 v, int size) {
1956
if (size == 0) {
1957
_dbg_assert_msg_(false, "Zero-sized PerformMemorySet: %08x, value %02x, size %d", dest, v, size);
1958
return false;
1959
}
1960
1961
// If dest is not a valid address, just ignore.
1962
if (!Memory::IsValidAddress(dest)) {
1963
_dbg_assert_msg_(false, "Invalid address for PerformMemorySet: %08x, size %d", dest, size);
1964
return false;
1965
}
1966
1967
// Check for invalid memory range. Should we reject? For now, let's clamp it.
1968
if (Memory::ClampValidSizeAt(dest, size) < (u32)size) {
1969
ERROR_LOG_REPORT_ONCE(invalidmemset, Log::G3D, "PerformMemorySet with invalid range: %08x, size %d", dest, size);
1970
size = Memory::ClampValidSizeAt(dest, size);
1971
}
1972
1973
// This may indicate a memset, usually to 0, of a framebuffer.
1974
if (framebufferManager_->MayIntersectFramebufferColor(dest)) {
1975
Memory::Memset(dest, v, size, "GPUMemset");
1976
if (!framebufferManager_->NotifyFramebufferCopy(dest, dest, size, GPUCopyFlag::MEMSET, gstate_c.skipDrawReason)) {
1977
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
1978
}
1979
return true;
1980
}
1981
1982
NotifyMemInfo(MemBlockFlags::WRITE, dest, size, "GPUMemset");
1983
// Or perhaps a texture, let's invalidate.
1984
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
1985
recorder_.NotifyMemset(dest, v, size);
1986
return false;
1987
}
1988
1989
bool GPUCommon::PerformReadbackToMemory(u32 dest, int size) {
1990
if (Memory::IsVRAMAddress(dest)) {
1991
return PerformMemoryCopy(dest, dest, size, GPUCopyFlag::FORCE_DST_MATCH_MEM);
1992
}
1993
return false;
1994
}
1995
1996
bool GPUCommon::PerformWriteColorFromMemory(u32 dest, int size) {
1997
if (Memory::IsVRAMAddress(dest)) {
1998
recorder_.NotifyUpload(dest, size);
1999
return PerformMemoryCopy(dest, dest, size, GPUCopyFlag::FORCE_SRC_MATCH_MEM | GPUCopyFlag::DEBUG_NOTIFIED);
2000
}
2001
return false;
2002
}
2003
2004
void GPUCommon::PerformWriteFormattedFromMemory(u32 addr, int size, int frameWidth, GEBufferFormat format) {
2005
if (Memory::IsVRAMAddress(addr)) {
2006
framebufferManager_->PerformWriteFormattedFromMemory(addr, size, frameWidth, format);
2007
}
2008
textureCache_->NotifyWriteFormattedFromMemory(addr, size, frameWidth, format);
2009
InvalidateCache(addr, size, GPU_INVALIDATE_SAFE);
2010
}
2011
2012
bool GPUCommon::PerformWriteStencilFromMemory(u32 dest, int size, WriteStencil flags) {
2013
if (framebufferManager_->MayIntersectFramebufferColor(dest)) {
2014
framebufferManager_->PerformWriteStencilFromMemory(dest, size, flags);
2015
return true;
2016
}
2017
return false;
2018
}
2019
2020
bool GPUCommon::GetCurrentDrawAsDebugVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
2021
gstate_c.UpdateUVScaleOffset();
2022
return ::GetCurrentDrawAsDebugVertices(drawEngineCommon_, count, vertices, indices);
2023
}
2024
2025
bool GPUCommon::DescribeCodePtr(const u8 *ptr, std::string &name) {
2026
// The only part of GPU emulation (other than software) that jits is the vertex decoder, currently,
2027
// which is owned by the drawengine.
2028
return drawEngineCommon_->DescribeCodePtr(ptr, name);
2029
}
2030
2031
bool GPUCommon::NeedsSlowInterpreter() const {
2032
return breakNext_ != GPUDebug::BreakNext::NONE;
2033
}
2034
2035
void GPUCommon::ClearBreakNext() {
2036
breakNext_ = GPUDebug::BreakNext::NONE;
2037
breakAtCount_ = -1;
2038
GPUStepping::ResumeFromStepping();
2039
}
2040
2041
void GPUCommon::SetBreakNext(GPUDebug::BreakNext next) {
2042
breakNext_ = next;
2043
breakAtCount_ = -1;
2044
switch (next) {
2045
case GPUDebug::BreakNext::TEX:
2046
breakpoints_.AddTextureChangeTempBreakpoint();
2047
break;
2048
case GPUDebug::BreakNext::PRIM:
2049
case GPUDebug::BreakNext::COUNT:
2050
breakpoints_.AddCmdBreakpoint(GE_CMD_PRIM, true);
2051
breakpoints_.AddCmdBreakpoint(GE_CMD_BEZIER, true);
2052
breakpoints_.AddCmdBreakpoint(GE_CMD_SPLINE, true);
2053
breakpoints_.AddCmdBreakpoint(GE_CMD_VAP, true);
2054
breakpoints_.AddCmdBreakpoint(GE_CMD_TRANSFERSTART, true); // We count block transfers as prims, too.
2055
break;
2056
case GPUDebug::BreakNext::CURVE:
2057
breakpoints_.AddCmdBreakpoint(GE_CMD_BEZIER, true);
2058
breakpoints_.AddCmdBreakpoint(GE_CMD_SPLINE, true);
2059
break;
2060
case GPUDebug::BreakNext::DRAW:
2061
// This is now handled by switching to BreakNext::PRIM when we encounter a flush.
2062
// This will take us to the following actual draw.
2063
primAfterDraw_ = true;
2064
break;
2065
case GPUDebug::BreakNext::BLOCK_TRANSFER:
2066
breakpoints_.AddCmdBreakpoint(GE_CMD_TRANSFERSTART, true);
2067
break;
2068
default:
2069
break;
2070
}
2071
2072
if (GPUStepping::IsStepping()) {
2073
GPUStepping::ResumeFromStepping();
2074
}
2075
}
2076
2077
void GPUCommon::SetBreakCount(int c, bool relative) {
2078
if (relative) {
2079
breakAtCount_ = primsThisFrame_ + c;
2080
} else {
2081
breakAtCount_ = c;
2082
}
2083
}
2084
2085
GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoints) {
2086
using namespace GPUDebug;
2087
2088
u32 op = Memory::ReadUnchecked_U32(pc);
2089
u32 cmd = op >> 24;
2090
if (thisFlipNum_ != gpuStats.numFlips) {
2091
primsLastFrame_ = primsThisFrame_;
2092
primsThisFrame_ = 0;
2093
thisFlipNum_ = gpuStats.numFlips;
2094
}
2095
2096
bool isPrim = false;
2097
2098
bool process = true; // Process is only for the restrictPrimRanges functionality
2099
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE || cmd == GE_CMD_VAP || cmd == GE_CMD_TRANSFERSTART) { // VAP is immediate mode prims.
2100
isPrim = true;
2101
primsThisFrame_++;
2102
2103
// TODO: Should restricted prim ranges also avoid breakpoints?
2104
2105
if (!restrictPrimRanges_.empty()) {
2106
process = false;
2107
for (const auto &range : restrictPrimRanges_) {
2108
if ((primsThisFrame_ + 1) >= range.first && (primsThisFrame_ + 1) <= range.second) {
2109
process = true;
2110
break;
2111
}
2112
}
2113
}
2114
}
2115
2116
bool debugBreak = false;
2117
if (breakNext_ == BreakNext::OP) {
2118
debugBreak = true;
2119
} else if (breakNext_ == BreakNext::COUNT) {
2120
debugBreak = primsThisFrame_ == breakAtCount_;
2121
} else if (breakpoints->HasBreakpoints()) {
2122
debugBreak = breakpoints->IsBreakpoint(pc, op);
2123
}
2124
2125
if (debugBreak && pc == skipPcOnce_) {
2126
INFO_LOG(Log::GeDebugger, "Skipping GE break at %08x (last break was here)", skipPcOnce_);
2127
skipPcOnce_ = 0;
2128
if (isPrim)
2129
primsThisFrame_--; // Compensate for the wrong increment above - we didn't run anything.
2130
return process ? NotifyResult::Execute : NotifyResult::Skip;
2131
}
2132
skipPcOnce_ = 0;
2133
2134
if (debugBreak) {
2135
breakpoints->ClearTempBreakpoints();
2136
2137
u32 op = Memory::Read_U32(pc);
2138
auto info = DisassembleOp(pc, op);
2139
NOTICE_LOG(Log::GeDebugger, "Waiting at %08x, %s", pc, info.desc.c_str());
2140
2141
skipPcOnce_ = pc;
2142
breakNext_ = BreakNext::NONE;
2143
// Not incrementing the prim counter!
2144
return NotifyResult::Break; // caller will call GPUStepping::EnterStepping().
2145
}
2146
2147
return process ? NotifyResult::Execute : NotifyResult::Skip;
2148
}
2149
2150
void GPUCommon::NotifyFlush() {
2151
using namespace GPUDebug;
2152
if (breakNext_ == BreakNext::DRAW && !GPUStepping::IsStepping()) {
2153
// Break on the first PRIM after a flush.
2154
if (primAfterDraw_) {
2155
NOTICE_LOG(Log::GeDebugger, "Flush detected, breaking at next PRIM");
2156
primAfterDraw_ = false;
2157
2158
// We've got one to rewind.
2159
primsThisFrame_--;
2160
2161
// Switch to PRIM mode.
2162
SetBreakNext(BreakNext::PRIM);
2163
}
2164
}
2165
}
2166
2167
void GPUCommon::NotifyDisplay(u32 framebuf, u32 stride, int format) {
2168
using namespace GPUDebug;
2169
if (breakNext_ == BreakNext::FRAME) {
2170
// Start stepping at the first op of the new frame.
2171
breakNext_ = BreakNext::OP;
2172
}
2173
recorder_.NotifyDisplay(framebuf, stride, format);
2174
}
2175
2176
bool GPUCommon::SetRestrictPrims(std::string_view rule) {
2177
if (rule.empty() || rule == "*") {
2178
restrictPrimRanges_.clear();
2179
restrictPrimRule_.clear();
2180
return true;
2181
}
2182
2183
if (GPUDebug::ParsePrimRanges(rule, &restrictPrimRanges_)) {
2184
restrictPrimRule_ = rule;
2185
return true;
2186
} else {
2187
return false;
2188
}
2189
}
2190
2191