CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

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

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/GEDebugger/VertexPreview.cpp
Views: 1401
1
// Copyright (c) 2013- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "Common/Math/lin/matrix4x4.h"
19
#include "Common/GPU/OpenGL/GLSLProgram.h"
20
#include "Common/GPU/OpenGL/GLFeatures.h"
21
#include "Windows/GEDebugger/GEDebugger.h"
22
#include "Windows/GEDebugger/SimpleGLWindow.h"
23
#include "Core/System.h"
24
#include "Core/Config.h"
25
#include "GPU/GPUInterface.h"
26
#include "GPU/Common/GPUDebugInterface.h"
27
#include "GPU/Common/SplineCommon.h"
28
#include "GPU/GPUState.h"
29
#include "Common/Log.h"
30
#include "Common/MemoryUtil.h"
31
32
static const char preview_fs[] =
33
"#ifdef GL_ES\n"
34
"precision mediump float;\n"
35
"#endif\n"
36
"void main() {\n"
37
" gl_FragColor = vec4(1.0, 0.0, 0.0, 0.6);\n"
38
"}\n";
39
40
static const char preview_vs[] =
41
"#version 120\n"
42
"attribute vec4 a_position;\n"
43
"uniform mat4 u_viewproj;\n"
44
"void main() {\n"
45
" gl_Position = u_viewproj * a_position;\n"
46
" gl_Position.z = 1.0;\n"
47
"}\n";
48
49
static GLSLProgram *previewProgram = nullptr;
50
static GLSLProgram *texPreviewProgram = nullptr;
51
52
static GLuint previewVao = 0;
53
static GLuint texPreviewVao = 0;
54
static GLuint vbuf = 0;
55
static GLuint ibuf = 0;
56
57
static const GLuint glprim[8] = {
58
GL_POINTS,
59
GL_LINES,
60
GL_LINE_STRIP,
61
GL_TRIANGLES,
62
GL_TRIANGLE_STRIP,
63
GL_TRIANGLE_FAN,
64
// This is for RECTANGLES (see ExpandRectangles().)
65
GL_TRIANGLES,
66
};
67
68
static void BindPreviewProgram(GLSLProgram *&prog) {
69
if (prog == nullptr) {
70
prog = glsl_create_source(preview_vs, preview_fs);
71
}
72
73
glsl_bind(prog);
74
}
75
76
static void SwapUVs(GPUDebugVertex &a, GPUDebugVertex &b) {
77
float tempu = a.u;
78
float tempv = a.v;
79
a.u = b.u;
80
a.v = b.v;
81
b.u = tempu;
82
b.v = tempv;
83
}
84
85
static void RotateUVThrough(GPUDebugVertex v[4]) {
86
float x1 = v[2].x;
87
float x2 = v[0].x;
88
float y1 = v[2].y;
89
float y2 = v[0].y;
90
91
if ((x1 < x2 && y1 > y2) || (x1 > x2 && y1 < y2))
92
SwapUVs(v[1], v[3]);
93
}
94
95
static void ExpandRectangles(std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int &count, bool throughMode) {
96
static std::vector<GPUDebugVertex> newVerts;
97
static std::vector<u16> newInds;
98
99
bool useInds = true;
100
size_t numInds = indices.size();
101
if (indices.empty()) {
102
useInds = false;
103
numInds = count;
104
}
105
106
//rectangles always need 2 vertices, disregard the last one if there's an odd number
107
numInds = numInds & ~1;
108
109
// Will need 4 coords and 6 points per rectangle (currently 2 each.)
110
newVerts.resize(numInds * 2);
111
newInds.resize(numInds * 3);
112
113
u16 v = 0;
114
GPUDebugVertex *vert = &newVerts[0];
115
u16 *ind = &newInds[0];
116
for (size_t i = 0; i < numInds; i += 2) {
117
const auto &orig_tl = useInds ? vertices[indices[i + 0]] : vertices[i + 0];
118
const auto &orig_br = useInds ? vertices[indices[i + 1]] : vertices[i + 1];
119
120
vert[0] = orig_br;
121
122
// Top right.
123
vert[1] = orig_br;
124
vert[1].y = orig_tl.y;
125
vert[1].v = orig_tl.v;
126
127
vert[2] = orig_tl;
128
129
// Bottom left.
130
vert[3] = orig_br;
131
vert[3].x = orig_tl.x;
132
vert[3].u = orig_tl.u;
133
134
// That's the four corners. Now process UV rotation.
135
// This is the same for through and non-through, since it's already transformed.
136
RotateUVThrough(vert);
137
138
// Build the two 3 point triangles from our 4 coordinates.
139
*ind++ = v + 0;
140
*ind++ = v + 1;
141
*ind++ = v + 2;
142
*ind++ = v + 3;
143
*ind++ = v + 0;
144
*ind++ = v + 2;
145
146
vert += 4;
147
v += 4;
148
}
149
150
std::swap(vertices, newVerts);
151
std::swap(indices, newInds);
152
count *= 3;
153
}
154
155
u32 CGEDebugger::PrimPreviewOp() {
156
DisplayList list;
157
if (gpuDebug != nullptr && gpuDebug->GetCurrentDisplayList(list) && !showClut_) {
158
const u32 op = Memory::Read_U32(list.pc);
159
const u32 cmd = op >> 24;
160
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE) {
161
return op;
162
}
163
}
164
165
return 0;
166
}
167
168
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) {
169
using namespace Spline;
170
171
int count_u = (op >> 0) & 0xFF;
172
int count_v = (op >> 8) & 0xFF;
173
// Real hardware seems to draw nothing when given < 4 either U or V.
174
if (count_u < 4 || count_v < 4)
175
return;
176
177
BezierSurface surface;
178
surface.num_points_u = count_u;
179
surface.num_points_v = count_v;
180
surface.tess_u = gstate.getPatchDivisionU();
181
surface.tess_v = gstate.getPatchDivisionV();
182
surface.num_patches_u = (count_u - 1) / 3;
183
surface.num_patches_v = (count_v - 1) / 3;
184
surface.primType = gstate.getPatchPrimitiveType();
185
surface.patchFacing = false;
186
187
int num_points = count_u * count_v;
188
// Make an array of pointers to the control points, to get rid of indices.
189
std::vector<const SimpleVertex *> points(num_points);
190
for (int idx = 0; idx < num_points; idx++)
191
points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx);
192
193
int total_patches = surface.num_patches_u * surface.num_patches_v;
194
generatedVerts.resize((surface.tess_u + 1) * (surface.tess_v + 1) * total_patches);
195
generatedInds.resize(surface.tess_u * surface.tess_v * 6 * total_patches);
196
197
OutputBuffers output;
198
output.vertices = generatedVerts.data();
199
output.indices = generatedInds.data();
200
output.count = 0;
201
202
ControlPoints cpoints;
203
cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * num_points, 16);
204
cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * num_points, 16);
205
cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * num_points, 16);
206
cpoints.Convert(points.data(), num_points);
207
208
surface.Init((int)generatedVerts.size());
209
SoftwareTessellation(output, surface, gstate.vertType, cpoints);
210
count = output.count;
211
212
FreeAlignedMemory(cpoints.pos);
213
FreeAlignedMemory(cpoints.tex);
214
FreeAlignedMemory(cpoints.col);
215
}
216
217
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) {
218
using namespace Spline;
219
220
int count_u = (op >> 0) & 0xFF;
221
int count_v = (op >> 8) & 0xFF;
222
// Real hardware seems to draw nothing when given < 4 either U or V.
223
if (count_u < 4 || count_v < 4)
224
return;
225
226
SplineSurface surface;
227
surface.num_points_u = count_u;
228
surface.num_points_v = count_v;
229
surface.tess_u = gstate.getPatchDivisionU();
230
surface.tess_v = gstate.getPatchDivisionV();
231
surface.type_u = (op >> 16) & 0x3;
232
surface.type_v = (op >> 18) & 0x3;
233
surface.num_patches_u = count_u - 3;
234
surface.num_patches_v = count_v - 3;
235
surface.primType = gstate.getPatchPrimitiveType();
236
surface.patchFacing = false;
237
238
int num_points = count_u * count_v;
239
// Make an array of pointers to the control points, to get rid of indices.
240
std::vector<const SimpleVertex *> points(num_points);
241
for (int idx = 0; idx < num_points; idx++)
242
points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx);
243
244
int patch_div_s = surface.num_patches_u * surface.tess_u;
245
int patch_div_t = surface.num_patches_v * surface.tess_v;
246
generatedVerts.resize((patch_div_s + 1) * (patch_div_t + 1));
247
generatedInds.resize(patch_div_s * patch_div_t * 6);
248
249
OutputBuffers output;
250
output.vertices = generatedVerts.data();
251
output.indices = generatedInds.data();
252
output.count = 0;
253
254
ControlPoints cpoints;
255
cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * num_points, 16);
256
cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * num_points, 16);
257
cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * num_points, 16);
258
cpoints.Convert(points.data(), num_points);
259
260
surface.Init((int)generatedVerts.size());
261
SoftwareTessellation(output, surface, gstate.vertType, cpoints);
262
count = output.count;
263
264
FreeAlignedMemory(cpoints.pos);
265
FreeAlignedMemory(cpoints.tex);
266
FreeAlignedMemory(cpoints.col);
267
}
268
269
void CGEDebugger::UpdatePrimPreview(u32 op, int which) {
270
u32 prim_type = GE_PRIM_INVALID;
271
int count = 0;
272
int count_u = 0;
273
int count_v = 0;
274
275
const u32 cmd = op >> 24;
276
if (cmd == GE_CMD_PRIM) {
277
prim_type = (op >> 16) & 0x7;
278
count = op & 0xFFFF;
279
} else {
280
const GEPrimitiveType primLookup[] = { GE_PRIM_TRIANGLES, GE_PRIM_LINES, GE_PRIM_POINTS, GE_PRIM_POINTS };
281
if (gstate.getPatchPrimitiveType() < ARRAY_SIZE(primLookup))
282
prim_type = primLookup[gstate.getPatchPrimitiveType()];
283
count_u = (op & 0x00FF) >> 0;
284
count_v = (op & 0xFF00) >> 8;
285
count = count_u * count_v;
286
}
287
288
if (prim_type >= 7) {
289
ERROR_LOG(Log::G3D, "Unsupported prim type: %x", op);
290
return;
291
}
292
if (!gpuDebug) {
293
ERROR_LOG(Log::G3D, "Invalid debugging environment, shutting down?");
294
return;
295
}
296
which &= previewsEnabled_;
297
if (count == 0 || which == 0) {
298
return;
299
}
300
301
const GEPrimitiveType prim = static_cast<GEPrimitiveType>(prim_type);
302
static std::vector<GPUDebugVertex> vertices;
303
static std::vector<u16> indices;
304
305
if (!gpuDebug->GetCurrentSimpleVertices(count, vertices, indices)) {
306
ERROR_LOG(Log::G3D, "Vertex preview not yet supported");
307
return;
308
}
309
310
if (cmd != GE_CMD_PRIM) {
311
static std::vector<SimpleVertex> generatedVerts;
312
static std::vector<u16> generatedInds;
313
314
static std::vector<SimpleVertex> simpleVerts;
315
simpleVerts.resize(vertices.size());
316
for (size_t i = 0; i < vertices.size(); ++i) {
317
// For now, let's just copy back so we can use TessellateBezierPatch/TessellateSplinePatch...
318
simpleVerts[i].uv[0] = vertices[i].u;
319
simpleVerts[i].uv[1] = vertices[i].v;
320
simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z);
321
}
322
323
if (cmd == GE_CMD_BEZIER) {
324
ExpandBezier(count, op, simpleVerts, indices, generatedVerts, generatedInds);
325
} else if (cmd == GE_CMD_SPLINE) {
326
ExpandSpline(count, op, simpleVerts, indices, generatedVerts, generatedInds);
327
}
328
329
vertices.resize(generatedVerts.size());
330
for (size_t i = 0; i < vertices.size(); ++i) {
331
vertices[i].u = generatedVerts[i].uv[0];
332
vertices[i].v = generatedVerts[i].uv[1];
333
vertices[i].x = generatedVerts[i].pos.x;
334
vertices[i].y = generatedVerts[i].pos.y;
335
vertices[i].z = generatedVerts[i].pos.z;
336
}
337
indices = generatedInds;
338
}
339
340
if (prim == GE_PRIM_RECTANGLES) {
341
ExpandRectangles(vertices, indices, count, gpuDebug->GetGState().isModeThrough());
342
}
343
344
float fw, fh;
345
float x, y;
346
347
// TODO: Probably there's a better way and place to do this.
348
u16 minIndex = 0;
349
u16 maxIndex = count - 1;
350
if (!indices.empty()) {
351
_dbg_assert_(count <= indices.size());
352
minIndex = 0xFFFF;
353
maxIndex = 0;
354
for (int i = 0; i < count; ++i) {
355
if (minIndex > indices[i]) {
356
minIndex = indices[i];
357
}
358
if (maxIndex < indices[i]) {
359
maxIndex = indices[i];
360
}
361
}
362
}
363
364
auto wrapCoord = [](float &coord) {
365
if (coord < 0.0f) {
366
coord += ceilf(-coord);
367
}
368
if (coord > 1.0f) {
369
coord -= floorf(coord);
370
}
371
};
372
373
const float invTexWidth = 1.0f / gpuDebug->GetGState().getTextureWidth(0);
374
const float invTexHeight = 1.0f / gpuDebug->GetGState().getTextureHeight(0);
375
const float invRealTexWidth = 1.0f / gstate_c.curTextureWidth;
376
const float invRealTexHeight = 1.0f / gstate_c.curTextureHeight;
377
bool clampS = gpuDebug->GetGState().isTexCoordClampedS();
378
bool clampT = gpuDebug->GetGState().isTexCoordClampedT();
379
for (u16 i = minIndex; i <= maxIndex; ++i) {
380
vertices[i].u *= invTexWidth;
381
vertices[i].v *= invTexHeight;
382
if (!clampS)
383
wrapCoord(vertices[i].u);
384
if (!clampT)
385
wrapCoord(vertices[i].v);
386
}
387
388
if (which & 1) {
389
primaryWindow->Begin();
390
primaryWindow->GetContentSize(x, y, fw, fh);
391
392
glEnable(GL_BLEND);
393
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
394
glBlendEquation(GL_FUNC_ADD);
395
glBindTexture(GL_TEXTURE_2D, 0);
396
// The surface is upside down, so vertical offsets are negated.
397
glViewport((GLint)x, (GLint)-(y + fh - primaryWindow->Height()), (GLsizei)fw, (GLsizei)fh);
398
glScissor((GLint)x, (GLint)-(y + fh - primaryWindow->Height()), (GLsizei)fw, (GLsizei)fh);
399
BindPreviewProgram(previewProgram);
400
401
if (previewVao == 0 && gl_extensions.ARB_vertex_array_object) {
402
glGenVertexArrays(1, &previewVao);
403
glBindVertexArray(previewVao);
404
glEnableVertexAttribArray(previewProgram->a_position);
405
406
if (ibuf == 0)
407
glGenBuffers(1, &ibuf);
408
if (vbuf == 0)
409
glGenBuffers(1, &vbuf);
410
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);
411
glBindBuffer(GL_ARRAY_BUFFER, vbuf);
412
413
glVertexAttribPointer(previewProgram->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (void *)(2 * sizeof(float)));
414
}
415
416
if (vbuf != 0) {
417
glBindBuffer(GL_ARRAY_BUFFER, vbuf);
418
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GPUDebugVertex), vertices.data(), GL_STREAM_DRAW);
419
}
420
421
if (ibuf != 0 && !indices.empty()) {
422
glBindVertexArray(previewVao);
423
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u16), indices.data(), GL_STREAM_DRAW);
424
}
425
426
float scale[] = {
427
480.0f / (float)PSP_CoreParameter().renderWidth,
428
272.0f / (float)PSP_CoreParameter().renderHeight,
429
};
430
431
Lin::Matrix4x4 ortho;
432
ortho.setOrtho(-(float)gstate_c.curRTOffsetX, (primaryWindow->TexWidth() - (int)gstate_c.curRTOffsetX) * scale[0], primaryWindow->TexHeight() * scale[1], 0, -1, 1);
433
glUniformMatrix4fv(previewProgram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr());
434
if (previewVao != 0) {
435
glBindVertexArray(previewVao);
436
} else {
437
glEnableVertexAttribArray(previewProgram->a_position);
438
glVertexAttribPointer(previewProgram->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (float *)vertices.data() + 2);
439
}
440
441
if (indices.empty()) {
442
glDrawArrays(glprim[prim], 0, count);
443
} else {
444
glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, previewVao != 0 ? 0 : indices.data());
445
}
446
447
if (previewVao == 0) {
448
glDisableVertexAttribArray(previewProgram->a_position);
449
}
450
451
primaryWindow->End();
452
}
453
454
if (which & 2) {
455
secondWindow->Begin();
456
secondWindow->GetContentSize(x, y, fw, fh);
457
458
glEnable(GL_BLEND);
459
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
460
glBlendEquation(GL_FUNC_ADD);
461
glBindTexture(GL_TEXTURE_2D, 0);
462
// The surface is upside down, so vertical offsets are flipped.
463
glViewport((GLint)x, (GLint)-(y + fh - secondWindow->Height()), (GLsizei)fw, (GLsizei)fh);
464
glScissor((GLint)x, (GLint)-(y + fh - secondWindow->Height()), (GLsizei)fw, (GLsizei)fh);
465
BindPreviewProgram(texPreviewProgram);
466
467
if (texPreviewVao == 0 && vbuf != 0 && ibuf != 0 && gl_extensions.ARB_vertex_array_object) {
468
glGenVertexArrays(1, &texPreviewVao);
469
glBindVertexArray(texPreviewVao);
470
glEnableVertexAttribArray(texPreviewProgram->a_position);
471
472
if (ibuf == 0)
473
glGenBuffers(1, &ibuf);
474
if (vbuf == 0)
475
glGenBuffers(1, &vbuf);
476
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);
477
glBindBuffer(GL_ARRAY_BUFFER, vbuf);
478
479
glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), 0);
480
}
481
482
// TODO: For some reason we have to re-upload the data?
483
if (vbuf != 0) {
484
glBindBuffer(GL_ARRAY_BUFFER, vbuf);
485
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GPUDebugVertex), vertices.data(), GL_STREAM_DRAW);
486
}
487
488
if (ibuf != 0 && !indices.empty()) {
489
glBindVertexArray(texPreviewVao);
490
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u16), indices.data(), GL_STREAM_DRAW);
491
}
492
493
Lin::Matrix4x4 ortho;
494
ortho.setOrtho(0.0f - (float)gstate_c.curTextureXOffset * invRealTexWidth, 1.0f - (float)gstate_c.curTextureXOffset * invRealTexWidth, 1.0f - (float)gstate_c.curTextureYOffset * invRealTexHeight, 0.0f - (float)gstate_c.curTextureYOffset * invRealTexHeight, -1.0f, 1.0f);
495
glUniformMatrix4fv(texPreviewProgram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr());
496
if (texPreviewVao != 0) {
497
glBindVertexArray(texPreviewVao);
498
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);
499
glBindBuffer(GL_ARRAY_BUFFER, vbuf);
500
glEnableVertexAttribArray(texPreviewProgram->a_position);
501
glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), 0);
502
} else {
503
glEnableVertexAttribArray(texPreviewProgram->a_position);
504
glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (float *)vertices.data());
505
}
506
507
if (indices.empty()) {
508
glDrawArrays(glprim[prim], 0, count);
509
} else {
510
glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, texPreviewVao != 0 ? 0 : indices.data());
511
}
512
513
if (texPreviewVao == 0) {
514
glDisableVertexAttribArray(texPreviewProgram->a_position);
515
}
516
517
secondWindow->End();
518
}
519
}
520
521
void CGEDebugger::CleanupPrimPreview() {
522
if (previewProgram) {
523
glsl_destroy(previewProgram);
524
}
525
if (texPreviewProgram) {
526
glsl_destroy(texPreviewProgram);
527
}
528
}
529
530
void CGEDebugger::HandleRedraw(int which) {
531
if (updating_) {
532
return;
533
}
534
535
u32 op = PrimPreviewOp();
536
if (op) {
537
UpdatePrimPreview(op, which);
538
}
539
}
540
541