diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index da74dc8751a8..16af7be7f945 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -236,17 +236,38 @@ static bool AlphaToColorDoubling() { if (!gstate.isAlphaBlendEnabled()) { return false; } - // 2x alpha in the source function and full alpha = source color doubling. - // If we see this, we don't really need to care about the dest alpha function - sure we can't handle - // the doubling dest ones, but there's nothing we can do about that. - if (gstate.getBlendFuncA() != GE_SRCBLEND_DOUBLESRCALPHA) { + // 2x alpha in the source function and not in the dest = source color doubling. + switch (gstate.getBlendFuncA()) { + case GE_SRCBLEND_DOUBLESRCALPHA: + case GE_SRCBLEND_DOUBLEINVSRCALPHA: + break; + + case GE_SRCBLEND_DOUBLEDSTALPHA: + case GE_SRCBLEND_DOUBLEINVDSTALPHA: + // Even dest alpha is safe, since we're moving the * 2.0 into the src color. + break; + + default: return false; } - if (gstate.getBlendFuncB() == GE_DSTBLEND_INVSRCALPHA) { - // If it's 1.0 or 0.0, then we can still color double (since 0.0 will blend out anyway.) - return (gstate_c.vertexFullAlpha && (gstate_c.textureSimpleAlpha || !gstate.isTextureAlphaUsed())); + switch (gstate.getBlendFuncB()) { + case GE_DSTBLEND_SRCCOLOR: + case GE_DSTBLEND_INVSRCCOLOR: + // Can't double, we need the source color to be correct. + return false; + + case GE_DSTBLEND_DOUBLESRCALPHA: + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + case GE_DSTBLEND_DOUBLEDSTALPHA: + case GE_DSTBLEND_DOUBLEINVDSTALPHA: + // Won't do the trick, would be better to double both sides. + return false; + + default: + // In all other cases, we're pre-multiplying the src side by 2. + // For example, src * (2.0 * a) + dst * fixB, we're just moving the 2.0. + return true; } - return (gstate_c.vertexFullAlpha && (gstate_c.textureFullAlpha || !gstate.isTextureAlphaUsed())); } static bool CanDoubleSrcBlendMode() { @@ -256,18 +277,17 @@ static bool CanDoubleSrcBlendMode() { int funcA = gstate.getBlendFuncA(); int funcB = gstate.getBlendFuncB(); - if (funcA != GE_SRCBLEND_DOUBLESRCALPHA) { + if (funcA != GE_SRCBLEND_DOUBLESRCALPHA && funcA != GE_SRCBLEND_DOUBLEINVSRCALPHA) { funcB = funcA; funcA = gstate.getBlendFuncB(); } - if (funcA != GE_SRCBLEND_DOUBLESRCALPHA) { + if (funcA != GE_SRCBLEND_DOUBLESRCALPHA && funcA != GE_SRCBLEND_DOUBLEINVSRCALPHA) { return false; } // One side should be doubled. Let's check the other side. - // LittleBigPlanet, for example, uses 2.0 * src, 1.0 - src, which can't double. - // Persona 2 uses the same function, which is the reason for its darkness. It only ever passes - // 1.0 as src alpha though, so in effect it's a color doubling. + // LittleBigPlanet and Persona 2, for example, uses 2.0 * src.a, 1.0 - src.a, which can't double. + // In that case, we can double the src rgb instead. switch (funcB) { case GE_DSTBLEND_SRCALPHA: case GE_DSTBLEND_INVSRCALPHA: @@ -291,8 +311,17 @@ bool ShouldUseShaderBlending() { GEBlendDstFactor funcB = gstate.getBlendFuncB(); GEBlendMode eq = gstate.getBlendEq(); - if (eq == GE_BLENDMODE_ABSDIFF) { + switch (eq) { + case GE_BLENDMODE_ABSDIFF: return true; + + case GE_BLENDMODE_MIN: + case GE_BLENDMODE_MAX: + // These don't use the factors. + return !gl_extensions.EXT_blend_minmax && !gl_extensions.GLES3; + + default: + break; } // This normally involves a blit, so try to skip it. @@ -335,6 +364,19 @@ bool ShouldUseShaderBlending() { return false; } +// Doesn't need to be in the shader id, ShouldUseShaderBlending contains all parts. +bool ShouldUseShaderFixedBlending() { + if (!ShouldUseShaderBlending()) { + return false; + } + + if (gstate.getBlendFuncA() == GE_SRCBLEND_FIXA && gstate.getBlendFuncB() == GE_DSTBLEND_FIXB) { + GEBlendMode blendEq = gstate.getBlendEq(); + return blendEq != GE_BLENDMODE_MIN && blendEq != GE_BLENDMODE_MAX && blendEq != GE_BLENDMODE_ABSDIFF; + } + return false; +} + // Here we must take all the bits of the gstate that determine what the fragment shader will // look like, and concatenate them together into an ID. void ComputeFragmentShaderID(FragmentShaderID *id) { @@ -349,10 +391,11 @@ void ComputeFragmentShaderID(FragmentShaderID *id) { bool enableAlphaTest = gstate.isAlphaTestEnabled() && !IsAlphaTestTriviallyTrue() && !g_Config.bDisableAlphaTest; bool alphaTestAgainstZero = gstate.getAlphaTestRef() == 0 && gstate.getAlphaTestMask() == 0xFF; bool enableColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue(); - bool alphaToColorDoubling = AlphaToColorDoubling(); + bool useShaderBlending = ShouldUseShaderBlending(); + bool alphaToColorDoubling = AlphaToColorDoubling() && !useShaderBlending; bool enableColorDoubling = (gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled()) || alphaToColorDoubling; // This isn't really correct, but it's a hack to get doubled blend modes to work more correctly. - bool enableAlphaDoubling = !alphaToColorDoubling && CanDoubleSrcBlendMode(); + bool enableAlphaDoubling = !alphaToColorDoubling && !useShaderBlending && CanDoubleSrcBlendMode(); bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; bool doTextureAlpha = gstate.isTextureAlphaUsed(); ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(); @@ -409,7 +452,7 @@ void ComputeFragmentShaderID(FragmentShaderID *id) { // 29 - 31 are free. - if (ShouldUseShaderBlending()) { + if (useShaderBlending) { // 12 bits total. id1 |= 1 << 0; id1 |= gstate.getBlendEq() << 1; @@ -500,10 +543,11 @@ void GenerateFragmentShader(char *buffer) { bool enableAlphaTest = gstate.isAlphaTestEnabled() && !IsAlphaTestTriviallyTrue() && !gstate.isModeClear() && !g_Config.bDisableAlphaTest; bool alphaTestAgainstZero = gstate.getAlphaTestRef() == 0 && gstate.getAlphaTestMask() == 0xFF; bool enableColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue() && !gstate.isModeClear(); - bool alphaToColorDoubling = AlphaToColorDoubling(); + bool useShaderBlending = ShouldUseShaderBlending(); + bool alphaToColorDoubling = AlphaToColorDoubling() && !useShaderBlending; bool enableColorDoubling = (gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled()) || alphaToColorDoubling; // This isn't really correct, but it's a hack to get doubled blend modes to work more correctly. - bool enableAlphaDoubling = !alphaToColorDoubling && CanDoubleSrcBlendMode(); + bool enableAlphaDoubling = !alphaToColorDoubling && !useShaderBlending && CanDoubleSrcBlendMode(); bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; bool doTextureAlpha = gstate.isTextureAlphaUsed(); bool textureAtOffset = gstate_c.curTextureXOffset != 0 || gstate_c.curTextureYOffset != 0; @@ -514,7 +558,7 @@ void GenerateFragmentShader(char *buffer) { if (doTexture) WRITE(p, "uniform sampler2D tex;\n"); - if (!gstate.isModeClear() && ShouldUseShaderBlending()) { + if (!gstate.isModeClear() && useShaderBlending) { if (!gl_extensions.NV_shader_framebuffer_fetch) { if (!gl_extensions.VersionGEThan(3, 0, 0) && !gl_extensions.GLES3) { WRITE(p, "uniform vec2 u_fbotexSize;\n"); @@ -776,7 +820,10 @@ void GenerateFragmentShader(char *buffer) { // WRITE(p, " v.x = v_depth;\n"); } - if (ShouldUseShaderBlending()) { + if (ShouldUseShaderFixedBlending()) { + // Just premultiply by u_blendFixA. + WRITE(p, " v.rgb = v.rgb * u_blendFixA;\n"); + } else if (useShaderBlending) { // If we have NV_shader_framebuffer_fetch / EXT_shader_framebuffer_fetch, we skip the blit. // We can just read the prev value more directly. // TODO: EXT_shader_framebuffer_fetch on iOS 6, possibly others. diff --git a/GPU/GLES/FragmentShaderGenerator.h b/GPU/GLES/FragmentShaderGenerator.h index 2357739d3728..c931c5efe562 100644 --- a/GPU/GLES/FragmentShaderGenerator.h +++ b/GPU/GLES/FragmentShaderGenerator.h @@ -63,3 +63,4 @@ bool IsColorTestTriviallyTrue(); StencilValueType ReplaceAlphaWithStencilType(); ReplaceAlphaType ReplaceAlphaWithStencil(); bool ShouldUseShaderBlending(); +bool ShouldUseShaderFixedBlending(); diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 6feb5e2ac34b..e103430b5fe5 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -563,8 +563,12 @@ void LinkedShader::UpdateUniforms(u32 vertType) { #endif if (dirty & DIRTY_SHADERBLEND) { - SetColorUniform3(u_blendFixA, gstate.getFixA()); - SetColorUniform3(u_blendFixB, gstate.getFixB()); + if (u_blendFixA != -1) { + SetColorUniform3(u_blendFixA, gstate.getFixA()); + } + if (u_blendFixB != -1) { + SetColorUniform3(u_blendFixB, gstate.getFixB()); + } const float fbotexSize[2] = { 1.0f / (float)gstate_c.curRTRenderWidth, diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 245d8d1c4339..ddd40b6d5363 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -199,190 +199,222 @@ bool TransformDrawEngine::ApplyShaderBlending() { return true; } -void TransformDrawEngine::ResetShaderBlending() { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - fboTexBound_ = false; +inline void TransformDrawEngine::ResetShaderBlending() { + if (fboTexBound_) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + fboTexBound_ = false; + } } -void TransformDrawEngine::ApplyDrawState(int prim) { - // 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. +void TransformDrawEngine::ApplyBlendState() { + // Blending is a bit complex to emulate. This is due to several reasons: + // + // * Doubled blend modes (src, dst, inversed) aren't supported in OpenGL. + // If possible, we double the src color or src alpha in the shader to account for these. + // These may clip incorrectly, but they're close. + // * OpenGL only has one arbitrary fixed color. We premultiply the other in the shader. + // * The written output alpha should actually be the stencil value. Alpha is not written. + // + // If we can't apply blending, we make a copy of the framebuffer and do it manually. + + GEBlendMode blendFuncEq = gstate.getBlendEq(); + + if (ShouldUseShaderBlending()) { + if (ShouldUseShaderFixedBlending()) { + // If both sides are fixed, we can do this without a blit but still in the shader. + Vec3f fixB = Vec3f::FromRGB(gstate.getFixB()); - if (gstate_c.textureChanged != TEXCHANGE_UNCHANGED && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { - textureCache_->SetTexture(); - gstate_c.textureChanged = TEXCHANGE_UNCHANGED; - if (gstate_c.needShaderTexClamp) { - // We will rarely need to set this, so let's do it every time on use rather than in runloop. - // Most of the time non-framebuffer textures will be used which can be clamped themselves. - shaderManager_->DirtyUniform(DIRTY_TEXCLAMP); - } - } + // Okay, so we'll use src * 1.0 + dst * fixB, and then premultiply in the shader. + const float blendColor[4] = {fixB.x, fixB.y, fixB.z, 1.0f}; + glstate.blend.enable(); + glstate.blendColor.set(blendColor); - // Set blend - unless we need to do it in the shader. - bool wantBlend = !gstate.isModeClear() && gstate.isAlphaBlendEnabled(); - if (wantBlend && ShouldUseShaderBlending()) { - if (ApplyShaderBlending()) { + ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(); + if (replaceAlphaWithStencil != REPLACE_ALPHA_NO) { + glstate.blendFuncSeparate.set(GL_ONE, GL_CONSTANT_COLOR, GL_ONE, GL_ZERO); + } else { + glstate.blendFuncSeparate.set(GL_ONE, GL_CONSTANT_COLOR, GL_ZERO, GL_ONE); + } + + // Min/max/absdiff are not possible here. + glstate.blendEquation.set(eqLookup[blendFuncEq]); + + shaderManager_->DirtyUniform(DIRTY_SHADERBLEND); + ResetShaderBlending(); + return; + } else if (ApplyShaderBlending()) { // None of the below logic is interesting, we're gonna do it entirely in the shader. - wantBlend = false; + glstate.blend.disable(); + return; } - } else if (fboTexBound_) { - ResetShaderBlending(); } + ResetShaderBlending(); - glstate.blend.set(wantBlend); - if (wantBlend) { - // This can't be done exactly as there are several PSP blend modes that are impossible to do on OpenGL ES 2.0, and some even on regular OpenGL for desktop. - // HOWEVER - we should be able to approximate the 2x modes in the shader, although they will clip wrongly. - - // Examples of seen unimplementable blend states: - // Mortal Kombat Unchained: FixA=0000ff FixB=000080 FuncA=10 FuncB=10 - - int blendFuncA = gstate.getBlendFuncA(); - int blendFuncB = gstate.getBlendFuncB(); - GEBlendMode blendFuncEq = gstate.getBlendEq(); - if (blendFuncA > GE_SRCBLEND_FIXA) blendFuncA = GE_SRCBLEND_FIXA; - if (blendFuncB > GE_DSTBLEND_FIXB) blendFuncB = GE_DSTBLEND_FIXB; - - float constantAlpha = 1.0f; - ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(); - if (gstate.isStencilTestEnabled() && replaceAlphaWithStencil == REPLACE_ALPHA_NO) { - if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_UNIFORM) { - constantAlpha = (float) gstate.getStencilTestRef() * (1.0f / 255.0f); - } - } + glstate.blend.enable(); - // Shortcut by using GL_ONE where possible, no need to set blendcolor - GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? blendColor2Func(gstate.getFixA()) : aLookup[blendFuncA]; - GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? blendColor2Func(gstate.getFixB()) : bLookup[blendFuncB]; + int blendFuncA = gstate.getBlendFuncA(); + int blendFuncB = gstate.getBlendFuncB(); + if (blendFuncA > GE_SRCBLEND_FIXA) blendFuncA = GE_SRCBLEND_FIXA; + if (blendFuncB > GE_DSTBLEND_FIXB) blendFuncB = GE_DSTBLEND_FIXB; - if (replaceAlphaWithStencil == REPLACE_ALPHA_DUALSOURCE) { - glBlendFuncA = toDualSource(glBlendFuncA); - glBlendFuncB = toDualSource(glBlendFuncB); + float constantAlpha = 1.0f; + ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(); + if (gstate.isStencilTestEnabled() && replaceAlphaWithStencil == REPLACE_ALPHA_NO) { + if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_UNIFORM) { + constantAlpha = (float) gstate.getStencilTestRef() * (1.0f / 255.0f); } + } - if (blendFuncA == GE_SRCBLEND_FIXA || blendFuncB == GE_DSTBLEND_FIXB) { - Vec3f fixA = Vec3f::FromRGB(gstate.getFixA()); - Vec3f fixB = Vec3f::FromRGB(gstate.getFixB()); - if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB != GL_INVALID_ENUM) { - // Can use blendcolor trivially. + // Shortcut by using GL_ONE where possible, no need to set blendcolor + GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? blendColor2Func(gstate.getFixA()) : aLookup[blendFuncA]; + GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? blendColor2Func(gstate.getFixB()) : bLookup[blendFuncB]; + + if (replaceAlphaWithStencil == REPLACE_ALPHA_DUALSOURCE) { + glBlendFuncA = toDualSource(glBlendFuncA); + glBlendFuncB = toDualSource(glBlendFuncB); + } + + if (blendFuncA == GE_SRCBLEND_FIXA || blendFuncB == GE_DSTBLEND_FIXB) { + Vec3f fixA = Vec3f::FromRGB(gstate.getFixA()); + Vec3f fixB = Vec3f::FromRGB(gstate.getFixB()); + if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB != GL_INVALID_ENUM) { + // Can use blendcolor trivially. + const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; + glstate.blendColor.set(blendColor); + glBlendFuncA = GL_CONSTANT_COLOR; + } else if (glBlendFuncA != GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { + // Can use blendcolor trivially. + const float blendColor[4] = {fixB.x, fixB.y, fixB.z, constantAlpha}; + glstate.blendColor.set(blendColor); + glBlendFuncB = GL_CONSTANT_COLOR; + } else if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { + if (blendColorSimilar(fixA, Vec3f::AssignToAll(constantAlpha) - fixB)) { + glBlendFuncA = GL_CONSTANT_COLOR; + glBlendFuncB = GL_ONE_MINUS_CONSTANT_COLOR; const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; glstate.blendColor.set(blendColor); + } else if (blendColorSimilar(fixA, fixB)) { glBlendFuncA = GL_CONSTANT_COLOR; - } else if (glBlendFuncA != GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { - // Can use blendcolor trivially. - const float blendColor[4] = {fixB.x, fixB.y, fixB.z, constantAlpha}; - glstate.blendColor.set(blendColor); glBlendFuncB = GL_CONSTANT_COLOR; - } else if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { - if (blendColorSimilar(fixA, Vec3f::AssignToAll(constantAlpha) - fixB)) { - glBlendFuncA = GL_CONSTANT_COLOR; - glBlendFuncB = GL_ONE_MINUS_CONSTANT_COLOR; - const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; - glstate.blendColor.set(blendColor); - } else if (blendColorSimilar(fixA, fixB)) { - glBlendFuncA = GL_CONSTANT_COLOR; + const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; + glstate.blendColor.set(blendColor); + } else { + static bool didReportBlend = false; + if (!didReportBlend) + Reporting::ReportMessage("ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", gstate.getFixA(), gstate.getFixB(), gstate.getBlendFuncA(), gstate.getBlendFuncB()); + didReportBlend = true; + + DEBUG_LOG(G3D, "ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", gstate.getFixA(), gstate.getFixB(), gstate.getBlendFuncA(), gstate.getBlendFuncB()); + // Let's approximate, at least. Close is better than totally off. + const bool nearZeroA = blendColorSimilar(fixA, Vec3f::AssignToAll(0.0f), 0.25f); + const bool nearZeroB = blendColorSimilar(fixB, Vec3f::AssignToAll(0.0f), 0.25f); + if (nearZeroA || blendColorSimilar(fixA, Vec3f::AssignToAll(1.0f), 0.25f)) { + glBlendFuncA = nearZeroA ? GL_ZERO : GL_ONE; glBlendFuncB = GL_CONSTANT_COLOR; - const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; + const float blendColor[4] = {fixB.x, fixB.y, fixB.z, constantAlpha}; glstate.blendColor.set(blendColor); + // We need to pick something. Let's go with A as the fixed color. } else { - static bool didReportBlend = false; - if (!didReportBlend) - Reporting::ReportMessage("ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", gstate.getFixA(), gstate.getFixB(), gstate.getBlendFuncA(), gstate.getBlendFuncB()); - didReportBlend = true; - - DEBUG_LOG(G3D, "ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", gstate.getFixA(), gstate.getFixB(), gstate.getBlendFuncA(), gstate.getBlendFuncB()); - // Let's approximate, at least. Close is better than totally off. - const bool nearZeroA = blendColorSimilar(fixA, Vec3f::AssignToAll(0.0f), 0.25f); - const bool nearZeroB = blendColorSimilar(fixB, Vec3f::AssignToAll(0.0f), 0.25f); - if (nearZeroA || blendColorSimilar(fixA, Vec3f::AssignToAll(1.0f), 0.25f)) { - glBlendFuncA = nearZeroA ? GL_ZERO : GL_ONE; - glBlendFuncB = GL_CONSTANT_COLOR; - const float blendColor[4] = {fixB.x, fixB.y, fixB.z, constantAlpha}; - glstate.blendColor.set(blendColor); - // We need to pick something. Let's go with A as the fixed color. - } else { - glBlendFuncA = GL_CONSTANT_COLOR; - glBlendFuncB = nearZeroB ? GL_ZERO : GL_ONE; - const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; - glstate.blendColor.set(blendColor); - } - } - } else { - // We optimized both, but that's probably not necessary, so let's pick one to be constant. - // For now let's just pick whichever was fixed instead of checking error. - if (blendFuncA == GE_SRCBLEND_FIXA) { glBlendFuncA = GL_CONSTANT_COLOR; + glBlendFuncB = nearZeroB ? GL_ZERO : GL_ONE; const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; glstate.blendColor.set(blendColor); - } else { - glBlendFuncB = GL_CONSTANT_COLOR; - const float blendColor[4] = {fixB.x, fixB.y, fixB.z, constantAlpha}; - glstate.blendColor.set(blendColor); } } - } else if (constantAlpha < 1.0f) { - const float blendColor[4] = {1.0f, 1.0f, 1.0f, constantAlpha}; - glstate.blendColor.set(blendColor); + } else { + // We optimized both, but that's probably not necessary, so let's pick one to be constant. + // For now let's just pick whichever was fixed instead of checking error. + if (blendFuncA == GE_SRCBLEND_FIXA) { + glBlendFuncA = GL_CONSTANT_COLOR; + const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; + glstate.blendColor.set(blendColor); + } else { + glBlendFuncB = GL_CONSTANT_COLOR; + const float blendColor[4] = {fixB.x, fixB.y, fixB.z, constantAlpha}; + glstate.blendColor.set(blendColor); + } } + } else if (constantAlpha < 1.0f) { + const float blendColor[4] = {1.0f, 1.0f, 1.0f, constantAlpha}; + glstate.blendColor.set(blendColor); + } - // Some Android devices (especially Mali, it seems) composite badly if there's alpha in the backbuffer. - // So in non-buffered rendering, we will simply consider the dest alpha to be zero in blending equations. + // Some Android devices (especially Mali, it seems) composite badly if there's alpha in the backbuffer. + // So in non-buffered rendering, we will simply consider the dest alpha to be zero in blending equations. #ifdef ANDROID - if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE) { - if (glBlendFuncA == GL_DST_ALPHA) glBlendFuncA = GL_ZERO; - if (glBlendFuncB == GL_DST_ALPHA) glBlendFuncB = GL_ZERO; - if (glBlendFuncA == GL_ONE_MINUS_DST_ALPHA) glBlendFuncA = GL_ONE; - if (glBlendFuncB == GL_ONE_MINUS_DST_ALPHA) glBlendFuncB = GL_ONE; - } + if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE) { + if (glBlendFuncA == GL_DST_ALPHA) glBlendFuncA = GL_ZERO; + if (glBlendFuncB == GL_DST_ALPHA) glBlendFuncB = GL_ZERO; + if (glBlendFuncA == GL_ONE_MINUS_DST_ALPHA) glBlendFuncA = GL_ONE; + if (glBlendFuncB == GL_ONE_MINUS_DST_ALPHA) glBlendFuncB = GL_ONE; + } #endif - // At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set right somehow. - - // The stencil-to-alpha in fragment shader doesn't apply here (blending is enabled), and we shouldn't - // do any blending in the alpha channel as that doesn't seem to happen on PSP. So lacking a better option, - // the only value we can set alpha to here without multipass and dual source alpha is zero (by setting - // the factors to zero). So let's do that. - if (replaceAlphaWithStencil != REPLACE_ALPHA_NO) { - // Let the fragment shader take care of it. - glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ZERO); - } else if (gstate.isStencilTestEnabled()) { - switch (ReplaceAlphaWithStencilType()) { - case STENCIL_VALUE_KEEP: - glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ONE); - break; - case STENCIL_VALUE_ONE: - // This won't give one but it's our best shot... - glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ONE); - break; - case STENCIL_VALUE_ZERO: - glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ZERO); - break; - case STENCIL_VALUE_UNIFORM: - // This won't give a correct value (it multiplies) but it may be better than random values. - glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_CONSTANT_ALPHA, GL_ZERO); - break; - case STENCIL_VALUE_UNKNOWN: - // For now, let's err at zero. This is INVERT or INCR/DECR. - glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ZERO); - break; - } - } else { - // Retain the existing value when stencil testing is off. + // At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set right somehow. + + // The stencil-to-alpha in fragment shader doesn't apply here (blending is enabled), and we shouldn't + // do any blending in the alpha channel as that doesn't seem to happen on PSP. So lacking a better option, + // the only value we can set alpha to here without multipass and dual source alpha is zero (by setting + // the factors to zero). So let's do that. + if (replaceAlphaWithStencil != REPLACE_ALPHA_NO) { + // Let the fragment shader take care of it. + glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ZERO); + } else if (gstate.isStencilTestEnabled()) { + switch (ReplaceAlphaWithStencilType()) { + case STENCIL_VALUE_KEEP: glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ONE); + break; + case STENCIL_VALUE_ONE: + // This won't give one but it's our best shot... + glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ONE); + break; + case STENCIL_VALUE_ZERO: + glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ZERO); + break; + case STENCIL_VALUE_UNIFORM: + // This won't give a correct value (it multiplies) but it may be better than random values. + glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_CONSTANT_ALPHA, GL_ZERO); + break; + case STENCIL_VALUE_UNKNOWN: + // For now, let's err at zero. This is INVERT or INCR/DECR. + glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ZERO); + break; } + } else { + // Retain the existing value when stencil testing is off. + glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ONE); + } - if (blendFuncEq == GE_BLENDMODE_ABSDIFF) { - WARN_LOG_REPORT_ONCE(blendAbsdiff, G3D, "Unsupported absdiff blend mode"); - } + if (gl_extensions.EXT_blend_minmax || gl_extensions.GLES3) { + glstate.blendEquation.set(eqLookup[blendFuncEq]); + } else { + glstate.blendEquation.set(eqLookupNoMinMax[blendFuncEq]); + } +} - if (((blendFuncEq >= GE_BLENDMODE_MIN) && gl_extensions.EXT_blend_minmax) || gl_extensions.GLES3) { - glstate.blendEquation.set(eqLookup[blendFuncEq]); - } else { - glstate.blendEquation.set(eqLookupNoMinMax[blendFuncEq]); +void TransformDrawEngine::ApplyDrawState(int prim) { + // 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. + + if (gstate_c.textureChanged != TEXCHANGE_UNCHANGED && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { + textureCache_->SetTexture(); + gstate_c.textureChanged = TEXCHANGE_UNCHANGED; + if (gstate_c.needShaderTexClamp) { + // We will rarely need to set this, so let's do it every time on use rather than in runloop. + // Most of the time non-framebuffer textures will be used which can be clamped themselves. + shaderManager_->DirtyUniform(DIRTY_TEXCLAMP); } } + // Set blend - unless we need to do it in the shader. + if (!gstate.isModeClear() && gstate.isAlphaBlendEnabled()) { + ApplyBlendState(); + } else { + glstate.blend.disable(); + ResetShaderBlending(); + } + bool alwaysDepthWrite = g_Config.bAlwaysDepthWrite; bool enableStencilTest = !g_Config.bDisableStencilTest; diff --git a/GPU/GLES/TransformPipeline.h b/GPU/GLES/TransformPipeline.h index 1ec8aa455f33..62851d117af7 100644 --- a/GPU/GLES/TransformPipeline.h +++ b/GPU/GLES/TransformPipeline.h @@ -174,8 +174,9 @@ class TransformDrawEngine : public GfxResourceHolder { void DoFlush(); void SoftwareTransformAndDraw(int prim, u8 *decoded, LinkedShader *program, int vertexCount, u32 vertexType, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex); void ApplyDrawState(int prim); + void ApplyBlendState(); bool ApplyShaderBlending(); - void ResetShaderBlending(); + inline void ResetShaderBlending(); bool IsReallyAClear(int numVerts) const; GLuint AllocateBuffer(); void FreeBuffer(GLuint buf);