<div dir="ltr">I feel so called out, it's not the only thing I care about. I'll let dual source blending wait until future improvements though :)</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, May 5, 2019 at 7:26 PM Alyssa Rosenzweig <<a href="mailto:alyssa@rosenzweig.io">alyssa@rosenzweig.io</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">This new lowering pass implements the OpenGL ES blend pipeline in<br>
shaders, applicable to hardware lacking full-featured blending hardware<br>
(including Midgard/Bifrost and vc4). This pass is run on a fragment<br>
shader, rewriting the store to a blended version, loading in the<br>
framebuffer destination color and constant color via intrinsics as<br>
necessary. This pass is sufficient for OpenGL ES 2.0 and is verified to<br>
pass dEQP's blend tests. That said, at present it has the following<br>
limitations:<br>
<br>
 - MRT is not supported.<br>
 - Logic ops are not supported.<br>
<br>
MRT support is on my TODO list but paused until MRT is implemented in<br>
the rest of the driver. Both changes should be fairly trivial.<br>
<br>
It also includes MIN/MAX modes, so in conjunction with the advanced<br>
blend mode lowering it should be sufficient for ES3, though this has not<br>
been thoroughly tested. It is an open question whether the current GLSL<br>
IR based advanced blend lowering should be NIRified and merged into this<br>
pass.<br>
<br>
 ...Dual-source blending is not supported, Ryan.<br>
<br>
Signed-off-by: Alyssa Rosenzweig <<a href="mailto:alyssa@rosenzweig.io" target="_blank">alyssa@rosenzweig.io</a>><br>
Cc: Eric Anholt <<a href="mailto:eric@anholt.net" target="_blank">eric@anholt.net</a>><br>
Cc: Kenneth Graunke <<a href="mailto:kenneth@whitecape.org" target="_blank">kenneth@whitecape.org</a>><br>
---<br>
 src/compiler/Makefile.sources      |   1 +<br>
 src/compiler/nir/meson.build       |   1 +<br>
 src/compiler/nir/nir.h             |  22 +++<br>
 src/compiler/nir/nir_lower_blend.c | 214 +++++++++++++++++++++++++++++<br>
 4 files changed, 238 insertions(+)<br>
 create mode 100644 src/compiler/nir/nir_lower_blend.c<br>
<br>
diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources<br>
index 9bebc3d8867..d68b9550b02 100644<br>
--- a/src/compiler/Makefile.sources<br>
+++ b/src/compiler/Makefile.sources<br>
@@ -238,6 +238,7 @@ NIR_FILES = \<br>
        nir/nir_lower_bit_size.c \<br>
        nir/nir_lower_bool_to_float.c \<br>
        nir/nir_lower_bool_to_int32.c \<br>
+       nir/nir_lower_blend.c \<br>
        nir/nir_lower_clamp_color_outputs.c \<br>
        nir/nir_lower_clip.c \<br>
        nir/nir_lower_clip_cull_distance_arrays.c \<br>
diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build<br>
index a8faeb9c018..73ab62d4b46 100644<br>
--- a/src/compiler/nir/meson.build<br>
+++ b/src/compiler/nir/meson.build<br>
@@ -116,6 +116,7 @@ files_libnir = files(<br>
   'nir_lower_array_deref_of_vec.c',<br>
   'nir_lower_atomics_to_ssbo.c',<br>
   'nir_lower_bitmap.c',<br>
+  'nir_lower_blend.c',<br>
   'nir_lower_bool_to_float.c',<br>
   'nir_lower_bool_to_int32.c',<br>
   'nir_lower_clamp_color_outputs.c',<br>
diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h<br>
index 37161e83e4d..8b68faed819 100644<br>
--- a/src/compiler/nir/nir.h<br>
+++ b/src/compiler/nir/nir.h<br>
@@ -3447,6 +3447,28 @@ typedef enum  {<br>
<br>
 bool nir_lower_to_source_mods(nir_shader *shader, nir_lower_to_source_mods_flags options);<br>
<br>
+/* These structs encapsulates the blend state such that it can be lowered<br>
+ * cleanly */<br>
+<br>
+typedef struct {<br>
+      enum blend_func func;<br>
+<br>
+      enum blend_factor src_factor;<br>
+      bool invert_src_factor;<br>
+<br>
+      enum blend_factor dst_factor;<br>
+      bool invert_dst_factor;<br>
+} nir_lower_blend_channel;<br>
+<br>
+typedef struct {<br>
+   struct {<br>
+      nir_lower_blend_channel rgb;<br>
+      nir_lower_blend_channel alpha;<br>
+   } rt[8];<br>
+} nir_lower_blend_options;<br>
+<br>
+void nir_lower_blend(nir_shader *shader, nir_lower_blend_options options);<br>
+<br>
 bool nir_lower_gs_intrinsics(nir_shader *shader);<br>
<br>
 typedef unsigned (*nir_lower_bit_size_callback)(const nir_alu_instr *, void *);<br>
diff --git a/src/compiler/nir/nir_lower_blend.c b/src/compiler/nir/nir_lower_blend.c<br>
new file mode 100644<br>
index 00000000000..5a874f08834<br>
--- /dev/null<br>
+++ b/src/compiler/nir/nir_lower_blend.c<br>
@@ -0,0 +1,214 @@<br>
+/*<br>
+ * Copyright (C) 2019 Alyssa Rosenzweig<br>
+ *<br>
+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS<br>
+ * IN THE SOFTWARE.<br>
+ */<br>
+<br>
+/**<br>
+ * @file<br>
+ *<br>
+ * Implements the fragment pipeline (blending and writeout) in software, to be<br>
+ * run as a dedicated "blend shader" stage on Midgard/Bifrost, or as a fragment<br>
+ * shader variant on typical GPUs. This pass is useful if hardware lacks<br>
+ * fixed-function blending in part or in full.<br>
+ */<br>
+<br>
+#include "nir/nir.h"<br>
+#include "nir/nir_builder.h"<br>
+<br>
+/* Given processed factors, combine them per a blend function */<br>
+<br>
+static nir_ssa_def *<br>
+nir_blend_func(<br>
+      nir_builder *b,<br>
+      enum blend_func func,<br>
+      nir_ssa_def *src, nir_ssa_def *dst)<br>
+{<br>
+   switch (func) {<br>
+      case BLEND_FUNC_ADD:<br>
+         return nir_fadd(b, src, dst);<br>
+      case BLEND_FUNC_SUBTRACT:<br>
+         return nir_fsub(b, src, dst);<br>
+      case BLEND_FUNC_REVERSE_SUBTRACT:<br>
+         return nir_fsub(b, dst, src);<br>
+      case BLEND_FUNC_MIN:<br>
+         return nir_fmin(b, src, dst);<br>
+      case BLEND_FUNC_MAX:<br>
+         return nir_fmax(b, src, dst);<br>
+      default:<br>
+         unreachable("Invalid blend function");<br>
+         break;<br>
+   }<br>
+}<br>
+<br>
+/* Does this blend function multiply by a blend factor? */<br>
+<br>
+static bool<br>
+nir_blend_factored(enum blend_func func) {<br>
+   switch (func) {<br>
+      case BLEND_FUNC_ADD:<br>
+      case BLEND_FUNC_SUBTRACT:<br>
+      case BLEND_FUNC_REVERSE_SUBTRACT:<br>
+         return true;<br>
+      default:<br>
+         return false;<br>
+   }<br>
+}<br>
+<br>
+/* Returns a scalar single factor, unmultiplied */<br>
+<br>
+static nir_ssa_def *<br>
+nir_blend_factor_value(<br>
+      nir_builder *b,<br>
+      nir_ssa_def *src, nir_ssa_def *dst, nir_ssa_def *bconst,<br>
+      unsigned chan,<br>
+      enum blend_factor factor)<br>
+{<br>
+   switch (factor) {<br>
+      case BLEND_FACTOR_ZERO:<br>
+         return nir_imm_float(b, 0.0);<br>
+      case BLEND_FACTOR_SRC_COLOR:<br>
+         return nir_channel(b, src, chan);<br>
+      case BLEND_FACTOR_DST_COLOR:<br>
+         return nir_channel(b, dst, chan);<br>
+      case BLEND_FACTOR_SRC_ALPHA:<br>
+         return nir_channel(b, src, 3);<br>
+      case BLEND_FACTOR_DST_ALPHA:<br>
+         return nir_channel(b, dst, 3);<br>
+      case BLEND_FACTOR_CONSTANT_COLOR:<br>
+         return nir_channel(b, bconst, chan);<br>
+      case BLEND_FACTOR_CONSTANT_ALPHA:<br>
+         return nir_channel(b, bconst, 3);<br>
+      case BLEND_FACTOR_SRC_ALPHA_SATURATE: {<br>
+         nir_ssa_def *Asrc = nir_channel(b, src, 3);<br>
+         nir_ssa_def *Adst = nir_channel(b, dst, 3);<br>
+         nir_ssa_def *one = nir_imm_float(b, 1.0);<br>
+         nir_ssa_def *Adsti = nir_fsub(b, one, Adst);<br>
+<br>
+         return (chan < 3) ? nir_fmin(b, Asrc, Adsti) : one;<br>
+      }<br>
+      default:<br>
+         unreachable("Invalid blend factor");<br>
+   }<br>
+}<br>
+<br>
+static nir_ssa_def *<br>
+nir_blend_factor(<br>
+      nir_builder *b,<br>
+      nir_ssa_def *raw_scalar,<br>
+      nir_ssa_def *src, nir_ssa_def *dst, nir_ssa_def *bconst,<br>
+      unsigned chan,<br>
+      enum blend_factor factor,<br>
+      bool inverted)<br>
+{<br>
+   nir_ssa_def *f =<br>
+      nir_blend_factor_value(b, src, dst, bconst, chan, factor);<br>
+<br>
+   if (inverted)<br>
+      f = nir_fsub(b, nir_imm_float(b, 1.0), f);<br>
+<br>
+   return nir_fmul(b, raw_scalar, f);<br>
+}<br>
+<br>
+/* Given a blend state, the source color, and the destination color,<br>
+ * return the blended color */<br>
+<br>
+static nir_ssa_def *<br>
+nir_blend(<br>
+      nir_builder *b,<br>
+      nir_lower_blend_options options,<br>
+      nir_ssa_def *src, nir_ssa_def *dst)<br>
+{<br>
+   /* Grab the blend constant ahead of time */<br>
+   nir_ssa_def *bconst = nir_load_blend_const_color_rgba(b);<br>
+<br>
+   /* We blend per channel and recombine later */<br>
+   nir_ssa_def *channels[4];<br>
+<br>
+   for (unsigned c = 0; c < 4; ++c) {<br>
+      /* Decide properties based on channel */<br>
+      nir_lower_blend_channel chan =<br>
+         (c < 3) ? options.rt[0].rgb : options.rt[0].alpha;<br>
+<br>
+      nir_ssa_def *psrc = nir_channel(b, src, c);<br>
+      nir_ssa_def *pdst = nir_channel(b, dst, c);<br>
+<br>
+      if (nir_blend_factored(chan.func)) {<br>
+         psrc = nir_blend_factor(<br>
+                  b, psrc,<br>
+                  src, dst, bconst, c,<br>
+                  chan.src_factor, chan.invert_src_factor);<br>
+<br>
+         pdst = nir_blend_factor(<br>
+                  b, pdst,<br>
+                  src, dst, bconst, c,<br>
+                  chan.dst_factor, chan.invert_dst_factor);<br>
+      }<br>
+<br>
+      channels[c] = nir_blend_func(b, chan.func, psrc, pdst);<br>
+   }<br>
+<br>
+   return nir_vec(b, channels, 4);<br>
+}<br>
+<br>
+void<br>
+nir_lower_blend(nir_shader *shader, nir_lower_blend_options options)<br>
+{<br>
+   /* Blend shaders are represented as special fragment shaders */<br>
+   assert(shader->info.stage == MESA_SHADER_FRAGMENT);<br>
+<br>
+   nir_foreach_function(func, shader) {<br>
+      nir_foreach_block(block, func->impl) {<br>
+         nir_foreach_instr_safe(instr, block) {<br>
+            if (instr->type != nir_instr_type_intrinsic)<br>
+               continue;<br>
+<br>
+            nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);<br>
+            if (intr->intrinsic != nir_intrinsic_store_deref)<br>
+               continue;<br>
+<br>
+            /* TODO: Extending to MRT */<br>
+            nir_variable *var = nir_intrinsic_get_var(intr, 0);<br>
+            if (var->data.location != FRAG_RESULT_COLOR)<br>
+               continue;<br>
+<br>
+            nir_builder b;<br>
+            nir_builder_init(&b, func->impl);<br>
+            b.cursor = nir_before_instr(instr);<br>
+<br>
+            /* Grab the input color */<br>
+            nir_ssa_def *src = nir_ssa_for_src(&b, intr->src[1], 4);<br>
+<br>
+            /* Grab the tilebuffer color - io lowered to load_output */<br>
+            nir_ssa_def *dst = nir_load_var(&b, var);<br>
+<br>
+            /* Blend and write that instead */<br>
+            nir_ssa_def *blended = nir_blend(&b, options, src, dst);<br>
+<br>
+            nir_instr_rewrite_src(instr, &intr->src[1],<br>
+                                  nir_src_for_ssa(blended));<br>
+ <br>
+         }<br>
+      }<br>
+<br>
+      nir_metadata_preserve(func->impl, nir_metadata_block_index |<br>
+                            nir_metadata_dominance);<br>
+   }<br>
+}<br>
-- <br>
2.20.1<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org" target="_blank">mesa-dev@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev" rel="noreferrer" target="_blank">https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a></blockquote></div>