From 3bf17d57ac7a55640cf1c6f16f5bf41d96e31d43 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Fri, 18 Aug 2017 23:51:59 +0200
Subject: [PATCH] wined3d: Implement dual source blending.

---
 dlls/d3d11/tests/d3d11.c       |  2 +-
 dlls/wined3d/adapter_gl.c      | 10 ++++++++++
 dlls/wined3d/context.c         | 12 +++++++++++-
 dlls/wined3d/glsl_shader.c     | 20 +++++++++++++++++---
 dlls/wined3d/shader.c          |  2 ++
 dlls/wined3d/state.c           | 14 ++++++++++++--
 dlls/wined3d/wined3d_private.h | 24 ++++++++++++++++++++++--
 7 files changed, 75 insertions(+), 9 deletions(-)

diff --git a/dlls/d3d11/tests/d3d11.c b/dlls/d3d11/tests/d3d11.c
index 4b2f435..d6d4f0a 100644
--- a/dlls/d3d11/tests/d3d11.c
+++ b/dlls/d3d11/tests/d3d11.c
@@ -29441,7 +29441,7 @@ static void test_dual_blending(void)
     ID3D11DeviceContext_ClearRenderTargetView(context, rtv[1], white);
     ID3D11DeviceContext_Draw(context, 3, 0);
 
-    todo_wine check_texture_color(test_context.backbuffer, 0xff00ffff, 0);
+    check_texture_color(test_context.backbuffer, 0xff00ffff, 0);
     todo_wine check_texture_color(render_target, 0xff0000ff, 0);
 
     ID3D11BlendState_Release(blend_state);
diff --git a/dlls/wined3d/adapter_gl.c b/dlls/wined3d/adapter_gl.c
index 09ea350..231955a 100644
--- a/dlls/wined3d/adapter_gl.c
+++ b/dlls/wined3d/adapter_gl.c
@@ -2924,6 +2924,12 @@ static void wined3d_adapter_init_limits(struct wined3d_gl_info *gl_info, struct
         gl_info->limits.buffers = min(MAX_RENDER_TARGET_VIEWS, gl_max);
         TRACE("Max draw buffers: %u.\n", gl_max);
     }
+    if (gl_info->supported[ARB_BLEND_FUNC_EXTENDED])
+    {
+        gl_info->gl_ops.gl.p_glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, &gl_max);
+        gl_info->limits.dual_buffers = gl_max;
+        TRACE("Max dual source draw buffers: %u.\n", gl_max);
+    }
     if (gl_info->supported[ARB_MULTITEXTURE])
     {
         if (gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
@@ -3751,6 +3757,10 @@ static BOOL wined3d_adapter_init_gl_caps(struct wined3d_adapter *adapter,
 
     TRACE("Max texture stages: %u.\n", d3d_info->limits.ffp_blend_stages);
 
+    d3d_info->valid_dual_rt_mask = 0;
+    for (i = 0; i < gl_info->limits.dual_buffers; ++i)
+        d3d_info->valid_dual_rt_mask |= (1u << i);
+
     if (!d3d_info->shader_color_key)
     {
         /* We do not want to deal with re-creating immutable texture storage
diff --git a/dlls/wined3d/context.c b/dlls/wined3d/context.c
index aa4d260..2c7935c 100644
--- a/dlls/wined3d/context.c
+++ b/dlls/wined3d/context.c
@@ -3153,10 +3153,20 @@ static unsigned int find_draw_buffers_mask(const struct wined3d_context *context
     else if (!context->render_offscreen)
         return context_generate_rt_mask_from_resource(rts[0]->resource);
 
+    /* If we attach more buffers than supported in dual blend mode, the NVIDIA
+     * driver generates the following error:
+     *      GL_INVALID_OPERATION error generated. State(s) are invalid: blend.
+     * DX11 does not treat this configuration as invalid, so disable the unused ones.
+     */
     rt_mask = ps ? ps->reg_maps.rt_mask : 1;
-    rt_mask &= (1u << gl_info->limits.buffers) - 1;
+
+    if (wined3d_dualblend_enabled(state, context->gl_info))
+        rt_mask &= context->d3d_info->valid_dual_rt_mask;
+    else
+        rt_mask &= (1u << gl_info->limits.buffers) - 1;
 
     mask = rt_mask;
+    i = 0;
     while (mask)
     {
         i = wined3d_bit_scan(&mask);
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index f85fb5a..066a8fd 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -2811,6 +2811,7 @@ static void shader_glsl_get_register_name(const struct wined3d_shader_register *
             break;
 
         case WINED3DSPR_COLOROUT:
+            /* FIXME: should check dual_buffers when dual blending is enabled */
             if (reg->idx[0].offset >= gl_info->limits.buffers)
                 WARN("Write to render target %u, only %d supported.\n",
                         reg->idx[0].offset, gl_info->limits.buffers);
@@ -7715,7 +7716,10 @@ static GLuint shader_glsl_generate_pshader(const struct wined3d_context *context
     {
         const struct wined3d_shader_signature *output_signature = &shader->output_signature;
 
-        shader_addline(buffer, "vec4 ps_out[%u];\n", gl_info->limits.buffers);
+        if (args->dual_source_blend)
+            shader_addline(buffer, "vec4 ps_out[%u];\n", gl_info->limits.dual_buffers * 2);
+        else
+            shader_addline(buffer, "vec4 ps_out[%u];\n", gl_info->limits.buffers);
         if (output_signature->element_count)
         {
             for (i = 0; i < output_signature->element_count; ++i)
@@ -7730,7 +7734,12 @@ static GLuint shader_glsl_generate_pshader(const struct wined3d_context *context
                     continue;
                 }
                 if (shader_glsl_use_explicit_attrib_location(gl_info))
-                    shader_addline(buffer, "layout(location = %u) ", output->semantic_idx);
+                {
+                    if (args->dual_source_blend)
+                        shader_addline(buffer, "layout(location = %u, index = %u) ", output->semantic_idx / 2, output->semantic_idx % 2);
+                    else
+                        shader_addline(buffer, "layout(location = %u) ", output->semantic_idx);
+                }
                 shader_addline(buffer, "out %s4 color_out%u;\n",
                         component_type_info[output->component_type].glsl_vector_type, output->semantic_idx);
             }
@@ -7743,7 +7752,12 @@ static GLuint shader_glsl_generate_pshader(const struct wined3d_context *context
             {
                 i = wined3d_bit_scan(&mask);
                 if (shader_glsl_use_explicit_attrib_location(gl_info))
-                    shader_addline(buffer, "layout(location = %u) ", i);
+                {
+                    if (args->dual_source_blend)
+                        shader_addline(buffer, "layout(location = %u, index = %u) ", i / 2, i % 2);
+                    else
+                        shader_addline(buffer, "layout(location = %u) ", i);
+                }
                 shader_addline(buffer, "out vec4 color_out%u;\n", i);
             }
         }
diff --git a/dlls/wined3d/shader.c b/dlls/wined3d/shader.c
index 609b574..138b2dd 100644
--- a/dlls/wined3d/shader.c
+++ b/dlls/wined3d/shader.c
@@ -4137,6 +4137,8 @@ void find_ps_compile_args(const struct wined3d_state *state, const struct wined3
                 args->rt_alpha_swizzle |= 1u << i;
         }
     }
+
+    args->dual_source_blend = wined3d_dualblend_enabled(state, gl_info);
 }
 
 static HRESULT pixel_shader_init(struct wined3d_shader *shader, struct wined3d_device *device,
diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
index 08423e8..b22122d 100644
--- a/dlls/wined3d/state.c
+++ b/dlls/wined3d/state.c
@@ -533,12 +533,14 @@ static void state_blend(struct wined3d_context *context, const struct wined3d_st
     const struct wined3d_format *rt_format;
     GLenum src_blend, dst_blend;
     unsigned int rt_fmt_flags;
+    BOOL enable_dual_blend;
     BOOL enable_blend;
 
     enable_blend = state->fb->render_targets[0] && state->render_states[WINED3D_RS_ALPHABLENDENABLE];
-    if (enable_blend)
+    enable_dual_blend = wined3d_dualblend_enabled(state, context->gl_info);
+
+    if (enable_blend && !enable_dual_blend)
     {
-        rt_format = state->fb->render_targets[0]->format;
         rt_fmt_flags = state->fb->render_targets[0]->format_flags;
 
         /* Disable blending in all cases even without pixelshaders.
@@ -548,6 +550,13 @@ static void state_blend(struct wined3d_context *context, const struct wined3d_st
             enable_blend = FALSE;
     }
 
+    /* Dual state blending changes the assignment of the output variables */
+    if (context->last_was_dual_blend != enable_dual_blend)
+    {
+        context->shader_update_mask |= 1u << WINED3D_SHADER_TYPE_PIXEL;
+        context->last_was_dual_blend = enable_dual_blend;
+    }
+
     if (!enable_blend)
     {
         gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
@@ -558,6 +567,7 @@ static void state_blend(struct wined3d_context *context, const struct wined3d_st
     gl_info->gl_ops.gl.p_glEnable(GL_BLEND);
     checkGLcall("glEnable(GL_BLEND)");
 
+    rt_format = state->fb->render_targets[0]->format;
     gl_blend_from_d3d(&src_blend, &dst_blend,
             state->render_states[WINED3D_RS_SRCBLEND],
             state->render_states[WINED3D_RS_DESTBLEND], rt_format);
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index dca3e81..a1c6d4e 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -197,6 +197,7 @@ struct wined3d_d3d_info
 {
     struct wined3d_d3d_limits limits;
     struct wined3d_ffp_attrib_ops ffp_attrib_ops;
+    DWORD valid_dual_rt_mask;
     unsigned int wined3d_creation_flags;
     unsigned int xyzrhw : 1;
     unsigned int emulated_flatshading : 1;
@@ -1384,7 +1385,8 @@ struct ps_compile_args
     DWORD alpha_test_func : 3;
     DWORD render_offscreen : 1;
     DWORD rt_alpha_swizzle : 8; /* MAX_RENDER_TARGET_VIEWS, 8 */
-    DWORD padding : 18;
+    DWORD dual_source_blend : 1;
+    DWORD padding : 17;
 };
 
 enum fog_src_type
@@ -1938,7 +1940,8 @@ struct wined3d_context
     DWORD shader_update_mask : 6; /* WINED3D_SHADER_TYPE_COUNT, 6 */
     DWORD clip_distance_mask : 8; /* WINED3D_MAX_CLIP_DISTANCES, 8 */
     DWORD num_untracked_materials : 2;  /* Max value 2 */
-    DWORD padding : 9;
+    DWORD last_was_dual_blend : 1;
+    DWORD padding : 8;
 
     DWORD constant_update_mask;
     DWORD numbered_array_mask;
@@ -2614,6 +2617,7 @@ struct wined3d_fbo_ops
 struct wined3d_gl_limits
 {
     UINT buffers;
+    UINT dual_buffers;
     UINT lights;
     UINT textures;
     UINT texture_coords;
@@ -3079,6 +3083,22 @@ struct wined3d_state
     struct wined3d_rasterizer_state *rasterizer_state;
 };
 
+static inline BOOL wined3d_dualblend_enabled(const struct wined3d_state *state, const struct wined3d_gl_info *gl_info)
+{
+    if (!state->fb->render_targets[0]) return FALSE;
+    if (!state->render_states[WINED3D_RS_ALPHABLENDENABLE]) return FALSE;
+    if (!gl_info->supported[ARB_BLEND_FUNC_EXTENDED]) return FALSE;
+
+#define IS_DUAL_SOURCE_BLEND(x) ((x) >= WINED3D_BLEND_SRC1COLOR && (x) <= WINED3D_BLEND_INVSRC1ALPHA)
+    if (IS_DUAL_SOURCE_BLEND(state->render_states[WINED3D_RS_SRCBLEND]))  return TRUE;
+    if (IS_DUAL_SOURCE_BLEND(state->render_states[WINED3D_RS_DESTBLEND])) return TRUE;
+    if (IS_DUAL_SOURCE_BLEND(state->render_states[WINED3D_RS_SRCBLENDALPHA]))  return TRUE;
+    if (IS_DUAL_SOURCE_BLEND(state->render_states[WINED3D_RS_DESTBLENDALPHA])) return TRUE;
+#undef IS_DUAL_SOURCE_BLEND
+
+    return FALSE;
+}
+
 struct wined3d_dummy_textures
 {
     GLuint tex_1d;
-- 
1.9.1

