Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Debugger/State.cpp
3187 views
1
#include <algorithm>
2
#include <cstdio>
3
#include "Common/Common.h"
4
#include "Common/StringUtils.h"
5
#include "Common/Data/Convert/ColorConv.h"
6
#include "GPU/Debugger/State.h"
7
#include "GPU/GPU.h"
8
#include "GPU/Common/GPUStateUtils.h"
9
#include "GPU/Common/GPUDebugInterface.h"
10
#include "GPU/GeDisasm.h"
11
#include "GPU/Common/VertexDecoderCommon.h"
12
#include "GPU/Common/SplineCommon.h"
13
#include "Core/System.h"
14
15
void FormatStateRow(GPUDebugInterface *gpudebug, char *dest, size_t destSize, CmdFormatType fmt, u32 value, bool enabled, u32 otherValue, u32 otherValue2) {
16
value &= 0xFFFFFF;
17
otherValue &= 0xFFFFFF;
18
otherValue2 &= 0xFFFFFF;
19
switch (fmt) {
20
case CMD_FMT_HEX:
21
snprintf(dest, destSize, "%06x", value);
22
break;
23
24
case CMD_FMT_NUM:
25
snprintf(dest, destSize, "%d", value);
26
break;
27
28
case CMD_FMT_FLOAT24:
29
snprintf(dest, destSize, "%f", getFloat24(value));
30
break;
31
32
case CMD_FMT_PTRWIDTH:
33
value |= (otherValue & 0x00FF0000) << 8;
34
otherValue &= 0xFFFF;
35
snprintf(dest, destSize, "%08x, w=%d", value, otherValue);
36
break;
37
38
case CMD_FMT_XY:
39
{
40
int x = value & 0x3FF;
41
int y = value >> 10;
42
snprintf(dest, destSize, "%d,%d", x, y);
43
}
44
break;
45
46
case CMD_FMT_XYPLUS1:
47
{
48
int x = value & 0x3FF;
49
int y = value >> 10;
50
snprintf(dest, destSize, "%d,%d", x + 1, y + 1);
51
}
52
break;
53
54
case CMD_FMT_XYXY:
55
{
56
int x1 = value & 0x3FF;
57
int y1 = value >> 10;
58
int x2 = otherValue & 0x3FF;
59
int y2 = otherValue >> 10;
60
snprintf(dest, destSize, "%d,%d - %d,%d", x1, y1, x2, y2);
61
}
62
break;
63
64
case CMD_FMT_XYZ:
65
{
66
float x = getFloat24(value);
67
float y = getFloat24(otherValue);
68
float z = getFloat24(otherValue2);
69
snprintf(dest, destSize, "%f, %f, %f", x, y, z);
70
}
71
break;
72
73
case CMD_FMT_TEXSIZE:
74
{
75
int w = 1 << (value & 0x1f);
76
int h = 1 << ((value >> 8) & 0x1f);
77
snprintf(dest, destSize, "%dx%d", w, h);
78
}
79
break;
80
81
case CMD_FMT_F16_XY:
82
{
83
float x = (float)value / 16.0f;
84
float y = (float)otherValue / 16.0f;
85
snprintf(dest, destSize, "%fx%f", x, y);
86
}
87
break;
88
89
case CMD_FMT_VERTEXTYPE:
90
{
91
char buffer[256];
92
GeDescribeVertexType(value, buffer);
93
snprintf(dest, destSize, "%s", buffer);
94
}
95
break;
96
97
case CMD_FMT_TEXFMT:
98
{
99
static const char *texformats[] = {
100
"5650",
101
"5551",
102
"4444",
103
"8888",
104
"CLUT4",
105
"CLUT8",
106
"CLUT16",
107
"CLUT32",
108
"DXT1",
109
"DXT3",
110
"DXT5",
111
};
112
if (value < (u32)ARRAY_SIZE(texformats)) {
113
snprintf(dest, destSize, "%s", texformats[value]);
114
} else if ((value & 0xF) < (u32)ARRAY_SIZE(texformats)) {
115
snprintf(dest, destSize, "%s (extra bits %06x)", texformats[value & 0xF], value & ~0xF);
116
} else {
117
snprintf(dest, destSize, "%06x", value);
118
}
119
}
120
break;
121
122
case CMD_FMT_CLUTFMT:
123
{
124
const char *clutformats[] = {
125
"BGR 5650",
126
"ABGR 1555",
127
"ABGR 4444",
128
"ABGR 8888",
129
};
130
const u8 palette = (value >> 0) & 3;
131
const u8 shift = (value >> 2) & 0x3F;
132
const u8 mask = (value >> 8) & 0xFF;
133
const u8 offset = (value >> 16) & 0xFF;
134
if (offset < 0x20 && shift < 0x20) {
135
if (offset == 0 && shift == 0) {
136
snprintf(dest, destSize, "%s ind & %02x", clutformats[palette], mask);
137
} else {
138
snprintf(dest, destSize, "%s (ind >> %d) & %02x, offset +%d", clutformats[palette], shift, mask, offset);
139
}
140
} else {
141
snprintf(dest, destSize, "%06x", value);
142
}
143
}
144
break;
145
146
case CMD_FMT_COLORTEST:
147
{
148
static const char *colorTests[] = { "NEVER", "ALWAYS", " == ", " != " };
149
const u32 mask = otherValue2;
150
const u32 ref = otherValue;
151
if (value < (u32)ARRAY_SIZE(colorTests)) {
152
snprintf(dest, destSize, "pass if (c & %06x) %s (%06x & %06x)", mask, colorTests[value], ref, mask);
153
} else {
154
snprintf(dest, destSize, "%06x, ref=%06x, maks=%06x", value, ref, mask);
155
}
156
}
157
break;
158
159
case CMD_FMT_ALPHATEST:
160
case CMD_FMT_STENCILTEST:
161
{
162
static const char *alphaTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" };
163
const u8 mask = (value >> 16) & 0xff;
164
const u8 ref = (value >> 8) & 0xff;
165
const u8 func = (value >> 0) & 0xff;
166
if (func < (u8)ARRAY_SIZE(alphaTestFuncs)) {
167
if (fmt == CMD_FMT_ALPHATEST) {
168
snprintf(dest, destSize, "pass if (a & %02x) %s (%02x & %02x)", mask, alphaTestFuncs[func], ref, mask);
169
} else if (fmt == CMD_FMT_STENCILTEST) {
170
// Stencil test is the other way around.
171
snprintf(dest, destSize, "pass if (%02x & %02x) %s (a & %02x)", ref, mask, alphaTestFuncs[func], mask);
172
}
173
} else {
174
snprintf(dest, destSize, "%06x", value);
175
}
176
}
177
break;
178
179
case CMD_FMT_ZTEST:
180
{
181
static const char *zTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" };
182
if (value < (u32)ARRAY_SIZE(zTestFuncs)) {
183
snprintf(dest, destSize, "pass if src %s dst", zTestFuncs[value]);
184
} else {
185
snprintf(dest, destSize, "%06x", value);
186
}
187
}
188
break;
189
190
case CMD_FMT_OFFSETADDR:
191
snprintf(dest, destSize, "%08x", gpuDebug->GetRelativeAddress(0));
192
break;
193
194
case CMD_FMT_VADDR:
195
snprintf(dest, destSize, "%08x", gpuDebug->GetVertexAddress());
196
break;
197
198
case CMD_FMT_IADDR:
199
snprintf(dest, destSize, "%08x", gpuDebug->GetIndexAddress());
200
break;
201
202
case CMD_FMT_MATERIALUPDATE:
203
{
204
static const char *materialTypes[] = {
205
"none",
206
"ambient",
207
"diffuse",
208
"ambient, diffuse",
209
"specular",
210
"ambient, specular",
211
"diffuse, specular",
212
"ambient, diffuse, specular",
213
};
214
if (value < (u32)ARRAY_SIZE(materialTypes)) {
215
snprintf(dest, destSize, "%s", materialTypes[value]);
216
} else {
217
snprintf(dest, destSize, "%06x", value);
218
}
219
}
220
break;
221
222
case CMD_FMT_SHADEMODEL:
223
if (value == 0) {
224
snprintf(dest, destSize, "flat");
225
} else if (value == 1) {
226
snprintf(dest, destSize, "gouraud");
227
} else {
228
snprintf(dest, destSize, "%06x", value);
229
}
230
break;
231
232
case CMD_FMT_STENCILOP:
233
{
234
static const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT" };
235
const u8 sfail = (value >> 0) & 0xFF;
236
const u8 zfail = (value >> 8) & 0xFF;
237
const u8 pass = (value >> 16) & 0xFF;
238
const u8 totalValid = (u8)ARRAY_SIZE(stencilOps);
239
if (sfail < totalValid && zfail < totalValid && pass < totalValid) {
240
snprintf(dest, destSize, "fail=%s, pass/depthfail=%s, pass=%s", stencilOps[sfail], stencilOps[zfail], stencilOps[pass]);
241
} else {
242
snprintf(dest, destSize, "%06x", value);
243
}
244
}
245
break;
246
247
case CMD_FMT_BLENDMODE:
248
{
249
const char *blendModes[] = {
250
"add",
251
"subtract",
252
"reverse subtract",
253
"min",
254
"max",
255
"abs subtract",
256
};
257
const char *blendFactorsA[] = {
258
"dst",
259
"1.0 - dst",
260
"src.a",
261
"1.0 - src.a",
262
"dst.a",
263
"1.0 - dst.a",
264
"2.0 * src.a",
265
"1.0 - 2.0 * src.a",
266
"2.0 * dst.a",
267
"1.0 - 2.0 * dst.a",
268
"fixed",
269
};
270
const char *blendFactorsB[] = {
271
"src",
272
"1.0 - src",
273
"src.a",
274
"1.0 - src.a",
275
"dst.a",
276
"1.0 - dst.a",
277
"2.0 * src.a",
278
"1.0 - 2.0 * src.a",
279
"2.0 * dst.a",
280
"1.0 - 2.0 * dst.a",
281
"fixed",
282
};
283
const u8 blendFactorA = (value >> 0) & 0xF;
284
const u8 blendFactorB = (value >> 4) & 0xF;
285
const u32 blendMode = (value >> 8);
286
287
if (blendFactorA < (u8)ARRAY_SIZE(blendFactorsA) && blendFactorB < (u8)ARRAY_SIZE(blendFactorsB) && blendMode < (u32)ARRAY_SIZE(blendModes)) {
288
snprintf(dest, destSize, "%s: %s, %s", blendModes[blendMode], blendFactorsA[blendFactorA], blendFactorsB[blendFactorB]);
289
} else {
290
snprintf(dest, destSize, "%06x", value);
291
}
292
}
293
break;
294
295
case CMD_FMT_CLEARMODE:
296
if (value == 0) {
297
snprintf(dest, destSize, "%d", value);
298
} else if ((value & ~(GE_CLEARMODE_ALL | 1)) == 0) {
299
const char *clearmodes[] = {
300
"1, write disabled",
301
"1, write color",
302
"1, write alpha/stencil",
303
"1, write color, alpha/stencil",
304
"1, write depth",
305
"1, write color, depth",
306
"1, write alpha/stencil, depth",
307
"1, write color, alpha/stencil, depth",
308
};
309
snprintf(dest, destSize, "%s", clearmodes[value >> 8]);
310
} else {
311
snprintf(dest, destSize, "%06x", value);
312
}
313
break;
314
315
case CMD_FMT_TEXFUNC:
316
{
317
const char *texfuncs[] = {
318
"modulate",
319
"decal",
320
"blend",
321
"replace",
322
"add",
323
};
324
const u8 func = (value >> 0) & 0xFF;
325
const u8 rgba = (value >> 8) & 0xFF;
326
const u8 colorDouble = (value >> 16) & 0xFF;
327
328
if (rgba <= 1 && colorDouble <= 1 && func < (u8)ARRAY_SIZE(texfuncs)) {
329
snprintf(dest, destSize, "%s, %s%s", texfuncs[func], rgba ? "RGBA" : "RGB", colorDouble ? ", color doubling" : "");
330
} else {
331
snprintf(dest, destSize, "%06x", value);
332
}
333
}
334
break;
335
336
case CMD_FMT_TEXMODE:
337
{
338
const u8 swizzle = (value >> 0) & 0xFF;
339
const u8 clutLevels = (value >> 8) & 0xFF;
340
const u8 maxLevel = (value >> 16) & 0xFF;
341
342
if (swizzle <= 1 && clutLevels <= 1 && maxLevel <= 7) {
343
snprintf(dest, destSize, "%s%d levels%s", swizzle ? "swizzled, " : "", maxLevel + 1, clutLevels ? ", CLUT per level" : "");
344
} else {
345
snprintf(dest, destSize, "%06x", value);
346
}
347
}
348
break;
349
350
case CMD_FMT_LOGICOP:
351
{
352
const char *logicOps[] = {
353
"clear",
354
"and",
355
"reverse and",
356
"copy",
357
"inverted and",
358
"noop",
359
"xor",
360
"or",
361
"negated or",
362
"equivalence",
363
"inverted",
364
"reverse or",
365
"inverted copy",
366
"inverted or",
367
"negated and",
368
"set",
369
};
370
371
if (value < ARRAY_SIZE(logicOps)) {
372
snprintf(dest, destSize, "%s", logicOps[value]);
373
} else {
374
snprintf(dest, destSize, "%06x", value);
375
}
376
}
377
break;
378
379
case CMD_FMT_TEXWRAP:
380
{
381
if ((value & ~0x0101) == 0) {
382
const bool clampS = (value & 0x0001) != 0;
383
const bool clampT = (value & 0x0100) != 0;
384
snprintf(dest, destSize, "%s s, %s t", clampS ? "clamp" : "wrap", clampT ? "clamp" : "wrap");
385
} else {
386
snprintf(dest, destSize, "%06x", value);
387
}
388
}
389
break;
390
391
case CMD_FMT_TEXLEVEL:
392
{
393
static const char *mipLevelModes[3] = {
394
"auto + bias",
395
"bias",
396
"slope + bias",
397
};
398
const int mipLevel = value & 0xFFFF;
399
const int biasFixed = (s8)(value >> 16);
400
const float bias = (float)biasFixed / 16.0f;
401
402
if (mipLevel == 0 || mipLevel == 1 || mipLevel == 2) {
403
snprintf(dest, destSize, "%s: %f", mipLevelModes[mipLevel], bias);
404
} else {
405
snprintf(dest, destSize, "%06x", value);
406
}
407
}
408
break;
409
410
case CMD_FMT_TEXFILTER:
411
{
412
const char *textureFilters[] = {
413
"nearest",
414
"linear",
415
NULL,
416
NULL,
417
"nearest, mipmap nearest",
418
"linear, mipmap nearest",
419
"nearest, mipmap linear",
420
"linear, mipmap linear",
421
};
422
if ((value & ~0x0107) == 0 && textureFilters[value & 7] != NULL) {
423
const int min = (value & 0x0007) >> 0;
424
const int mag = (value & 0x0100) >> 8;
425
snprintf(dest, destSize, "min: %s, mag: %s", textureFilters[min], textureFilters[mag]);
426
} else {
427
snprintf(dest, destSize, "%06x", value);
428
}
429
}
430
break;
431
432
case CMD_FMT_TEXMAPMODE:
433
{
434
static const char *const uvGenModes[] = {
435
"tex coords",
436
"tex matrix",
437
"tex env map",
438
"unknown (tex coords?)",
439
};
440
static const char *const uvProjModes[] = {
441
"pos",
442
"uv",
443
"normalized normal",
444
"normal",
445
};
446
if ((value & ~0x0303) == 0) {
447
const int uvGen = (value & 0x0003) >> 0;
448
const int uvProj = (value & 0x0300) >> 8;
449
snprintf(dest, destSize, "gen: %s, proj: %s", uvGenModes[uvGen], uvProjModes[uvProj]);
450
} else {
451
snprintf(dest, destSize, "%06x", value);
452
}
453
}
454
break;
455
456
case CMD_FMT_TEXSHADELS:
457
if ((value & ~0x0303) == 0) {
458
const int sLight = (value & 0x0003) >> 0;
459
const int tLight = (value & 0x0300) >> 8;
460
snprintf(dest, destSize, "s: %d, t: %d", sLight, tLight);
461
} else {
462
snprintf(dest, destSize, "%06x", value);
463
}
464
break;
465
466
case CMD_FMT_LIGHTMODE:
467
if (value == 0) {
468
snprintf(dest, destSize, "mixed color");
469
} else if (value == 1) {
470
snprintf(dest, destSize, "separate specular");
471
} else {
472
snprintf(dest, destSize, "%06x", value);
473
}
474
break;
475
476
case CMD_FMT_LIGHTTYPE:
477
{
478
static const char * const lightComputations[] = {
479
"diffuse",
480
"diffuse + spec",
481
"pow(diffuse)",
482
"unknown (diffuse?)",
483
};
484
static const char * const lightTypes[] = {
485
"directional",
486
"point",
487
"spot",
488
"unknown (directional?)",
489
};
490
if ((value & ~0x0303) == 0) {
491
const int comp = (value & 0x0003) >> 0;
492
const int type = (value & 0x0300) >> 8;
493
snprintf(dest, destSize, "type: %s, comp: %s", lightTypes[type], lightComputations[comp]);
494
} else {
495
snprintf(dest, destSize, "%06x", value);
496
}
497
}
498
break;
499
500
case CMD_FMT_CULL:
501
if (value == 0) {
502
snprintf(dest, destSize, "front (CW)");
503
} else if (value == 1) {
504
snprintf(dest, destSize, "back (CCW)");
505
} else {
506
snprintf(dest, destSize, "%06x", value);
507
}
508
break;
509
510
case CMD_FMT_PATCHPRIMITIVE:
511
{
512
static const char * const patchPrims[] = {
513
"triangles",
514
"lines",
515
"points",
516
};
517
if (value < (u32)ARRAY_SIZE(patchPrims)) {
518
snprintf(dest, destSize, "%s", patchPrims[value]);
519
} else {
520
snprintf(dest, destSize, "%06x", value);
521
}
522
}
523
break;
524
525
case CMD_FMT_FLAG:
526
if ((value & ~1) == 0) {
527
snprintf(dest, destSize, "%d", value);
528
} else {
529
snprintf(dest, destSize, "%06x", value);
530
}
531
break;
532
533
default:
534
snprintf(dest, destSize, "BAD FORMAT %06x", value);
535
}
536
537
// TODO: Turn row grey or some such?
538
if (!enabled) {
539
strcat(dest, " (disabled)");
540
}
541
}
542
543
void FormatVertCol(char *dest, size_t destSize, const GPUDebugVertex &vert, int col) {
544
switch (col) {
545
case VERTEXLIST_COL_X: snprintf(dest, destSize, "%f", vert.x); break;
546
case VERTEXLIST_COL_Y: snprintf(dest, destSize, "%f", vert.y); break;
547
case VERTEXLIST_COL_Z: snprintf(dest, destSize, "%f", vert.z); break;
548
case VERTEXLIST_COL_U: snprintf(dest, destSize, "%f", vert.u); break;
549
case VERTEXLIST_COL_V: snprintf(dest, destSize, "%f", vert.v); break;
550
case VERTEXLIST_COL_COLOR:
551
snprintf(dest, destSize, "%02x%02x%02x%02x", vert.c[0], vert.c[1], vert.c[2], vert.c[3]);
552
break;
553
case VERTEXLIST_COL_NX: snprintf(dest, destSize, "%f", vert.nx); break;
554
case VERTEXLIST_COL_NY: snprintf(dest, destSize, "%f", vert.ny); break;
555
case VERTEXLIST_COL_NZ: snprintf(dest, destSize, "%f", vert.nz); break;
556
557
default:
558
truncate_cpy(dest, destSize, "Invalid");
559
break;
560
}
561
}
562
563
static void FormatVertColRawType(char *dest, size_t destSize, const void *data, int type, int offset);
564
static void FormatVertColRawColor(char *dest, size_t destSize, const void *data, int type);
565
566
void FormatVertColRaw(VertexDecoder *decoder, char *dest, size_t destSize, int row, int col) {
567
if (PSP_GetBootState() != BootState::Complete) {
568
truncate_cpy(dest, destSize, "Invalid");
569
return;
570
}
571
572
// We could use the vertex decoder and reader, but those already do some minor adjustments.
573
// There's only a few values - let's just go after them directly.
574
const u8 *vert = Memory::GetPointer(gpuDebug->GetVertexAddress()) + row * decoder->size;
575
const u8 *pos = vert + decoder->posoff;
576
const u8 *tc = vert + decoder->tcoff;
577
const u8 *color = vert + decoder->coloff;
578
const u8 *norm = vert + decoder->nrmoff;
579
580
switch (col) {
581
case VERTEXLIST_COL_X:
582
FormatVertColRawType(dest, destSize, pos, decoder->pos, 0);
583
break;
584
case VERTEXLIST_COL_Y:
585
FormatVertColRawType(dest, destSize, pos, decoder->pos, 1);
586
break;
587
case VERTEXLIST_COL_Z:
588
FormatVertColRawType(dest, destSize, pos, decoder->pos, 2);
589
break;
590
case VERTEXLIST_COL_U:
591
FormatVertColRawType(dest, destSize, tc, decoder->tc, 0);
592
break;
593
case VERTEXLIST_COL_V:
594
FormatVertColRawType(dest, destSize, tc, decoder->tc, 1);
595
break;
596
case VERTEXLIST_COL_COLOR:
597
FormatVertColRawColor(dest, destSize, color, decoder->col);
598
break;
599
600
case VERTEXLIST_COL_NX: FormatVertColRawType(dest, destSize, norm, decoder->nrm, 0); break;
601
case VERTEXLIST_COL_NY: FormatVertColRawType(dest, destSize, norm, decoder->nrm, 1); break;
602
case VERTEXLIST_COL_NZ: FormatVertColRawType(dest, destSize, norm, decoder->nrm, 2); break;
603
604
default:
605
truncate_cpy(dest, destSize, "Invalid");
606
break;
607
}
608
}
609
610
static void FormatVertColRawType(char *dest, size_t destSize, const void *data, int type, int offset) {
611
switch (type) {
612
case 0:
613
truncate_cpy(dest, destSize, "-");
614
break;
615
616
case 1: // 8-bit
617
snprintf(dest, destSize, "%02x", ((const u8 *)data)[offset]);
618
break;
619
620
case 2: // 16-bit
621
snprintf(dest, destSize, "%04x", ((const u16_le *)data)[offset]);
622
break;
623
624
case 3: // float
625
snprintf(dest, destSize, "%f", ((const float *)data)[offset]);
626
break;
627
628
default:
629
truncate_cpy(dest, destSize, "Invalid");
630
break;
631
}
632
}
633
634
static void FormatVertColRawColor(char *dest, size_t destSize, const void *data, int type) {
635
switch (type) {
636
case GE_VTYPE_COL_NONE >> GE_VTYPE_COL_SHIFT:
637
truncate_cpy(dest, destSize, "-");
638
break;
639
640
case GE_VTYPE_COL_565 >> GE_VTYPE_COL_SHIFT:
641
case GE_VTYPE_COL_5551 >> GE_VTYPE_COL_SHIFT:
642
case GE_VTYPE_COL_4444 >> GE_VTYPE_COL_SHIFT:
643
snprintf(dest, destSize, "%04x", *(const u16_le *)data);
644
break;
645
646
case GE_VTYPE_COL_8888 >> GE_VTYPE_COL_SHIFT:
647
snprintf(dest, destSize, "%08x", *(const u32_le *)data);
648
break;
649
650
default:
651
truncate_cpy(dest, destSize, "Invalid");
652
break;
653
}
654
}
655
656
657
static void SwapUVs(GPUDebugVertex &a, GPUDebugVertex &b) {
658
float tempu = a.u;
659
float tempv = a.v;
660
a.u = b.u;
661
a.v = b.v;
662
b.u = tempu;
663
b.v = tempv;
664
}
665
666
static void RotateUVThrough(GPUDebugVertex v[4]) {
667
float x1 = v[2].x;
668
float x2 = v[0].x;
669
float y1 = v[2].y;
670
float y2 = v[0].y;
671
672
if ((x1 < x2 && y1 > y2) || (x1 > x2 && y1 < y2))
673
SwapUVs(v[1], v[3]);
674
}
675
676
static void ExpandRectangles(std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int &count, bool throughMode) {
677
static std::vector<GPUDebugVertex> newVerts;
678
static std::vector<u16> newInds;
679
680
bool useInds = true;
681
size_t numInds = indices.size();
682
if (indices.empty()) {
683
useInds = false;
684
numInds = count;
685
}
686
687
//rectangles always need 2 vertices, disregard the last one if there's an odd number
688
numInds = numInds & ~1;
689
690
// Will need 4 coords and 6 points per rectangle (currently 2 each.)
691
newVerts.resize(numInds * 2);
692
newInds.resize(numInds * 3);
693
694
u16 v = 0;
695
GPUDebugVertex *vert = &newVerts[0];
696
u16 *ind = &newInds[0];
697
for (size_t i = 0; i < numInds; i += 2) {
698
const auto &orig_tl = useInds ? vertices[indices[i + 0]] : vertices[i + 0];
699
const auto &orig_br = useInds ? vertices[indices[i + 1]] : vertices[i + 1];
700
701
vert[0] = orig_br;
702
703
// Top right.
704
vert[1] = orig_br;
705
vert[1].y = orig_tl.y;
706
vert[1].v = orig_tl.v;
707
708
vert[2] = orig_tl;
709
710
// Bottom left.
711
vert[3] = orig_br;
712
vert[3].x = orig_tl.x;
713
vert[3].u = orig_tl.u;
714
715
// That's the four corners. Now process UV rotation.
716
// This is the same for through and non-through, since it's already transformed.
717
RotateUVThrough(vert);
718
719
// Build the two 3 point triangles from our 4 coordinates.
720
*ind++ = v + 0;
721
*ind++ = v + 1;
722
*ind++ = v + 2;
723
*ind++ = v + 3;
724
*ind++ = v + 0;
725
*ind++ = v + 2;
726
727
vert += 4;
728
v += 4;
729
}
730
731
std::swap(vertices, newVerts);
732
std::swap(indices, newInds);
733
count *= 3;
734
}
735
736
static void ExpandBezier(int &count, int op, const std::vector<SimpleVertex> &simpleVerts, const std::vector<u16> &indices, std::vector<SimpleVertex> &generatedVerts, std::vector<u16> &generatedInds) {
737
using namespace Spline;
738
739
int count_u = (op >> 0) & 0xFF;
740
int count_v = (op >> 8) & 0xFF;
741
// Real hardware seems to draw nothing when given < 4 either U or V.
742
if (count_u < 4 || count_v < 4)
743
return;
744
745
BezierSurface surface;
746
surface.num_points_u = count_u;
747
surface.num_points_v = count_v;
748
surface.tess_u = gstate.getPatchDivisionU();
749
surface.tess_v = gstate.getPatchDivisionV();
750
surface.num_patches_u = (count_u - 1) / 3;
751
surface.num_patches_v = (count_v - 1) / 3;
752
surface.primType = gstate.getPatchPrimitiveType();
753
surface.patchFacing = false;
754
755
int num_points = count_u * count_v;
756
// Make an array of pointers to the control points, to get rid of indices.
757
std::vector<const SimpleVertex *> points(num_points);
758
for (int idx = 0; idx < num_points; idx++)
759
points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx);
760
761
int total_patches = surface.num_patches_u * surface.num_patches_v;
762
generatedVerts.resize((surface.tess_u + 1) * (surface.tess_v + 1) * total_patches);
763
generatedInds.resize(surface.tess_u * surface.tess_v * 6 * total_patches);
764
765
OutputBuffers output;
766
output.vertices = generatedVerts.data();
767
output.indices = generatedInds.data();
768
output.count = 0;
769
770
ControlPoints cpoints;
771
cpoints.pos = new Vec3f[num_points];
772
cpoints.tex = new Vec2f[num_points];
773
cpoints.col = new Vec4f[num_points];
774
cpoints.Convert(points.data(), num_points);
775
776
surface.Init((int)generatedVerts.size());
777
SoftwareTessellation(output, surface, gstate.vertType, cpoints);
778
count = output.count;
779
780
delete[] cpoints.pos;
781
delete[] cpoints.tex;
782
delete[] cpoints.col;
783
}
784
785
static void ExpandSpline(int &count, int op, const std::vector<SimpleVertex> &simpleVerts, const std::vector<u16> &indices, std::vector<SimpleVertex> &generatedVerts, std::vector<u16> &generatedInds) {
786
using namespace Spline;
787
788
int count_u = (op >> 0) & 0xFF;
789
int count_v = (op >> 8) & 0xFF;
790
// Real hardware seems to draw nothing when given < 4 either U or V.
791
if (count_u < 4 || count_v < 4)
792
return;
793
794
SplineSurface surface;
795
surface.num_points_u = count_u;
796
surface.num_points_v = count_v;
797
surface.tess_u = gstate.getPatchDivisionU();
798
surface.tess_v = gstate.getPatchDivisionV();
799
surface.type_u = (op >> 16) & 0x3;
800
surface.type_v = (op >> 18) & 0x3;
801
surface.num_patches_u = count_u - 3;
802
surface.num_patches_v = count_v - 3;
803
surface.primType = gstate.getPatchPrimitiveType();
804
surface.patchFacing = false;
805
806
int num_points = count_u * count_v;
807
// Make an array of pointers to the control points, to get rid of indices.
808
std::vector<const SimpleVertex *> points(num_points);
809
for (int idx = 0; idx < num_points; idx++)
810
points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx);
811
812
int patch_div_s = surface.num_patches_u * surface.tess_u;
813
int patch_div_t = surface.num_patches_v * surface.tess_v;
814
generatedVerts.resize((patch_div_s + 1) * (patch_div_t + 1));
815
generatedInds.resize(patch_div_s * patch_div_t * 6);
816
817
OutputBuffers output;
818
output.vertices = generatedVerts.data();
819
output.indices = generatedInds.data();
820
output.count = 0;
821
822
ControlPoints cpoints;
823
cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * num_points, 16);
824
cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * num_points, 16);
825
cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * num_points, 16);
826
cpoints.Convert(points.data(), num_points);
827
828
surface.Init((int)generatedVerts.size());
829
SoftwareTessellation(output, surface, gstate.vertType, cpoints);
830
count = output.count;
831
832
FreeAlignedMemory(cpoints.pos);
833
FreeAlignedMemory(cpoints.tex);
834
FreeAlignedMemory(cpoints.col);
835
}
836
837
bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int &count) {
838
u32 prim_type = GE_PRIM_INVALID;
839
int count_u = 0;
840
int count_v = 0;
841
842
const u32 cmd = op >> 24;
843
if (cmd == GE_CMD_PRIM) {
844
prim_type = (op >> 16) & 0x7;
845
count = op & 0xFFFF;
846
} else {
847
const GEPrimitiveType primLookup[] = { GE_PRIM_TRIANGLES, GE_PRIM_LINES, GE_PRIM_POINTS, GE_PRIM_POINTS };
848
prim_type = primLookup[gstate.getPatchPrimitiveType()];
849
count_u = (op & 0x00FF) >> 0;
850
count_v = (op & 0xFF00) >> 8;
851
count = count_u * count_v;
852
}
853
854
if (prim_type >= 7) {
855
ERROR_LOG(Log::G3D, "Unsupported prim type: %x", op);
856
return false;
857
}
858
if (!gpuDebug) {
859
ERROR_LOG(Log::G3D, "Invalid debugging environment, shutting down?");
860
return false;
861
}
862
if (count == 0) {
863
return false;
864
}
865
866
prim = static_cast<GEPrimitiveType>(prim_type);
867
868
if (!gpuDebug->GetCurrentDrawAsDebugVertices(count, vertices, indices)) {
869
ERROR_LOG(Log::G3D, "Vertex preview not yet supported");
870
return false;
871
}
872
873
if (cmd != GE_CMD_PRIM) {
874
static std::vector<SimpleVertex> generatedVerts;
875
static std::vector<u16> generatedInds;
876
877
static std::vector<SimpleVertex> simpleVerts;
878
simpleVerts.resize(vertices.size());
879
for (size_t i = 0; i < vertices.size(); ++i) {
880
// For now, let's just copy back so we can use TessellateBezierPatch/TessellateSplinePatch...
881
simpleVerts[i].uv[0] = vertices[i].u;
882
simpleVerts[i].uv[1] = vertices[i].v;
883
simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z);
884
}
885
886
if (cmd == GE_CMD_BEZIER) {
887
ExpandBezier(count, op, simpleVerts, indices, generatedVerts, generatedInds);
888
} else if (cmd == GE_CMD_SPLINE) {
889
ExpandSpline(count, op, simpleVerts, indices, generatedVerts, generatedInds);
890
}
891
892
vertices.resize(generatedVerts.size());
893
for (size_t i = 0; i < vertices.size(); ++i) {
894
vertices[i].u = generatedVerts[i].uv[0];
895
vertices[i].v = generatedVerts[i].uv[1];
896
vertices[i].x = generatedVerts[i].pos.x;
897
vertices[i].y = generatedVerts[i].pos.y;
898
vertices[i].z = generatedVerts[i].pos.z;
899
}
900
indices = generatedInds;
901
}
902
903
if (prim == GE_PRIM_RECTANGLES) {
904
ExpandRectangles(vertices, indices, count, gpuDebug->GetGState().isModeThrough());
905
}
906
907
// TODO: Probably there's a better way and place to do this.
908
u16 minIndex = 0;
909
u16 maxIndex = count - 1;
910
if (!indices.empty()) {
911
minIndex = 0xFFFF;
912
maxIndex = 0;
913
for (int i = 0; i < std::min((size_t)count, indices.size()); ++i) {
914
if (minIndex > indices[i]) {
915
minIndex = indices[i];
916
}
917
if (maxIndex < indices[i]) {
918
maxIndex = indices[i];
919
}
920
}
921
}
922
923
auto wrapCoord = [](float &coord) {
924
if (coord < 0.0f) {
925
coord += ceilf(-coord);
926
}
927
if (coord > 1.0f) {
928
coord -= floorf(coord);
929
}
930
};
931
932
const float invTexWidth = 1.0f / gpuDebug->GetGState().getTextureWidth(0);
933
const float invTexHeight = 1.0f / gpuDebug->GetGState().getTextureHeight(0);
934
bool clampS = gpuDebug->GetGState().isTexCoordClampedS();
935
bool clampT = gpuDebug->GetGState().isTexCoordClampedT();
936
for (u16 i = minIndex; i <= maxIndex; ++i) {
937
vertices[i].u *= invTexWidth;
938
vertices[i].v *= invTexHeight;
939
if (!clampS)
940
wrapCoord(vertices[i].u);
941
if (!clampT)
942
wrapCoord(vertices[i].v);
943
}
944
945
return true;
946
}
947
948
void DescribePixel(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]) {
949
switch (fmt) {
950
case GPU_DBG_FORMAT_565:
951
case GPU_DBG_FORMAT_565_REV:
952
case GPU_DBG_FORMAT_5551:
953
case GPU_DBG_FORMAT_5551_REV:
954
case GPU_DBG_FORMAT_5551_BGRA:
955
case GPU_DBG_FORMAT_4444:
956
case GPU_DBG_FORMAT_4444_REV:
957
case GPU_DBG_FORMAT_4444_BGRA:
958
case GPU_DBG_FORMAT_8888:
959
case GPU_DBG_FORMAT_8888_BGRA:
960
DescribePixelRGBA(pix, fmt, x, y, desc);
961
break;
962
963
case GPU_DBG_FORMAT_16BIT:
964
snprintf(desc, 256, "%d,%d: %d / %f", x, y, pix, pix * (1.0f / 65535.0f));
965
break;
966
967
case GPU_DBG_FORMAT_8BIT:
968
snprintf(desc, 256, "%d,%d: %d / %f", x, y, pix, pix * (1.0f / 255.0f));
969
break;
970
971
case GPU_DBG_FORMAT_24BIT_8X:
972
{
973
DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags());
974
// These are only ever going to be depth values, so let's also show scaled to 16 bit.
975
snprintf(desc, 256, "%d,%d: %d / %f / %f", x, y, pix & 0x00FFFFFF, (pix & 0x00FFFFFF) * (1.0f / 16777215.0f), depthScale.DecodeToU16((pix & 0x00FFFFFF) * (1.0f / 16777215.0f)));
976
break;
977
}
978
979
case GPU_DBG_FORMAT_24BIT_8X_DIV_256:
980
{
981
// These are only ever going to be depth values, so let's also show scaled to 16 bit.
982
int z24 = pix & 0x00FFFFFF;
983
int z16 = z24 - 0x800000 + 0x8000;
984
snprintf(desc, 256, "%d,%d: %d / %f", x, y, z16, z16 * (1.0f / 65535.0f));
985
}
986
break;
987
988
case GPU_DBG_FORMAT_24X_8BIT:
989
snprintf(desc, 256, "%d,%d: %d / %f", x, y, (pix >> 24) & 0xFF, ((pix >> 24) & 0xFF) * (1.0f / 255.0f));
990
break;
991
992
case GPU_DBG_FORMAT_FLOAT:
993
{
994
float pixf = *(float *)&pix;
995
DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags());
996
snprintf(desc, 256, "%d,%d: %f / %f", x, y, pixf, depthScale.DecodeToU16(pixf));
997
break;
998
}
999
1000
case GPU_DBG_FORMAT_FLOAT_DIV_256:
1001
{
1002
double z = *(float *)&pix;
1003
int z24 = (int)(z * 16777215.0);
1004
1005
DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());
1006
// TODO: Use GetDepthScaleFactors here too, verify it's the same.
1007
int z16 = z24 - 0x800000 + 0x8000;
1008
1009
int z16_2 = factors.DecodeToU16(z);
1010
1011
snprintf(desc, 256, "%d,%d: %d / %f", x, y, z16, (z - 0.5 + (1.0 / 512.0)) * 256.0);
1012
}
1013
break;
1014
1015
default:
1016
snprintf(desc, 256, "Unexpected format");
1017
}
1018
}
1019
1020
void DescribePixelRGBA(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]) {
1021
u32 r = -1, g = -1, b = -1, a = -1;
1022
1023
switch (fmt) {
1024
case GPU_DBG_FORMAT_565:
1025
r = Convert5To8((pix >> 0) & 0x1F);
1026
g = Convert6To8((pix >> 5) & 0x3F);
1027
b = Convert5To8((pix >> 11) & 0x1F);
1028
break;
1029
case GPU_DBG_FORMAT_565_REV:
1030
b = Convert5To8((pix >> 0) & 0x1F);
1031
g = Convert6To8((pix >> 5) & 0x3F);
1032
r = Convert5To8((pix >> 11) & 0x1F);
1033
break;
1034
case GPU_DBG_FORMAT_5551:
1035
r = Convert5To8((pix >> 0) & 0x1F);
1036
g = Convert5To8((pix >> 5) & 0x1F);
1037
b = Convert5To8((pix >> 10) & 0x1F);
1038
a = (pix >> 15) & 1 ? 255 : 0;
1039
break;
1040
case GPU_DBG_FORMAT_5551_REV:
1041
a = pix & 1 ? 255 : 0;
1042
b = Convert5To8((pix >> 1) & 0x1F);
1043
g = Convert5To8((pix >> 6) & 0x1F);
1044
r = Convert5To8((pix >> 11) & 0x1F);
1045
break;
1046
case GPU_DBG_FORMAT_5551_BGRA:
1047
b = Convert5To8((pix >> 0) & 0x1F);
1048
g = Convert5To8((pix >> 5) & 0x1F);
1049
r = Convert5To8((pix >> 10) & 0x1F);
1050
a = (pix >> 15) & 1 ? 255 : 0;
1051
break;
1052
case GPU_DBG_FORMAT_4444:
1053
r = Convert4To8((pix >> 0) & 0x0F);
1054
g = Convert4To8((pix >> 4) & 0x0F);
1055
b = Convert4To8((pix >> 8) & 0x0F);
1056
a = Convert4To8((pix >> 12) & 0x0F);
1057
break;
1058
case GPU_DBG_FORMAT_4444_REV:
1059
a = Convert4To8((pix >> 0) & 0x0F);
1060
b = Convert4To8((pix >> 4) & 0x0F);
1061
g = Convert4To8((pix >> 8) & 0x0F);
1062
r = Convert4To8((pix >> 12) & 0x0F);
1063
break;
1064
case GPU_DBG_FORMAT_4444_BGRA:
1065
b = Convert4To8((pix >> 0) & 0x0F);
1066
g = Convert4To8((pix >> 4) & 0x0F);
1067
r = Convert4To8((pix >> 8) & 0x0F);
1068
a = Convert4To8((pix >> 12) & 0x0F);
1069
break;
1070
case GPU_DBG_FORMAT_8888:
1071
r = (pix >> 0) & 0xFF;
1072
g = (pix >> 8) & 0xFF;
1073
b = (pix >> 16) & 0xFF;
1074
a = (pix >> 24) & 0xFF;
1075
break;
1076
case GPU_DBG_FORMAT_8888_BGRA:
1077
b = (pix >> 0) & 0xFF;
1078
g = (pix >> 8) & 0xFF;
1079
r = (pix >> 16) & 0xFF;
1080
a = (pix >> 24) & 0xFF;
1081
break;
1082
1083
default:
1084
snprintf(desc, 256, "Unexpected format");
1085
return;
1086
}
1087
1088
snprintf(desc, 256, "%d,%d: r=%d, g=%d, b=%d, a=%d", x, y, r, g, b, a);
1089
}
1090
1091