[Mesa-dev] [PATCH 4/7] nir: Add nir_lower_blend pass

Alyssa Rosenzweig alyssa at rosenzweig.io
Mon May 6 02:26:08 UTC 2019


This new lowering pass implements the OpenGL ES blend pipeline in
shaders, applicable to hardware lacking full-featured blending hardware
(including Midgard/Bifrost and vc4). This pass is run on a fragment
shader, rewriting the store to a blended version, loading in the
framebuffer destination color and constant color via intrinsics as
necessary. This pass is sufficient for OpenGL ES 2.0 and is verified to
pass dEQP's blend tests. That said, at present it has the following
limitations:

 - MRT is not supported.
 - Logic ops are not supported.

MRT support is on my TODO list but paused until MRT is implemented in
the rest of the driver. Both changes should be fairly trivial.

It also includes MIN/MAX modes, so in conjunction with the advanced
blend mode lowering it should be sufficient for ES3, though this has not
been thoroughly tested. It is an open question whether the current GLSL
IR based advanced blend lowering should be NIRified and merged into this
pass.

 ...Dual-source blending is not supported, Ryan.

Signed-off-by: Alyssa Rosenzweig <alyssa at rosenzweig.io>
Cc: Eric Anholt <eric at anholt.net>
Cc: Kenneth Graunke <kenneth at whitecape.org>
---
 src/compiler/Makefile.sources      |   1 +
 src/compiler/nir/meson.build       |   1 +
 src/compiler/nir/nir.h             |  22 +++
 src/compiler/nir/nir_lower_blend.c | 214 +++++++++++++++++++++++++++++
 4 files changed, 238 insertions(+)
 create mode 100644 src/compiler/nir/nir_lower_blend.c

diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources
index 9bebc3d8867..d68b9550b02 100644
--- a/src/compiler/Makefile.sources
+++ b/src/compiler/Makefile.sources
@@ -238,6 +238,7 @@ NIR_FILES = \
 	nir/nir_lower_bit_size.c \
 	nir/nir_lower_bool_to_float.c \
 	nir/nir_lower_bool_to_int32.c \
+	nir/nir_lower_blend.c \
 	nir/nir_lower_clamp_color_outputs.c \
 	nir/nir_lower_clip.c \
 	nir/nir_lower_clip_cull_distance_arrays.c \
diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build
index a8faeb9c018..73ab62d4b46 100644
--- a/src/compiler/nir/meson.build
+++ b/src/compiler/nir/meson.build
@@ -116,6 +116,7 @@ files_libnir = files(
   'nir_lower_array_deref_of_vec.c',
   'nir_lower_atomics_to_ssbo.c',
   'nir_lower_bitmap.c',
+  'nir_lower_blend.c',
   'nir_lower_bool_to_float.c',
   'nir_lower_bool_to_int32.c',
   'nir_lower_clamp_color_outputs.c',
diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h
index 37161e83e4d..8b68faed819 100644
--- a/src/compiler/nir/nir.h
+++ b/src/compiler/nir/nir.h
@@ -3447,6 +3447,28 @@ typedef enum  {
 
 bool nir_lower_to_source_mods(nir_shader *shader, nir_lower_to_source_mods_flags options);
 
+/* These structs encapsulates the blend state such that it can be lowered
+ * cleanly */
+
+typedef struct {
+      enum blend_func func;
+
+      enum blend_factor src_factor;
+      bool invert_src_factor;
+
+      enum blend_factor dst_factor;
+      bool invert_dst_factor;
+} nir_lower_blend_channel;
+
+typedef struct {
+   struct {
+      nir_lower_blend_channel rgb;
+      nir_lower_blend_channel alpha;
+   } rt[8];
+} nir_lower_blend_options;
+
+void nir_lower_blend(nir_shader *shader, nir_lower_blend_options options);
+
 bool nir_lower_gs_intrinsics(nir_shader *shader);
 
 typedef unsigned (*nir_lower_bit_size_callback)(const nir_alu_instr *, void *);
diff --git a/src/compiler/nir/nir_lower_blend.c b/src/compiler/nir/nir_lower_blend.c
new file mode 100644
index 00000000000..5a874f08834
--- /dev/null
+++ b/src/compiler/nir/nir_lower_blend.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/**
+ * @file
+ *
+ * Implements the fragment pipeline (blending and writeout) in software, to be
+ * run as a dedicated "blend shader" stage on Midgard/Bifrost, or as a fragment
+ * shader variant on typical GPUs. This pass is useful if hardware lacks
+ * fixed-function blending in part or in full.
+ */
+
+#include "nir/nir.h"
+#include "nir/nir_builder.h"
+
+/* Given processed factors, combine them per a blend function */
+
+static nir_ssa_def *
+nir_blend_func(
+      nir_builder *b,
+      enum blend_func func,
+      nir_ssa_def *src, nir_ssa_def *dst)
+{
+   switch (func) {
+      case BLEND_FUNC_ADD:
+         return nir_fadd(b, src, dst);
+      case BLEND_FUNC_SUBTRACT:
+         return nir_fsub(b, src, dst);
+      case BLEND_FUNC_REVERSE_SUBTRACT:
+         return nir_fsub(b, dst, src);
+      case BLEND_FUNC_MIN:
+         return nir_fmin(b, src, dst);
+      case BLEND_FUNC_MAX:
+         return nir_fmax(b, src, dst);
+      default:
+         unreachable("Invalid blend function");
+         break;
+   }
+}
+
+/* Does this blend function multiply by a blend factor? */
+
+static bool
+nir_blend_factored(enum blend_func func) {
+   switch (func) {
+      case BLEND_FUNC_ADD:
+      case BLEND_FUNC_SUBTRACT:
+      case BLEND_FUNC_REVERSE_SUBTRACT:
+         return true;
+      default:
+         return false;
+   }
+}
+
+/* Returns a scalar single factor, unmultiplied */
+
+static nir_ssa_def *
+nir_blend_factor_value(
+      nir_builder *b,
+      nir_ssa_def *src, nir_ssa_def *dst, nir_ssa_def *bconst,
+      unsigned chan,
+      enum blend_factor factor)
+{
+   switch (factor) {
+      case BLEND_FACTOR_ZERO:
+         return nir_imm_float(b, 0.0);
+      case BLEND_FACTOR_SRC_COLOR:
+         return nir_channel(b, src, chan);
+      case BLEND_FACTOR_DST_COLOR:
+         return nir_channel(b, dst, chan);
+      case BLEND_FACTOR_SRC_ALPHA:
+         return nir_channel(b, src, 3);
+      case BLEND_FACTOR_DST_ALPHA:
+         return nir_channel(b, dst, 3);
+      case BLEND_FACTOR_CONSTANT_COLOR:
+         return nir_channel(b, bconst, chan);
+      case BLEND_FACTOR_CONSTANT_ALPHA:
+         return nir_channel(b, bconst, 3);
+      case BLEND_FACTOR_SRC_ALPHA_SATURATE: {
+         nir_ssa_def *Asrc = nir_channel(b, src, 3);
+         nir_ssa_def *Adst = nir_channel(b, dst, 3);
+         nir_ssa_def *one = nir_imm_float(b, 1.0);
+         nir_ssa_def *Adsti = nir_fsub(b, one, Adst);
+
+         return (chan < 3) ? nir_fmin(b, Asrc, Adsti) : one;
+      }
+      default:
+         unreachable("Invalid blend factor");
+   }
+}
+
+static nir_ssa_def *
+nir_blend_factor(
+      nir_builder *b,
+      nir_ssa_def *raw_scalar,
+      nir_ssa_def *src, nir_ssa_def *dst, nir_ssa_def *bconst,
+      unsigned chan,
+      enum blend_factor factor,
+      bool inverted)
+{
+   nir_ssa_def *f =
+      nir_blend_factor_value(b, src, dst, bconst, chan, factor);
+
+   if (inverted)
+      f = nir_fsub(b, nir_imm_float(b, 1.0), f);
+
+   return nir_fmul(b, raw_scalar, f);
+}
+
+/* Given a blend state, the source color, and the destination color,
+ * return the blended color */
+
+static nir_ssa_def *
+nir_blend(
+      nir_builder *b,
+      nir_lower_blend_options options,
+      nir_ssa_def *src, nir_ssa_def *dst)
+{
+   /* Grab the blend constant ahead of time */
+   nir_ssa_def *bconst = nir_load_blend_const_color_rgba(b);
+
+   /* We blend per channel and recombine later */
+   nir_ssa_def *channels[4];
+
+   for (unsigned c = 0; c < 4; ++c) {
+      /* Decide properties based on channel */
+      nir_lower_blend_channel chan =
+         (c < 3) ? options.rt[0].rgb : options.rt[0].alpha;
+
+      nir_ssa_def *psrc = nir_channel(b, src, c);
+      nir_ssa_def *pdst = nir_channel(b, dst, c);
+
+      if (nir_blend_factored(chan.func)) {
+         psrc = nir_blend_factor(
+                  b, psrc,
+                  src, dst, bconst, c,
+                  chan.src_factor, chan.invert_src_factor);
+
+         pdst = nir_blend_factor(
+                  b, pdst,
+                  src, dst, bconst, c,
+                  chan.dst_factor, chan.invert_dst_factor);
+      }
+
+      channels[c] = nir_blend_func(b, chan.func, psrc, pdst);
+   }
+
+   return nir_vec(b, channels, 4);
+}
+
+void
+nir_lower_blend(nir_shader *shader, nir_lower_blend_options options)
+{
+   /* Blend shaders are represented as special fragment shaders */
+   assert(shader->info.stage == MESA_SHADER_FRAGMENT);
+
+   nir_foreach_function(func, shader) {
+      nir_foreach_block(block, func->impl) {
+         nir_foreach_instr_safe(instr, block) {
+            if (instr->type != nir_instr_type_intrinsic)
+               continue;
+
+            nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
+            if (intr->intrinsic != nir_intrinsic_store_deref)
+               continue;
+
+            /* TODO: Extending to MRT */
+            nir_variable *var = nir_intrinsic_get_var(intr, 0);
+            if (var->data.location != FRAG_RESULT_COLOR)
+               continue;
+
+            nir_builder b;
+            nir_builder_init(&b, func->impl);
+            b.cursor = nir_before_instr(instr);
+
+            /* Grab the input color */
+            nir_ssa_def *src = nir_ssa_for_src(&b, intr->src[1], 4);
+
+            /* Grab the tilebuffer color - io lowered to load_output */
+            nir_ssa_def *dst = nir_load_var(&b, var);
+
+            /* Blend and write that instead */
+            nir_ssa_def *blended = nir_blend(&b, options, src, dst);
+
+            nir_instr_rewrite_src(instr, &intr->src[1],
+                                  nir_src_for_ssa(blended));
+ 
+         }
+      }
+
+      nir_metadata_preserve(func->impl, nir_metadata_block_index |
+                            nir_metadata_dominance);
+   }
+}
-- 
2.20.1



More information about the mesa-dev mailing list