Merge pull request #6350 from unknownbrackets/gpu-blend

Avoid blitting for a bunch of gpu blending modes
This commit is contained in:
Henrik Rydgård committed 2014-06-17 09:54:57 +02:00
commit 565bb98db1
5 files changed
+274 -189

No files matched your search

+68 -21
View File
@@ -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.
+1
View File
@@ -63,3 +63,4 @@ bool IsColorTestTriviallyTrue();
StencilValueType ReplaceAlphaWithStencilType();
ReplaceAlphaType ReplaceAlphaWithStencil();
bool ShouldUseShaderBlending();
bool ShouldUseShaderFixedBlending();
+6 -2
View File
@@ -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,
+197 -165
View File
@@ -199,11 +199,199 @@ 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::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());
// 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.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.
glstate.blend.disable();
return;
}
}
ResetShaderBlending();
glstate.blend.enable();
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;
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);
}
}
// 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;
glBlendFuncB = 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] = {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;
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.
#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;
}
#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.
glstate.blendFuncSeparate.set(glBlendFuncA, glBlendFuncB, GL_ZERO, GL_ONE);
}
if (gl_extensions.EXT_blend_minmax || gl_extensions.GLES3) {
glstate.blendEquation.set(eqLookup[blendFuncEq]);
} else {
glstate.blendEquation.set(eqLookupNoMinMax[blendFuncEq]);
}
}
void TransformDrawEngine::ApplyDrawState(int prim) {
@@ -220,169 +408,13 @@ void TransformDrawEngine::ApplyDrawState(int prim) {
}
// Set blend - unless we need to do it in the shader.
bool wantBlend = !gstate.isModeClear() && gstate.isAlphaBlendEnabled();
if (wantBlend && ShouldUseShaderBlending()) {
if (ApplyShaderBlending()) {
// None of the below logic is interesting, we're gonna do it entirely in the shader.
wantBlend = false;
}
} else if (fboTexBound_) {
if (!gstate.isModeClear() && gstate.isAlphaBlendEnabled()) {
ApplyBlendState();
} else {
glstate.blend.disable();
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);
}
}
// 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;
glBlendFuncB = 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] = {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;
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.
#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;
}
#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.
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 (((blendFuncEq >= GE_BLENDMODE_MIN) && gl_extensions.EXT_blend_minmax) || gl_extensions.GLES3) {
glstate.blendEquation.set(eqLookup[blendFuncEq]);
} else {
glstate.blendEquation.set(eqLookupNoMinMax[blendFuncEq]);
}
}
bool alwaysDepthWrite = g_Config.bAlwaysDepthWrite;
bool enableStencilTest = !g_Config.bDisableStencilTest;
+2 -1
View File
@@ -174,8 +174,9 @@ private:
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);