mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
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.
This commit is contained in:
@ -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");
|
||||
}
|
||||
|
Reference in New Issue
Block a user