1
// Copyright (c) 2012- PPSSPP Project.
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.
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.
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
19
// Alpha/stencil is a convoluted mess. Some good comments are here:
20
// https://github.com/hrydgard/ppsspp/issues/3768
23
#include "StateMapping.h"
24
#include "profiler/profiler.h"
26
#include "GPU/Math3D.h"
27
#include "GPU/GPUState.h"
28
#include "GPU/ge_constants.h"
29
#include "Core/System.h"
30
#include "Core/Config.h"
31
#include "Core/Reporting.h"
32
#include "GPU/GLES/GLES_GPU.h"
33
#include "GPU/GLES/GLStateCache.h"
34
#include "GPU/GLES/ShaderManager.h"
35
#include "GPU/GLES/TextureCache.h"
36
#include "GPU/GLES/Framebuffer.h"
37
#include "GPU/GLES/FragmentShaderGenerator.h"
39
static const GLushort aLookup[11] = {
41
GL_ONE_MINUS_DST_COLOR,
43
GL_ONE_MINUS_SRC_ALPHA,
45
GL_ONE_MINUS_DST_ALPHA,
46
GL_SRC_ALPHA, // GE_SRCBLEND_DOUBLESRCALPHA
47
GL_ONE_MINUS_SRC_ALPHA, // GE_SRCBLEND_DOUBLEINVSRCALPHA
48
GL_DST_ALPHA, // GE_SRCBLEND_DOUBLEDSTALPHA
49
GL_ONE_MINUS_DST_ALPHA, // GE_SRCBLEND_DOUBLEINVDSTALPHA
50
GL_CONSTANT_COLOR, // FIXA
53
static const GLushort bLookup[11] = {
55
GL_ONE_MINUS_SRC_COLOR,
57
GL_ONE_MINUS_SRC_ALPHA,
59
GL_ONE_MINUS_DST_ALPHA,
60
GL_SRC_ALPHA, // GE_DSTBLEND_DOUBLESRCALPHA
61
GL_ONE_MINUS_SRC_ALPHA, // GE_DSTBLEND_DOUBLEINVSRCALPHA
62
GL_DST_ALPHA, // GE_DSTBLEND_DOUBLEDSTALPHA
63
GL_ONE_MINUS_DST_ALPHA, // GE_DSTBLEND_DOUBLEINVDSTALPHA
64
GL_CONSTANT_COLOR, // FIXB
67
static const GLushort eqLookupNoMinMax[] = {
70
GL_FUNC_REVERSE_SUBTRACT,
71
GL_FUNC_ADD, // GE_BLENDMODE_MIN
72
GL_FUNC_ADD, // GE_BLENDMODE_MAX
73
GL_FUNC_ADD, // GE_BLENDMODE_ABSDIFF
76
static const GLushort eqLookup[] = {
79
GL_FUNC_REVERSE_SUBTRACT,
81
GL_MIN_EXT, // GE_BLENDMODE_MIN
82
GL_MAX_EXT, // GE_BLENDMODE_MAX
83
GL_MAX_EXT, // GE_BLENDMODE_ABSDIFF
85
GL_MIN, // GE_BLENDMODE_MIN
86
GL_MAX, // GE_BLENDMODE_MAX
87
GL_MAX, // GE_BLENDMODE_ABSDIFF
91
static const GLushort cullingMode[] = {
96
static const GLushort ztests[] = {
97
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL,
98
GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL,
101
static const GLushort stencilOps[] = {
112
#if !defined(USING_GLES2)
113
static const GLushort logicOps[] = {
133
static GLenum toDualSource(GLenum blendfunc) {
135
#if !defined(USING_GLES2) // TODO: Remove when we have better headers
137
return GL_SRC1_ALPHA;
138
case GL_ONE_MINUS_SRC_ALPHA:
139
return GL_ONE_MINUS_SRC1_ALPHA;
146
static GLenum blendColor2Func(u32 fix, bool &approx) {
152
// Otherwise, it's approximate if we pick ONE/ZERO.
155
const Vec3f fix3 = Vec3f::FromRGB(fix);
156
if (fix3.x >= 0.99 && fix3.y >= 0.99 && fix3.z >= 0.99)
158
else if (fix3.x <= 0.01 && fix3.y <= 0.01 && fix3.z <= 0.01)
160
return GL_INVALID_ENUM;
163
static inline bool blendColorSimilar(const Vec3f &a, const Vec3f &b, float margin = 0.1f) {
164
const Vec3f diff = a - b;
165
if (fabsf(diff.x) <= margin && fabsf(diff.y) <= margin && fabsf(diff.z) <= margin)
170
bool TransformDrawEngine::ApplyShaderBlending() {
171
if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) {
175
static const int MAX_REASONABLE_BLITS_PER_FRAME = 24;
177
static int lastFrameBlit = -1;
178
static int blitsThisFrame = 0;
179
if (lastFrameBlit != gpuStats.numFlips) {
180
if (blitsThisFrame > MAX_REASONABLE_BLITS_PER_FRAME) {
181
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());
184
lastFrameBlit = gpuStats.numFlips;
187
if (blitsThisFrame > MAX_REASONABLE_BLITS_PER_FRAME * 2) {
188
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());
189
ResetShaderBlending();
193
fboTexNeedBind_ = true;
195
shaderManager_->DirtyUniform(DIRTY_SHADERBLEND);
199
inline void TransformDrawEngine::ResetShaderBlending() {
200
// Wait - what does this have to do with FBOs?
202
glActiveTexture(GL_TEXTURE1);
203
glBindTexture(GL_TEXTURE_2D, 0);
204
glActiveTexture(GL_TEXTURE0);
205
fboTexBound_ = false;
209
// Try to simulate some common logic ops.
210
void TransformDrawEngine::ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil) {
211
StencilValueType stencilType = STENCIL_VALUE_KEEP;
212
if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) {
213
stencilType = ReplaceAlphaWithStencilType();
216
// Normally, we would add src + 0, but the logic op may have us do differently.
217
GLenum srcBlend = GL_ONE;
218
GLenum dstBlend = GL_ZERO;
219
GLenum blendOp = GL_FUNC_ADD;
221
if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
222
if (gstate.isLogicOpEnabled()) {
223
switch (gstate.getLogicOp())
229
case GE_LOGIC_AND_REVERSE:
230
WARN_LOG_REPORT_ONCE(d3dLogicOpAnd, G3D, "Unsupported AND logic op: %x", gstate.getLogicOp());
233
// This is the same as off.
235
case GE_LOGIC_COPY_INVERTED:
236
// Handled in the shader.
238
case GE_LOGIC_AND_INVERTED:
242
// Handled in the shader.
243
WARN_LOG_REPORT_ONCE(d3dLogicOpAndInverted, G3D, "Attempted invert for logic op: %x", gstate.getLogicOp());
245
case GE_LOGIC_INVERTED:
248
blendOp = GL_FUNC_SUBTRACT;
249
WARN_LOG_REPORT_ONCE(d3dLogicOpInverted, G3D, "Attempted inverse for logic op: %x", gstate.getLogicOp());
256
WARN_LOG_REPORT_ONCE(d3dLogicOpOrXor, G3D, "Unsupported XOR logic op: %x", gstate.getLogicOp());
259
case GE_LOGIC_OR_INVERTED:
260
// Inverted in shader.
262
WARN_LOG_REPORT_ONCE(d3dLogicOpOr, G3D, "Attempted or for logic op: %x", gstate.getLogicOp());
264
case GE_LOGIC_OR_REVERSE:
265
WARN_LOG_REPORT_ONCE(d3dLogicOpOrReverse, G3D, "Unsupported OR REVERSE logic op: %x", gstate.getLogicOp());
269
WARN_LOG_REPORT_ONCE(d3dLogicOpSet, G3D, "Attempted set for logic op: %x", gstate.getLogicOp());
275
// We're not blending, but we may still want to blend for stencil.
276
// This is only useful for INCR/DECR/INVERT. Others can write directly.
277
switch (stencilType) {
278
case STENCIL_VALUE_INCR_4:
279
case STENCIL_VALUE_INCR_8:
280
// We'll add the incremented value output by the shader.
281
glstate.blendFuncSeparate.set(srcBlend, dstBlend, GL_ONE, GL_ONE);
282
glstate.blendEquationSeparate.set(blendOp, GL_FUNC_ADD);
283
glstate.blend.enable();
286
case STENCIL_VALUE_DECR_4:
287
case STENCIL_VALUE_DECR_8:
288
// We'll subtract the incremented value output by the shader.
289
glstate.blendFuncSeparate.set(srcBlend, dstBlend, GL_ONE, GL_ONE);
290
glstate.blendEquationSeparate.set(blendOp, GL_FUNC_SUBTRACT);
291
glstate.blend.enable();
294
case STENCIL_VALUE_INVERT:
295
// The shader will output one, and reverse subtracting will essentially invert.
296
glstate.blendFuncSeparate.set(srcBlend, dstBlend, GL_ONE, GL_ONE);
297
glstate.blendEquationSeparate.set(blendOp, GL_FUNC_REVERSE_SUBTRACT);
298
glstate.blend.enable();
302
if (srcBlend == GL_ONE && dstBlend == GL_ZERO && blendOp == GL_FUNC_ADD) {
303
glstate.blend.disable();
305
glstate.blendFuncSeparate.set(srcBlend, dstBlend, GL_ONE, GL_ZERO);
306
glstate.blendEquationSeparate.set(blendOp, GL_FUNC_ADD);
307
glstate.blend.enable();
313
// Called even if AlphaBlendEnable == false - it also deals with stencil-related blend state.
315
void TransformDrawEngine::ApplyBlendState() {
316
// Blending is a bit complex to emulate. This is due to several reasons:
318
// * Doubled blend modes (src, dst, inversed) aren't supported in OpenGL.
319
// If possible, we double the src color or src alpha in the shader to account for these.
320
// These may clip incorrectly, so we avoid unfortunately.
321
// * OpenGL only has one arbitrary fixed color. We premultiply the other in the shader.
322
// * The written output alpha should actually be the stencil value. Alpha is not written.
324
// If we can't apply blending, we make a copy of the framebuffer and do it manually.
325
gstate_c.allowShaderBlend = !g_Config.bDisableSlowFramebufEffects;
327
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowShaderBlend);
328
ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend);
329
bool usePreSrc = false;
331
switch (replaceBlend) {
332
case REPLACE_BLEND_NO:
333
ResetShaderBlending();
334
// We may still want to do something about stencil -> alpha.
335
ApplyStencilReplaceAndLogicOp(replaceAlphaWithStencil);
338
case REPLACE_BLEND_COPY_FBO:
339
if (ApplyShaderBlending()) {
340
// We may still want to do something about stencil -> alpha.
341
ApplyStencilReplaceAndLogicOp(replaceAlphaWithStencil);
344
// Until next time, force it off.
345
gstate_c.allowShaderBlend = false;
348
case REPLACE_BLEND_PRE_SRC:
349
case REPLACE_BLEND_PRE_SRC_2X_ALPHA:
353
case REPLACE_BLEND_STANDARD:
354
case REPLACE_BLEND_2X_ALPHA:
355
case REPLACE_BLEND_2X_SRC:
359
glstate.blend.enable();
360
ResetShaderBlending();
362
const GEBlendMode blendFuncEq = gstate.getBlendEq();
363
int blendFuncA = gstate.getBlendFuncA();
364
int blendFuncB = gstate.getBlendFuncB();
365
const u32 fixA = gstate.getFixA();
366
const u32 fixB = gstate.getFixB();
368
if (blendFuncA > GE_SRCBLEND_FIXA)
369
blendFuncA = GE_SRCBLEND_FIXA;
370
if (blendFuncB > GE_DSTBLEND_FIXB)
371
blendFuncB = GE_DSTBLEND_FIXB;
373
float constantAlpha = 1.0f;
374
GLenum constantAlphaGL = GL_ONE;
375
if (gstate.isStencilTestEnabled() && replaceAlphaWithStencil == REPLACE_ALPHA_NO) {
376
switch (ReplaceAlphaWithStencilType()) {
377
case STENCIL_VALUE_UNIFORM:
378
constantAlpha = (float) gstate.getStencilTestRef() * (1.0f / 255.0f);
381
case STENCIL_VALUE_INCR_4:
382
case STENCIL_VALUE_DECR_4:
383
constantAlpha = 1.0f / 15.0f;
386
case STENCIL_VALUE_INCR_8:
387
case STENCIL_VALUE_DECR_8:
388
constantAlpha = 1.0f / 255.0f;
395
// Otherwise it will stay GL_ONE.
396
if (constantAlpha <= 0.0f) {
397
constantAlphaGL = GL_ZERO;
398
} else if (constantAlpha < 1.0f) {
399
constantAlphaGL = GL_CONSTANT_ALPHA;
403
// Shortcut by using GL_ONE where possible, no need to set blendcolor
404
bool approxFuncA = false;
405
GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? blendColor2Func(fixA, approxFuncA) : aLookup[blendFuncA];
406
bool approxFuncB = false;
407
GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? blendColor2Func(fixB, approxFuncB) : bLookup[blendFuncB];
410
glBlendFuncA = GL_ONE;
411
// Need to pull in the fixed color.
412
if (blendFuncA == GE_SRCBLEND_FIXA) {
413
shaderManager_->DirtyUniform(DIRTY_SHADERBLEND);
417
if (replaceAlphaWithStencil == REPLACE_ALPHA_DUALSOURCE && gstate_c.Supports(GPU_SUPPORTS_DUALSOURCE_BLEND)) {
418
glBlendFuncA = toDualSource(glBlendFuncA);
419
glBlendFuncB = toDualSource(glBlendFuncB);
422
auto setBlendColorv = [&](const Vec3f &c) {
423
const float blendColor[4] = {c.x, c.y, c.z, constantAlpha};
424
glstate.blendColor.set(blendColor);
426
auto defaultBlendColor = [&]() {
427
if (constantAlphaGL == GL_CONSTANT_ALPHA) {
428
const float blendColor[4] = {1.0f, 1.0f, 1.0f, constantAlpha};
429
glstate.blendColor.set(blendColor);
433
if (blendFuncA == GE_SRCBLEND_FIXA || blendFuncB == GE_DSTBLEND_FIXB) {
434
const Vec3f fixAVec = Vec3f::FromRGB(fixA);
435
const Vec3f fixBVec = Vec3f::FromRGB(fixB);
436
if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB != GL_INVALID_ENUM) {
437
// Can use blendcolor trivially.
438
setBlendColorv(fixAVec);
439
glBlendFuncA = GL_CONSTANT_COLOR;
440
} else if (glBlendFuncA != GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) {
441
// Can use blendcolor trivially.
442
setBlendColorv(fixBVec);
443
glBlendFuncB = GL_CONSTANT_COLOR;
444
} else if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) {
445
if (blendColorSimilar(fixAVec, Vec3f::AssignToAll(1.0f) - fixBVec)) {
446
glBlendFuncA = GL_CONSTANT_COLOR;
447
glBlendFuncB = GL_ONE_MINUS_CONSTANT_COLOR;
448
setBlendColorv(fixAVec);
449
} else if (blendColorSimilar(fixAVec, fixBVec)) {
450
glBlendFuncA = GL_CONSTANT_COLOR;
451
glBlendFuncB = GL_CONSTANT_COLOR;
452
setBlendColorv(fixAVec);
454
DEBUG_LOG(G3D, "ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", fixA, fixB, blendFuncA, blendFuncB);
455
// Let's approximate, at least. Close is better than totally off.
456
const bool nearZeroA = blendColorSimilar(fixAVec, Vec3f::AssignToAll(0.0f), 0.25f);
457
const bool nearZeroB = blendColorSimilar(fixBVec, Vec3f::AssignToAll(0.0f), 0.25f);
458
if (nearZeroA || blendColorSimilar(fixAVec, Vec3f::AssignToAll(1.0f), 0.25f)) {
459
glBlendFuncA = nearZeroA ? GL_ZERO : GL_ONE;
460
glBlendFuncB = GL_CONSTANT_COLOR;
461
setBlendColorv(fixBVec);
463
// We need to pick something. Let's go with A as the fixed color.
464
glBlendFuncA = GL_CONSTANT_COLOR;
465
glBlendFuncB = nearZeroB ? GL_ZERO : GL_ONE;
466
setBlendColorv(fixAVec);
470
// We optimized both, but that's probably not necessary, so let's pick one to be constant.
471
if (blendFuncA == GE_SRCBLEND_FIXA && !usePreSrc && approxFuncA) {
472
glBlendFuncA = GL_CONSTANT_COLOR;
473
setBlendColorv(fixAVec);
474
} else if (approxFuncB) {
475
glBlendFuncB = GL_CONSTANT_COLOR;
476
setBlendColorv(fixBVec);
485
// Some Android devices (especially Mali, it seems) composite badly if there's alpha in the backbuffer.
486
// So in non-buffered rendering, we will simply consider the dest alpha to be zero in blending equations.
488
if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE) {
489
if (glBlendFuncA == GL_DST_ALPHA) glBlendFuncA = GL_ZERO;
490
if (glBlendFuncB == GL_DST_ALPHA) glBlendFuncB = GL_ZERO;
491
if (glBlendFuncA == GL_ONE_MINUS_DST_ALPHA) glBlendFuncA = GL_ONE;
492
if (glBlendFuncB == GL_ONE_MINUS_DST_ALPHA) glBlendFuncB = GL_ONE;
496
// At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set right somehow.
498
// The stencil-to-alpha in fragment shader doesn't apply here (blending is enabled), and we shouldn't
499
// do any blending in the alpha channel as that doesn't seem to happen on PSP. So, we attempt to
500
// apply the stencil to the alpha, since that's what should be stored.
501
GLenum alphaEq = GL_FUNC_ADD;
502
if (replaceAlphaWithStencil != REPLACE_ALPHA_NO) {
503
// Let the fragment shader take care of it.
504
switch (ReplaceAlphaWithStencilType()) {
505
case STENCIL_VALUE_INCR_4:
506
case STENCIL_VALUE_INCR_8:
507
// We'll add the increment value.
508
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ONE);
511
case STENCIL_VALUE_DECR_4:
512
case STENCIL_VALUE_DECR_8:
513
// Like add with a small value, but subtracting.
514
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ONE);
515
alphaEq = GL_FUNC_SUBTRACT;
518
case STENCIL_VALUE_INVERT:
519
// This will subtract by one, effectively inverting the bits.
520
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ONE);
521
alphaEq = GL_FUNC_REVERSE_SUBTRACT;
525
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ZERO);
528
} else if (gstate.isStencilTestEnabled()) {
529
switch (ReplaceAlphaWithStencilType()) {
530
case STENCIL_VALUE_KEEP:
531
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ONE);
533
case STENCIL_VALUE_ONE:
534
// This won't give one but it's our best shot...
535
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ONE);
537
case STENCIL_VALUE_ZERO:
538
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ZERO);
540
case STENCIL_VALUE_UNIFORM:
541
// This won't give a correct value (it multiplies) but it may be better than random values.
542
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, constantAlphaGL, GL_ZERO);
544
case STENCIL_VALUE_INCR_4:
545
case STENCIL_VALUE_INCR_8:
546
// This won't give a correct value always, but it will try to increase at least.
547
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, constantAlphaGL, GL_ONE);
549
case STENCIL_VALUE_DECR_4:
550
case STENCIL_VALUE_DECR_8:
551
// This won't give a correct value always, but it will try to decrease at least.
552
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, constantAlphaGL, GL_ONE);
553
alphaEq = GL_FUNC_SUBTRACT;
555
case STENCIL_VALUE_INVERT:
556
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ONE);
557
// If the output alpha is near 1, this will basically invert. It's our best shot.
558
alphaEq = GL_FUNC_REVERSE_SUBTRACT;
562
// Retain the existing value when stencil testing is off.
563
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ONE);
566
if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) {
567
glstate.blendEquationSeparate.set(eqLookup[blendFuncEq], alphaEq);
569
glstate.blendEquationSeparate.set(eqLookupNoMinMax[blendFuncEq], alphaEq);
573
void TransformDrawEngine::ApplyDrawState(int prim) {
575
// TODO: All this setup is soon so expensive that we'll need dirty flags, or simply do it in the command writes where we detect dirty by xoring. Silly to do all this work on every drawcall.
577
if (gstate_c.textureChanged != TEXCHANGE_UNCHANGED && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
578
textureCache_->SetTexture();
579
gstate_c.textureChanged = TEXCHANGE_UNCHANGED;
580
if (gstate_c.needShaderTexClamp) {
581
// We will rarely need to set this, so let's do it every time on use rather than in runloop.
582
// Most of the time non-framebuffer textures will be used which can be clamped themselves.
583
shaderManager_->DirtyUniform(DIRTY_TEXCLAMP);
587
// Start profiling here to skip SetTexture which is already accounted for
588
PROFILE_THIS_SCOPE("applydrawstate");
590
// Set blend - unless we need to do it in the shader.
593
bool alwaysDepthWrite = g_Config.bAlwaysDepthWrite;
594
bool enableStencilTest = !g_Config.bDisableStencilTest;
597
if (gstate.isDitherEnabled()) {
598
glstate.dither.enable();
599
glstate.dither.set(GL_TRUE);
601
glstate.dither.disable();
603
if (gstate.isModeClear()) {
605
if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
607
glstate.colorLogicOp.disable();
611
glstate.cullFace.disable();
614
glstate.depthTest.enable();
615
glstate.depthFunc.set(GL_ALWAYS);
616
glstate.depthWrite.set(gstate.isClearModeDepthMask() || alwaysDepthWrite ? GL_TRUE : GL_FALSE);
617
if (gstate.isClearModeDepthMask() || alwaysDepthWrite) {
618
framebufferManager_->SetDepthUpdated();
622
bool colorMask = gstate.isClearModeColorMask();
623
bool alphaMask = gstate.isClearModeAlphaMask();
624
glstate.colorMask.set(colorMask, colorMask, colorMask, alphaMask);
627
if (alphaMask && enableStencilTest) {
628
glstate.stencilTest.enable();
629
glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE);
630
// TODO: In clear mode, the stencil value is set to the alpha value of the vertex.
631
// A normal clear will be 2 points, the second point has the color.
632
// We should set "ref" to that value instead of 0.
633
// In case of clear rectangles, we set it again once we know what the color is.
634
glstate.stencilFunc.set(GL_ALWAYS, 255, 0xFF);
635
glstate.stencilMask.set(0xFF);
637
glstate.stencilTest.disable();
641
if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
642
// TODO: Make this dynamic
644
if (gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) {
645
glstate.colorLogicOp.enable();
646
glstate.logicOp.set(logicOps[gstate.getLogicOp()]);
648
glstate.colorLogicOp.disable();
653
bool cullEnabled = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
655
glstate.cullFace.enable();
656
glstate.cullFaceMode.set(cullingMode[gstate.getCullMode()]);
658
glstate.cullFace.disable();
662
if (gstate.isDepthTestEnabled()) {
663
glstate.depthTest.enable();
664
glstate.depthFunc.set(ztests[gstate.getDepthTestFunction()]);
665
glstate.depthWrite.set(gstate.isDepthWriteEnabled() || alwaysDepthWrite ? GL_TRUE : GL_FALSE);
666
if (gstate.isDepthWriteEnabled() || alwaysDepthWrite) {
667
framebufferManager_->SetDepthUpdated();
670
glstate.depthTest.disable();
673
// PSP color/alpha mask is per bit but we can only support per byte.
674
// But let's do that, at least. And let's try a threshold.
675
bool rmask = (gstate.pmskc & 0xFF) < 128;
676
bool gmask = ((gstate.pmskc >> 8) & 0xFF) < 128;
677
bool bmask = ((gstate.pmskc >> 16) & 0xFF) < 128;
678
bool amask = (gstate.pmska & 0xFF) < 128;
680
u8 abits = (gstate.pmska >> 0) & 0xFF;
681
#ifndef MOBILE_DEVICE
682
u8 rbits = (gstate.pmskc >> 0) & 0xFF;
683
u8 gbits = (gstate.pmskc >> 8) & 0xFF;
684
u8 bbits = (gstate.pmskc >> 16) & 0xFF;
685
if ((rbits != 0 && rbits != 0xFF) || (gbits != 0 && gbits != 0xFF) || (bbits != 0 && bbits != 0xFF)) {
686
WARN_LOG_REPORT_ONCE(rgbmask, G3D, "Unsupported RGB mask: r=%02x g=%02x b=%02x", rbits, gbits, bbits);
688
if (abits != 0 && abits != 0xFF) {
689
// The stencil part of the mask is supported.
690
WARN_LOG_REPORT_ONCE(amask, G3D, "Unsupported alpha/stencil mask: %02x", abits);
694
// Let's not write to alpha if stencil isn't enabled.
695
if (!gstate.isStencilTestEnabled()) {
698
// If the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel.
699
if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) {
704
glstate.colorMask.set(rmask, gmask, bmask, amask);
707
if (gstate.isStencilTestEnabled() && enableStencilTest) {
708
glstate.stencilTest.enable();
709
glstate.stencilFunc.set(ztests[gstate.getStencilTestFunction()],
710
gstate.getStencilTestRef(),
711
gstate.getStencilTestMask());
712
glstate.stencilOp.set(stencilOps[gstate.getStencilOpSFail()], // stencil fail
713
stencilOps[gstate.getStencilOpZFail()], // depth fail
714
stencilOps[gstate.getStencilOpZPass()]); // depth pass
716
if (gstate.FrameBufFormat() == GE_FORMAT_5551) {
717
glstate.stencilMask.set(abits <= 0x7f ? 0xff : 0x00);
719
glstate.stencilMask.set(~abits);
722
glstate.stencilTest.disable();
726
bool throughmode = gstate.isModeThrough();
728
float renderWidthFactor, renderHeightFactor;
729
float renderWidth, renderHeight;
730
float renderX, renderY;
731
bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
732
if (useBufferedRendering) {
735
renderWidth = framebufferManager_->GetRenderWidth();
736
renderHeight = framebufferManager_->GetRenderHeight();
737
renderWidthFactor = (float)renderWidth / framebufferManager_->GetTargetBufferWidth();
738
renderHeightFactor = (float)renderHeight / framebufferManager_->GetTargetBufferHeight();
740
float pixelW = PSP_CoreParameter().pixelWidth;
741
float pixelH = PSP_CoreParameter().pixelHeight;
742
CenterRect(&renderX, &renderY, &renderWidth, &renderHeight, 480, 272, pixelW, pixelH, ROTATION_LOCKED_HORIZONTAL);
743
renderWidthFactor = renderWidth / 480.0f;
744
renderHeightFactor = renderHeight / 272.0f;
747
renderX += gstate_c.curRTOffsetX * renderWidthFactor;
750
int scissorX1 = gstate.getScissorX1();
751
int scissorY1 = gstate.getScissorY1();
752
int scissorX2 = gstate.getScissorX2() + 1;
753
int scissorY2 = gstate.getScissorY2() + 1;
755
// This is a bit of a hack as the render buffer isn't always that size
756
if (scissorX1 == 0 && scissorY1 == 0
757
&& scissorX2 >= (int) gstate_c.curRTWidth
758
&& scissorY2 >= (int) gstate_c.curRTHeight) {
759
glstate.scissorTest.disable();
761
glstate.scissorTest.enable();
762
glstate.scissorRect.set(
763
renderX + scissorX1 * renderWidthFactor,
764
renderY + renderHeight - (scissorY2 * renderHeightFactor),
765
(scissorX2 - scissorX1) * renderWidthFactor,
766
(scissorY2 - scissorY1) * renderHeightFactor);
770
int regionX1 = gstate.region1 & 0x3FF;
771
int regionY1 = (gstate.region1 >> 10) & 0x3FF;
772
int regionX2 = (gstate.region2 & 0x3FF) + 1;
773
int regionY2 = ((gstate.region2 >> 10) & 0x3FF) + 1;
777
int regionX2 = gstate_c.curRTWidth;
778
int regionY2 = gstate_c.curRTHeight;
780
float offsetX = gstate.getOffsetX();
781
float offsetY = gstate.getOffsetY();
784
// If the buffer is too large, offset the viewport to the top.
785
renderY += renderHeight - framebufferManager_->GetTargetHeight() * renderHeightFactor;
787
// No viewport transform here. Let's experiment with using region.
788
glstate.viewport.set(
789
renderX + (0 + regionX1) * renderWidthFactor,
790
renderY + (0 - regionY1) * renderHeightFactor,
791
(regionX2 - regionX1) * renderWidthFactor,
792
(regionY2 - regionY1) * renderHeightFactor);
793
glstate.depthRange.set(0.0f, 1.0f);
795
// These we can turn into a glViewport call, offset by offsetX and offsetY. Math after.
796
float vpXScale = gstate.getViewportXScale();
797
float vpXCenter = gstate.getViewportXCenter();
798
float vpYScale = gstate.getViewportYScale();
799
float vpYCenter = gstate.getViewportYCenter();
801
// The viewport transform appears to go like this:
802
// Xscreen = -offsetX + vpXCenter + vpXScale * Xview
803
// Yscreen = -offsetY + vpYCenter + vpYScale * Yview
804
// Zscreen = vpZCenter + vpZScale * Zview
806
// This means that to get the analogue glViewport we must:
807
float vpX0 = vpXCenter - offsetX - fabsf(vpXScale);
808
float vpY0 = vpYCenter - offsetY + fabsf(vpYScale); // Need to account for sign of Y
809
gstate_c.vpWidth = vpXScale * 2.0f;
810
gstate_c.vpHeight = -vpYScale * 2.0f;
812
float vpWidth = fabsf(gstate_c.vpWidth);
813
float vpHeight = fabsf(gstate_c.vpHeight);
815
vpX0 *= renderWidthFactor;
816
vpY0 *= renderHeightFactor;
817
vpWidth *= renderWidthFactor;
818
vpHeight *= renderHeightFactor;
820
// Flip vpY0 to match the OpenGL coordinate system.
821
vpY0 = renderHeight - vpY0;
823
// We used to apply the viewport here via glstate, but there are limits which vary by driver.
824
// This may mean some games won't work, or at least won't work at higher render resolutions.
825
// So we apply it in the shader instead.
826
float left = renderX + vpX0;
827
float bottom = renderY + vpY0;
828
float right = left + vpWidth;
829
float top = bottom + vpHeight;
832
float xOffset = 0.0f;
834
float yOffset = 0.0f;
836
// If we're within the bounds, we want clipping the viewport way. So leave it be.
837
if (left < 0.0f || right > renderWidth) {
838
float overageLeft = std::max(-left, 0.0f);
839
float overageRight = std::max(right - renderWidth, 0.0f);
840
// Our center drifted by the difference in overages.
841
float drift = overageRight - overageLeft;
844
right -= overageRight;
846
wScale = vpWidth / (right - left);
847
xOffset = drift / (right - left);
850
if (bottom < 0.0f || top > renderHeight) {
851
float overageBottom = std::max(-bottom, 0.0f);
852
float overageTop = std::max(top - renderHeight, 0.0f);
853
// Our center drifted by the difference in overages.
854
float drift = overageTop - overageBottom;
856
bottom += overageBottom;
859
hScale = vpHeight / (top - bottom);
860
yOffset = drift / (top - bottom);
863
bool scaleChanged = gstate_c.vpWidthScale != wScale || gstate_c.vpHeightScale != hScale;
864
bool offsetChanged = gstate_c.vpXOffset != xOffset || gstate_c.vpYOffset != yOffset;
865
if (scaleChanged || offsetChanged) {
866
gstate_c.vpWidthScale = wScale;
867
gstate_c.vpHeightScale = hScale;
868
gstate_c.vpXOffset = xOffset;
869
gstate_c.vpYOffset = yOffset;
870
shaderManager_->DirtyUniform(DIRTY_PROJMATRIX);
873
glstate.viewport.set(left, bottom, right - left, top - bottom);
875
float zScale = gstate.getViewportZScale();
876
float zCenter = gstate.getViewportZCenter();
877
float depthRangeMin = zCenter - zScale;
878
float depthRangeMax = zCenter + zScale;
879
glstate.depthRange.set(depthRangeMin * (1.0f / 65535.0f), depthRangeMax * (1.0f / 65535.0f));
881
#ifndef MOBILE_DEVICE
882
float minz = gstate.getDepthRangeMin() * (1.0f / 65535.0f);
883
float maxz = gstate.getDepthRangeMax() * (1.0f / 65535.0f);
884
if ((minz > depthRangeMin && minz > depthRangeMax) || (maxz < depthRangeMin && maxz < depthRangeMax)) {
885
WARN_LOG_REPORT_ONCE(minmaxz, G3D, "Unsupported depth range test - depth range: %f-%f, test: %f-%f", depthRangeMin, depthRangeMax, minz, maxz);
886
} else if ((gstate.clipEnable & 1) == 0) {
887
// TODO: Need to test whether clipEnable should even affect depth or not.
888
if ((minz < depthRangeMin && minz < depthRangeMax) || (maxz > depthRangeMin && maxz > depthRangeMax)) {
889
WARN_LOG_REPORT_ONCE(znoclip, G3D, "Unsupported depth range test without clipping - depth range: %f-%f, test: %f-%f", depthRangeMin, depthRangeMax, minz, maxz);
896
void TransformDrawEngine::ApplyDrawStateLate() {
897
// At this point, we know if the vertices are full alpha or not.
898
// TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
899
if (!gstate.isModeClear()) {
900
if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) {
901
fragmentTestCache_->BindTestTexture(GL_TEXTURE2);
904
textureCache_->ApplyTexture();
906
if (fboTexNeedBind_) {
907
framebufferManager_->BindFramebufferColor(GL_TEXTURE1, gstate.getFrameBufRawAddress(), nullptr, BINDFBCOLOR_MAY_COPY_WITH_UV);
908
framebufferManager_->RebindFramebuffer();
910
glActiveTexture(GL_TEXTURE1);
911
// If we are rendering at a higher resolution, linear is probably best for the dest color.
912
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
913
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
914
glActiveTexture(GL_TEXTURE0);
916
fboTexNeedBind_ = false;