diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 263ac0d89e..7de092670a 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -388,7 +388,7 @@ static const HardHashTableEntry hardcodedHashes[] = { { 0xe83a7a9d80a21c11, 4448, "_strtod_r", }, { 0xe894bda909a8a8f9, 1064, "expensive_wipeout_pulse", }, { 0xe8ad7719be44e7c8, 276, "strchr", }, - { 0xeabb9c1b4f83d2b4, 52, "memset", }, + { 0xeabb9c1b4f83d2b4, 52, "memset", }, // Crisis Core { 0xeb0f7bf63d52ece9, 88, "strncat", }, { 0xeb8c0834d8bbc28c, 416, "fmodf", }, { 0xedbbe9bf9fbceca8, 172, "dl_write_viewport2", }, diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index 65364bd1e6..2298e87bc0 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -137,16 +137,14 @@ const bool nonAlphaDestFactors[16] = { true, // GE_DSTBLEND_FIXB, }; -ReplaceAlphaType ReplaceAlphaWithStencil() { +ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) { if (!gstate.isStencilTestEnabled() || gstate.isModeClear()) { return REPLACE_ALPHA_NO; } - if (gstate.isAlphaBlendEnabled()) { + if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_COPY_FBO) { if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) { return REPLACE_ALPHA_YES; - } else if (ShouldUseShaderBlending()) { - return REPLACE_ALPHA_YES; } else { if (gl_extensions.ARB_blend_func_extended) { return REPLACE_ALPHA_DUALSOURCE; @@ -232,149 +230,124 @@ bool IsColorTestTriviallyTrue() { } } -static bool AlphaToColorDoubling() { - if (!gstate.isAlphaBlendEnabled()) { - return false; - } - // 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; - } - 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; - } -} - -static bool CanDoubleSrcBlendMode() { - if (!gstate.isAlphaBlendEnabled()) { - return false; - } - - int funcA = gstate.getBlendFuncA(); - int funcB = gstate.getBlendFuncB(); - if (funcA != GE_SRCBLEND_DOUBLESRCALPHA && funcA != GE_SRCBLEND_DOUBLEINVSRCALPHA) { - funcB = funcA; - funcA = gstate.getBlendFuncB(); - } - if (funcA != GE_SRCBLEND_DOUBLESRCALPHA && funcA != GE_SRCBLEND_DOUBLEINVSRCALPHA) { - return false; - } - - // One side should be doubled. Let's check the other side. - // 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: - return false; - - default: - return true; - } -} - -// TODO: Setting to disable? -bool ShouldUseShaderBlending() { - if (!gstate.isAlphaBlendEnabled()) { - return false; - } - if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE) { - return false; +ReplaceBlendType ReplaceBlendWithShader() { + if (!gstate.isAlphaBlendEnabled() || gstate.isModeClear()) { + return REPLACE_BLEND_NO; } GEBlendSrcFactor funcA = gstate.getBlendFuncA(); GEBlendDstFactor funcB = gstate.getBlendFuncB(); GEBlendMode eq = gstate.getBlendEq(); + // Let's get the non-factor modes out of the way first. switch (eq) { case GE_BLENDMODE_ABSDIFF: - return !g_Config.bDisableSlowFramebufEffects; + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; case GE_BLENDMODE_MIN: case GE_BLENDMODE_MAX: - // These don't use the factors. - return !gl_extensions.EXT_blend_minmax && !gl_extensions.GLES3 && !g_Config.bDisableSlowFramebufEffects; - - default: - break; - } - - // This normally involves a blit, so try to skip it. - if (AlphaToColorDoubling() || CanDoubleSrcBlendMode()) { - return false; - } - - switch (funcA) { - case GE_SRCBLEND_DOUBLESRCALPHA: - case GE_SRCBLEND_DOUBLEINVSRCALPHA: - case GE_SRCBLEND_DOUBLEDSTALPHA: - case GE_SRCBLEND_DOUBLEINVDSTALPHA: - return !g_Config.bDisableSlowFramebufEffects; - - case GE_SRCBLEND_FIXA: - if (funcB == GE_DSTBLEND_FIXB) { - u32 fixA = gstate.getFixA(); - u32 fixB = gstate.getFixB(); - // OpenGL only supports one constant color, so check if we could be more exact. - if (fixA != fixB && fixA != 0xFFFFFF - fixB && fixA != 0 && fixB != 0 && fixA != 0xFFFFFF && fixB != 0xFFFFFF) { - return true; - } + if (gl_extensions.EXT_blend_minmax || gl_extensions.GLES3) { + return REPLACE_BLEND_STANDARD; + } else { + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; } default: break; } - switch (funcB) { - case GE_DSTBLEND_DOUBLESRCALPHA: - case GE_DSTBLEND_DOUBLEINVSRCALPHA: - case GE_DSTBLEND_DOUBLEDSTALPHA: - case GE_DSTBLEND_DOUBLEINVDSTALPHA: - return !g_Config.bDisableSlowFramebufEffects; + switch (funcA) { + case GE_SRCBLEND_DOUBLESRCALPHA: + case GE_SRCBLEND_DOUBLEINVSRCALPHA: + // 2x alpha in the source function and not in the dest = source color doubling. + // Even dest alpha is safe, since we're moving the * 2.0 into the src color. + switch (funcB) { + case GE_DSTBLEND_SRCCOLOR: + case GE_DSTBLEND_INVSRCCOLOR: + // Can't double, we need the source color to be correct. + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + + case GE_DSTBLEND_DOUBLEDSTALPHA: + case GE_DSTBLEND_DOUBLEINVDSTALPHA: + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + + case GE_DSTBLEND_DOUBLESRCALPHA: + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + // We can't technically do this correctly (due to clamping) without reading the dst color. + // Using a copy isn't accurate either, though, when there's overlap. + return REPLACE_BLEND_PRE_SRC_2X_ALPHA; + + default: + // TODO: Could use vertexFullAlpha, but it's not calculated yet. + return REPLACE_BLEND_PRE_SRC; + } + + case GE_SRCBLEND_DOUBLEDSTALPHA: + case GE_SRCBLEND_DOUBLEINVDSTALPHA: + switch (funcB) { + case GE_DSTBLEND_SRCCOLOR: + case GE_DSTBLEND_INVSRCCOLOR: + // Can't double, we need the source color to be correct. + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + + case GE_DSTBLEND_DOUBLEDSTALPHA: + case GE_DSTBLEND_DOUBLEINVDSTALPHA: + case GE_DSTBLEND_DOUBLESRCALPHA: + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; + + default: + // We can't technically do this correctly (due to clamping) without reading the dst alpha. + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; + } + + case GE_SRCBLEND_FIXA: + switch (funcB) { + case GE_DSTBLEND_DOUBLESRCALPHA: + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + // Can't safely double alpha, will clamp. + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + + case GE_DSTBLEND_DOUBLEDSTALPHA: + case GE_DSTBLEND_DOUBLEINVDSTALPHA: + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + + case GE_DSTBLEND_FIXB: + if (gstate.getFixA() == 0xFFFFFF && gstate.getFixB() == 0x000000) { + // Some games specify this. Some cards may prefer blending off entirely. + return REPLACE_BLEND_NO; + } else if (gstate.getFixA() == 0xFFFFFF || gstate.getFixA() == 0x000000 || gstate.getFixB() == 0xFFFFFF || gstate.getFixB() == 0x000000) { + return REPLACE_BLEND_STANDARD; + } else { + return REPLACE_BLEND_PRE_SRC; + } + + default: + return REPLACE_BLEND_STANDARD; + } default: - break; - } + switch (funcB) { + case GE_DSTBLEND_DOUBLESRCALPHA: + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) { + // Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap. + return REPLACE_BLEND_PRE_SRC_2X_ALPHA; + } else { + // This means dst alpha/color is used in the src factor. + // Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?) + // We will just hope that doubling alpha for the dst factor will not clamp too badly. + return REPLACE_BLEND_2X_ALPHA; + } - return false; -} + case GE_DSTBLEND_DOUBLEDSTALPHA: + case GE_DSTBLEND_DOUBLEINVDSTALPHA: + return !gstate_c.allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; -// Doesn't need to be in the shader id, ShouldUseShaderBlending contains all parts. -bool ShouldUseShaderFixedBlending() { - if (!ShouldUseShaderBlending()) { - return false; + default: + return REPLACE_BLEND_STANDARD; + } } - - 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 @@ -391,14 +364,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 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 && !useShaderBlending && CanDoubleSrcBlendMode(); + bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled(); bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; bool doTextureAlpha = gstate.isTextureAlphaUsed(); - ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(); + ReplaceBlendType replaceBlend = ReplaceBlendWithShader(); + ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend); // All texfuncs except replace are the same for RGB as for RGBA with full alpha. if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE) @@ -435,29 +405,29 @@ void ComputeFragmentShaderID(FragmentShaderID *id) { id0 |= (enableFog & 1) << 19; id0 |= (doTextureProjection & 1) << 20; id0 |= (enableColorDoubling & 1) << 21; - id0 |= (enableAlphaDoubling & 1) << 22; // 2 bits - id0 |= (stencilToAlpha) << 23; + id0 |= (stencilToAlpha) << 22; if (stencilToAlpha != REPLACE_ALPHA_NO) { // 3 bits - id0 |= ReplaceAlphaWithStencilType() << 25; + id0 |= ReplaceAlphaWithStencilType() << 24; } - id0 |= (alphaTestAgainstZero & 1) << 28; + id0 |= (alphaTestAgainstZero & 1) << 27; if (enableAlphaTest) gpuStats.numAlphaTestedDraws++; else gpuStats.numNonAlphaTestedDraws++; - // 29 - 31 are free. + // 28 - 31 are free. - if (useShaderBlending) { - // 12 bits total. - id1 |= 1 << 0; - id1 |= gstate.getBlendEq() << 1; - id1 |= gstate.getBlendFuncA() << 4; - id1 |= gstate.getBlendFuncB() << 8; + // 3 bits. + id1 |= replaceBlend << 0; + if (replaceBlend > REPLACE_BLEND_STANDARD) { + // 11 bits total. + id1 |= gstate.getBlendEq() << 3; + id1 |= gstate.getBlendFuncA() << 6; + id1 |= gstate.getBlendFuncB() << 10; } } @@ -548,23 +518,20 @@ 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 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 && !useShaderBlending && CanDoubleSrcBlendMode(); + bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled(); bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; bool doTextureAlpha = gstate.isTextureAlphaUsed(); bool textureAtOffset = gstate_c.curTextureXOffset != 0 || gstate_c.curTextureYOffset != 0; - ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(); + ReplaceBlendType replaceBlend = ReplaceBlendWithShader(); + ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend); if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE) doTextureAlpha = false; if (doTexture) WRITE(p, "uniform sampler2D tex;\n"); - if (!gstate.isModeClear() && useShaderBlending) { - if (!gl_extensions.NV_shader_framebuffer_fetch) { + if (!gstate.isModeClear() && replaceBlend > REPLACE_BLEND_STANDARD) { + if (!gl_extensions.NV_shader_framebuffer_fetch && replaceBlend == REPLACE_BLEND_COPY_FBO) { if (!texelFetch) { WRITE(p, "uniform vec2 u_fbotexSize;\n"); } @@ -815,12 +782,10 @@ void GenerateFragmentShader(char *buffer) { } // Color doubling happens after the color test. - if (enableColorDoubling && enableAlphaDoubling) { - WRITE(p, " v = v * 2.0;\n"); - } else if (enableColorDoubling) { + if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 4.0;\n"); + } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { WRITE(p, " v.rgb = v.rgb * 2.0;\n"); - } else if (enableAlphaDoubling) { - WRITE(p, " v.a = v.a * 2.0;\n"); } if (enableFog) { @@ -829,10 +794,28 @@ void GenerateFragmentShader(char *buffer) { // WRITE(p, " v.x = v_depth;\n"); } - if (ShouldUseShaderFixedBlending()) { - // Just premultiply by u_blendFixA. - WRITE(p, " v.rgb = v.rgb * u_blendFixA;\n"); - } else if (useShaderBlending) { + if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) { + GEBlendSrcFactor funcA = gstate.getBlendFuncA(); + const char *srcFactor = "ERROR"; + switch (funcA) { + case GE_SRCBLEND_DSTCOLOR: srcFactor = "ERROR"; break; + case GE_SRCBLEND_INVDSTCOLOR: srcFactor = "ERROR"; break; + case GE_SRCBLEND_SRCALPHA: srcFactor = "vec3(v.a)"; break; + case GE_SRCBLEND_INVSRCALPHA: srcFactor = "vec3(1.0 - v.a)"; break; + case GE_SRCBLEND_DSTALPHA: srcFactor = "ERROR"; break; + case GE_SRCBLEND_INVDSTALPHA: srcFactor = "ERROR"; break; + case GE_SRCBLEND_DOUBLESRCALPHA: srcFactor = "vec3(v.a * 2.0)"; break; + // TODO: Double inverse, or inverse double? Following softgpu for now... + case GE_SRCBLEND_DOUBLEINVSRCALPHA: srcFactor = "vec3(1.0 - v.a * 2.0)"; break; + case GE_SRCBLEND_DOUBLEDSTALPHA: srcFactor = "ERROR"; break; + case GE_SRCBLEND_DOUBLEINVDSTALPHA: srcFactor = "ERROR"; break; + case GE_SRCBLEND_FIXA: srcFactor = "u_blendFixA"; break; + } + + WRITE(p, " v.rgb = v.rgb * %s;\n", srcFactor); + } + + if (replaceBlend == REPLACE_BLEND_COPY_FBO) { // 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. @@ -903,6 +886,10 @@ void GenerateFragmentShader(char *buffer) { break; } } + + if (replaceBlend == REPLACE_BLEND_2X_ALPHA || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) { + WRITE(p, " v.a = v.a * 2.0;\n"); + } } switch (stencilToAlpha) { diff --git a/GPU/GLES/FragmentShaderGenerator.h b/GPU/GLES/FragmentShaderGenerator.h index c931c5efe5..429514d370 100644 --- a/GPU/GLES/FragmentShaderGenerator.h +++ b/GPU/GLES/FragmentShaderGenerator.h @@ -58,9 +58,18 @@ enum ReplaceAlphaType { REPLACE_ALPHA_DUALSOURCE = 2, }; +enum ReplaceBlendType { + REPLACE_BLEND_NO, + REPLACE_BLEND_STANDARD, + REPLACE_BLEND_PRE_SRC, + REPLACE_BLEND_PRE_SRC_2X_ALPHA, + REPLACE_BLEND_2X_ALPHA, + REPLACE_BLEND_2X_SRC, + REPLACE_BLEND_COPY_FBO, +}; + bool IsAlphaTestTriviallyTrue(); bool IsColorTestTriviallyTrue(); StencilValueType ReplaceAlphaWithStencilType(); -ReplaceAlphaType ReplaceAlphaWithStencil(); -bool ShouldUseShaderBlending(); -bool ShouldUseShaderFixedBlending(); +ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend); +ReplaceBlendType ReplaceBlendWithShader(); diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 1875c4aa63..eadb11896d 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -213,54 +213,56 @@ void TransformDrawEngine::ApplyBlendState() { // // * 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. + // These may clip incorrectly, so we avoid unfortunately. // * 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. + gstate_c.allowShaderBlend = !g_Config.bDisableSlowFramebufEffects; - GEBlendMode blendFuncEq = gstate.getBlendEq(); + ReplaceBlendType replaceBlend = ReplaceBlendWithShader(); + bool usePreSrc = false; - 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()); + switch (replaceBlend) { + case REPLACE_BLEND_NO: + glstate.blend.disable(); + ResetShaderBlending(); + return; - // 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); - - 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.blendEquationSeparate.set(eqLookup[blendFuncEq], GL_FUNC_ADD); - - shaderManager_->DirtyUniform(DIRTY_SHADERBLEND); - ResetShaderBlending(); - return; - } else if (ApplyShaderBlending()) { + case REPLACE_BLEND_COPY_FBO: + if (ApplyShaderBlending()) { // None of the below logic is interesting, we're gonna do it entirely in the shader. glstate.blend.disable(); return; } + // Until next time, force it off. + gstate_c.allowShaderBlend = false; + break; + + case REPLACE_BLEND_PRE_SRC: + case REPLACE_BLEND_PRE_SRC_2X_ALPHA: + usePreSrc = true; + break; + + case REPLACE_BLEND_STANDARD: + case REPLACE_BLEND_2X_ALPHA: + case REPLACE_BLEND_2X_SRC: + break; } - ResetShaderBlending(); glstate.blend.enable(); + ResetShaderBlending(); + GEBlendMode blendFuncEq = gstate.getBlendEq(); 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 (blendFuncA > GE_SRCBLEND_FIXA) + blendFuncA = GE_SRCBLEND_FIXA; + if (blendFuncB > GE_DSTBLEND_FIXB) + blendFuncB = GE_DSTBLEND_FIXB; float constantAlpha = 1.0f; - ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(); + ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend); if (gstate.isStencilTestEnabled() && replaceAlphaWithStencil == REPLACE_ALPHA_NO) { if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_UNIFORM) { constantAlpha = (float) gstate.getStencilTestRef() * (1.0f / 255.0f); @@ -271,6 +273,14 @@ void TransformDrawEngine::ApplyBlendState() { GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? blendColor2Func(gstate.getFixA()) : aLookup[blendFuncA]; GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? blendColor2Func(gstate.getFixB()) : bLookup[blendFuncB]; + if (usePreSrc) { + glBlendFuncA = GL_ONE; + // Need to pull in the fixed color. + if (blendFuncA == GE_SRCBLEND_FIXA) { + shaderManager_->DirtyUniform(DIRTY_SHADERBLEND); + } + } + if (replaceAlphaWithStencil == REPLACE_ALPHA_DUALSOURCE) { glBlendFuncA = toDualSource(glBlendFuncA); glBlendFuncB = toDualSource(glBlendFuncB); @@ -326,7 +336,7 @@ void TransformDrawEngine::ApplyBlendState() { } 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) { + if (blendFuncA == GE_SRCBLEND_FIXA && !usePreSrc) { glBlendFuncA = GL_CONSTANT_COLOR; const float blendColor[4] = {fixA.x, fixA.y, fixA.z, constantAlpha}; glstate.blendColor.set(blendColor); @@ -375,7 +385,11 @@ void TransformDrawEngine::ApplyBlendState() { 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); + if (constantAlpha < 1.0f) { + glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_CONSTANT_ALPHA, GL_ZERO); + } else { + glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ONE, GL_ZERO); + } break; case STENCIL_VALUE_UNKNOWN: // For now, let's err at zero. This is INVERT or INCR/DECR. @@ -408,12 +422,7 @@ void TransformDrawEngine::ApplyDrawState(int prim) { } // Set blend - unless we need to do it in the shader. - if (!gstate.isModeClear() && gstate.isAlphaBlendEnabled()) { - ApplyBlendState(); - } else { - glstate.blend.disable(); - ResetShaderBlending(); - } + ApplyBlendState(); bool alwaysDepthWrite = g_Config.bAlwaysDepthWrite; bool enableStencilTest = !g_Config.bDisableStencilTest; diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 7ca203456c..71c07a7371 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -619,6 +619,7 @@ void TextureCache::GetSamplingParams(int &minFilt, int &magFilt, bool &sClamp, b minFilt |= 1; } if (g_Config.iTexFiltering == LINEAR && (!gstate.isColorTestEnabled() || IsColorTestTriviallyTrue())) { + // TODO: IsAlphaTestTriviallyTrue() is unsafe here. vertexFullAlpha is not calculated yet. if (!gstate.isAlphaTestEnabled() || IsAlphaTestTriviallyTrue()) { magFilt |= 1; minFilt |= 1; diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 30db2b0775..310200941e 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -461,6 +461,7 @@ struct GPUStateCache UVScale uv; bool flipTexture; bool needShaderTexClamp; + bool allowShaderBlend; float morphWeights[8];