mirror of
https://github.com/libretro/ppsspp.git
synced 2024-11-24 16:49:50 +00:00
Premultiply for doubled alpha blending if possible.
This makes the effect more accurate where the values were being clamped before. Some obscure methods of blending may be slower.
This commit is contained in:
parent
d52fdafa3c
commit
44620c92ed
@ -145,7 +145,7 @@ ReplaceAlphaType ReplaceAlphaWithStencil() {
|
||||
if (gstate.isAlphaBlendEnabled()) {
|
||||
if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) {
|
||||
return REPLACE_ALPHA_YES;
|
||||
} else if (ShouldUseShaderBlending()) {
|
||||
} else if (ReplaceBlendWithShader() == REPLACE_BLEND_COPY_FBO) {
|
||||
return REPLACE_ALPHA_YES;
|
||||
} else {
|
||||
if (gl_extensions.ARB_blend_func_extended) {
|
||||
@ -232,149 +232,110 @@ 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 g_Config.bDisableSlowFramebufEffects ? 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 g_Config.bDisableSlowFramebufEffects ? 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 g_Config.bDisableSlowFramebufEffects ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
|
||||
|
||||
case GE_DSTBLEND_DOUBLEDSTALPHA:
|
||||
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
|
||||
return g_Config.bDisableSlowFramebufEffects ? 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.
|
||||
if (g_Config.bDisableSlowFramebufEffects) {
|
||||
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
|
||||
} else {
|
||||
return REPLACE_BLEND_COPY_FBO;
|
||||
}
|
||||
|
||||
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 g_Config.bDisableSlowFramebufEffects ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
|
||||
|
||||
case GE_DSTBLEND_DOUBLEDSTALPHA:
|
||||
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
|
||||
case GE_DSTBLEND_DOUBLESRCALPHA:
|
||||
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
|
||||
return g_Config.bDisableSlowFramebufEffects ? 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 g_Config.bDisableSlowFramebufEffects ? 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 g_Config.bDisableSlowFramebufEffects ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
|
||||
|
||||
case GE_DSTBLEND_DOUBLEDSTALPHA:
|
||||
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
|
||||
return g_Config.bDisableSlowFramebufEffects ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
|
||||
|
||||
case GE_DSTBLEND_FIXB:
|
||||
return REPLACE_BLEND_PRE_SRC;
|
||||
|
||||
default:
|
||||
return REPLACE_BLEND_STANDARD;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (funcB) {
|
||||
case GE_DSTBLEND_DOUBLESRCALPHA:
|
||||
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
|
||||
case GE_DSTBLEND_DOUBLEDSTALPHA:
|
||||
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
|
||||
return g_Config.bDisableSlowFramebufEffects ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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 +352,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();
|
||||
|
||||
// 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 +393,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 +506,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();
|
||||
|
||||
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 +770,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 +782,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 +874,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();
|
||||
ReplaceBlendType ReplaceBlendWithShader();
|
||||
|
@ -213,51 +213,51 @@ 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.
|
||||
|
||||
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;
|
||||
}
|
||||
// TODO: Otherwise, we probably apply shading wrong. Hmm.
|
||||
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();
|
||||
@ -271,6 +271,10 @@ 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;
|
||||
}
|
||||
|
||||
if (replaceAlphaWithStencil == REPLACE_ALPHA_DUALSOURCE) {
|
||||
glBlendFuncA = toDualSource(glBlendFuncA);
|
||||
glBlendFuncB = toDualSource(glBlendFuncB);
|
||||
@ -326,7 +330,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 +379,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 +416,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;
|
||||
|
Loading…
Reference in New Issue
Block a user