st/mesa: Optionally override RGB/RGBX dst alpha blend factors
authorKenneth Graunke <kenneth@whitecape.org>
Sun, 22 Jul 2018 06:40:16 +0000 (23:40 -0700)
committerKenneth Graunke <kenneth@whitecape.org>
Wed, 16 Jan 2019 04:53:44 +0000 (20:53 -0800)
Intel's blending hardware does not properly return 1.0 for destination
alpha for RGBX formats; it requires the factors to be overridden to
either zero or one.  Broadcom vc4 and v3d also could use this override.
While overriding these factors is safe in general, Nouveau and Radeon
would prefer not to.  Their blending hardware already returns correct
values for RGB/RGBX formats, and would like to avoid the resulting
per-buffer blending and independent blend factors (rgb != a) since it
can cause additional overhead.

I considered simply handling this in the driver, but it's not as nice.
pipe_blend_state doesn't have any format information, so we'd need the
hardware blend state to depend on both pipe_blend_state and
pipe_framebuffer_state.  Furthermore, Intel GPUs don't have a native
RGBX_SNORM format, so I avoid exposing one, which makes Gallium fall
back to RGBA_SNORM.  The pipe_surfaces we get in the driver have an RGBA
format, making it impossible to tell that there shouldn't be an alpha
channel.  One could argue that st not handling it in that case is a bug.
To work around this, we'd have to expose RGBX pipe formats, mapped to
RGBA hardware formats, and add format swizzling special cases.  All
doable, but it ends up being more code than I'd like.

st_atom_blend already has access to the right information and it's
trivial to accomplish there, so we just add a cap bit and do that.

Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Reviewed-by: Eric Anholt <eric@anholt.net>
src/gallium/auxiliary/util/u_screen.c
src/gallium/docs/source/screen.rst
src/gallium/include/pipe/p_defines.h
src/mesa/main/fbobject.c
src/mesa/main/mtypes.h
src/mesa/state_tracker/st_atom_blend.c
src/mesa/state_tracker/st_context.c
src/mesa/state_tracker/st_context.h

index 66dfa852540f7c785c81661afd36cd2cc77336c3..c14edde8592fcf9385610854b5951f7b72f70258 100644 (file)
@@ -77,6 +77,7 @@ u_pipe_screen_get_param_defaults(struct pipe_screen *pscreen,
    case PIPE_CAP_MIXED_COLORBUFFER_FORMATS:
    case PIPE_CAP_SEAMLESS_CUBE_MAP:
    case PIPE_CAP_SEAMLESS_CUBE_MAP_PER_TEXTURE:
+   case PIPE_CAP_RGB_OVERRIDE_DST_ALPHA_BLEND:
       return 0;
 
    case PIPE_CAP_MIN_TEXEL_OFFSET:
index 14a769cc0ee94eebdf6939d5637a21531958a40d..9b75a407db39facc4a9f21f18548490dc627288e 100644 (file)
@@ -482,6 +482,7 @@ The integer capabilities:
   enable EXT_multisampled_render_to_texture.
 * ``PIPE_CAP_TGSI_ATOMFADD``: Atomic floating point adds are supported on
   images, buffers, and shared memory.
+* ``PIPE_CAP_RGB_OVERRIDE_DST_ALPHA_BLEND``: True if the driver needs blend state to use zero/one instead of destination alpha for RGB/XRGB formats.
 
 .. _pipe_capf:
 
index d76fadadfdf2354975806da784e9ad2bc2ce8248..ae53c723c7e5c5b534e80799a9cb2c5e120c7b06 100644 (file)
@@ -853,6 +853,7 @@ enum pipe_cap
    PIPE_CAP_SURFACE_SAMPLE_COUNT,
    PIPE_CAP_TGSI_ATOMFADD,
    PIPE_CAP_QUERY_PIPELINE_STATISTICS_SINGLE,
+   PIPE_CAP_RGB_OVERRIDE_DST_ALPHA_BLEND,
 };
 
 /**
index 23e49396199ce9ad42facbc2299142841af9937a..4ff00f273b50e15f41536933fef41383c7dc3efc 100644 (file)
@@ -1003,6 +1003,7 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx,
    fb->_HasSNormOrFloatColorBuffer = GL_FALSE;
    fb->_HasAttachments = true;
    fb->_IntegerBuffers = 0;
+   fb->_RGBBuffers = 0;
 
    /* Start at -2 to more easily loop over all attachment points.
     *  -2: depth buffer
@@ -1149,6 +1150,9 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx,
          if (_mesa_is_format_integer_color(attFormat))
             fb->_IntegerBuffers |= (1 << i);
 
+         if (f == GL_RGB)
+            fb->_RGBBuffers |= (1 << i);
+
          fb->_AllColorBuffersFixedPoint =
             fb->_AllColorBuffersFixedPoint &&
             (type == GL_UNSIGNED_NORMALIZED || type == GL_SIGNED_NORMALIZED);
index 3d4673aa7e882d74237d8510aaade165bf0d11bf..241c2b92f7a07ffbda073937ad0ca522f5fa5490 100644 (file)
@@ -3506,6 +3506,7 @@ struct gl_framebuffer
    bool _HasAttachments;
 
    GLbitfield _IntegerBuffers;  /**< Which color buffers are integer valued */
+   GLbitfield _RGBBuffers;  /**< Which color buffers have baseformat == RGB */
 
    /* ARB_color_buffer_float */
    GLboolean _AllColorBuffersFixedPoint; /* no integer, no float */
index 9f7f779289e90d7adbc58deacb98f3813d864dda..a3ad037125b1b225bb945e843e410dbf91951bdf 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "framebuffer.h"
 #include "main/blend.h"
+#include "main/glformats.h"
 #include "main/macros.h"
 
 /**
@@ -126,8 +127,9 @@ colormask_per_rt(const struct gl_context *ctx, unsigned num_cb)
  * Figure out if blend enables/state are different per rt.
  */
 static GLboolean
-blend_per_rt(const struct gl_context *ctx, unsigned num_cb)
+blend_per_rt(const struct st_context *st, unsigned num_cb)
 {
+   const struct gl_context *ctx = st->ctx;
    GLbitfield cb_mask = u_bit_consecutive(0, num_cb);
    GLbitfield blend_enabled = ctx->Color.BlendEnabled & cb_mask;
 
@@ -145,9 +147,49 @@ blend_per_rt(const struct gl_context *ctx, unsigned num_cb)
        * must be handled on a per buffer basis. */
       return GL_TRUE;
    }
+
+   if (st->needs_rgb_dst_alpha_override && ctx->DrawBuffer->_RGBBuffers) {
+      /* Overriding requires independent blend functions (not just enables),
+       * require drivers exposing PIPE_CAP_RGB_OVERRIDE_DST_ALPHA_BLEND to
+       * also expose PIPE_CAP_INDEP_BLEND_FUNC.
+       */
+      assert(st->has_indep_blend_func);
+
+      /* If some of the buffers are RGB, we may need to override blend
+       * factors that reference destination-alpha to constants.  We may
+       * need different blend factor overrides per buffer (say one uses
+       * a DST_ALPHA factor and another uses INV_DST_ALPHA), so we flip
+       * on independent blending.  This may not be required in all cases,
+       * but burning the CPU to figure it out is probably not worthwhile.
+       */
+      return GL_TRUE;
+   }
+
    return GL_FALSE;
 }
 
+/**
+ * Modify blend function to force destination alpha to 1.0
+ *
+ * If \c function specifies a blend function that uses destination alpha,
+ * replace it with a function that hard-wires destination alpha to 1.0.
+ * This is useful when emulating a GL RGB format with an RGBA pipe_format.
+ */
+static enum pipe_blendfactor
+fix_xrgb_alpha(enum pipe_blendfactor factor)
+{
+   switch (factor) {
+   case PIPE_BLENDFACTOR_DST_ALPHA:
+      return PIPE_BLENDFACTOR_ONE;
+
+   case PIPE_BLENDFACTOR_INV_DST_ALPHA:
+   case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
+      return PIPE_BLENDFACTOR_ZERO;
+   default:
+      return factor;
+   }
+}
+
 void
 st_update_blend( struct st_context *st )
 {
@@ -160,7 +202,7 @@ st_update_blend( struct st_context *st )
    memset(blend, 0, sizeof(*blend));
 
    if (num_cb > 1 &&
-       (blend_per_rt(ctx, num_cb) || colormask_per_rt(ctx, num_cb))) {
+       (blend_per_rt(st, num_cb) || colormask_per_rt(ctx, num_cb))) {
       num_state = num_cb;
       blend->independent_blend_enable = 1;
    }
@@ -216,6 +258,18 @@ st_update_blend( struct st_context *st )
             blend->rt[i].alpha_dst_factor =
                translate_blend(ctx->Color.Blend[j].DstA);
          }
+
+         const struct gl_renderbuffer *rb =
+            ctx->DrawBuffer->_ColorDrawBuffers[i];
+
+         if (st->needs_rgb_dst_alpha_override && rb &&
+             (ctx->DrawBuffer->_RGBBuffers & (1 << i))) {
+            struct pipe_rt_blend_state *rt = &blend->rt[i];
+            rt->rgb_src_factor = fix_xrgb_alpha(rt->rgb_src_factor);
+            rt->rgb_dst_factor = fix_xrgb_alpha(rt->rgb_dst_factor);
+            rt->alpha_src_factor = fix_xrgb_alpha(rt->alpha_src_factor);
+            rt->alpha_dst_factor = fix_xrgb_alpha(rt->alpha_dst_factor);
+         }
       }
    }
    else {
index 30380446041aa07795b2fc75ca3306a32bc61fe4..0a0bd8ba1ca6d2cc946a1bce798751f1de21d7d8 100644 (file)
@@ -464,6 +464,10 @@ st_create_context_priv(struct gl_context *ctx, struct pipe_context *pipe,
       screen->get_param(screen, PIPE_CAP_MULTI_DRAW_INDIRECT);
    st->has_single_pipe_stat =
       screen->get_param(screen, PIPE_CAP_QUERY_PIPELINE_STATISTICS_SINGLE);
+   st->has_indep_blend_func =
+      screen->get_param(screen, PIPE_CAP_INDEP_BLEND_FUNC);
+   st->needs_rgb_dst_alpha_override =
+      screen->get_param(screen, PIPE_CAP_RGB_OVERRIDE_DST_ALPHA_BLEND);
 
    st->has_hw_atomics =
       screen->get_shader_param(screen, PIPE_SHADER_FRAGMENT,
index 8b736ebff75ff76f7cf10af1d336b333a9a178cb..ed69e3d4873806a584ed7d52a149f666b5e52f6c 100644 (file)
@@ -128,6 +128,8 @@ struct st_context
    boolean has_half_float_packing;
    boolean has_multi_draw_indirect;
    boolean has_single_pipe_stat;
+   boolean has_indep_blend_func;
+   boolean needs_rgb_dst_alpha_override;
    boolean can_bind_const_buffer_as_vertex;
 
    /**