From bf7f07f918f3faeccbca53dcecf67e9c70a81bd4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Nov 2015 12:37:32 -0800 Subject: [PATCH] Optimize inverse blending modes, where possible. Also, take advantage of cases where we can double src and src.a in the shader, which can in some cases get us closer to accurate. --- GPU/Common/GPUStateUtils.cpp | 93 +++++++++++++++++++++--- GPU/Directx9/PixelShaderGeneratorDX9.cpp | 5 +- GPU/GLES/FragmentShaderGenerator.cpp | 4 +- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 8f6c784620..fb47c103ca 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -273,35 +273,46 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu switch (funcB) { case GE_DSTBLEND_SRCCOLOR: case GE_DSTBLEND_INVSRCCOLOR: + // When inversing, alpha clamping isn't an issue. + if (funcA == GE_SRCBLEND_DOUBLEINVSRCALPHA) + return REPLACE_BLEND_2X_ALPHA; // Can't double, we need the source color to be correct. + // Doubling only alpha would clamp the src alpha incorrectly. return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: - if (bufferFormat == GE_FORMAT_565) { + if (bufferFormat == GE_FORMAT_565) return REPLACE_BLEND_2X_ALPHA; - } return !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. if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; - default: + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + // For the inverse, doubling alpha is safe, because it will clamp correctly. + return REPLACE_BLEND_PRE_SRC_2X_ALPHA; + + case GE_DSTBLEND_SRCALPHA: + case GE_DSTBLEND_INVSRCALPHA: + case GE_DSTBLEND_DSTALPHA: + case GE_DSTBLEND_INVDSTALPHA: + case GE_DSTBLEND_FIXB: // TODO: Could use vertexFullAlpha, but it's not calculated yet. + // This outputs the original alpha for the dest factor. return REPLACE_BLEND_PRE_SRC; } case GE_SRCBLEND_DOUBLEDSTALPHA: - case GE_SRCBLEND_DOUBLEINVDSTALPHA: switch (funcB) { case GE_DSTBLEND_SRCCOLOR: case GE_DSTBLEND_INVSRCCOLOR: if (bufferFormat == GE_FORMAT_565) { + // Dest alpha should be zero. return REPLACE_BLEND_STANDARD; } // Can't double, we need the source color to be correct. @@ -310,6 +321,8 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: if (bufferFormat == GE_FORMAT_565) { + // Both blend factors are 0 or 1, no need to read it, since it's known. + // Doubling will have no effect here. return REPLACE_BLEND_STANDARD; } return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; @@ -319,9 +332,15 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_2X_ALPHA; } - return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; + // Double both src (for dst alpha) and alpha (for dst factor.) + // But to be accurate (clamping), we need to read the dst color. + return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; - default: + case GE_DSTBLEND_SRCALPHA: + case GE_DSTBLEND_INVSRCALPHA: + case GE_DSTBLEND_DSTALPHA: + case GE_DSTBLEND_INVDSTALPHA: + case GE_DSTBLEND_FIXB: if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } @@ -329,13 +348,47 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; } + case GE_SRCBLEND_DOUBLEINVDSTALPHA: + // Inverse double dst alpha is tricky. Doubling the src color is probably the wrong direction, + // halving might be more correct. We really need to read the dst color. + switch (funcB) { + case GE_DSTBLEND_SRCCOLOR: + case GE_DSTBLEND_INVSRCCOLOR: + case GE_DSTBLEND_DOUBLEDSTALPHA: + case GE_DSTBLEND_DOUBLEINVDSTALPHA: + if (bufferFormat == GE_FORMAT_565) { + return REPLACE_BLEND_STANDARD; + } + return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + + case GE_DSTBLEND_DOUBLESRCALPHA: + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + if (bufferFormat == GE_FORMAT_565) { + return REPLACE_BLEND_2X_ALPHA; + } + return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + + case GE_DSTBLEND_SRCALPHA: + case GE_DSTBLEND_INVSRCALPHA: + case GE_DSTBLEND_DSTALPHA: + case GE_DSTBLEND_INVDSTALPHA: + case GE_DSTBLEND_FIXB: + if (bufferFormat == GE_FORMAT_565) { + return REPLACE_BLEND_STANDARD; + } + return !allowShaderBlend ? REPLACE_BLEND_STANDARD : 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 !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + // Doubling alpha is safe for the inverse, will clamp to zero either way. + return REPLACE_BLEND_2X_ALPHA; + case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: if (bufferFormat == GE_FORMAT_565) { @@ -350,17 +403,27 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu } else if (gstate.getFixA() == 0xFFFFFF || gstate.getFixA() == 0x000000 || gstate.getFixB() == 0xFFFFFF || gstate.getFixB() == 0x000000) { return REPLACE_BLEND_STANDARD; } else { + // Multiply the src color in the shader, that way it's always accurate. return REPLACE_BLEND_PRE_SRC; } - default: + case GE_DSTBLEND_SRCCOLOR: + case GE_DSTBLEND_INVSRCCOLOR: + case GE_DSTBLEND_SRCALPHA: + case GE_DSTBLEND_INVSRCALPHA: + case GE_DSTBLEND_DSTALPHA: + case GE_DSTBLEND_INVDSTALPHA: return REPLACE_BLEND_STANDARD; } - default: + case GE_SRCBLEND_DSTCOLOR: + case GE_SRCBLEND_INVDSTCOLOR: + case GE_SRCBLEND_SRCALPHA: + case GE_SRCBLEND_INVSRCALPHA: + case GE_SRCBLEND_DSTALPHA: + case GE_SRCBLEND_INVDSTALPHA: 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. if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) @@ -375,6 +438,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu return REPLACE_BLEND_2X_ALPHA; } + case GE_DSTBLEND_DOUBLEINVSRCALPHA: + // For inverse, things are simpler. Clamping isn't an issue, as long as we avoid + // messing with the other factor's components. + if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) { + return REPLACE_BLEND_PRE_SRC_2X_ALPHA; + } + return REPLACE_BLEND_2X_ALPHA; + case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: if (bufferFormat == GE_FORMAT_565) { diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index 750d2b795f..ad1266435e 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -299,9 +299,10 @@ bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) { case GE_SRCBLEND_DSTALPHA: srcFactor = "ERROR"; break; case GE_SRCBLEND_INVDSTALPHA: srcFactor = "ERROR"; break; case GE_SRCBLEND_DOUBLESRCALPHA: srcFactor = "float3(v.a * 2.0, v.a * 2.0, v.a * 2.0)"; break; - // TODO: Double inverse, or inverse double? Following softgpu for now... case GE_SRCBLEND_DOUBLEINVSRCALPHA: srcFactor = "float3(1.0 - v.a * 2.0, 1.0 - v.a * 2.0, 1.0 - v.a * 2.0)"; break; - case GE_SRCBLEND_DOUBLEDSTALPHA: srcFactor = "ERROR"; break; + // PRE_SRC for REPLACE_BLEND_PRE_SRC_2X_ALPHA means "double the src." + // It's close to the same, but clamping can still be an issue. + case GE_SRCBLEND_DOUBLEDSTALPHA: srcFactor = "float3(2.0, 2.0, 2.0)"; break; case GE_SRCBLEND_DOUBLEINVDSTALPHA: srcFactor = "ERROR"; break; case GE_SRCBLEND_FIXA: srcFactor = "u_blendFixA"; break; } diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index 440b98908e..4c170166bb 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -497,7 +497,9 @@ bool GenerateFragmentShader(const ShaderID &id, char *buffer) { case GE_SRCBLEND_INVDSTALPHA: srcFactor = "ERROR"; break; case GE_SRCBLEND_DOUBLESRCALPHA: srcFactor = "vec3(v.a * 2.0)"; break; case GE_SRCBLEND_DOUBLEINVSRCALPHA: srcFactor = "vec3(1.0 - v.a * 2.0)"; break; - case GE_SRCBLEND_DOUBLEDSTALPHA: srcFactor = "ERROR"; break; + // PRE_SRC for REPLACE_BLEND_PRE_SRC_2X_ALPHA means "double the src." + // It's close to the same, but clamping can still be an issue. + case GE_SRCBLEND_DOUBLEDSTALPHA: srcFactor = "vec3(2.0)"; break; case GE_SRCBLEND_DOUBLEINVDSTALPHA: srcFactor = "ERROR"; break; case GE_SRCBLEND_FIXA: srcFactor = "u_blendFixA"; break; }