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/GPU/Software/FuncId.cpp
Views: 1401
1
// Copyright (c) 2021- 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/Data/Convert/ColorConv.h"
19
#include "Common/StringUtils.h"
20
#include "Core/MemMap.h"
21
#include "GPU/Common/TextureDecoder.h"
22
#include "GPU/GPUState.h"
23
#include "GPU/Software/FuncId.h"
24
25
static_assert(sizeof(SamplerID) == sizeof(SamplerID::fullKey) + sizeof(SamplerID::cached) + sizeof(SamplerID::pad), "Bad sampler ID size");
26
static_assert(sizeof(PixelFuncID) == sizeof(PixelFuncID::fullKey) + sizeof(PixelFuncID::cached), "Bad pixel func ID size");
27
28
static inline GEComparison OptimizeRefByteCompare(GEComparison func, u8 ref) {
29
// Not equal tests are easier.
30
if (ref == 0 && func == GE_COMP_GREATER)
31
return GE_COMP_NOTEQUAL;
32
if (ref == 0xFF && func == GE_COMP_LESS)
33
return GE_COMP_NOTEQUAL;
34
35
// Sometimes games pointlessly use tests like these.
36
if (ref == 0 && func == GE_COMP_GEQUAL)
37
return GE_COMP_ALWAYS;
38
if (ref == 0xFF && func == GE_COMP_LEQUAL)
39
return GE_COMP_ALWAYS;
40
return func;
41
}
42
43
static inline PixelBlendFactor OptimizeAlphaFactor(uint32_t color) {
44
if (color == 0x00000000)
45
return PixelBlendFactor::ZERO;
46
if (color == 0x00FFFFFF)
47
return PixelBlendFactor::ONE;
48
return PixelBlendFactor::FIX;
49
}
50
51
void ComputePixelFuncID(PixelFuncID *id) {
52
id->fullKey = 0;
53
54
// TODO: Could this be minz > 0x0000 || maxz < 0xFFFF? Maybe unsafe, depending on verts...
55
id->applyDepthRange = !gstate.isModeThrough();
56
// Dither happens even in clear mode.
57
id->dithering = gstate.isDitherEnabled();
58
id->fbFormat = gstate.FrameBufFormat();
59
id->useStandardStride = gstate.FrameBufStride() == 512;
60
id->applyColorWriteMask = gstate.getColorMask() != 0;
61
62
id->clearMode = gstate.isModeClear();
63
if (id->clearMode) {
64
id->colorTest = gstate.isClearModeColorMask();
65
id->stencilTest = gstate.isClearModeAlphaMask() && gstate.FrameBufFormat() != GE_FORMAT_565;
66
id->depthWrite = gstate.isClearModeDepthMask();
67
id->depthTestFunc = GE_COMP_ALWAYS;
68
id->alphaTestFunc = GE_COMP_ALWAYS;
69
} else {
70
id->colorTest = gstate.isColorTestEnabled() && gstate.getColorTestFunction() != GE_COMP_ALWAYS;
71
if (gstate.isStencilTestEnabled() && gstate.getStencilTestFunction() == GE_COMP_ALWAYS) {
72
// If stencil always passes, force off when we won't write any stencil bits.
73
bool stencilWrite = (gstate.pmska & 0xFF) != 0xFF && gstate.FrameBufFormat() != GE_FORMAT_565;
74
if (gstate.isDepthTestEnabled() && gstate.getDepthTestFunction() != GE_COMP_ALWAYS)
75
id->stencilTest = stencilWrite && (gstate.getStencilOpZPass() != GE_STENCILOP_KEEP || gstate.getStencilOpZFail() != GE_STENCILOP_KEEP);
76
else
77
id->stencilTest = stencilWrite && gstate.getStencilOpZPass() != GE_STENCILOP_KEEP;
78
} else {
79
id->stencilTest = gstate.isStencilTestEnabled();
80
}
81
id->depthWrite = gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled();
82
id->depthTestFunc = gstate.isDepthTestEnabled() ? gstate.getDepthTestFunction() : GE_COMP_ALWAYS;
83
84
if (id->stencilTest) {
85
id->stencilTestRef = gstate.getStencilTestRef() & gstate.getStencilTestMask();
86
id->stencilTestFunc = OptimizeRefByteCompare(gstate.getStencilTestFunction(), id->stencilTestRef);
87
id->hasStencilTestMask = gstate.getStencilTestMask() != 0xFF && gstate.FrameBufFormat() != GE_FORMAT_565;
88
89
// Stencil can't be written on 565, and any invalid op acts like KEEP, which is 0.
90
if (gstate.FrameBufFormat() != GE_FORMAT_565 && gstate.getStencilOpSFail() <= GE_STENCILOP_DECR)
91
id->sFail = gstate.getStencilOpSFail();
92
if (gstate.FrameBufFormat() != GE_FORMAT_565 && gstate.getStencilOpZFail() <= GE_STENCILOP_DECR)
93
id->zFail = gstate.isDepthTestEnabled() ? gstate.getStencilOpZFail() : GE_STENCILOP_KEEP;
94
if (gstate.FrameBufFormat() != GE_FORMAT_565 && gstate.getStencilOpZPass() <= GE_STENCILOP_DECR)
95
id->zPass = gstate.getStencilOpZPass();
96
97
// Normalize REPLACE 00 to ZERO, especially if using a mask.
98
if (gstate.getStencilTestRef() == 0) {
99
if (id->SFail() == GE_STENCILOP_REPLACE)
100
id->sFail = GE_STENCILOP_ZERO;
101
if (id->ZFail() == GE_STENCILOP_REPLACE)
102
id->zFail = GE_STENCILOP_ZERO;
103
if (id->ZPass() == GE_STENCILOP_REPLACE)
104
id->zPass = GE_STENCILOP_ZERO;
105
}
106
107
// For 5551, DECR is also the same as ZERO.
108
if (id->FBFormat() == GE_FORMAT_5551) {
109
if (id->SFail() == GE_STENCILOP_DECR)
110
id->sFail = GE_STENCILOP_ZERO;
111
if (id->ZFail() == GE_STENCILOP_DECR)
112
id->zFail = GE_STENCILOP_ZERO;
113
if (id->ZPass() == GE_STENCILOP_DECR)
114
id->zPass = GE_STENCILOP_ZERO;
115
}
116
117
// And same for sFail if there's no stencil test. Prefer KEEP, though.
118
if (id->StencilTestFunc() == GE_COMP_ALWAYS) {
119
if (id->DepthTestFunc() == GE_COMP_ALWAYS)
120
id->zFail = GE_STENCILOP_KEEP;
121
id->sFail = GE_STENCILOP_KEEP;
122
// Always doesn't need a mask.
123
id->stencilTestRef = gstate.getStencilTestRef();
124
id->hasStencilTestMask = false;
125
126
// Turn off stencil testing if it's doing nothing.
127
if (id->SFail() == GE_STENCILOP_KEEP && id->ZFail() == GE_STENCILOP_KEEP && id->ZPass() == GE_STENCILOP_KEEP) {
128
id->stencilTest = false;
129
id->stencilTestFunc = 0;
130
id->stencilTestRef = 0;
131
}
132
} else if (id->DepthTestFunc() == GE_COMP_ALWAYS) {
133
// Always treat zPass/zFail the same if there's no depth test.
134
id->zFail = id->zPass;
135
}
136
}
137
138
id->alphaTestFunc = gstate.isAlphaTestEnabled() ? gstate.getAlphaTestFunction() : GE_COMP_ALWAYS;
139
if (id->AlphaTestFunc() != GE_COMP_ALWAYS) {
140
id->alphaTestRef = gstate.getAlphaTestRef() & gstate.getAlphaTestMask();
141
id->hasAlphaTestMask = gstate.getAlphaTestMask() != 0xFF;
142
// Try to pick a more optimal variant.
143
id->alphaTestFunc = OptimizeRefByteCompare(id->AlphaTestFunc(), id->alphaTestRef);
144
if (id->alphaTestFunc == GE_COMP_ALWAYS) {
145
id->alphaTestRef = 0;
146
id->hasAlphaTestMask = false;
147
}
148
}
149
150
// If invalid (6 or 7), doesn't do any blending, so force off.
151
id->alphaBlend = gstate.isAlphaBlendEnabled() && gstate.getBlendEq() <= 5;
152
// Force it off if the factors are constant and don't blend. Some games use this...
153
if (id->alphaBlend && gstate.getBlendEq() == GE_BLENDMODE_MUL_AND_ADD) {
154
bool srcFixedOne = gstate.getBlendFuncA() == GE_SRCBLEND_FIXA && gstate.getFixA() == 0x00FFFFFF;
155
bool dstFixedZero = gstate.getBlendFuncB() == GE_DSTBLEND_FIXB && gstate.getFixB() == 0x00000000;
156
if (srcFixedOne && dstFixedZero)
157
id->alphaBlend = false;
158
}
159
if (id->alphaBlend)
160
id->alphaBlendEq = gstate.getBlendEq();
161
if (id->alphaBlend && id->alphaBlendEq <= GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE) {
162
id->alphaBlendSrc = gstate.getBlendFuncA();
163
id->alphaBlendDst = gstate.getBlendFuncB();
164
// Special values.
165
if (id->alphaBlendSrc >= GE_SRCBLEND_FIXA)
166
id->alphaBlendSrc = (uint8_t)OptimizeAlphaFactor(gstate.getFixA());
167
if (id->alphaBlendDst >= GE_DSTBLEND_FIXB)
168
id->alphaBlendDst = (uint8_t)OptimizeAlphaFactor(gstate.getFixB());
169
}
170
171
if (id->colorTest && gstate.getColorTestFunction() == GE_COMP_NOTEQUAL && gstate.getColorTestRef() == 0 && gstate.getColorTestMask() == 0xFFFFFF) {
172
if (!id->depthWrite && !id->stencilTest && id->alphaBlend && id->AlphaBlendEq() == GE_BLENDMODE_MUL_AND_ADD) {
173
// Might be a pointless color test (seen in Ridge Racer, for example.)
174
if (id->AlphaBlendDst() == PixelBlendFactor::ONE)
175
id->colorTest = false;
176
}
177
}
178
179
id->applyLogicOp = gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY;
180
id->applyFog = gstate.isFogEnabled() && !gstate.isModeThrough();
181
182
id->earlyZChecks = id->DepthTestFunc() != GE_COMP_ALWAYS;
183
if (id->stencilTest && id->earlyZChecks) {
184
// Can't do them early if stencil might need to write.
185
if (id->SFail() != GE_STENCILOP_KEEP || id->ZFail() != GE_STENCILOP_KEEP)
186
id->earlyZChecks = false;
187
}
188
}
189
190
if (id->useStandardStride && (id->depthTestFunc != GE_COMP_ALWAYS || id->depthWrite))
191
id->useStandardStride = gstate.DepthBufStride() == 512;
192
193
// Cache some values for later convenience.
194
if (id->dithering) {
195
for (int y = 0; y < 4; ++y) {
196
for (int x = 0; x < 4; ++x)
197
id->cached.ditherMatrix[y * 4 + x] = gstate.getDitherValue(x, y);
198
}
199
}
200
if (id->applyColorWriteMask) {
201
uint32_t mask = gstate.getColorMask();
202
// This flag means stencil clear or stencil test, basically whether writing to stencil.
203
if (!id->stencilTest)
204
mask |= 0xFF000000;
205
206
switch (id->fbFormat) {
207
case GE_FORMAT_565:
208
id->cached.colorWriteMask = RGBA8888ToRGB565(mask);
209
break;
210
211
case GE_FORMAT_5551:
212
id->cached.colorWriteMask = RGBA8888ToRGBA5551(mask);
213
break;
214
215
case GE_FORMAT_4444:
216
id->cached.colorWriteMask = RGBA8888ToRGBA4444(mask);
217
break;
218
219
case GE_FORMAT_8888:
220
id->cached.colorWriteMask = mask;
221
break;
222
}
223
}
224
if (id->applyFog) {
225
id->cached.fogColor = gstate.fogcolor & 0x00FFFFFF;
226
}
227
if (id->applyLogicOp)
228
id->cached.logicOp = gstate.getLogicOp();
229
id->cached.minz = gstate.getDepthRangeMin();
230
id->cached.maxz = gstate.getDepthRangeMax();
231
id->cached.framebufStride = gstate.FrameBufStride();
232
id->cached.depthbufStride = gstate.DepthBufStride();
233
234
if (id->hasStencilTestMask) {
235
// Without the mask applied, unlike the one in the key.
236
id->cached.stencilRef = gstate.getStencilTestRef();
237
id->cached.stencilTestMask = gstate.getStencilTestMask();
238
}
239
if (id->hasAlphaTestMask)
240
id->cached.alphaTestMask = gstate.getAlphaTestMask();
241
if (!id->clearMode && id->colorTest) {
242
id->cached.colorTestFunc = gstate.getColorTestFunction();
243
id->cached.colorTestMask = gstate.getColorTestMask();
244
id->cached.colorTestRef = gstate.getColorTestRef() & id->cached.colorTestMask;
245
}
246
if (id->alphaBlendSrc == GE_SRCBLEND_FIXA)
247
id->cached.alphaBlendSrc = gstate.getFixA();
248
if (id->alphaBlendDst == GE_DSTBLEND_FIXB)
249
id->cached.alphaBlendDst = gstate.getFixB();
250
}
251
252
std::string DescribePixelFuncID(const PixelFuncID &id) {
253
std::string desc;
254
if (id.clearMode) {
255
desc = "Clear";
256
if (id.ColorClear())
257
desc += "C";
258
if (id.StencilClear())
259
desc += "S";
260
if (id.DepthClear())
261
desc += "D";
262
desc += ":";
263
}
264
if (id.applyDepthRange)
265
desc += "DepthR:";
266
if (id.useStandardStride)
267
desc += "Str512:";
268
if (id.dithering)
269
desc += "Dith:";
270
if (id.applyColorWriteMask)
271
desc += "Msk:";
272
273
switch (id.fbFormat) {
274
case GE_FORMAT_565: desc += "5650:"; break;
275
case GE_FORMAT_5551: desc += "5551:"; break;
276
case GE_FORMAT_4444: desc += "4444:"; break;
277
case GE_FORMAT_8888: desc += "8888:"; break;
278
}
279
280
if (id.AlphaTestFunc() != GE_COMP_ALWAYS) {
281
if (id.clearMode)
282
desc = "INVALID:" + desc;
283
switch (id.AlphaTestFunc()) {
284
case GE_COMP_NEVER: desc += "ANever"; break;
285
case GE_COMP_ALWAYS: break;
286
case GE_COMP_EQUAL: desc += "AEQ"; break;
287
case GE_COMP_NOTEQUAL: desc += "ANE"; break;
288
case GE_COMP_LESS: desc += "ALT"; break;
289
case GE_COMP_LEQUAL: desc += "ALE"; break;
290
case GE_COMP_GREATER: desc += "AGT"; break;
291
case GE_COMP_GEQUAL: desc += "AGE"; break;
292
}
293
if (id.hasAlphaTestMask)
294
desc += "Msk";
295
desc += StringFromFormat("%02X:", id.alphaTestRef);
296
} else if (id.hasAlphaTestMask || id.alphaTestRef != 0) {
297
desc = "INVALID:" + desc;
298
}
299
300
if (id.earlyZChecks)
301
desc += "ZEarly:";
302
if (id.DepthTestFunc() != GE_COMP_ALWAYS) {
303
if (id.clearMode)
304
desc = "INVALID:" + desc;
305
switch (id.DepthTestFunc()) {
306
case GE_COMP_NEVER: desc += "ZNever:"; break;
307
case GE_COMP_ALWAYS: break;
308
case GE_COMP_EQUAL: desc += "ZEQ:"; break;
309
case GE_COMP_NOTEQUAL: desc += "ZNE:"; break;
310
case GE_COMP_LESS: desc += "ZLT:"; break;
311
case GE_COMP_LEQUAL: desc += "ZLE:"; break;
312
case GE_COMP_GREATER: desc += "ZGT:"; break;
313
case GE_COMP_GEQUAL: desc += "ZGE:"; break;
314
}
315
}
316
if (id.depthWrite && !id.clearMode)
317
desc += "ZWr:";
318
319
if (id.colorTest && !id.clearMode)
320
desc += "CTest:";
321
322
if (id.stencilTest && !id.clearMode) {
323
switch (id.StencilTestFunc()) {
324
case GE_COMP_NEVER: desc += "SNever"; break;
325
case GE_COMP_ALWAYS: desc += "SAlways"; break;
326
case GE_COMP_EQUAL: desc += "SEQ"; break;
327
case GE_COMP_NOTEQUAL: desc += "SNE"; break;
328
case GE_COMP_LESS: desc += "SLT"; break;
329
case GE_COMP_LEQUAL: desc += "SLE"; break;
330
case GE_COMP_GREATER: desc += "SGT"; break;
331
case GE_COMP_GEQUAL: desc += "SGE"; break;
332
}
333
if (id.hasStencilTestMask)
334
desc += "Msk";
335
if (id.StencilTestFunc() != GE_COMP_ALWAYS || id.DepthTestFunc() != GE_COMP_ALWAYS)
336
desc += StringFromFormat("%02X:", id.stencilTestRef);
337
} else if (id.hasStencilTestMask || id.stencilTestRef != 0 || id.stencilTestFunc != 0) {
338
desc = "INVALID:" + desc;
339
}
340
341
switch (id.SFail()) {
342
case GE_STENCILOP_KEEP: break;
343
case GE_STENCILOP_ZERO: desc += "STstF0:"; break;
344
case GE_STENCILOP_REPLACE: desc += "STstFRpl:"; break;
345
case GE_STENCILOP_INVERT: desc += "STstFXor:"; break;
346
case GE_STENCILOP_INCR: desc += "STstFInc:"; break;
347
case GE_STENCILOP_DECR: desc += "STstFDec:"; break;
348
}
349
switch (id.ZFail()) {
350
case GE_STENCILOP_KEEP: break;
351
case GE_STENCILOP_ZERO: desc += "ZTstF0:"; break;
352
case GE_STENCILOP_REPLACE: desc += "ZTstFRpl:"; break;
353
case GE_STENCILOP_INVERT: desc += "ZTstFXor:"; break;
354
case GE_STENCILOP_INCR: desc += "ZTstFInc:"; break;
355
case GE_STENCILOP_DECR: desc += "ZTstFDec:"; break;
356
}
357
if (id.StencilTestFunc() == GE_COMP_ALWAYS && id.DepthTestFunc() == GE_COMP_ALWAYS) {
358
switch (id.ZPass()) {
359
case GE_STENCILOP_KEEP: break;
360
case GE_STENCILOP_ZERO: desc += "Zero:"; break;
361
case GE_STENCILOP_REPLACE: desc += StringFromFormat("Rpl%02X:", id.stencilTestRef); break;
362
case GE_STENCILOP_INVERT: desc += "Xor:"; break;
363
case GE_STENCILOP_INCR: desc += "Inc:"; break;
364
case GE_STENCILOP_DECR: desc += "Dec:"; break;
365
}
366
} else {
367
switch (id.ZPass()) {
368
case GE_STENCILOP_KEEP: break;
369
case GE_STENCILOP_ZERO: desc += "ZTstT0:"; break;
370
case GE_STENCILOP_REPLACE: desc += "ZTstTRpl:"; break;
371
case GE_STENCILOP_INVERT: desc += "ZTstTXor:"; break;
372
case GE_STENCILOP_INCR: desc += "ZTstTInc:"; break;
373
case GE_STENCILOP_DECR: desc += "ZTstTDec:"; break;
374
}
375
}
376
if (!id.stencilTest || id.clearMode) {
377
if (id.sFail != 0 || id.zFail != 0 || id.zPass != 0)
378
desc = "INVALID:" + desc;
379
}
380
381
if (id.alphaBlend) {
382
if (id.clearMode)
383
desc = "INVALID:" + desc;
384
switch (id.AlphaBlendEq()) {
385
case GE_BLENDMODE_MUL_AND_ADD: desc += "BlendAdd<"; break;
386
case GE_BLENDMODE_MUL_AND_SUBTRACT: desc += "BlendSub<"; break;
387
case GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE: desc += "BlendRSub<"; break;
388
case GE_BLENDMODE_MIN: desc += "BlendMin<"; break;
389
case GE_BLENDMODE_MAX: desc += "BlendMax<"; break;
390
case GE_BLENDMODE_ABSDIFF: desc += "BlendDiff<"; break;
391
}
392
switch (id.AlphaBlendSrc()) {
393
case PixelBlendFactor::OTHERCOLOR: desc += "DstRGB,"; break;
394
case PixelBlendFactor::INVOTHERCOLOR: desc += "1-DstRGB,"; break;
395
case PixelBlendFactor::SRCALPHA: desc += "SrcA,"; break;
396
case PixelBlendFactor::INVSRCALPHA: desc += "1-SrcA,"; break;
397
case PixelBlendFactor::DSTALPHA: desc += "DstA,"; break;
398
case PixelBlendFactor::INVDSTALPHA: desc += "1-DstA,"; break;
399
case PixelBlendFactor::DOUBLESRCALPHA: desc += "2*SrcA,"; break;
400
case PixelBlendFactor::DOUBLEINVSRCALPHA: desc += "1-2*SrcA,"; break;
401
case PixelBlendFactor::DOUBLEDSTALPHA: desc += "2*DstA,"; break;
402
case PixelBlendFactor::DOUBLEINVDSTALPHA: desc += "1-2*DstA,"; break;
403
case PixelBlendFactor::FIX: desc += "Fix,"; break;
404
case PixelBlendFactor::ZERO: desc += "0,"; break;
405
case PixelBlendFactor::ONE: desc += "1,"; break;
406
}
407
switch (id.AlphaBlendDst()) {
408
case PixelBlendFactor::OTHERCOLOR: desc += "SrcRGB>:"; break;
409
case PixelBlendFactor::INVOTHERCOLOR: desc += "1-SrcRGB>:"; break;
410
case PixelBlendFactor::SRCALPHA: desc += "SrcA>:"; break;
411
case PixelBlendFactor::INVSRCALPHA: desc += "1-SrcA>:"; break;
412
case PixelBlendFactor::DSTALPHA: desc += "DstA>:"; break;
413
case PixelBlendFactor::INVDSTALPHA: desc += "1-DstA>:"; break;
414
case PixelBlendFactor::DOUBLESRCALPHA: desc += "2*SrcA>:"; break;
415
case PixelBlendFactor::DOUBLEINVSRCALPHA: desc += "1-2*SrcA>:"; break;
416
case PixelBlendFactor::DOUBLEDSTALPHA: desc += "2*DstA>:"; break;
417
case PixelBlendFactor::DOUBLEINVDSTALPHA: desc += "1-2*DstA>:"; break;
418
case PixelBlendFactor::FIX: desc += "Fix>:"; break;
419
case PixelBlendFactor::ZERO: desc += "0>:"; break;
420
case PixelBlendFactor::ONE: desc += "1>:"; break;
421
}
422
} else if (id.alphaBlendEq != 0 || id.alphaBlendSrc != 0 || id.alphaBlendDst != 0) {
423
desc = "INVALID:" + desc;
424
}
425
426
if (id.applyLogicOp)
427
desc += "Logic:";
428
else if (id.clearMode)
429
desc = "INVALID:" + desc;
430
if (id.applyFog)
431
desc += "Fog:";
432
else if (id.clearMode)
433
desc = "INVALID:" + desc;
434
435
if (desc.empty())
436
return "INVALID";
437
desc.resize(desc.size() - 1);
438
return desc;
439
}
440
441
void ComputeSamplerID(SamplerID *id_out) {
442
SamplerID id{};
443
444
id.useStandardBufw = true;
445
id.overReadSafe = true;
446
int maxLevel = gstate.isMipmapEnabled() ? gstate.getTextureMaxLevel() : 0;
447
GETextureFormat fmt = gstate.getTextureFormat();
448
for (int i = 0; i <= maxLevel; ++i) {
449
uint32_t addr = gstate.getTextureAddress(i);
450
if (!Memory::IsValidAddress(addr))
451
id.hasInvalidPtr = true;
452
453
int bufw = GetTextureBufw(i, addr, fmt);
454
int bitspp = textureBitsPerPixel[fmt];
455
// We use a 16 byte minimum for all small bufws, so allow those as standard.
456
int w = gstate.getTextureWidth(i);
457
if (bitspp == 0 || std::max(w, 128 / bitspp) != bufw)
458
id.useStandardBufw = false;
459
// TODO: Verify 16 bit bufw align handling in DXT.
460
if (fmt >= GE_TFMT_DXT1 && w != bufw)
461
id.useStandardBufw = false;
462
463
int h = gstate.getTextureHeight(i);
464
int bytes = h * (bufw * bitspp) / 8;
465
if (bitspp < 32 && !Memory::IsValidAddress(addr + bytes + (32 - bitspp) / 8))
466
id.overReadSafe = false;
467
468
id.cached.sizes[i].w = w;
469
id.cached.sizes[i].h = h;
470
}
471
// TODO: What specifically happens if these are above 11?
472
id.width0Shift = gstate.texsize[0] & 0xF;
473
id.height0Shift = (gstate.texsize[0] >> 8) & 0xF;
474
id.hasAnyMips = maxLevel != 0;
475
476
id.texfmt = fmt;
477
id.swizzle = gstate.isTextureSwizzled();
478
// Only CLUT4 can use separate CLUTs per mimap.
479
id.useSharedClut = fmt != GE_TFMT_CLUT4 || maxLevel == 0 || !gstate.isMipmapEnabled() || gstate.isClutSharedForMipmaps();
480
if (gstate.isTextureFormatIndexed()) {
481
id.clutfmt = gstate.getClutPaletteFormat();
482
id.hasClutMask = gstate.getClutIndexMask() != 0xFF;
483
id.hasClutShift = gstate.getClutIndexShift() != 0;
484
id.hasClutOffset = gstate.getClutIndexStartPos() != 0;
485
id.cached.clutFormat = gstate.clutformat;
486
}
487
488
id.clampS = gstate.isTexCoordClampedS();
489
id.clampT = gstate.isTexCoordClampedT();
490
491
id.useTextureAlpha = gstate.isTextureAlphaUsed();
492
id.useColorDoubling = gstate.isColorDoublingEnabled();
493
id.texFunc = gstate.getTextureFunction();
494
if (id.texFunc > GE_TEXFUNC_ADD)
495
id.texFunc = GE_TEXFUNC_ADD;
496
497
if (id.texFunc == GE_TEXFUNC_BLEND)
498
id.cached.texBlendColor = gstate.getTextureEnvColRGB();
499
500
*id_out = id;
501
}
502
503
std::string DescribeSamplerID(const SamplerID &id) {
504
std::string name;
505
switch (id.TexFmt()) {
506
case GE_TFMT_5650: name = "5650"; break;
507
case GE_TFMT_5551: name = "5551"; break;
508
case GE_TFMT_4444: name = "4444"; break;
509
case GE_TFMT_8888: name = "8888"; break;
510
case GE_TFMT_CLUT4: name = "CLUT4"; break;
511
case GE_TFMT_CLUT8: name = "CLUT8"; break;
512
case GE_TFMT_CLUT16: name = "CLUT16"; break;
513
case GE_TFMT_CLUT32: name = "CLUT32"; break;
514
case GE_TFMT_DXT1: name = "DXT1"; break;
515
case GE_TFMT_DXT3: name = "DXT3"; break;
516
case GE_TFMT_DXT5: name = "DXT5"; break;
517
default: name = "INVALID"; break;
518
}
519
switch (id.ClutFmt()) {
520
case GE_CMODE_16BIT_BGR5650:
521
switch (id.TexFmt()) {
522
case GE_TFMT_CLUT4:
523
case GE_TFMT_CLUT8:
524
case GE_TFMT_CLUT16:
525
case GE_TFMT_CLUT32:
526
name += ":C5650";
527
break;
528
default:
529
// Ignore 0 clutfmt when no clut.
530
break;
531
}
532
break;
533
case GE_CMODE_16BIT_ABGR5551: name += ":C5551"; break;
534
case GE_CMODE_16BIT_ABGR4444: name += ":C4444"; break;
535
case GE_CMODE_32BIT_ABGR8888: name += ":C8888"; break;
536
}
537
if (id.swizzle) {
538
name += ":SWZ";
539
}
540
if (!id.useSharedClut) {
541
name += ":CMIP";
542
}
543
if (id.hasInvalidPtr) {
544
name += ":INV";
545
}
546
if (id.hasClutMask) {
547
name += ":CMASK";
548
}
549
if (id.hasClutShift) {
550
name += ":CSHF";
551
}
552
if (id.hasClutOffset) {
553
name += ":COFF";
554
}
555
if (id.clampS || id.clampT) {
556
name += std::string(":CL") + (id.clampS ? "S" : "") + (id.clampT ? "T" : "");
557
}
558
if (!id.useStandardBufw) {
559
name += ":BUFW";
560
}
561
if (!id.overReadSafe) {
562
name += ":XRD";
563
}
564
if (id.hasAnyMips) {
565
name += ":MIP";
566
}
567
if (id.linear) {
568
name += ":LERP";
569
}
570
if (id.fetch) {
571
name += ":FETCH";
572
}
573
if (id.useTextureAlpha) {
574
name += ":A";
575
}
576
if (id.useColorDoubling) {
577
name += ":DBL";
578
}
579
switch (id.texFunc) {
580
case GE_TEXFUNC_MODULATE:
581
name += ":MOD";
582
break;
583
case GE_TEXFUNC_DECAL:
584
name += ":DECAL";
585
break;
586
case GE_TEXFUNC_BLEND:
587
name += ":BLEND";
588
break;
589
case GE_TEXFUNC_REPLACE:
590
break;
591
case GE_TEXFUNC_ADD:
592
name += ":ADD";
593
default:
594
break;
595
}
596
name += StringFromFormat(":W%dH%d", 1 << id.width0Shift, 1 << id.height0Shift);
597
if (id.width0Shift > 10 || id.height0Shift > 10)
598
name = "INVALID:" + name;
599
600
return name;
601
}
602
603