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 <algorithm>
19
20 #include "Common/Data/Convert/ColorConv.h"
21 #include "Common/Profiler/Profiler.h"
22 #include "Core/Config.h"
23 #include "GPU/Common/DrawEngineCommon.h"
24 #include "GPU/Common/SplineCommon.h"
25 #include "GPU/Common/VertexDecoderCommon.h"
26 #include "GPU/ge_constants.h"
27 #include "GPU/GPUState.h"
28
29 #define QUAD_INDICES_MAX 65536
30
31 enum {
32 TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex)
33 };
34
DrawEngineCommon()35 DrawEngineCommon::DrawEngineCommon() : decoderMap_(16) {
36 decJitCache_ = new VertexDecoderJitCache();
37 transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
38 transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
39 }
40
~DrawEngineCommon()41 DrawEngineCommon::~DrawEngineCommon() {
42 FreeMemoryPages(transformed, TRANSFORMED_VERTEX_BUFFER_SIZE);
43 FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE);
44 delete decJitCache_;
45 decoderMap_.Iterate([&](const uint32_t vtype, VertexDecoder *decoder) {
46 delete decoder;
47 });
48 ClearSplineBezierWeights();
49 }
50
Init()51 void DrawEngineCommon::Init() {
52 useHWTransform_ = g_Config.bHardwareTransform;
53 useHWTessellation_ = UpdateUseHWTessellation(g_Config.bHardwareTessellation);
54 }
55
GetVertexDecoder(u32 vtype)56 VertexDecoder *DrawEngineCommon::GetVertexDecoder(u32 vtype) {
57 VertexDecoder *dec = decoderMap_.Get(vtype);
58 if (dec)
59 return dec;
60 dec = new VertexDecoder();
61 dec->SetVertexType(vtype, decOptions_, decJitCache_);
62 decoderMap_.Insert(vtype, dec);
63 return dec;
64 }
65
ComputeNumVertsToDecode() const66 int DrawEngineCommon::ComputeNumVertsToDecode() const {
67 int vertsToDecode = 0;
68 if (drawCalls[0].indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
69 for (int i = 0; i < numDrawCalls; i++) {
70 const DeferredDrawCall &dc = drawCalls[i];
71 vertsToDecode += dc.vertexCount;
72 }
73 } else {
74 // TODO: Share this computation with DecodeVertsStep?
75 for (int i = 0; i < numDrawCalls; i++) {
76 const DeferredDrawCall &dc = drawCalls[i];
77 int lastMatch = i;
78 const int total = numDrawCalls;
79 int indexLowerBound = dc.indexLowerBound;
80 int indexUpperBound = dc.indexUpperBound;
81 for (int j = i + 1; j < total; ++j) {
82 if (drawCalls[j].verts != dc.verts)
83 break;
84
85 indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound);
86 indexUpperBound = std::max(indexUpperBound, (int)drawCalls[j].indexUpperBound);
87 lastMatch = j;
88 }
89 vertsToDecode += indexUpperBound - indexLowerBound + 1;
90 i = lastMatch;
91 }
92 }
93 return vertsToDecode;
94 }
95
DecodeVerts(u8 * dest)96 void DrawEngineCommon::DecodeVerts(u8 *dest) {
97 const UVScale origUV = gstate_c.uv;
98 for (; decodeCounter_ < numDrawCalls; decodeCounter_++) {
99 gstate_c.uv = drawCalls[decodeCounter_].uvScale;
100 DecodeVertsStep(dest, decodeCounter_, decodedVerts_); // NOTE! DecodeVertsStep can modify decodeCounter_!
101 }
102 gstate_c.uv = origUV;
103
104 // Sanity check
105 if (indexGen.Prim() < 0) {
106 ERROR_LOG_REPORT(G3D, "DecodeVerts: Failed to deduce prim: %i", indexGen.Prim());
107 // Force to points (0)
108 indexGen.AddPrim(GE_PRIM_POINTS, 0, true);
109 }
110 }
111
DebugGetVertexLoaderIDs()112 std::vector<std::string> DrawEngineCommon::DebugGetVertexLoaderIDs() {
113 std::vector<std::string> ids;
114 decoderMap_.Iterate([&](const uint32_t vtype, VertexDecoder *decoder) {
115 std::string id;
116 id.resize(sizeof(vtype));
117 memcpy(&id[0], &vtype, sizeof(vtype));
118 ids.push_back(id);
119 });
120 return ids;
121 }
122
DebugGetVertexLoaderString(std::string id,DebugShaderStringType stringType)123 std::string DrawEngineCommon::DebugGetVertexLoaderString(std::string id, DebugShaderStringType stringType) {
124 u32 mapId;
125 memcpy(&mapId, &id[0], sizeof(mapId));
126 VertexDecoder *dec = decoderMap_.Get(mapId);
127 return dec ? dec->GetString(stringType) : "N/A";
128 }
129
130 struct Plane {
131 float x, y, z, w;
SetPlane132 void Set(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; }
TestPlane133 float Test(float f[3]) const { return x * f[0] + y * f[1] + z * f[2] + w; }
134 };
135
PlanesFromMatrix(float mtx[16],Plane planes[6])136 static void PlanesFromMatrix(float mtx[16], Plane planes[6]) {
137 planes[0].Set(mtx[3]-mtx[0], mtx[7]-mtx[4], mtx[11]-mtx[8], mtx[15]-mtx[12]); // Right
138 planes[1].Set(mtx[3]+mtx[0], mtx[7]+mtx[4], mtx[11]+mtx[8], mtx[15]+mtx[12]); // Left
139 planes[2].Set(mtx[3]+mtx[1], mtx[7]+mtx[5], mtx[11]+mtx[9], mtx[15]+mtx[13]); // Bottom
140 planes[3].Set(mtx[3]-mtx[1], mtx[7]-mtx[5], mtx[11]-mtx[9], mtx[15]-mtx[13]); // Top
141 planes[4].Set(mtx[3]+mtx[2], mtx[7]+mtx[6], mtx[11]+mtx[10], mtx[15]+mtx[14]); // Near
142 planes[5].Set(mtx[3]-mtx[2], mtx[7]-mtx[6], mtx[11]-mtx[10], mtx[15]-mtx[14]); // Far
143 }
144
ClipToScreen(const Vec4f & coords)145 static Vec3f ClipToScreen(const Vec4f& coords) {
146 float xScale = gstate.getViewportXScale();
147 float xCenter = gstate.getViewportXCenter();
148 float yScale = gstate.getViewportYScale();
149 float yCenter = gstate.getViewportYCenter();
150 float zScale = gstate.getViewportZScale();
151 float zCenter = gstate.getViewportZCenter();
152
153 float x = coords.x * xScale / coords.w + xCenter;
154 float y = coords.y * yScale / coords.w + yCenter;
155 float z = coords.z * zScale / coords.w + zCenter;
156
157 // 16 = 0xFFFF / 4095.9375
158 return Vec3f(x * 16, y * 16, z);
159 }
160
ScreenToDrawing(const Vec3f & coords)161 static Vec3f ScreenToDrawing(const Vec3f& coords) {
162 Vec3f ret;
163 ret.x = (coords.x - gstate.getOffsetX16()) * (1.0f / 16.0f);
164 ret.y = (coords.y - gstate.getOffsetY16()) * (1.0f / 16.0f);
165 ret.z = coords.z;
166 return ret;
167 }
168
Resized()169 void DrawEngineCommon::Resized() {
170 decJitCache_->Clear();
171 lastVType_ = -1;
172 dec_ = nullptr;
173 decoderMap_.Iterate([&](const uint32_t vtype, VertexDecoder *decoder) {
174 delete decoder;
175 });
176 decoderMap_.Clear();
177 ClearTrackedVertexArrays();
178
179 useHWTransform_ = g_Config.bHardwareTransform;
180 useHWTessellation_ = UpdateUseHWTessellation(g_Config.bHardwareTessellation);
181 }
182
NormalizeVertices(u8 * outPtr,u8 * bufPtr,const u8 * inPtr,int lowerBound,int upperBound,u32 vertType,int * vertexSize)183 u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, int lowerBound, int upperBound, u32 vertType, int *vertexSize) {
184 const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24);
185 VertexDecoder *dec = GetVertexDecoder(vertTypeID);
186 if (vertexSize)
187 *vertexSize = dec->VertexSize();
188 return DrawEngineCommon::NormalizeVertices(outPtr, bufPtr, inPtr, dec, lowerBound, upperBound, vertType);
189 }
190
191 // This code has plenty of potential for optimization.
192 //
193 // It does the simplest and safest test possible: If all points of a bbox is outside a single of
194 // our clipping planes, we reject the box. Tighter bounds would be desirable but would take more calculations.
TestBoundingBox(void * control_points,int vertexCount,u32 vertType,int * bytesRead)195 bool DrawEngineCommon::TestBoundingBox(void* control_points, int vertexCount, u32 vertType, int *bytesRead) {
196 SimpleVertex *corners = (SimpleVertex *)(decoded + 65536 * 12);
197 float *verts = (float *)(decoded + 65536 * 18);
198
199 // Try to skip NormalizeVertices if it's pure positions. No need to bother with a vertex decoder
200 // and a large vertex format.
201 if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_FLOAT) {
202 verts = (float *)control_points;
203 *bytesRead = 3 * sizeof(float) * vertexCount;
204 } else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_8BIT) {
205 const s8 *vtx = (const s8 *)control_points;
206 for (int i = 0; i < vertexCount * 3; i++) {
207 verts[i] = vtx[i] * (1.0f / 128.0f);
208 }
209 *bytesRead = 3 * sizeof(s8) * vertexCount;
210 } else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_16BIT) {
211 const s16 *vtx = (const s16*)control_points;
212 for (int i = 0; i < vertexCount * 3; i++) {
213 verts[i] = vtx[i] * (1.0f / 32768.0f);
214 }
215 *bytesRead = 3 * sizeof(s16) * vertexCount;
216 } else {
217 // Simplify away bones and morph before proceeding
218 u8 *temp_buffer = decoded + 65536 * 24;
219 int vertexSize = 0;
220 NormalizeVertices((u8 *)corners, temp_buffer, (u8 *)control_points, 0, vertexCount, vertType, &vertexSize);
221 for (int i = 0; i < vertexCount; i++) {
222 verts[i * 3] = corners[i].pos.x;
223 verts[i * 3 + 1] = corners[i].pos.y;
224 verts[i * 3 + 2] = corners[i].pos.z;
225 }
226 *bytesRead = vertexSize * vertexCount;
227 }
228
229 Plane planes[6];
230
231 float world[16];
232 float view[16];
233 float worldview[16];
234 float worldviewproj[16];
235 ConvertMatrix4x3To4x4(world, gstate.worldMatrix);
236 ConvertMatrix4x3To4x4(view, gstate.viewMatrix);
237 Matrix4ByMatrix4(worldview, world, view);
238 Matrix4ByMatrix4(worldviewproj, worldview, gstate.projMatrix);
239 PlanesFromMatrix(worldviewproj, planes);
240 for (int plane = 0; plane < 6; plane++) {
241 int inside = 0;
242 int out = 0;
243 for (int i = 0; i < vertexCount; i++) {
244 // Here we can test against the frustum planes!
245 float value = planes[plane].Test(verts + i * 3);
246 if (value < 0)
247 out++;
248 else
249 inside++;
250 }
251
252 if (inside == 0) {
253 // All out
254 return false;
255 }
256
257 // Any out. For testing that the planes are in the right locations.
258 // if (out != 0) return false;
259 }
260 return true;
261 }
262
263 // TODO: This probably is not the best interface.
GetCurrentSimpleVertices(int count,std::vector<GPUDebugVertex> & vertices,std::vector<u16> & indices)264 bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
265 // This is always for the current vertices.
266 u16 indexLowerBound = 0;
267 u16 indexUpperBound = count - 1;
268
269 if (!Memory::IsValidAddress(gstate_c.vertexAddr))
270 return false;
271
272 bool savedVertexFullAlpha = gstate_c.vertexFullAlpha;
273
274 if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
275 const u8 *inds = Memory::GetPointer(gstate_c.indexAddr);
276 const u16_le *inds16 = (const u16_le *)inds;
277 const u32_le *inds32 = (const u32_le *)inds;
278
279 if (inds) {
280 GetIndexBounds(inds, count, gstate.vertType, &indexLowerBound, &indexUpperBound);
281 indices.resize(count);
282 switch (gstate.vertType & GE_VTYPE_IDX_MASK) {
283 case GE_VTYPE_IDX_8BIT:
284 for (int i = 0; i < count; ++i) {
285 indices[i] = inds[i];
286 }
287 break;
288 case GE_VTYPE_IDX_16BIT:
289 for (int i = 0; i < count; ++i) {
290 indices[i] = inds16[i];
291 }
292 break;
293 case GE_VTYPE_IDX_32BIT:
294 WARN_LOG_REPORT_ONCE(simpleIndexes32, G3D, "SimpleVertices: Decoding 32-bit indexes");
295 for (int i = 0; i < count; ++i) {
296 // These aren't documented and should be rare. Let's bounds check each one.
297 if (inds32[i] != (u16)inds32[i]) {
298 ERROR_LOG_REPORT_ONCE(simpleIndexes32Bounds, G3D, "SimpleVertices: Index outside 16-bit range");
299 }
300 indices[i] = (u16)inds32[i];
301 }
302 break;
303 }
304 } else {
305 indices.clear();
306 }
307 } else {
308 indices.clear();
309 }
310
311 static std::vector<u32> temp_buffer;
312 static std::vector<SimpleVertex> simpleVertices;
313 temp_buffer.resize(std::max((int)indexUpperBound, 8192) * 128 / sizeof(u32));
314 simpleVertices.resize(indexUpperBound + 1);
315 NormalizeVertices((u8 *)(&simpleVertices[0]), (u8 *)(&temp_buffer[0]), Memory::GetPointer(gstate_c.vertexAddr), indexLowerBound, indexUpperBound, gstate.vertType);
316
317 float world[16];
318 float view[16];
319 float worldview[16];
320 float worldviewproj[16];
321 ConvertMatrix4x3To4x4(world, gstate.worldMatrix);
322 ConvertMatrix4x3To4x4(view, gstate.viewMatrix);
323 Matrix4ByMatrix4(worldview, world, view);
324 Matrix4ByMatrix4(worldviewproj, worldview, gstate.projMatrix);
325
326 vertices.resize(indexUpperBound + 1);
327 uint32_t vertType = gstate.vertType;
328 for (int i = indexLowerBound; i <= indexUpperBound; ++i) {
329 const SimpleVertex &vert = simpleVertices[i];
330
331 if ((vertType & GE_VTYPE_THROUGH) != 0) {
332 if (vertType & GE_VTYPE_TC_MASK) {
333 vertices[i].u = vert.uv[0];
334 vertices[i].v = vert.uv[1];
335 } else {
336 vertices[i].u = 0.0f;
337 vertices[i].v = 0.0f;
338 }
339 vertices[i].x = vert.pos.x;
340 vertices[i].y = vert.pos.y;
341 vertices[i].z = vert.pos.z;
342 if (vertType & GE_VTYPE_COL_MASK) {
343 memcpy(vertices[i].c, vert.color, sizeof(vertices[i].c));
344 } else {
345 memset(vertices[i].c, 0, sizeof(vertices[i].c));
346 }
347 vertices[i].nx = 0; // No meaningful normals in through mode
348 vertices[i].ny = 0;
349 vertices[i].nz = 1.0f;
350 } else {
351 float clipPos[4];
352 Vec3ByMatrix44(clipPos, vert.pos.AsArray(), worldviewproj);
353 Vec3f screenPos = ClipToScreen(clipPos);
354 Vec3f drawPos = ScreenToDrawing(screenPos);
355
356 if (vertType & GE_VTYPE_TC_MASK) {
357 vertices[i].u = vert.uv[0] * (float)gstate.getTextureWidth(0);
358 vertices[i].v = vert.uv[1] * (float)gstate.getTextureHeight(0);
359 } else {
360 vertices[i].u = 0.0f;
361 vertices[i].v = 0.0f;
362 }
363 // Should really have separate coordinates for before and after transform.
364 vertices[i].x = drawPos.x;
365 vertices[i].y = drawPos.y;
366 vertices[i].z = drawPos.z;
367 if (vertType & GE_VTYPE_COL_MASK) {
368 memcpy(vertices[i].c, vert.color, sizeof(vertices[i].c));
369 } else {
370 memset(vertices[i].c, 0, sizeof(vertices[i].c));
371 }
372 vertices[i].nx = vert.nrm.x;
373 vertices[i].ny = vert.nrm.y;
374 vertices[i].nz = vert.nrm.z;
375 }
376 }
377
378 gstate_c.vertexFullAlpha = savedVertexFullAlpha;
379
380 return true;
381 }
382
383 // This normalizes a set of vertices in any format to SimpleVertex format, by processing away morphing AND skinning.
384 // The rest of the transform pipeline like lighting will go as normal, either hardware or software.
385 // The implementation is initially a bit inefficient but shouldn't be a big deal.
386 // An intermediate buffer of not-easy-to-predict size is stored at bufPtr.
NormalizeVertices(u8 * outPtr,u8 * bufPtr,const u8 * inPtr,VertexDecoder * dec,int lowerBound,int upperBound,u32 vertType)387 u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, VertexDecoder *dec, int lowerBound, int upperBound, u32 vertType) {
388 // First, decode the vertices into a GPU compatible format. This step can be eliminated but will need a separate
389 // implementation of the vertex decoder.
390 dec->DecodeVerts(bufPtr, inPtr, lowerBound, upperBound);
391
392 // OK, morphing eliminated but bones still remain to be taken care of.
393 // Let's do a partial software transform where we only do skinning.
394
395 VertexReader reader(bufPtr, dec->GetDecVtxFmt(), vertType);
396
397 SimpleVertex *sverts = (SimpleVertex *)outPtr;
398
399 const u8 defaultColor[4] = {
400 (u8)gstate.getMaterialAmbientR(),
401 (u8)gstate.getMaterialAmbientG(),
402 (u8)gstate.getMaterialAmbientB(),
403 (u8)gstate.getMaterialAmbientA(),
404 };
405
406 // Let's have two separate loops, one for non skinning and one for skinning.
407 if (!g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) {
408 int numBoneWeights = vertTypeGetNumBoneWeights(vertType);
409 for (int i = lowerBound; i <= upperBound; i++) {
410 reader.Goto(i - lowerBound);
411 SimpleVertex &sv = sverts[i];
412 if (vertType & GE_VTYPE_TC_MASK) {
413 reader.ReadUV(sv.uv);
414 }
415
416 if (vertType & GE_VTYPE_COL_MASK) {
417 reader.ReadColor0_8888(sv.color);
418 } else {
419 memcpy(sv.color, defaultColor, 4);
420 }
421
422 float nrm[3], pos[3];
423 float bnrm[3], bpos[3];
424
425 if (vertType & GE_VTYPE_NRM_MASK) {
426 // Normals are generated during tessellation anyway, not sure if any need to supply
427 reader.ReadNrm(nrm);
428 } else {
429 nrm[0] = 0;
430 nrm[1] = 0;
431 nrm[2] = 1.0f;
432 }
433 reader.ReadPos(pos);
434
435 // Apply skinning transform directly
436 float weights[8];
437 reader.ReadWeights(weights);
438 // Skinning
439 Vec3Packedf psum(0, 0, 0);
440 Vec3Packedf nsum(0, 0, 0);
441 for (int w = 0; w < numBoneWeights; w++) {
442 if (weights[w] != 0.0f) {
443 Vec3ByMatrix43(bpos, pos, gstate.boneMatrix + w * 12);
444 Vec3Packedf tpos(bpos);
445 psum += tpos * weights[w];
446
447 Norm3ByMatrix43(bnrm, nrm, gstate.boneMatrix + w * 12);
448 Vec3Packedf tnorm(bnrm);
449 nsum += tnorm * weights[w];
450 }
451 }
452 sv.pos = psum;
453 sv.nrm = nsum;
454 }
455 } else {
456 for (int i = lowerBound; i <= upperBound; i++) {
457 reader.Goto(i - lowerBound);
458 SimpleVertex &sv = sverts[i];
459 if (vertType & GE_VTYPE_TC_MASK) {
460 reader.ReadUV(sv.uv);
461 } else {
462 sv.uv[0] = 0.0f; // This will get filled in during tessellation
463 sv.uv[1] = 0.0f;
464 }
465 if (vertType & GE_VTYPE_COL_MASK) {
466 reader.ReadColor0_8888(sv.color);
467 } else {
468 memcpy(sv.color, defaultColor, 4);
469 }
470 if (vertType & GE_VTYPE_NRM_MASK) {
471 // Normals are generated during tessellation anyway, not sure if any need to supply
472 reader.ReadNrm((float *)&sv.nrm);
473 } else {
474 sv.nrm.x = 0.0f;
475 sv.nrm.y = 0.0f;
476 sv.nrm.z = 1.0f;
477 }
478 reader.ReadPos((float *)&sv.pos);
479 }
480 }
481
482 // Okay, there we are! Return the new type (but keep the index bits)
483 return GE_VTYPE_TC_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_NRM_FLOAT | GE_VTYPE_POS_FLOAT | (vertType & (GE_VTYPE_IDX_MASK | GE_VTYPE_THROUGH));
484 }
485
ApplyFramebufferRead(bool * fboTexNeedsBind)486 bool DrawEngineCommon::ApplyFramebufferRead(bool *fboTexNeedsBind) {
487 if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) {
488 *fboTexNeedsBind = false;
489 return true;
490 }
491
492 static const int MAX_REASONABLE_BLITS_PER_FRAME = 24;
493
494 static int lastFrameBlit = -1;
495 static int blitsThisFrame = 0;
496 if (lastFrameBlit != gpuStats.numFlips) {
497 if (blitsThisFrame > MAX_REASONABLE_BLITS_PER_FRAME) {
498 WARN_LOG_REPORT_ONCE(blendingBlit, G3D, "Lots of blits needed for obscure blending: %d per frame, blend %d/%d/%d", blitsThisFrame, gstate.getBlendFuncA(), gstate.getBlendFuncB(), gstate.getBlendEq());
499 }
500 blitsThisFrame = 0;
501 lastFrameBlit = gpuStats.numFlips;
502 }
503 ++blitsThisFrame;
504 if (blitsThisFrame > MAX_REASONABLE_BLITS_PER_FRAME * 2) {
505 WARN_LOG_ONCE(blendingBlit2, G3D, "Skipping additional blits needed for obscure blending: %d per frame, blend %d/%d/%d", blitsThisFrame, gstate.getBlendFuncA(), gstate.getBlendFuncB(), gstate.getBlendEq());
506 return false;
507 }
508
509 *fboTexNeedsBind = true;
510
511 gstate_c.Dirty(DIRTY_SHADERBLEND);
512 return true;
513 }
514
DecodeVertsStep(u8 * dest,int & i,int & decodedVerts)515 void DrawEngineCommon::DecodeVertsStep(u8 *dest, int &i, int &decodedVerts) {
516 PROFILE_THIS_SCOPE("vertdec");
517
518 const DeferredDrawCall &dc = drawCalls[i];
519
520 indexGen.SetIndex(decodedVerts);
521 int indexLowerBound = dc.indexLowerBound;
522 int indexUpperBound = dc.indexUpperBound;
523
524 if (dc.indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
525 // Decode the verts and apply morphing. Simple.
526 dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride,
527 dc.verts, indexLowerBound, indexUpperBound);
528 decodedVerts += indexUpperBound - indexLowerBound + 1;
529
530 bool clockwise = true;
531 if (gstate.isCullEnabled() && gstate.getCullMode() != dc.cullMode) {
532 clockwise = false;
533 }
534 indexGen.AddPrim(dc.prim, dc.vertexCount, clockwise);
535 } else {
536 // It's fairly common that games issue long sequences of PRIM calls, with differing
537 // inds pointer but the same base vertex pointer. We'd like to reuse vertices between
538 // these as much as possible, so we make sure here to combine as many as possible
539 // into one nice big drawcall, sharing data.
540
541 // 1. Look ahead to find the max index, only looking as "matching" drawcalls.
542 // Expand the lower and upper bounds as we go.
543 int lastMatch = i;
544 const int total = numDrawCalls;
545 for (int j = i + 1; j < total; ++j) {
546 if (drawCalls[j].verts != dc.verts)
547 break;
548
549 indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound);
550 indexUpperBound = std::max(indexUpperBound, (int)drawCalls[j].indexUpperBound);
551 lastMatch = j;
552 }
553
554 // 2. Loop through the drawcalls, translating indices as we go.
555 switch (dc.indexType) {
556 case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT:
557 for (int j = i; j <= lastMatch; j++) {
558 bool clockwise = true;
559 if (gstate.isCullEnabled() && gstate.getCullMode() != drawCalls[j].cullMode) {
560 clockwise = false;
561 }
562 indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u8 *)drawCalls[j].inds, indexLowerBound, clockwise);
563 }
564 break;
565 case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT:
566 for (int j = i; j <= lastMatch; j++) {
567 bool clockwise = true;
568 if (gstate.isCullEnabled() && gstate.getCullMode() != drawCalls[j].cullMode) {
569 clockwise = false;
570 }
571 indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u16_le *)drawCalls[j].inds, indexLowerBound, clockwise);
572 }
573 break;
574 case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT:
575 for (int j = i; j <= lastMatch; j++) {
576 bool clockwise = true;
577 if (gstate.isCullEnabled() && gstate.getCullMode() != drawCalls[j].cullMode) {
578 clockwise = false;
579 }
580 indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u32_le *)drawCalls[j].inds, indexLowerBound, clockwise);
581 }
582 break;
583 }
584
585 const int vertexCount = indexUpperBound - indexLowerBound + 1;
586
587 // This check is a workaround for Pangya Fantasy Golf, which sends bogus index data when switching items in "My Room" sometimes.
588 if (decodedVerts + vertexCount > VERTEX_BUFFER_MAX) {
589 return;
590 }
591
592 // 3. Decode that range of vertex data.
593 dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride,
594 dc.verts, indexLowerBound, indexUpperBound);
595 decodedVerts += vertexCount;
596
597 // 4. Advance indexgen vertex counter.
598 indexGen.Advance(vertexCount);
599 i = lastMatch;
600 }
601 }
602
ComputeMiniHashRange(const void * ptr,size_t sz)603 inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) {
604 // Switch to u32 units, and round up to avoid unaligned accesses.
605 // Probably doesn't matter if we skip the first few bytes in some cases.
606 const u32 *p = (const u32 *)(((uintptr_t)ptr + 3) & ~3);
607 sz >>= 2;
608
609 if (sz > 100) {
610 size_t step = sz / 4;
611 u32 hash = 0;
612 for (size_t i = 0; i < sz; i += step) {
613 hash += XXH3_64bits(p + i, 100);
614 }
615 return hash;
616 } else {
617 return p[0] + p[sz - 1];
618 }
619 }
620
ComputeMiniHash()621 u32 DrawEngineCommon::ComputeMiniHash() {
622 u32 fullhash = 0;
623 const int vertexSize = dec_->GetDecVtxFmt().stride;
624 const int indexSize = IndexSize(dec_->VertexType());
625
626 int step;
627 if (numDrawCalls < 3) {
628 step = 1;
629 } else if (numDrawCalls < 8) {
630 step = 4;
631 } else {
632 step = numDrawCalls / 8;
633 }
634 for (int i = 0; i < numDrawCalls; i += step) {
635 const DeferredDrawCall &dc = drawCalls[i];
636 if (!dc.inds) {
637 fullhash += ComputeMiniHashRange(dc.verts, vertexSize * dc.vertexCount);
638 } else {
639 int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound;
640 fullhash += ComputeMiniHashRange((const u8 *)dc.verts + vertexSize * indexLowerBound, vertexSize * (indexUpperBound - indexLowerBound));
641 fullhash += ComputeMiniHashRange(dc.inds, indexSize * dc.vertexCount);
642 }
643 }
644
645 return fullhash;
646 }
647
ComputeHash()648 uint64_t DrawEngineCommon::ComputeHash() {
649 uint64_t fullhash = 0;
650 const int vertexSize = dec_->GetDecVtxFmt().stride;
651 const int indexSize = IndexSize(dec_->VertexType());
652
653 // TODO: Add some caps both for numDrawCalls and num verts to check?
654 // It is really very expensive to check all the vertex data so often.
655 for (int i = 0; i < numDrawCalls; i++) {
656 const DeferredDrawCall &dc = drawCalls[i];
657 if (!dc.inds) {
658 fullhash += XXH3_64bits((const char *)dc.verts, vertexSize * dc.vertexCount);
659 } else {
660 int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound;
661 int j = i + 1;
662 int lastMatch = i;
663 while (j < numDrawCalls) {
664 if (drawCalls[j].verts != dc.verts)
665 break;
666 indexLowerBound = std::min(indexLowerBound, (int)dc.indexLowerBound);
667 indexUpperBound = std::max(indexUpperBound, (int)dc.indexUpperBound);
668 lastMatch = j;
669 j++;
670 }
671 // This could get seriously expensive with sparse indices. Need to combine hashing ranges the same way
672 // we do when drawing.
673 fullhash += XXH3_64bits((const char *)dc.verts + vertexSize * indexLowerBound,
674 vertexSize * (indexUpperBound - indexLowerBound));
675 // Hm, we will miss some indices when combining above, but meh, it should be fine.
676 fullhash += XXH3_64bits((const char *)dc.inds, indexSize * dc.vertexCount);
677 i = lastMatch;
678 }
679 }
680
681 fullhash += XXH3_64bits(&drawCalls[0].uvScale, sizeof(drawCalls[0].uvScale) * numDrawCalls);
682 return fullhash;
683 }
684
685 // vertTypeID is the vertex type but with the UVGen mode smashed into the top bits.
SubmitPrim(void * verts,void * inds,GEPrimitiveType prim,int vertexCount,u32 vertTypeID,int cullMode,int * bytesRead)686 void DrawEngineCommon::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead) {
687 if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) {
688 DispatchFlush();
689 }
690
691 // TODO: Is this the right thing to do?
692 if (prim == GE_PRIM_KEEP_PREVIOUS) {
693 prim = prevPrim_ != GE_PRIM_INVALID ? prevPrim_ : GE_PRIM_POINTS;
694 } else {
695 prevPrim_ = prim;
696 }
697
698 // If vtype has changed, setup the vertex decoder.
699 if (vertTypeID != lastVType_) {
700 dec_ = GetVertexDecoder(vertTypeID);
701 lastVType_ = vertTypeID;
702 }
703
704 *bytesRead = vertexCount * dec_->VertexSize();
705
706 // Check that we have enough vertices to form the requested primitive.
707 if ((vertexCount < 2 && prim > 0) || (vertexCount < 3 && prim > 2 && prim != GE_PRIM_RECTANGLES))
708 return;
709
710 if (g_Config.bVertexCache) {
711 u32 dhash = dcid_;
712 dhash = __rotl(dhash ^ (u32)(uintptr_t)verts, 13);
713 dhash = __rotl(dhash ^ (u32)(uintptr_t)inds, 13);
714 dhash = __rotl(dhash ^ (u32)vertTypeID, 13);
715 dhash = __rotl(dhash ^ (u32)vertexCount, 13);
716 dcid_ = dhash ^ (u32)prim;
717 }
718
719 DeferredDrawCall &dc = drawCalls[numDrawCalls];
720 dc.verts = verts;
721 dc.inds = inds;
722 dc.indexType = (vertTypeID & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
723 dc.prim = prim;
724 dc.vertexCount = vertexCount;
725 dc.uvScale = gstate_c.uv;
726 dc.cullMode = cullMode;
727
728 if (inds) {
729 GetIndexBounds(inds, vertexCount, vertTypeID, &dc.indexLowerBound, &dc.indexUpperBound);
730 } else {
731 dc.indexLowerBound = 0;
732 dc.indexUpperBound = vertexCount - 1;
733 }
734
735 numDrawCalls++;
736 vertexCountInDrawCalls_ += vertexCount;
737
738 if (g_Config.bSoftwareSkinning && (vertTypeID & GE_VTYPE_WEIGHT_MASK)) {
739 DecodeVertsStep(decoded, decodeCounter_, decodedVerts_);
740 decodeCounter_++;
741 }
742
743 if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) {
744 // Rendertarget == texture? Shouldn't happen. Still, try some mitigations.
745 gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
746 DispatchFlush();
747 }
748 }
749
CanUseHardwareTransform(int prim)750 bool DrawEngineCommon::CanUseHardwareTransform(int prim) {
751 if (!useHWTransform_)
752 return false;
753 return !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES;
754 }
755
CanUseHardwareTessellation(GEPatchPrimType prim)756 bool DrawEngineCommon::CanUseHardwareTessellation(GEPatchPrimType prim) {
757 if (useHWTessellation_) {
758 return CanUseHardwareTransform(PatchPrimToPrim(prim));
759 }
760 return false;
761 }
762
CopyControlPoints(float * pos,float * tex,float * col,int posStride,int texStride,int colStride,const SimpleVertex * const * points,int size,u32 vertType)763 void TessellationDataTransfer::CopyControlPoints(float *pos, float *tex, float *col, int posStride, int texStride, int colStride, const SimpleVertex *const *points, int size, u32 vertType) {
764 bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0;
765 bool hasTexCoord = (vertType & GE_VTYPE_TC_MASK) != 0;
766
767 for (int i = 0; i < size; ++i) {
768 memcpy(pos, points[i]->pos.AsArray(), 3 * sizeof(float));
769 pos += posStride;
770 }
771 if (hasTexCoord) {
772 for (int i = 0; i < size; ++i) {
773 memcpy(tex, points[i]->uv, 2 * sizeof(float));
774 tex += texStride;
775 }
776 }
777 if (hasColor) {
778 for (int i = 0; i < size; ++i) {
779 memcpy(col, Vec4f::FromRGBA(points[i]->color_32).AsArray(), 4 * sizeof(float));
780 col += colStride;
781 }
782 }
783 }
784