[RFC] gallium/nir: Add shader-based blending helpers

Submitted by Alyssa Rosenzweig on Nov. 11, 2018, 7:23 p.m.

Details

Message ID 20181111192359.GA7563@kevin
State New
Headers show
Series "gallium/nir: Add shader-based blending helpers" ( rev: 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Alyssa Rosenzweig Nov. 11, 2018, 7:23 p.m.
Some mobile GPUs lack fixed-function hardware for blending, instead
emulating blending via internal shaders. In particular for us, vc4 lacks
most of the fragment pipeline, implementing blending in the epilogue of
the fragment shader. Newer Malis supported by Panfrost have limitations
in their fixed-function pipeline, introducing a dedicated "blend shader"
stage to implement arbitrarily complex operations.

This helper, originally from vc4, generates NIR programs from a Gallium
blend state. At the moment, it implements only floating-point ES2 blend
operations; integer blending and non-ES2 modes like logic ops are
planned for the future.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
---
 src/gallium/auxiliary/meson.build           |   2 +
 src/gallium/auxiliary/nir/nir_lower_blend.c | 188 ++++++++++++++++++++
 src/gallium/auxiliary/nir/nir_lower_blend.h |  36 ++++
 3 files changed, 226 insertions(+)
 create mode 100644 src/gallium/auxiliary/nir/nir_lower_blend.c
 create mode 100644 src/gallium/auxiliary/nir/nir_lower_blend.h

Patch hide | download patch | download mbox

diff --git a/src/gallium/auxiliary/meson.build b/src/gallium/auxiliary/meson.build
index e1497992b1..4fb3620374 100644
--- a/src/gallium/auxiliary/meson.build
+++ b/src/gallium/auxiliary/meson.build
@@ -365,6 +365,8 @@  files_libgallium = files(
   'util/u_viewport.h',
   'nir/tgsi_to_nir.c',
   'nir/tgsi_to_nir.h',
+  'nir/nir_lower_blend.c',
+  'nir/nir_lower_blend.h',
 )
 
 if dep_libdrm.found()
diff --git a/src/gallium/auxiliary/nir/nir_lower_blend.c b/src/gallium/auxiliary/nir/nir_lower_blend.c
new file mode 100644
index 0000000000..2cb32ca1e4
--- /dev/null
+++ b/src/gallium/auxiliary/nir/nir_lower_blend.c
@@ -0,0 +1,188 @@ 
+/*
+ * Copyright © 2015 Broadcom
+ * Copyright © 2018 Alyssa Rosenzweig
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "nir_lower_blend.h"
+
+/* Implements fixed-function blending in software. The standard entrypoint for
+ * floating point blending is nir_blending_f, called with the Gallium blend
+ * state and nir_ssa_def's for the various parameters used in blending. These
+ * routines may be used to construct dedicated blend shaders or appended to
+ * fragment shaders; accordingly, they do not perform I/O to maximize
+ * flexibility.
+ *
+ * Inputs are assumed to be clamped to [0, 1]. fsat instructions must be added
+ * by the caller if clamping is not otherwise performed.
+ *
+ * TODO: sRGB, logic ops, integers, dual-source blending, advanced blending
+ */
+
+/* src and dst are vec4 */
+
+static nir_ssa_def *
+nir_blend_channel_f(nir_builder *b,
+                    nir_ssa_def **src,
+                    nir_ssa_def **dst,
+                    nir_ssa_def *constant,
+                    unsigned factor,
+                    int channel)
+{
+   switch(factor) {
+   case PIPE_BLENDFACTOR_ONE:
+      return nir_imm_float(b, 1.0);
+   case PIPE_BLENDFACTOR_SRC_COLOR:
+      return src[channel];
+   case PIPE_BLENDFACTOR_SRC_ALPHA:
+      return src[3];
+   case PIPE_BLENDFACTOR_DST_ALPHA:
+      return dst[3];
+   case PIPE_BLENDFACTOR_DST_COLOR:
+      return dst[channel];
+   case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
+      if (channel != 3) {
+         return nir_fmin(b,
+                         src[3],
+                         nir_fsub(b,
+                                  nir_imm_float(b, 1.0),
+                                  dst[3]));
+      } else {
+         return nir_imm_float(b, 1.0);
+      }
+   case PIPE_BLENDFACTOR_CONST_COLOR:
+      return nir_channel(b, constant, channel);
+   case PIPE_BLENDFACTOR_CONST_ALPHA:
+      return nir_channel(b, constant, 3);
+   case PIPE_BLENDFACTOR_ZERO:
+      return nir_imm_float(b, 0.0);
+   case PIPE_BLENDFACTOR_INV_SRC_COLOR:
+      return nir_fsub(b, nir_imm_float(b, 1.0), src[channel]);
+   case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
+      return nir_fsub(b, nir_imm_float(b, 1.0), src[3]);
+   case PIPE_BLENDFACTOR_INV_DST_ALPHA:
+      return nir_fsub(b, nir_imm_float(b, 1.0), dst[3]);
+   case PIPE_BLENDFACTOR_INV_DST_COLOR:
+      return nir_fsub(b, nir_imm_float(b, 1.0), dst[channel]);
+   case PIPE_BLENDFACTOR_INV_CONST_COLOR:
+      return nir_fsub(b, nir_imm_float(b, 1.0),
+                      nir_channel(b, constant, channel));
+   case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
+      return nir_fsub(b, nir_imm_float(b, 1.0),
+                      nir_channel(b, constant, 3));
+
+
+   default:
+   case PIPE_BLENDFACTOR_SRC1_COLOR:
+   case PIPE_BLENDFACTOR_SRC1_ALPHA:
+   case PIPE_BLENDFACTOR_INV_SRC1_COLOR:
+   case PIPE_BLENDFACTOR_INV_SRC1_ALPHA:
+      /* Unsupported. */
+      fprintf(stderr, "Unknown blend factor %d\n", factor);
+      return nir_imm_float(b, 1.0);
+   }
+}
+
+
+static nir_ssa_def *
+nir_blend_func_f(nir_builder *b, nir_ssa_def *src, nir_ssa_def *dst,
+                 unsigned func)
+{
+   switch (func) {
+   case PIPE_BLEND_ADD:
+      return nir_fadd(b, src, dst);
+   case PIPE_BLEND_SUBTRACT:
+      return nir_fsub(b, src, dst);
+   case PIPE_BLEND_REVERSE_SUBTRACT:
+      return nir_fsub(b, dst, src);
+   case PIPE_BLEND_MIN:
+      return nir_fmin(b, src, dst);
+   case PIPE_BLEND_MAX:
+      return nir_fmax(b, src, dst);
+
+   default:
+      /* Unsupported. */
+      fprintf(stderr, "Unknown blend func %d\n", func);
+      return src;
+
+   }
+}
+
+static void
+nir_per_channel_blending_f(const struct pipe_rt_blend_state *blend, nir_builder *b, nir_ssa_def **result,
+                           nir_ssa_def **src_color, nir_ssa_def **dst_color, nir_ssa_def *con)
+{
+   if (!blend->blend_enable) {
+      for (int i = 0; i < 4; i++)
+         result[i] = src_color[i];
+      return;
+   }
+
+   nir_ssa_def *src_blend[4], *dst_blend[4];
+   for (int i = 0; i < 4; i++) {
+      int src_factor = ((i != 3) ? blend->rgb_src_factor :
+                        blend->alpha_src_factor);
+      int dst_factor = ((i != 3) ? blend->rgb_dst_factor :
+                        blend->alpha_dst_factor);
+      src_blend[i] = nir_fmul(b, src_color[i],
+                              nir_blend_channel_f(b,
+                                    src_color, dst_color,
+                                    con, src_factor, i));
+      dst_blend[i] = nir_fmul(b, dst_color[i],
+                              nir_blend_channel_f(b,
+                                    src_color, dst_color,
+                                    con, dst_factor, i));
+   }
+
+   for (int i = 0; i < 4; i++) {
+      result[i] = nir_blend_func_f(b, src_blend[i], dst_blend[i],
+                                   ((i != 3) ? blend->rgb_func :
+                                    blend->alpha_func));
+   }
+}
+
+/* Arguments are vec4s */
+
+nir_ssa_def *
+nir_blending_f(const struct pipe_rt_blend_state *blend, nir_builder *b,
+               nir_ssa_def *src_color, nir_ssa_def *dst_color,
+               nir_ssa_def *constant)
+{
+   nir_ssa_def* result[4];
+
+   nir_ssa_def* src_components[4] = {
+      nir_channel(b, src_color, 0),
+      nir_channel(b, src_color, 1),
+      nir_channel(b, src_color, 2),
+      nir_channel(b, src_color, 3)
+   };
+
+   nir_ssa_def* dst_components[4] = {
+      nir_channel(b, dst_color, 0),
+      nir_channel(b, dst_color, 1),
+      nir_channel(b, dst_color, 2),
+      nir_channel(b, dst_color, 3)
+   };
+
+   nir_per_channel_blending_f(blend, b, result, src_components, dst_components, constant);
+
+   return nir_vec(b, result, 4);
+}
diff --git a/src/gallium/auxiliary/nir/nir_lower_blend.h b/src/gallium/auxiliary/nir/nir_lower_blend.h
new file mode 100644
index 0000000000..da508babbe
--- /dev/null
+++ b/src/gallium/auxiliary/nir/nir_lower_blend.h
@@ -0,0 +1,36 @@ 
+/*
+ * Copyright © 2015 Broadcom
+ * Copyright © 2018 Alyssa Rosenzweig
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef _NIR_LOWER_BLEND_H_
+#define _NIR_LOWER_BLEND_H_
+
+#include "nir_builder.h"
+#include "pipe/p_state.h"
+
+nir_ssa_def *
+nir_blending_f(const struct pipe_rt_blend_state *blend, nir_builder *b,
+               nir_ssa_def *src_color, nir_ssa_def *dst_color,
+               nir_ssa_def *constant);
+
+#endif

Comments

Alyssa Rosenzweig <alyssa@rosenzweig.io> writes:

> Some mobile GPUs lack fixed-function hardware for blending, instead
> emulating blending via internal shaders. In particular for us, vc4 lacks
> most of the fragment pipeline, implementing blending in the epilogue of
> the fragment shader. Newer Malis supported by Panfrost have limitations
> in their fixed-function pipeline, introducing a dedicated "blend shader"
> stage to implement arbitrarily complex operations.
>
> This helper, originally from vc4, generates NIR programs from a Gallium
> blend state. At the moment, it implements only floating-point ES2 blend
> operations; integer blending and non-ES2 modes like logic ops are
> planned for the future.
>
> Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>

I would love to see this up in src/compiler/nir.  We'd have to define
enums for the blend factors in shader_enums.h, but we've done that for
compare funcs and advanced blend, already.  Also, we should probably
convert vc4 over in the process, so it's not just new unused code.

I'm also hoping that at some point I can figure out how to represent the
TLB loads properly in NIR (with respect to multisampling, in particular)
so that we can move the whole blend pipeline up.
> I would love to see this up in src/compiler/nir.

Is there a compelling reason to have it in NIR rather in Gallium, adding
an extra layer of translation overhead / indirection when coming from
Gallium blend state? (Mythical future Vulkan support, I guess?)

> Also, we should probably convert vc4 over in the process, so it's not
> just new unused code.

Probably. This version of the patch is pretty close to the _f blend
functions in vc4, so it should be at least partially drop-in. I'd love
to get the corresponding Panfrost code upstreamed as well, but for
$REASONS that probably can't happen yet.

> I'm also hoping that at some point I can figure out how to represent
> the TLB loads properly in NIR (with respect to multisampling, in
> particular) so that we can move the whole blend pipeline up.

It's worth noting that for us, because we're in the wacky dedicated
shader stage, it's unclear whether that would actually help. I'll need
to poke harder, I guess ;)

I haven't considered how MSAA interacts with this, ick...
Alyssa Rosenzweig <alyssa@rosenzweig.io> writes:

>> I would love to see this up in src/compiler/nir.
>
> Is there a compelling reason to have it in NIR rather in Gallium, adding
> an extra layer of translation overhead / indirection when coming from
> Gallium blend state? (Mythical future Vulkan support, I guess?)

Exactly.  Also I think we had some reason for using it on i965,
somewhere.