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

Ryan Houdek sonicadvance1 at gmail.com
Mon May 6 05:21:42 UTC 2019


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 :)

On Sun, May 5, 2019 at 7:26 PM Alyssa Rosenzweig <alyssa at rosenzweig.io>
wrote:

> 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
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/mesa-dev/attachments/20190505/1943b570/attachment-0001.html>


More information about the mesa-dev mailing list