mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 00:59:41 +01:00
Merge pull request #6350 from unknownbrackets/gpu-blend
Avoid blitting for a bunch of gpu blending modes
This commit is contained in:
commit
565bb98db1
5 files changed
+274
-189
No files matched your search
@@ -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.
|
||||
|
||||
@@ -63,3 +63,4 @@ bool IsColorTestTriviallyTrue();
|
||||
StencilValueType ReplaceAlphaWithStencilType();
|
||||
ReplaceAlphaType ReplaceAlphaWithStencil();
|
||||
bool ShouldUseShaderBlending();
|
||||
bool ShouldUseShaderFixedBlending();
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user