summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/PixelShaderGen.cpp
diff options
context:
space:
mode:
authorJonathan Hamilton <jtrhamilton@gmail.com>2017-10-25 22:44:39 -0700
committerJonathan Hamilton <jtrhamilton@gmail.com>2018-01-05 09:56:46 -0800
commit29a9ed043bd0946497edf7c4d392ce46041b9817 (patch)
tree3104979b668377da1499a3b4afc7ac29c557e3ce /Source/Core/VideoCommon/PixelShaderGen.cpp
parent11976526d11f0475a3af126758260cdd69dd2a35 (diff)
Implement dual-source blending in shader
For some GLES drivers that don't support dual-source blending, but do support GL_EXT_shader_framebuffer_fetch, this might be useful.
Diffstat (limited to 'Source/Core/VideoCommon/PixelShaderGen.cpp')
-rw-r--r--Source/Core/VideoCommon/PixelShaderGen.cpp120
1 files changed, 118 insertions, 2 deletions
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index 0ccfb09679..1a427b70d0 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -16,6 +16,7 @@
#include "VideoCommon/DriverDetails.h"
#include "VideoCommon/LightingShaderGen.h"
#include "VideoCommon/NativeVertexFormat.h"
+#include "VideoCommon/RenderState.h"
#include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
@@ -319,6 +320,21 @@ PixelShaderUid GetPixelShaderUid()
uid_data->fog_proj = bpmem.fog.c_proj_fsel.proj;
uid_data->fog_RangeBaseEnabled = bpmem.fogRange.Base.Enabled;
+ BlendingState state = {};
+ state.Generate(bpmem);
+
+ if (state.usedualsrc && state.dstalpha && g_ActiveConfig.backend_info.bSupportsFramebufferFetch &&
+ !g_ActiveConfig.backend_info.bSupportsDualSourceBlend)
+ {
+ uid_data->blend_enable = state.blendenable;
+ uid_data->blend_src_factor = state.srcfactor;
+ uid_data->blend_src_factor_alpha = state.srcfactoralpha;
+ uid_data->blend_dst_factor = state.dstfactor;
+ uid_data->blend_dst_factor_alpha = state.dstfactoralpha;
+ uid_data->blend_subtract = state.subtract;
+ uid_data->blend_subtract_alpha = state.subtractAlpha;
+ }
+
return out;
}
@@ -447,6 +463,7 @@ static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_dat
static void WriteFog(ShaderCode& out, const pixel_shader_uid_data* uid_data);
static void WriteColor(ShaderCode& out, APIType api_type, const pixel_shader_uid_data* uid_data,
bool use_dual_source);
+static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data);
ShaderCode GeneratePixelShaderCode(APIType ApiType, const ShaderHostConfig& host_config,
const pixel_shader_uid_data* uid_data)
@@ -519,6 +536,8 @@ ShaderCode GeneratePixelShaderCode(APIType ApiType, const ShaderHostConfig& host
host_config.backend_dual_source_blend &&
(!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) ||
uid_data->useDstAlpha);
+ const bool use_shader_blend =
+ !use_dual_source && (uid_data->useDstAlpha && host_config.backend_shader_framebuffer_fetch);
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
{
@@ -535,6 +554,17 @@ ShaderCode GeneratePixelShaderCode(APIType ApiType, const ShaderHostConfig& host
out.Write("FRAGMENT_OUTPUT_LOCATION_INDEXED(0, 1) out vec4 ocol1;\n");
}
}
+ else if (use_shader_blend)
+ {
+ if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_FRAGMENT_SHADER_INDEX_DECORATION))
+ {
+ out.Write("FRAGMENT_OUTPUT_LOCATION(0) FRAGMENT_INOUT vec4 ocol0;\n");
+ }
+ else
+ {
+ out.Write("FRAGMENT_OUTPUT_LOCATION_INDEXED(0, 0) FRAGMENT_INOUT vec4 ocol0;\n");
+ }
+ }
else
{
out.Write("FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n");
@@ -575,6 +605,11 @@ ShaderCode GeneratePixelShaderCode(APIType ApiType, const ShaderHostConfig& host
out.Write("void main()\n{\n");
out.Write("\tfloat4 rawpos = gl_FragCoord;\n");
+ if (use_shader_blend)
+ {
+ // Store off a copy of the initial fb value for blending
+ out.Write("\tfloat4 initial_ocol0 = FB_FETCH_VALUE;\n");
+ }
}
else // D3D
{
@@ -710,7 +745,8 @@ ShaderCode GeneratePixelShaderCode(APIType ApiType, const ShaderHostConfig& host
// testing result)
if (uid_data->Pretest == AlphaTest::UNDETERMINED ||
(uid_data->Pretest == AlphaTest::FAIL && uid_data->late_ztest))
- WriteAlphaTest(out, uid_data, ApiType, uid_data->per_pixel_depth, use_dual_source);
+ WriteAlphaTest(out, uid_data, ApiType, uid_data->per_pixel_depth,
+ use_dual_source || use_shader_blend);
if (uid_data->zfreeze)
{
@@ -793,7 +829,11 @@ ShaderCode GeneratePixelShaderCode(APIType ApiType, const ShaderHostConfig& host
WriteFog(out, uid_data);
// Write the color and alpha values to the framebuffer
- WriteColor(out, ApiType, uid_data, use_dual_source);
+ // If using shader blend, we still use the separate alpha
+ WriteColor(out, ApiType, uid_data, use_dual_source || use_shader_blend);
+
+ if (use_shader_blend)
+ WriteBlend(out, uid_data);
if (uid_data->bounding_box)
{
@@ -1358,3 +1398,79 @@ static void WriteColor(ShaderCode& out, APIType api_type, const pixel_shader_uid
}
}
}
+
+static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data)
+{
+ if (uid_data->blend_enable)
+ {
+ static const std::array<const char*, 8> blendSrcFactor = {
+ "float3(0,0,0);", // ZERO
+ "float3(1,1,1);", // ONE
+ "initial_ocol0.rgb;", // DSTCLR
+ "float3(1,1,1) - initial_ocol0.rgb;", // INVDSTCLR
+ "ocol1.aaa;", // SRCALPHA
+ "float3(1,1,1) - ocol1.aaa;", // INVSRCALPHA
+ "initial_ocol0.aaa;", // DSTALPHA
+ "float3(1,1,1) - initial_ocol0.aaa;", // INVDSTALPHA
+ };
+ static const std::array<const char*, 8> blendSrcFactorAlpha = {
+ "0.0;", // ZERO
+ "1.0;", // ONE
+ "initial_ocol0.a;", // DSTCLR
+ "1.0 - initial_ocol0.a;", // INVDSTCLR
+ "ocol1.a;", // SRCALPHA
+ "1.0 - ocol1.a;", // INVSRCALPHA
+ "initial_ocol0.a;", // DSTALPHA
+ "1.0 - initial_ocol0.a;", // INVDSTALPHA
+ };
+ static const std::array<const char*, 8> blendDstFactor = {
+ "float3(0,0,0);", // ZERO
+ "float3(1,1,1);", // ONE
+ "ocol0.rgb;", // SRCCLR
+ "float3(1,1,1) - ocol0.rgb;", // INVSRCCLR
+ "ocol1.aaa;", // SRCALHA
+ "float3(1,1,1) - ocol1.aaa;", // INVSRCALPHA
+ "initial_ocol0.aaa;", // DSTALPHA
+ "float3(1,1,1) - initial_ocol0.aaa;", // INVDSTALPHA
+ };
+ static const std::array<const char*, 8> blendDstFactorAlpha = {
+ "0.0;", // ZERO
+ "1.0;", // ONE
+ "ocol0.a;", // SRCCLR
+ "1.0 - ocol0.a;", // INVSRCCLR
+ "ocol1.a;", // SRCALPHA
+ "1.0 - ocol1.a;", // INVSRCALPHA
+ "initial_ocol0.a;", // DSTALPHA
+ "1.0 - initial_ocol0.a;", // INVDSTALPHA
+ };
+ out.Write("\tfloat4 blend_src;\n");
+ out.Write("\tblend_src.rgb = %s\n", blendSrcFactor[uid_data->blend_src_factor]);
+ out.Write("\tblend_src.a = %s\n", blendSrcFactorAlpha[uid_data->blend_src_factor_alpha]);
+ out.Write("\tfloat4 blend_dst;\n");
+ out.Write("\tblend_dst.rgb = %s\n", blendDstFactor[uid_data->blend_dst_factor]);
+ out.Write("\tblend_dst.a = %s\n", blendDstFactorAlpha[uid_data->blend_dst_factor_alpha]);
+
+ out.Write("\tfloat4 blend_result;\n");
+ if (uid_data->blend_subtract)
+ {
+ out.Write("\tblend_result.rgb = initial_ocol0.rgb * blend_dst.rgb - ocol0.rgb * "
+ "blend_src.rgb;\n");
+ }
+ else
+ {
+ out.Write(
+ "\tblend_result.rgb = initial_ocol0.rgb * blend_dst.rgb + ocol0.rgb * blend_src.rgb;\n");
+ }
+
+ if (uid_data->blend_subtract_alpha)
+ out.Write("\tblend_result.a = initial_ocol0.a * blend_dst.a - ocol0.a * blend_src.a;\n");
+ else
+ out.Write("\tblend_result.a = initial_ocol0.a * blend_dst.a + ocol0.a * blend_src.a;\n");
+ }
+ else
+ {
+ out.Write("\tfloat4 blend_result = ocol0;\n");
+ }
+
+ out.Write("\tocol0 = blend_result;\n");
+}