Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Common/DrawEngineCommon.cpp
3186 views
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
#include <cfloat>
20
21
#include "Common/Data/Convert/ColorConv.h"
22
#include "Common/Profiler/Profiler.h"
23
#include "Common/LogReporting.h"
24
#include "Common/Math/SIMDHeaders.h"
25
#include "Common/Math/CrossSIMD.h"
26
#include "Common/Math/lin/matrix4x4.h"
27
#include "Common/TimeUtil.h"
28
#include "Core/System.h"
29
#include "Core/Config.h"
30
#include "GPU/Common/DrawEngineCommon.h"
31
#include "GPU/Common/SplineCommon.h"
32
#include "GPU/Common/DepthRaster.h"
33
#include "GPU/Common/VertexDecoderCommon.h"
34
#include "GPU/Common/SoftwareTransformCommon.h"
35
#include "GPU/ge_constants.h"
36
#include "GPU/GPUState.h"
37
38
enum {
39
TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex),
40
};
41
42
DrawEngineCommon::DrawEngineCommon() : decoderMap_(32) {
43
if (g_Config.bVertexDecoderJit && (g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR)) {
44
decJitCache_ = new VertexDecoderJitCache();
45
}
46
transformed_ = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
47
transformedExpanded_ = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
48
decoded_ = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
49
decIndex_ = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE);
50
indexGen.Setup(decIndex_);
51
52
InitDepthRaster();
53
}
54
55
DrawEngineCommon::~DrawEngineCommon() {
56
FreeMemoryPages(decoded_, DECODED_VERTEX_BUFFER_SIZE);
57
FreeMemoryPages(decIndex_, DECODED_INDEX_BUFFER_SIZE);
58
FreeMemoryPages(transformed_, TRANSFORMED_VERTEX_BUFFER_SIZE);
59
FreeMemoryPages(transformedExpanded_, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE);
60
ShutdownDepthRaster();
61
delete decJitCache_;
62
decoderMap_.Iterate([&](const uint32_t vtype, VertexDecoder *decoder) {
63
delete decoder;
64
});
65
ClearSplineBezierWeights();
66
}
67
68
void DrawEngineCommon::Init() {
69
NotifyConfigChanged();
70
}
71
72
std::vector<std::string> DrawEngineCommon::DebugGetVertexLoaderIDs() {
73
std::vector<std::string> ids;
74
decoderMap_.Iterate([&](const uint32_t vtype, VertexDecoder *decoder) {
75
std::string id;
76
id.resize(sizeof(vtype));
77
memcpy(&id[0], &vtype, sizeof(vtype));
78
ids.push_back(id);
79
});
80
return ids;
81
}
82
83
std::string DrawEngineCommon::DebugGetVertexLoaderString(std::string id, DebugShaderStringType stringType) {
84
u32 mapId;
85
memcpy(&mapId, &id[0], sizeof(mapId));
86
VertexDecoder *dec;
87
if (decoderMap_.Get(mapId, &dec)) {
88
return dec->GetString(stringType);
89
} else {
90
return "N/A";
91
}
92
}
93
94
void DrawEngineCommon::NotifyConfigChanged() {
95
if (decJitCache_)
96
decJitCache_->Clear();
97
lastVType_ = -1;
98
dec_ = nullptr;
99
decoderMap_.Iterate([&](const uint32_t vtype, VertexDecoder *decoder) {
100
delete decoder;
101
});
102
decoderMap_.Clear();
103
104
useHWTransform_ = g_Config.bHardwareTransform;
105
useHWTessellation_ = UpdateUseHWTessellation(g_Config.bHardwareTessellation);
106
}
107
108
void DrawEngineCommon::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode, bool continuation) {
109
// Instead of plumbing through properly (we'd need to inject these pretransformed vertices in the middle
110
// of SoftwareTransform(), which would take a lot of refactoring), we'll cheat and just turn these into
111
// through vertices.
112
// Since the only known use is Thrillville and it only uses it to clear, we just use color and pos.
113
struct ImmVertex {
114
float uv[2];
115
uint32_t color;
116
float xyz[3];
117
};
118
std::vector<ImmVertex> temp;
119
temp.resize(vertexCount);
120
uint32_t color1Used = 0;
121
for (int i = 0; i < vertexCount; i++) {
122
// Since we're sending through, scale back up to w/h.
123
temp[i].uv[0] = buffer[i].u * gstate.getTextureWidth(0);
124
temp[i].uv[1] = buffer[i].v * gstate.getTextureHeight(0);
125
temp[i].color = buffer[i].color0_32;
126
temp[i].xyz[0] = buffer[i].pos[0];
127
temp[i].xyz[1] = buffer[i].pos[1];
128
temp[i].xyz[2] = buffer[i].pos[2];
129
color1Used |= buffer[i].color1_32;
130
}
131
int vtype = GE_VTYPE_TC_FLOAT | GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH;
132
// TODO: Handle fog and secondary color somehow?
133
134
if (gstate.isFogEnabled() && !gstate.isModeThrough()) {
135
WARN_LOG_REPORT_ONCE(geimmfog, Log::G3D, "Imm vertex used fog");
136
}
137
if (color1Used != 0 && gstate.isUsingSecondaryColor() && !gstate.isModeThrough()) {
138
WARN_LOG_REPORT_ONCE(geimmcolor1, Log::G3D, "Imm vertex used secondary color");
139
}
140
141
bool prevThrough = gstate.isModeThrough();
142
// Code checks this reg directly, not just the vtype ID.
143
if (!prevThrough) {
144
gstate.vertType |= GE_VTYPE_THROUGH;
145
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
146
}
147
148
int bytesRead;
149
uint32_t vertTypeID = GetVertTypeID(vtype, 0, applySkinInDecode_);
150
151
bool clockwise = !gstate.isCullEnabled() || gstate.getCullMode() == cullMode;
152
VertexDecoder *dec = GetVertexDecoder(vertTypeID);
153
SubmitPrim(&temp[0], nullptr, prim, vertexCount, dec, vertTypeID, clockwise, &bytesRead);
154
Flush();
155
156
if (!prevThrough) {
157
gstate.vertType &= ~GE_VTYPE_THROUGH;
158
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
159
}
160
}
161
162
// Gated by DIRTY_CULL_PLANES
163
void DrawEngineCommon::UpdatePlanes() {
164
float view[16];
165
float viewproj[16];
166
ConvertMatrix4x3To4x4(view, gstate.viewMatrix);
167
Matrix4ByMatrix4(viewproj, view, gstate.projMatrix);
168
169
// Next, we need to apply viewport, scissor, region, and even offset - but only for X/Y.
170
// Note that the PSP does not clip against the viewport.
171
const Vec2f baseOffset = Vec2f(gstate.getOffsetX(), gstate.getOffsetY());
172
// Region1 (rate) is used as an X1/Y1 here, matching PSP behavior.
173
minOffset_ = baseOffset + Vec2f(std::max(gstate.getRegionRateX() - 0x100, gstate.getScissorX1()), std::max(gstate.getRegionRateY() - 0x100, gstate.getScissorY1())) - Vec2f(1.0f, 1.0f);
174
maxOffset_ = baseOffset + Vec2f(std::min(gstate.getRegionX2(), gstate.getScissorX2()), std::min(gstate.getRegionY2(), gstate.getScissorY2())) + Vec2f(1.0f, 1.0f);
175
176
// Let's not handle these special cases in the fast culler.
177
offsetOutsideEdge_ = maxOffset_.x >= 4096.0f || minOffset_.x < 1.0f || minOffset_.y < 1.0f || maxOffset_.y >= 4096.0f;
178
179
// Now let's apply the viewport to our scissor/region + offset range.
180
Vec2f inverseViewportScale = Vec2f(1.0f / gstate.getViewportXScale(), 1.0f / gstate.getViewportYScale());
181
Vec2f minViewport = (minOffset_ - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale;
182
Vec2f maxViewport = (maxOffset_ - Vec2f(gstate.getViewportXCenter(), gstate.getViewportYCenter())) * inverseViewportScale;
183
184
Vec2f viewportInvSize = Vec2f(1.0f / (maxViewport.x - minViewport.x), 1.0f / (maxViewport.y - minViewport.y));
185
186
Lin::Matrix4x4 applyViewport{};
187
// Scale to the viewport's size.
188
applyViewport.xx = 2.0f * viewportInvSize.x;
189
applyViewport.yy = 2.0f * viewportInvSize.y;
190
applyViewport.zz = 1.0f;
191
applyViewport.ww = 1.0f;
192
// And offset to the viewport's centers.
193
applyViewport.wx = -(maxViewport.x + minViewport.x) * viewportInvSize.x;
194
applyViewport.wy = -(maxViewport.y + minViewport.y) * viewportInvSize.y;
195
196
float mtx[16];
197
Matrix4ByMatrix4(mtx, viewproj, applyViewport.m);
198
// I'm sure there's some fairly optimized way to set these.
199
planes_.Set(0, mtx[3] - mtx[0], mtx[7] - mtx[4], mtx[11] - mtx[8], mtx[15] - mtx[12]); // Right
200
planes_.Set(1, mtx[3] + mtx[0], mtx[7] + mtx[4], mtx[11] + mtx[8], mtx[15] + mtx[12]); // Left
201
planes_.Set(2, mtx[3] + mtx[1], mtx[7] + mtx[5], mtx[11] + mtx[9], mtx[15] + mtx[13]); // Bottom
202
planes_.Set(3, mtx[3] - mtx[1], mtx[7] - mtx[5], mtx[11] - mtx[9], mtx[15] - mtx[13]); // Top
203
planes_.Set(4, mtx[3] + mtx[2], mtx[7] + mtx[6], mtx[11] + mtx[10], mtx[15] + mtx[14]); // Near
204
planes_.Set(5, mtx[3] - mtx[2], mtx[7] - mtx[6], mtx[11] - mtx[10], mtx[15] - mtx[14]); // Far
205
}
206
207
// This code has plenty of potential for optimization.
208
//
209
// It does the simplest and safest test possible: If all points of a bbox is outside a single of
210
// our clipping planes, we reject the box. Tighter bounds would be desirable but would take more calculations.
211
// The name is a slight misnomer, because any bounding shape will work, not just boxes.
212
//
213
// Potential optimizations:
214
// * SIMD-ify the plane culling, and also the vertex data conversion (could even group together xxxxyyyyzzzz for example)
215
// * Compute min/max of the verts, and then compute a bounding sphere and check that against the planes.
216
// - Less accurate, but..
217
// - Only requires six plane evaluations then.
218
bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int vertexCount, const VertexDecoder *dec, u32 vertType) {
219
// Grab temp buffer space from large offsets in decoded_. Not exactly safe for large draws.
220
if (vertexCount > 1024) {
221
return true;
222
}
223
224
SimpleVertex *corners = (SimpleVertex *)(decoded_ + 65536 * 12);
225
float *verts = (float *)(decoded_ + 65536 * 18);
226
227
// Although this may lead to drawing that shouldn't happen, the viewport is more complex on VR.
228
// Let's always say objects are within bounds.
229
if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY))
230
return true;
231
232
// Due to world matrix updates per "thing", this isn't quite as effective as it could be if we did world transform
233
// in here as well. Though, it still does cut down on a lot of updates in Tekken 6.
234
if (gstate_c.IsDirty(DIRTY_CULL_PLANES)) {
235
UpdatePlanes();
236
gpuStats.numPlaneUpdates++;
237
gstate_c.Clean(DIRTY_CULL_PLANES);
238
}
239
240
// Try to skip NormalizeVertices if it's pure positions. No need to bother with a vertex decoder
241
// and a large vertex format.
242
if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_FLOAT && !inds) {
243
memcpy(verts, vdata, sizeof(float) * 3 * vertexCount);
244
} else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_8BIT && !inds) {
245
const s8 *vtx = (const s8 *)vdata;
246
for (int i = 0; i < vertexCount * 3; i++) {
247
verts[i] = vtx[i] * (1.0f / 128.0f);
248
}
249
} else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_16BIT && !inds) {
250
const s16 *vtx = (const s16 *)vdata;
251
for (int i = 0; i < vertexCount * 3; i++) {
252
verts[i] = vtx[i] * (1.0f / 32768.0f);
253
}
254
} else {
255
// Simplify away indices, bones, and morph before proceeding.
256
u8 *temp_buffer = decoded_ + 65536 * 24;
257
258
if ((inds || (vertType & (GE_VTYPE_WEIGHT_MASK | GE_VTYPE_MORPHCOUNT_MASK)))) {
259
u16 indexLowerBound = 0;
260
u16 indexUpperBound = (u16)vertexCount - 1;
261
262
if (vertexCount > 0 && inds) {
263
GetIndexBounds(inds, vertexCount, vertType, &indexLowerBound, &indexUpperBound);
264
}
265
// TODO: Avoid normalization if just plain skinning.
266
// Force software skinning.
267
const u32 vertTypeID = GetVertTypeID(vertType, gstate.getUVGenMode(), true);
268
::NormalizeVertices(corners, temp_buffer, (const u8 *)vdata, indexLowerBound, indexUpperBound, dec, vertType);
269
IndexConverter conv(vertType, inds);
270
for (int i = 0; i < vertexCount; i++) {
271
verts[i * 3] = corners[conv(i)].pos.x;
272
verts[i * 3 + 1] = corners[conv(i)].pos.y;
273
verts[i * 3 + 2] = corners[conv(i)].pos.z;
274
}
275
} else {
276
// Simple, most common case.
277
int stride = dec->VertexSize();
278
int offset = dec->posoff;
279
switch (vertType & GE_VTYPE_POS_MASK) {
280
case GE_VTYPE_POS_8BIT:
281
for (int i = 0; i < vertexCount; i++) {
282
const s8 *data = (const s8 *)vdata + i * stride + offset;
283
for (int j = 0; j < 3; j++) {
284
verts[i * 3 + j] = data[j] * (1.0f / 128.0f);
285
}
286
}
287
break;
288
case GE_VTYPE_POS_16BIT:
289
for (int i = 0; i < vertexCount; i++) {
290
const s16 *data = ((const s16 *)((const s8 *)vdata + i * stride + offset));
291
for (int j = 0; j < 3; j++) {
292
verts[i * 3 + j] = data[j] * (1.0f / 32768.0f);
293
}
294
}
295
break;
296
case GE_VTYPE_POS_FLOAT:
297
for (int i = 0; i < vertexCount; i++)
298
memcpy(&verts[i * 3], (const u8 *)vdata + stride * i + offset, sizeof(float) * 3);
299
break;
300
}
301
}
302
}
303
304
// Pretransform the verts in-place so we don't have to do it inside the loop.
305
// We do this differently in the fast version below since we skip the max/minOffset checks there
306
// making it easier to get the whole thing ready for SIMD.
307
for (int i = 0; i < vertexCount; i++) {
308
float worldpos[3];
309
Vec3ByMatrix43(worldpos, &verts[i * 3], gstate.worldMatrix);
310
memcpy(&verts[i * 3], worldpos, 12);
311
}
312
313
// Note: near/far are not checked without clamp/clip enabled, so we skip those planes.
314
int totalPlanes = gstate.isDepthClampEnabled() ? 6 : 4;
315
for (int plane = 0; plane < totalPlanes; plane++) {
316
int inside = 0;
317
int out = 0;
318
for (int i = 0; i < vertexCount; i++) {
319
// Test against the frustum planes, and count.
320
// TODO: We should test 4 vertices at a time using SIMD.
321
// I guess could also test one vertex against 4 planes at a time, though a lot of waste at the common case of 6.
322
const float *worldpos = verts + i * 3;
323
float value = planes_.Test(plane, worldpos);
324
if (value <= -FLT_EPSILON) // Not sure why we use exactly this value. Probably '< 0' would do.
325
out++;
326
else
327
inside++;
328
}
329
330
// No vertices inside this one plane? Don't need to draw.
331
if (inside == 0) {
332
// All out - but check for X and Y if the offset was near the cullbox edge.
333
bool outsideEdge = false;
334
switch (plane) {
335
case 0: outsideEdge = maxOffset_.x >= 4096.0f; break;
336
case 1: outsideEdge = minOffset_.x < 1.0f; break;
337
case 2: outsideEdge = minOffset_.y < 1.0f; break;
338
case 3: outsideEdge = maxOffset_.y >= 4096.0f; break;
339
}
340
341
// Only consider this outside if offset + scissor/region is fully inside the cullbox.
342
if (!outsideEdge)
343
return false;
344
}
345
346
// Any out. For testing that the planes are in the right locations.
347
// if (out != 0) return false;
348
}
349
return true;
350
}
351
352
// NOTE: This doesn't handle through-mode, indexing, morph, or skinning.
353
// TODO: For high vertex counts, we should just take the min/max of all the verts, and test the resulting six cube
354
// corners. That way we can cull more draws quite cheaply.
355
// We could take the min/max during the regular vertex decode, and just skip the draw call if it's trivially culled.
356
// This would help games like Midnight Club (that one does a lot of out-of-bounds drawing) immensely.
357
bool DrawEngineCommon::TestBoundingBoxFast(const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType) {
358
SimpleVertex *corners = (SimpleVertex *)(decoded_ + 65536 * 12);
359
float *verts = (float *)(decoded_ + 65536 * 18);
360
361
// Although this may lead to drawing that shouldn't happen, the viewport is more complex on VR.
362
// Let's always say objects are within bounds.
363
if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY))
364
return true;
365
366
// Due to world matrix updates per "thing", this isn't quite as effective as it could be if we did world transform
367
// in here as well. Though, it still does cut down on a lot of updates in Tekken 6.
368
if (gstate_c.IsDirty(DIRTY_CULL_PLANES)) {
369
UpdatePlanes();
370
gpuStats.numPlaneUpdates++;
371
gstate_c.Clean(DIRTY_CULL_PLANES);
372
}
373
374
// Also let's just bail if offsetOutsideEdge_ is set, instead of handling the cases.
375
// NOTE: This is written to in UpdatePlanes so can't check it before.
376
if (offsetOutsideEdge_)
377
return true;
378
379
// Simple, most common case.
380
int stride = dec->VertexSize();
381
int offset = dec->posoff;
382
int vertStride = 3;
383
384
// TODO: Possibly do the plane tests directly against the source formats instead of converting.
385
switch (vertType & GE_VTYPE_POS_MASK) {
386
case GE_VTYPE_POS_8BIT:
387
for (int i = 0; i < vertexCount; i++) {
388
const s8 *data = (const s8 *)vdata + i * stride + offset;
389
for (int j = 0; j < 3; j++) {
390
verts[i * 3 + j] = data[j] * (1.0f / 128.0f);
391
}
392
}
393
break;
394
case GE_VTYPE_POS_16BIT:
395
{
396
#if PPSSPP_ARCH(SSE2)
397
__m128 scaleFactor = _mm_set1_ps(1.0f / 32768.0f);
398
for (int i = 0; i < vertexCount; i++) {
399
const s16 *data = ((const s16 *)((const s8 *)vdata + i * stride + offset));
400
__m128i bits = _mm_castpd_si128(_mm_load_sd((const double *)data));
401
// Sign extension. Hacky without SSE4.
402
bits = _mm_srai_epi32(_mm_unpacklo_epi16(bits, bits), 16);
403
__m128 pos = _mm_mul_ps(_mm_cvtepi32_ps(bits), scaleFactor);
404
_mm_storeu_ps(verts + i * 3, pos); // TODO: use stride 4 to avoid clashing writes?
405
}
406
#elif PPSSPP_ARCH(ARM_NEON)
407
for (int i = 0; i < vertexCount; i++) {
408
const s16 *dataPtr = ((const s16 *)((const s8 *)vdata + i * stride + offset));
409
int32x4_t data = vmovl_s16(vld1_s16(dataPtr));
410
float32x4_t pos = vcvtq_n_f32_s32(data, 15); // >> 15 = division by 32768.0f
411
vst1q_f32(verts + i * 3, pos);
412
}
413
#else
414
for (int i = 0; i < vertexCount; i++) {
415
const s16 *data = ((const s16 *)((const s8 *)vdata + i * stride + offset));
416
for (int j = 0; j < 3; j++) {
417
verts[i * 3 + j] = data[j] * (1.0f / 32768.0f);
418
}
419
}
420
#endif
421
break;
422
}
423
case GE_VTYPE_POS_FLOAT:
424
// No need to copy in this case, we can just read directly from the source format with a stride.
425
verts = (float *)((uint8_t *)vdata + offset);
426
vertStride = stride / 4;
427
break;
428
}
429
430
// We only check the 4 sides. Near/far won't likely make a huge difference.
431
// We test one vertex against 4 planes to get some SIMD. Vertices need to be transformed to world space
432
// for testing, don't want to re-do that, so we have to use that "pivot" of the data.
433
#if PPSSPP_ARCH(SSE2)
434
const __m128 worldX = _mm_loadu_ps(gstate.worldMatrix);
435
const __m128 worldY = _mm_loadu_ps(gstate.worldMatrix + 3);
436
const __m128 worldZ = _mm_loadu_ps(gstate.worldMatrix + 6);
437
const __m128 worldW = _mm_loadu_ps(gstate.worldMatrix + 9);
438
const __m128 planeX = _mm_loadu_ps(planes_.x);
439
const __m128 planeY = _mm_loadu_ps(planes_.y);
440
const __m128 planeZ = _mm_loadu_ps(planes_.z);
441
const __m128 planeW = _mm_loadu_ps(planes_.w);
442
__m128 inside = _mm_set1_ps(0.0f);
443
for (int i = 0; i < vertexCount; i++) {
444
const float *pos = verts + i * vertStride;
445
__m128 worldpos = _mm_add_ps(
446
_mm_add_ps(
447
_mm_mul_ps(worldX, _mm_set1_ps(pos[0])),
448
_mm_mul_ps(worldY, _mm_set1_ps(pos[1]))
449
),
450
_mm_add_ps(
451
_mm_mul_ps(worldZ, _mm_set1_ps(pos[2])),
452
worldW
453
)
454
);
455
// OK, now we check it against the four planes.
456
// This is really curiously similar to a matrix multiplication (well, it is one).
457
__m128 posX = _mm_shuffle_ps(worldpos, worldpos, _MM_SHUFFLE(0, 0, 0, 0));
458
__m128 posY = _mm_shuffle_ps(worldpos, worldpos, _MM_SHUFFLE(1, 1, 1, 1));
459
__m128 posZ = _mm_shuffle_ps(worldpos, worldpos, _MM_SHUFFLE(2, 2, 2, 2));
460
__m128 planeDist = _mm_add_ps(
461
_mm_add_ps(
462
_mm_mul_ps(planeX, posX),
463
_mm_mul_ps(planeY, posY)
464
),
465
_mm_add_ps(
466
_mm_mul_ps(planeZ, posZ),
467
planeW
468
)
469
);
470
inside = _mm_or_ps(inside, _mm_cmpge_ps(planeDist, _mm_setzero_ps()));
471
}
472
// 0xF means that we found at least one vertex inside every one of the planes.
473
// We don't bother with counts, though it wouldn't be hard if we had a use for them.
474
return _mm_movemask_ps(inside) == 0xF;
475
#elif PPSSPP_ARCH(ARM_NEON)
476
const float32x4_t worldX = vld1q_f32(gstate.worldMatrix);
477
const float32x4_t worldY = vld1q_f32(gstate.worldMatrix + 3);
478
const float32x4_t worldZ = vld1q_f32(gstate.worldMatrix + 6);
479
const float32x4_t worldW = vld1q_f32(gstate.worldMatrix + 9);
480
const float32x4_t planeX = vld1q_f32(planes_.x);
481
const float32x4_t planeY = vld1q_f32(planes_.y);
482
const float32x4_t planeZ = vld1q_f32(planes_.z);
483
const float32x4_t planeW = vld1q_f32(planes_.w);
484
uint32x4_t inside = vdupq_n_u32(0);
485
for (int i = 0; i < vertexCount; i++) {
486
const float *pos = verts + i * vertStride;
487
float32x4_t objpos = vld1q_f32(pos);
488
float32x4_t worldpos = vaddq_f32(
489
vmlaq_laneq_f32(
490
vmulq_laneq_f32(worldX, objpos, 0),
491
worldY, objpos, 1),
492
vmlaq_laneq_f32(worldW, worldZ, objpos, 2)
493
);
494
// OK, now we check it against the four planes.
495
// This is really curiously similar to a matrix multiplication (well, it is one).
496
float32x4_t planeDist = vaddq_f32(
497
vmlaq_laneq_f32(
498
vmulq_laneq_f32(planeX, worldpos, 0),
499
planeY, worldpos, 1),
500
vmlaq_laneq_f32(planeW, planeZ, worldpos, 2)
501
);
502
inside = vorrq_u32(inside, vcgezq_f32(planeDist));
503
}
504
uint64_t insideBits = vget_lane_u64(vreinterpret_u64_u16(vmovn_u32(inside)), 0);
505
return ~insideBits == 0; // InsideBits all ones means that we found at least one vertex inside every one of the planes. We don't bother with counts, though it wouldn't be hard.
506
#else
507
int inside[4]{};
508
for (int i = 0; i < vertexCount; i++) {
509
const float *pos = verts + i * vertStride;
510
float worldpos[3];
511
Vec3ByMatrix43(worldpos, pos, gstate.worldMatrix);
512
for (int plane = 0; plane < 4; plane++) {
513
float value = planes_.Test(plane, worldpos);
514
if (value >= 0.0f)
515
inside[plane]++;
516
}
517
}
518
519
for (int plane = 0; plane < 4; plane++) {
520
if (inside[plane] == 0) {
521
return false;
522
}
523
}
524
#endif
525
return true;
526
}
527
528
// 2D bounding box test against scissor. No indexing yet.
529
// Only supports non-indexed draws with float positions.
530
bool DrawEngineCommon::TestBoundingBoxThrough(const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType, int *bytesRead) {
531
// Grab temp buffer space from large offsets in decoded_. Not exactly safe for large draws.
532
if (vertexCount > 16) {
533
return true;
534
}
535
536
// Although this may lead to drawing that shouldn't happen, the viewport is more complex on VR.
537
// Let's always say objects are within bounds.
538
if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY))
539
return true;
540
541
const int stride = dec->VertexSize();
542
const int posOffset = dec->posoff;
543
544
*bytesRead = stride * vertexCount;
545
546
bool allOutsideLeft = true;
547
bool allOutsideTop = true;
548
bool allOutsideRight = true;
549
bool allOutsideBottom = true;
550
const float left = gstate.getScissorX1();
551
const float top = gstate.getScissorY1();
552
const float right = gstate.getScissorX2();
553
const float bottom = gstate.getScissorY2();
554
555
switch (vertType & GE_VTYPE_POS_MASK) {
556
case GE_VTYPE_POS_FLOAT:
557
{
558
// TODO: This can be SIMD'd, with some trickery.
559
for (int i = 0; i < vertexCount; i++) {
560
const float *pos = (const float*)((const u8 *)vdata + stride * i + posOffset);
561
const float x = pos[0];
562
const float y = pos[1];
563
if (x >= left) {
564
allOutsideLeft = false;
565
}
566
if (x <= right + 1) {
567
allOutsideRight = false;
568
}
569
if (y >= top) {
570
allOutsideTop = false;
571
}
572
if (y <= bottom + 1) {
573
allOutsideBottom = false;
574
}
575
}
576
if (allOutsideLeft || allOutsideTop || allOutsideRight || allOutsideBottom) {
577
return false;
578
}
579
return true;
580
}
581
default:
582
// Shouldn't end up here with the checks outside this function.
583
_dbg_assert_(false);
584
return true;
585
}
586
}
587
588
void DrawEngineCommon::ApplyFramebufferRead(FBOTexState *fboTexState) {
589
if (gstate_c.Use(GPU_USE_FRAMEBUFFER_FETCH)) {
590
*fboTexState = FBO_TEX_READ_FRAMEBUFFER;
591
} else {
592
gpuStats.numCopiesForShaderBlend++;
593
*fboTexState = FBO_TEX_COPY_BIND_TEX;
594
}
595
gstate_c.Dirty(DIRTY_SHADERBLEND);
596
}
597
598
int DrawEngineCommon::ComputeNumVertsToDecode() const {
599
int sum = 0;
600
for (int i = 0; i < numDrawVerts_; i++) {
601
sum += drawVerts_[i].indexUpperBound + 1 - drawVerts_[i].indexLowerBound;
602
}
603
return sum;
604
}
605
606
// Takes a list of consecutive PRIM opcodes, and extends the current draw call to include them.
607
// This is just a performance optimization.
608
int DrawEngineCommon::ExtendNonIndexedPrim(const uint32_t *cmd, const uint32_t *stall, const VertexDecoder *dec, u32 vertTypeID, bool clockwise, int *bytesRead, bool isTriangle) {
609
const uint32_t *start = cmd;
610
int prevDrawVerts = numDrawVerts_ - 1;
611
DeferredVerts &dv = drawVerts_[prevDrawVerts];
612
int offset = dv.vertexCount;
613
614
_dbg_assert_(numDrawInds_ <= MAX_DEFERRED_DRAW_INDS); // if it's equal, the check below will take care of it before any action is taken.
615
_dbg_assert_(numDrawVerts_ > 0);
616
617
if (!clockwise) {
618
anyCCWOrIndexed_ = true;
619
}
620
int seenPrims = 0;
621
while (cmd != stall) {
622
uint32_t data = *cmd;
623
if ((data & 0xFFF80000) != 0x04000000) {
624
break;
625
}
626
GEPrimitiveType newPrim = static_cast<GEPrimitiveType>((data >> 16) & 7);
627
if (IsTrianglePrim(newPrim) != isTriangle)
628
break;
629
int vertexCount = data & 0xFFFF;
630
if (numDrawInds_ >= MAX_DEFERRED_DRAW_INDS || vertexCountInDrawCalls_ + offset + vertexCount > VERTEX_BUFFER_MAX) {
631
break;
632
}
633
DeferredInds &di = drawInds_[numDrawInds_++];
634
di.indexType = 0;
635
di.prim = newPrim;
636
seenPrims |= (1 << newPrim);
637
di.clockwise = clockwise;
638
di.vertexCount = vertexCount;
639
di.vertDecodeIndex = prevDrawVerts;
640
di.offset = offset;
641
offset += vertexCount;
642
cmd++;
643
}
644
645
seenPrims_ |= seenPrims;
646
647
int totalCount = offset - dv.vertexCount;
648
dv.vertexCount = offset;
649
dv.indexUpperBound = dv.vertexCount - 1;
650
vertexCountInDrawCalls_ += totalCount;
651
*bytesRead = totalCount * dec->VertexSize();
652
return cmd - start;
653
}
654
655
void DrawEngineCommon::SkipPrim(GEPrimitiveType prim, int vertexCount, const VertexDecoder *dec, u32 vertTypeID, int *bytesRead) {
656
if (!indexGen.PrimCompatible(prevPrim_, prim)) {
657
Flush();
658
}
659
660
// This isn't exactly right, if we flushed, since prims can straddle previous calls.
661
// But it generally works for common usage.
662
if (prim == GE_PRIM_KEEP_PREVIOUS) {
663
// Has to be set to something, let's assume POINTS (0) if no previous.
664
if (prevPrim_ == GE_PRIM_INVALID)
665
prevPrim_ = GE_PRIM_POINTS;
666
prim = prevPrim_;
667
} else {
668
prevPrim_ = prim;
669
}
670
671
*bytesRead = vertexCount * dec->VertexSize();
672
}
673
674
// vertTypeID is the vertex type but with the UVGen mode smashed into the top bits.
675
bool DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, const VertexDecoder *dec, u32 vertTypeID, bool clockwise, int *bytesRead) {
676
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawVerts_ >= MAX_DEFERRED_DRAW_VERTS || numDrawInds_ >= MAX_DEFERRED_DRAW_INDS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) {
677
Flush();
678
}
679
_dbg_assert_(numDrawVerts_ < MAX_DEFERRED_DRAW_VERTS);
680
_dbg_assert_(numDrawInds_ < MAX_DEFERRED_DRAW_INDS);
681
682
// This isn't exactly right, if we flushed, since prims can straddle previous calls.
683
// But it generally works for common usage.
684
if (prim == GE_PRIM_KEEP_PREVIOUS) {
685
// Has to be set to something, let's assume POINTS (0) if no previous.
686
if (prevPrim_ == GE_PRIM_INVALID)
687
prevPrim_ = GE_PRIM_POINTS;
688
prim = prevPrim_;
689
} else {
690
prevPrim_ = prim;
691
}
692
693
// If vtype has changed, setup the vertex decoder. Don't need to nullcheck dec_ since we set lastVType_ to an invalid value whenever we null it.
694
if (vertTypeID != lastVType_) {
695
dec_ = dec;
696
_dbg_assert_(dec->VertexType() == vertTypeID);
697
lastVType_ = vertTypeID;
698
} else {
699
_dbg_assert_(dec_->VertexType() == lastVType_);
700
}
701
702
*bytesRead = vertexCount * dec_->VertexSize();
703
704
// Check that we have enough vertices to form the requested primitive.
705
if (vertexCount < 3) {
706
if ((vertexCount < 2 && prim > 0) || (prim > GE_PRIM_LINE_STRIP && prim != GE_PRIM_RECTANGLES)) {
707
return false;
708
}
709
if (vertexCount <= 0) {
710
// Unfortunately we need to do this check somewhere since GetIndexBounds doesn't handle zero-length arrays.
711
return false;
712
}
713
}
714
715
bool applySkin = dec_->skinInDecode;
716
717
DeferredInds &di = drawInds_[numDrawInds_++];
718
di.inds = inds;
719
int indexType = (vertTypeID & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
720
if (indexType) {
721
anyCCWOrIndexed_ = true;
722
}
723
di.indexType = indexType;
724
di.prim = prim;
725
di.clockwise = clockwise;
726
if (!clockwise) {
727
anyCCWOrIndexed_ = true;
728
}
729
di.vertexCount = vertexCount;
730
di.vertDecodeIndex = numDrawVerts_;
731
di.offset = 0;
732
733
_dbg_assert_(numDrawVerts_ <= MAX_DEFERRED_DRAW_VERTS);
734
_dbg_assert_(numDrawInds_ <= MAX_DEFERRED_DRAW_INDS);
735
736
if (inds && numDrawVerts_ > decodeVertsCounter_ && drawVerts_[numDrawVerts_ - 1].verts == verts && !applySkin) {
737
// Same vertex pointer as a previous un-decoded draw call - let's just extend the decode!
738
di.vertDecodeIndex = numDrawVerts_ - 1;
739
u16 lb;
740
u16 ub;
741
GetIndexBounds(inds, vertexCount, vertTypeID, &lb, &ub);
742
DeferredVerts &dv = drawVerts_[numDrawVerts_ - 1];
743
if (lb < dv.indexLowerBound)
744
dv.indexLowerBound = lb;
745
if (ub > dv.indexUpperBound)
746
dv.indexUpperBound = ub;
747
} else {
748
// Record a new draw, and a new index gen.
749
DeferredVerts &dv = drawVerts_[numDrawVerts_++];
750
dv.verts = verts;
751
dv.vertexCount = vertexCount;
752
dv.uvScale = gstate_c.uv;
753
// Does handle the unindexed case.
754
GetIndexBounds(inds, vertexCount, vertTypeID, &dv.indexLowerBound, &dv.indexUpperBound);
755
}
756
757
vertexCountInDrawCalls_ += vertexCount;
758
seenPrims_ |= (1 << prim);
759
760
if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) {
761
// This prevents issues with consecutive self-renders in Ridge Racer.
762
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
763
Flush();
764
}
765
return true;
766
}
767
768
void DrawEngineCommon::BeginFrame() {
769
applySkinInDecode_ = g_Config.bSoftwareSkinning;
770
}
771
772
void DrawEngineCommon::DecodeVerts(const VertexDecoder *dec, u8 *dest) {
773
if (!numDrawVerts_) {
774
return;
775
}
776
// Note that this should be able to continue a partial decode - we don't necessarily start from zero here (although we do most of the time).
777
int i = decodeVertsCounter_;
778
int stride = (int)dec->GetDecVtxFmt().stride;
779
for (; i < numDrawVerts_; i++) {
780
const DeferredVerts &dv = drawVerts_[i];
781
782
int indexLowerBound = dv.indexLowerBound;
783
drawVertexOffsets_[i] = numDecodedVerts_ - indexLowerBound;
784
785
int indexUpperBound = dv.indexUpperBound;
786
787
if (indexUpperBound + 1 - indexLowerBound + numDecodedVerts_ >= VERTEX_BUFFER_MAX) {
788
// Hit our limit! Stop decoding in this draw.
789
break;
790
}
791
792
// Decode the verts (and at the same time apply morphing/skinning). Simple.
793
dec->DecodeVerts(dest + numDecodedVerts_ * stride, dv.verts, &dv.uvScale, indexLowerBound, indexUpperBound);
794
numDecodedVerts_ += indexUpperBound - indexLowerBound + 1;
795
}
796
decodeVertsCounter_ = i;
797
}
798
799
int DrawEngineCommon::DecodeInds() {
800
// Note that this should be able to continue a partial decode - we don't necessarily start from zero here (although we do most of the time).
801
802
int i = decodeIndsCounter_;
803
for (; i < numDrawInds_; i++) {
804
const DeferredInds &di = drawInds_[i];
805
806
int indexOffset = drawVertexOffsets_[di.vertDecodeIndex] + di.offset;
807
bool clockwise = di.clockwise;
808
// We've already collapsed subsequent draws with the same vertex pointer, so no tricky logic here anymore.
809
// 2. Loop through the drawcalls, translating indices as we go.
810
switch (di.indexType) {
811
case GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT:
812
indexGen.AddPrim(di.prim, di.vertexCount, indexOffset, clockwise);
813
break;
814
case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT:
815
indexGen.TranslatePrim(di.prim, di.vertexCount, (const u8 *)di.inds, indexOffset, clockwise);
816
break;
817
case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT:
818
indexGen.TranslatePrim(di.prim, di.vertexCount, (const u16_le *)di.inds, indexOffset, clockwise);
819
break;
820
case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT:
821
indexGen.TranslatePrim(di.prim, di.vertexCount, (const u32_le *)di.inds, indexOffset, clockwise);
822
break;
823
}
824
}
825
decodeIndsCounter_ = i;
826
827
return indexGen.VertexCount();
828
}
829
830
bool DrawEngineCommon::CanUseHardwareTransform(int prim) const {
831
if (!useHWTransform_)
832
return false;
833
return !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && prim > GE_PRIM_LINE_STRIP;
834
}
835
836
bool DrawEngineCommon::CanUseHardwareTessellation(GEPatchPrimType prim) const {
837
if (useHWTessellation_) {
838
return CanUseHardwareTransform(PatchPrimToPrim(prim));
839
}
840
return false;
841
}
842
843
void TessellationDataTransfer::CopyControlPoints(float *pos, float *tex, float *col, int posStride, int texStride, int colStride, const SimpleVertex *const *points, int size, u32 vertType) {
844
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0;
845
bool hasTexCoord = (vertType & GE_VTYPE_TC_MASK) != 0;
846
847
for (int i = 0; i < size; ++i) {
848
memcpy(pos, points[i]->pos.AsArray(), 3 * sizeof(float));
849
pos += posStride;
850
}
851
if (hasTexCoord) {
852
for (int i = 0; i < size; ++i) {
853
memcpy(tex, points[i]->uv, 2 * sizeof(float));
854
tex += texStride;
855
}
856
}
857
if (hasColor) {
858
for (int i = 0; i < size; ++i) {
859
memcpy(col, Vec4f::FromRGBA(points[i]->color_32).AsArray(), 4 * sizeof(float));
860
col += colStride;
861
}
862
}
863
}
864
865
bool DrawEngineCommon::DescribeCodePtr(const u8 *ptr, std::string &name) const {
866
if (!decJitCache_ || !decJitCache_->IsInSpace(ptr)) {
867
return false;
868
}
869
870
// Loop through all the decoders and see if we have a match.
871
VertexDecoder *found = nullptr;
872
u32 foundKey;
873
874
decoderMap_.Iterate([&](u32 key, VertexDecoder *value) {
875
if (!found) {
876
if (value->IsInSpace(ptr)) {
877
foundKey = key;
878
found = value;
879
}
880
}
881
});
882
883
if (found) {
884
char temp[256];
885
found->ToString(temp, false);
886
name = temp;
887
snprintf(temp, sizeof(temp), "_%08X", foundKey);
888
name += temp;
889
return true;
890
} else {
891
return false;
892
}
893
}
894
895
enum {
896
DEPTH_TRANSFORMED_MAX_VERTS = VERTEX_BUFFER_MAX,
897
DEPTH_TRANSFORMED_BYTES = DEPTH_TRANSFORMED_MAX_VERTS * 4 * sizeof(float),
898
DEPTH_SCREENVERTS_COMPONENT_COUNT = VERTEX_BUFFER_MAX,
899
DEPTH_SCREENVERTS_COMPONENT_BYTES = DEPTH_SCREENVERTS_COMPONENT_COUNT * sizeof(int) + 384,
900
DEPTH_SCREENVERTS_TOTAL_BYTES = DEPTH_SCREENVERTS_COMPONENT_BYTES * 3,
901
DEPTH_INDEXBUFFER_BYTES = DEPTH_TRANSFORMED_MAX_VERTS * 3 * sizeof(uint16_t), // hmmm
902
};
903
904
// We process vertices for depth rendering in several stages:
905
// First, we transform and collect vertices into depthTransformed_ (4-vectors, xyzw).
906
// Then, we group and cull the vertices into four-triangle groups, which are placed in
907
// depthScreenVerts_, with x, y and z separated into different part of the array.
908
// (Alternatively, if drawing rectangles, they're just added linearly).
909
// After that, we send these groups out for SIMD setup and rasterization.
910
void DrawEngineCommon::InitDepthRaster() {
911
switch ((DepthRasterMode)g_Config.iDepthRasterMode) {
912
case DepthRasterMode::DEFAULT:
913
case DepthRasterMode::LOW_QUALITY:
914
useDepthRaster_ = PSP_CoreParameter().compat.flags().SoftwareRasterDepth;
915
break;
916
case DepthRasterMode::FORCE_ON:
917
useDepthRaster_ = true;
918
break;
919
case DepthRasterMode::OFF:
920
useDepthRaster_ = false;
921
}
922
923
if (useDepthRaster_) {
924
depthDraws_.reserve(256);
925
depthTransformed_ = (float *)AllocateMemoryPages(DEPTH_TRANSFORMED_BYTES, MEM_PROT_READ | MEM_PROT_WRITE);
926
depthScreenVerts_ = (int *)AllocateMemoryPages(DEPTH_SCREENVERTS_TOTAL_BYTES, MEM_PROT_READ | MEM_PROT_WRITE);
927
depthIndices_ = (uint16_t *)AllocateMemoryPages(DEPTH_INDEXBUFFER_BYTES, MEM_PROT_READ | MEM_PROT_WRITE);
928
}
929
}
930
931
void DrawEngineCommon::ShutdownDepthRaster() {
932
if (depthTransformed_) {
933
FreeMemoryPages(depthTransformed_, DEPTH_TRANSFORMED_BYTES);
934
}
935
if (depthScreenVerts_) {
936
FreeMemoryPages(depthScreenVerts_, DEPTH_SCREENVERTS_TOTAL_BYTES);
937
}
938
if (depthIndices_) {
939
FreeMemoryPages(depthIndices_, DEPTH_INDEXBUFFER_BYTES);
940
}
941
}
942
943
Mat4F32 ComputeFinalProjMatrix() {
944
const float viewportTranslate[4] = {
945
gstate.getViewportXCenter() - gstate.getOffsetX(),
946
gstate.getViewportYCenter() - gstate.getOffsetY(),
947
gstate.getViewportZCenter(),
948
0.0f,
949
};
950
951
Mat4F32 wv = Mul4x3By4x4(Mat4x3F32(gstate.worldMatrix), Mat4F32::Load4x3(gstate.viewMatrix));
952
Mat4F32 m = Mul4x4By4x4(wv, Mat4F32(gstate.projMatrix));
953
// NOTE: Applying the translation actually works pre-divide, since W is also affected.
954
Vec4F32 scale = Vec4F32::LoadF24x3_One(&gstate.viewportxscale);
955
Vec4F32 translate = Vec4F32::Load(viewportTranslate);
956
TranslateAndScaleInplace(m, scale, translate);
957
return m;
958
}
959
960
bool DrawEngineCommon::CalculateDepthDraw(DepthDraw *draw, GEPrimitiveType prim, int vertexCount) {
961
switch (prim) {
962
case GE_PRIM_INVALID:
963
case GE_PRIM_KEEP_PREVIOUS:
964
case GE_PRIM_LINES:
965
case GE_PRIM_LINE_STRIP:
966
case GE_PRIM_POINTS:
967
return false;
968
default:
969
break;
970
}
971
972
// Ignore some useless compare modes.
973
switch (gstate.getDepthTestFunction()) {
974
case GE_COMP_ALWAYS:
975
draw->compareMode = ZCompareMode::Always;
976
break;
977
case GE_COMP_LEQUAL:
978
case GE_COMP_LESS:
979
draw->compareMode = ZCompareMode::Less;
980
break;
981
case GE_COMP_GEQUAL:
982
case GE_COMP_GREATER:
983
draw->compareMode = ZCompareMode::Greater; // Most common
984
break;
985
case GE_COMP_NEVER:
986
case GE_COMP_EQUAL:
987
// These will never have a useful effect in Z-only raster.
988
[[fallthrough]];
989
case GE_COMP_NOTEQUAL:
990
// This is highly unusual, let's just ignore it.
991
[[fallthrough]];
992
default:
993
return false;
994
}
995
if (gstate.isModeClear()) {
996
if (!gstate.isClearModeDepthMask()) {
997
return false;
998
}
999
draw->compareMode = ZCompareMode::Always;
1000
} else {
1001
// These should have been caught earlier.
1002
_dbg_assert_(gstate.isDepthTestEnabled());
1003
_dbg_assert_(gstate.isDepthWriteEnabled());
1004
}
1005
1006
if (depthVertexCount_ + vertexCount >= DEPTH_TRANSFORMED_MAX_VERTS) {
1007
// Can't add more. We need to flush.
1008
return false;
1009
}
1010
1011
draw->depthAddr = gstate.getDepthBufRawAddress() | 0x04000000;
1012
draw->depthStride = gstate.DepthBufStride();
1013
draw->vertexOffset = depthVertexCount_;
1014
draw->indexOffset = depthIndexCount_;
1015
draw->vertexCount = vertexCount;
1016
draw->cullEnabled = gstate.isCullEnabled();
1017
draw->cullMode = gstate.getCullMode();
1018
draw->prim = prim;
1019
draw->scissor.x1 = gstate.getScissorX1();
1020
draw->scissor.y1 = gstate.getScissorY1();
1021
draw->scissor.x2 = gstate.getScissorX2();
1022
draw->scissor.y2 = gstate.getScissorY2();
1023
return true;
1024
}
1025
1026
void DrawEngineCommon::DepthRasterSubmitRaw(GEPrimitiveType prim, const VertexDecoder *dec, uint32_t vertTypeID, int vertexCount) {
1027
if (!gstate.isModeClear() && (!gstate.isDepthTestEnabled() || !gstate.isDepthWriteEnabled())) {
1028
return;
1029
}
1030
1031
if (vertTypeID & (GE_VTYPE_WEIGHT_MASK | GE_VTYPE_MORPHCOUNT_MASK)) {
1032
return;
1033
}
1034
1035
_dbg_assert_(prim != GE_PRIM_RECTANGLES);
1036
1037
float worldviewproj[16];
1038
ComputeFinalProjMatrix().Store(worldviewproj);
1039
1040
DepthDraw draw;
1041
if (!CalculateDepthDraw(&draw, prim, vertexCount)) {
1042
return;
1043
}
1044
1045
TimeCollector collectStat(&gpuStats.msPrepareDepth, coreCollectDebugStats);
1046
1047
// Decode.
1048
int numDecoded = 0;
1049
for (int i = 0; i < numDrawVerts_; i++) {
1050
const DeferredVerts &dv = drawVerts_[i];
1051
if (dv.indexUpperBound + 1 - dv.indexLowerBound + numDecoded >= DEPTH_TRANSFORMED_MAX_VERTS) {
1052
// Hit our limit! Stop decoding in this draw.
1053
// We should have already broken out in CalculateDepthDraw.
1054
break;
1055
}
1056
// Decode the verts (and at the same time apply morphing/skinning). Simple.
1057
DecodeAndTransformForDepthRaster(depthTransformed_ + (draw.vertexOffset + numDecoded) * 4, worldviewproj, dv.verts, dv.indexLowerBound, dv.indexUpperBound, dec, vertTypeID);
1058
numDecoded += dv.indexUpperBound - dv.indexLowerBound + 1;
1059
}
1060
1061
// Copy indices.
1062
memcpy(depthIndices_ + draw.indexOffset, decIndex_, sizeof(uint16_t) * vertexCount);
1063
1064
// Commit
1065
depthIndexCount_ += vertexCount;
1066
depthVertexCount_ += numDecoded;
1067
1068
if (depthDraws_.empty()) {
1069
rasterTimeStart_ = time_now_d();
1070
}
1071
1072
depthDraws_.push_back(draw);
1073
1074
// FlushQueuedDepth();
1075
}
1076
1077
void DrawEngineCommon::DepthRasterPredecoded(GEPrimitiveType prim, const void *inVerts, int numDecoded, const VertexDecoder *dec, int vertexCount) {
1078
if (!gstate.isModeClear() && (!gstate.isDepthTestEnabled() || !gstate.isDepthWriteEnabled())) {
1079
return;
1080
}
1081
1082
DepthDraw draw;
1083
if (!CalculateDepthDraw(&draw, prim, vertexCount)) {
1084
return;
1085
}
1086
1087
TimeCollector collectStat(&gpuStats.msPrepareDepth, coreCollectDebugStats);
1088
1089
// Make sure these have already been indexed away.
1090
_dbg_assert_(prim != GE_PRIM_TRIANGLE_STRIP && prim != GE_PRIM_TRIANGLE_FAN);
1091
1092
if (dec->throughmode) {
1093
ConvertPredecodedThroughForDepthRaster(depthTransformed_ + 4 * draw.vertexOffset, decoded_, dec, numDecoded);
1094
} else {
1095
if (dec->VertexType() & (GE_VTYPE_WEIGHT_MASK | GE_VTYPE_MORPHCOUNT_MASK)) {
1096
return;
1097
}
1098
float worldviewproj[16];
1099
ComputeFinalProjMatrix().Store(worldviewproj);
1100
TransformPredecodedForDepthRaster(depthTransformed_ + 4 * draw.vertexOffset, worldviewproj, decoded_, dec, numDecoded);
1101
}
1102
1103
// Copy indices.
1104
memcpy(depthIndices_ + draw.indexOffset, decIndex_, sizeof(uint16_t) * vertexCount);
1105
1106
// Commit
1107
depthIndexCount_ += vertexCount;
1108
depthVertexCount_ += numDecoded;
1109
1110
depthDraws_.push_back(draw);
1111
1112
if (depthDraws_.empty()) {
1113
rasterTimeStart_ = time_now_d();
1114
}
1115
// FlushQueuedDepth();
1116
}
1117
1118
void DrawEngineCommon::FlushQueuedDepth() {
1119
if (rasterTimeStart_ != 0.0) {
1120
gpuStats.msRasterTimeAvailable += time_now_d() - rasterTimeStart_;
1121
rasterTimeStart_ = 0.0;
1122
}
1123
1124
const bool collectStats = coreCollectDebugStats;
1125
const bool lowQ = g_Config.iDepthRasterMode == (int)DepthRasterMode::LOW_QUALITY;
1126
1127
for (const auto &draw : depthDraws_) {
1128
int *tx = depthScreenVerts_;
1129
int *ty = depthScreenVerts_ + DEPTH_SCREENVERTS_COMPONENT_COUNT;
1130
float *tz = (float *)(depthScreenVerts_ + DEPTH_SCREENVERTS_COMPONENT_COUNT * 2);
1131
1132
int outVertCount = 0;
1133
1134
const float *vertices = depthTransformed_ + 4 * draw.vertexOffset;
1135
const uint16_t *indices = depthIndices_ + draw.indexOffset;
1136
1137
DepthScissor tileScissor = draw.scissor.Tile(0, 1);
1138
1139
{
1140
TimeCollector collectStat(&gpuStats.msCullDepth, collectStats);
1141
switch (draw.prim) {
1142
case GE_PRIM_RECTANGLES:
1143
outVertCount = DepthRasterClipIndexedRectangles(tx, ty, tz, vertices, indices, draw, tileScissor);
1144
break;
1145
case GE_PRIM_TRIANGLES:
1146
outVertCount = DepthRasterClipIndexedTriangles(tx, ty, tz, vertices, indices, draw, tileScissor);
1147
break;
1148
default:
1149
_dbg_assert_(false);
1150
break;
1151
}
1152
}
1153
{
1154
TimeCollector collectStat(&gpuStats.msRasterizeDepth, collectStats);
1155
DepthRasterScreenVerts((uint16_t *)Memory::GetPointerWrite(draw.depthAddr), draw.depthStride, tx, ty, tz, outVertCount, draw, tileScissor, lowQ);
1156
}
1157
}
1158
1159
// Reset queue
1160
depthIndexCount_ = 0;
1161
depthVertexCount_ = 0;
1162
depthDraws_.clear();
1163
}
1164
1165