mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-30 17:02:19 +00:00
Merge pull request #6679 from unknownbrackets/gpu-blend
Handle doubled alpha blending using premultiplication
This commit is contained in:
commit
f8a4236d58
@ -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", },
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -461,6 +461,7 @@ struct GPUStateCache
|
||||
UVScale uv;
|
||||
bool flipTexture;
|
||||
bool needShaderTexClamp;
|
||||
bool allowShaderBlend;
|
||||
|
||||
float morphWeights[8];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user