[36/42] panfrost: Refactor blend infrastructure

Submitted by Alyssa Rosenzweig on July 8, 2019, 2:08 p.m.

Details

Message ID 20190708140855.4126-37-alyssa.rosenzweig@collabora.com
State New
Headers show
Series "panfrost: Render target stuff" ( rev: 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Alyssa Rosenzweig July 8, 2019, 2:08 p.m.
We would like to permit keying blend shaders against the framebuffer
format, which requires some new blending abstractions.

Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
---
 src/gallium/drivers/panfrost/meson.build      |   1 +
 src/gallium/drivers/panfrost/pan_blend.h      | 109 +++++++
 src/gallium/drivers/panfrost/pan_blend_cso.c  | 268 ++++++++++++++++++
 .../drivers/panfrost/pan_blend_shaders.c      |  34 ++-
 .../drivers/panfrost/pan_blend_shaders.h      |   8 +-
 src/gallium/drivers/panfrost/pan_blending.c   |  75 ++---
 src/gallium/drivers/panfrost/pan_blending.h   |  13 +-
 src/gallium/drivers/panfrost/pan_context.c    | 133 ++-------
 src/gallium/drivers/panfrost/pan_context.h    |  16 +-
 9 files changed, 455 insertions(+), 202 deletions(-)
 create mode 100644 src/gallium/drivers/panfrost/pan_blend.h
 create mode 100644 src/gallium/drivers/panfrost/pan_blend_cso.c

Patch hide | download patch | download mbox

diff --git a/src/gallium/drivers/panfrost/meson.build b/src/gallium/drivers/panfrost/meson.build
index cc49903aaac..6b907f155ae 100644
--- a/src/gallium/drivers/panfrost/meson.build
+++ b/src/gallium/drivers/panfrost/meson.build
@@ -56,6 +56,7 @@  files_panfrost = files(
   'pan_format.c',
   'pan_blending.c',
   'pan_blend_shaders.c',
+  'pan_blend_cso.c',
   'pan_pretty_print.c',
   'pan_fragment.c',
   'pan_invocation.c',
diff --git a/src/gallium/drivers/panfrost/pan_blend.h b/src/gallium/drivers/panfrost/pan_blend.h
new file mode 100644
index 00000000000..486ed4dc034
--- /dev/null
+++ b/src/gallium/drivers/panfrost/pan_blend.h
@@ -0,0 +1,109 @@ 
+/*
+ * Copyright (C) 2019 Collabora
+ *
+ * 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.
+ *
+ * Authors (Collabora):
+ *   Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
+ *
+ */
+
+#ifndef __PAN_BLEND_H
+#define __PAN_BLEND_H
+
+#include "util/hash_table.h"
+
+/* An internal blend shader descriptor, from the compiler */
+
+struct panfrost_blend_shader {
+        /* The compiled shader in GPU memory */
+        struct panfrost_transfer shader;
+
+        /* Byte count of the shader */
+        unsigned size;
+
+        /* Number of 128-bit work registers required by the shader */
+        unsigned work_count;
+
+        /* Offset into the shader to patch constants. Zero to disable patching
+         * (it is illogical to have constants at offset 0). */
+        unsigned patch_index;
+
+        /* First instruction tag (for tagging the pointer) */
+        unsigned first_tag;
+};
+
+/* A blend shader descriptor ready for actual use */
+
+struct panfrost_blend_shader_final {
+        /* The upload, possibly to transient memory */
+        mali_ptr gpu;
+
+        /* Same meaning as panfrost_blend_shader */
+        unsigned work_count;
+};
+
+struct panfrost_blend_equation_final {
+        struct mali_blend_equation *equation;
+        float constant;
+};
+
+struct panfrost_blend_rt {
+        /* If has_fixed_function is set, equation is the
+         * fixed-function configuration for this blend state */
+        
+        bool has_fixed_function;
+        struct mali_blend_equation equation;
+
+        /* Mask of blend color components read */
+        unsigned constant_mask;
+
+        /* Regardless of fixed-function blending, this is a map of pipe_format
+         * to panfrost_blend_shader */
+
+        struct hash_table_u64 *shaders;
+};
+
+struct panfrost_blend_state {
+        struct pipe_blend_state base;
+
+        struct panfrost_blend_rt rt[PIPE_MAX_COLOR_BUFS];
+};
+
+/* Container for a final blend state, specialized to constants and a
+ * framebuffer formats. */
+
+struct panfrost_blend_final {
+        /* Set for a shader, clear for an equation */
+        bool is_shader;
+
+        union {
+                struct panfrost_blend_shader_final shader;
+                struct panfrost_blend_equation_final equation;
+        };
+};
+
+void
+panfrost_blend_context_init(struct pipe_context *pipe);
+
+struct panfrost_blend_final
+panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rt);
+
+#endif
diff --git a/src/gallium/drivers/panfrost/pan_blend_cso.c b/src/gallium/drivers/panfrost/pan_blend_cso.c
new file mode 100644
index 00000000000..a1b8111190f
--- /dev/null
+++ b/src/gallium/drivers/panfrost/pan_blend_cso.c
@@ -0,0 +1,268 @@ 
+/*
+ * Copyright (C) 2019 Collabora
+ *
+ * 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.
+ *
+ * Authors (Collabora):
+ *   Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
+ *
+ */
+
+#include <stdio.h>
+#include "util/u_memory.h"
+#include "pan_blend_shaders.h"
+#include "pan_blending.h"
+
+/* A given Gallium blend state can be encoded to the hardware in numerous,
+ * dramatically divergent ways due to the interactions of blending with
+ * framebuffer formats. Conceptually, there are two modes:
+ *
+ * - Fixed-function blending (for suitable framebuffer formats, suitable blend
+ *   state, and suitable blend constant)
+ *
+ * - Blend shaders (for everything else)
+ *
+ * A given Gallium blend configuration will compile to exactly one
+ * fixed-function blend state, if it compiles to any, although the constant
+ * will vary across runs as that is tracked outside of the Gallium CSO.
+ *
+ * However, that same blend configuration will compile to many different blend
+ * shaders, depending on the framebuffer formats active. The rationale is that
+ * blend shaders override not just fixed-function blending but also
+ * fixed-function format conversion. As such, each blend shader must be
+ * hardcoded to a particular framebuffer format to correctly pack/unpack it. As
+ * a concrete example, to the hardware there is no difference (!) between RG16F
+ * and RG16UI -- both are simply 4-byte-per-pixel chunks. Thus both formats
+ * require a blend shader (even with blending is totally disabled!), required
+ * to do conversion as necessary (if necessary).
+ *
+ * All of this state is encapsulated in the panfrost_blend_state struct
+ * (our subclass of pipe_blend_state).
+ */
+
+/* Given an initialized CSO and a particular framebuffer format, grab a
+ * blend shader, generating and compiling it if it doesn't exist
+ * (lazy-loading in a way). This routine, when the cache hits, should
+ * befast, suitable for calling every draw to avoid wacky dirty
+ * tracking paths. If the cache hits, boom, done. */
+
+static struct panfrost_blend_shader *
+panfrost_get_blend_shader(
+                struct panfrost_context *ctx,
+                struct panfrost_blend_state *blend,
+                enum pipe_format fmt,
+                unsigned rt)
+{
+        /* Prevent NULL collision issues.. */
+        assert(fmt != 0);
+
+        /* Check the cache */
+        struct hash_table_u64 *shaders = blend->rt[rt].shaders;
+
+        struct panfrost_blend_shader *shader =
+                _mesa_hash_table_u64_search(shaders, fmt);
+
+        if (shader)
+                return shader;
+
+        /* Cache miss. Build one instead, cache it, and go */
+
+        struct panfrost_blend_shader generated =
+                panfrost_compile_blend_shader(ctx, &blend->base, fmt);
+
+        shader = mem_dup(&generated, sizeof(generated));
+        _mesa_hash_table_u64_insert(shaders, fmt, shader);
+        return  shader;
+}
+
+/* Create a blend CSO. Essentially, try to compile a fixed-function
+ * expression and initialize blend shaders */
+
+static void *
+panfrost_create_blend_state(struct pipe_context *pipe,
+                            const struct pipe_blend_state *blend)
+{
+        struct panfrost_context *ctx = pan_context(pipe);
+        struct panfrost_blend_state *so = rzalloc(ctx, struct panfrost_blend_state);
+        so->base = *blend;
+
+        /* TODO: The following features are not yet implemented */
+        assert(!blend->logicop_enable);
+        assert(!blend->alpha_to_coverage);
+        assert(!blend->alpha_to_one);
+
+        for (unsigned c = 0; c < 4; ++c) {
+                struct panfrost_blend_rt *rt = &so->rt[c];
+
+                /* There are two paths. First, we would like to try a
+                 * fixed-function if we can */
+
+                rt->has_fixed_function =
+                        panfrost_make_fixed_blend_mode(
+                                        &blend->rt[c],
+                                        &rt->equation,
+                                        &rt->constant_mask,
+                                        blend->rt[c].colormask);
+
+                /* Regardless if that works, we also need to initialize
+                 * the blend shaders */
+
+                rt->shaders = _mesa_hash_table_u64_create(NULL);
+        }
+
+        return so;
+}
+
+static void
+panfrost_bind_blend_state(struct pipe_context *pipe,
+                          void *cso)
+{
+        struct panfrost_context *ctx = pan_context(pipe);
+        struct pipe_blend_state *blend = (struct pipe_blend_state *) cso;
+        struct panfrost_blend_state *pblend = (struct panfrost_blend_state *) cso;
+        ctx->blend = pblend;
+
+        if (!blend)
+                return;
+
+        SET_BIT(ctx->fragment_shader_core.unknown2_4, MALI_NO_DITHER, !blend->dither);
+
+        /* Shader itself is not dirty, but the shader core is */
+        ctx->dirty |= PAN_DIRTY_FS;
+}
+
+static void
+panfrost_delete_blend_state(struct pipe_context *pipe,
+                            void *blend)
+{
+        /* TODO: leaks internally? */
+
+        ralloc_free(blend);
+}
+
+static void
+panfrost_set_blend_color(struct pipe_context *pipe,
+                         const struct pipe_blend_color *blend_color)
+{
+        struct panfrost_context *ctx = pan_context(pipe);
+
+        if (blend_color)
+                ctx->blend_color = *blend_color;
+}
+
+/* Given a vec4 of constants, reduce it to just a single constant according to
+ * the mask (if we can) */
+
+static bool
+panfrost_blend_constant(float *out, float *in, unsigned mask)
+{
+        /* If there is no components used, it automatically works. Do set a
+         * dummy constant just to avoid reading uninitialized memory. */
+
+        if (!mask) {
+                *out = 0.0;
+                return true;
+        }
+
+        /* Find some starter mask */
+        unsigned first = ffs(mask) - 1;
+        float cons = in[first];
+        mask ^= (1 << first);
+
+        /* Ensure the rest are equal */
+        while (mask) {
+                unsigned i = u_bit_scan(&mask);
+
+                if (in[i] != cons) {
+                        *out = 0.0;
+                        return false;
+                }
+        }
+
+        /* Otherwise, we're good to go */
+        *out = cons;
+        return true;
+}
+
+/* Create a final blend given the context */
+
+struct panfrost_blend_final
+panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rti)
+{
+        /* Grab the format */
+        struct pipe_framebuffer_state *fb = &ctx->pipe_framebuffer;
+        assert(fb->nr_cbufs > rti);
+        enum pipe_format fmt = fb->cbufs[rti]->format;
+
+        /* Grab the blend state */
+        struct panfrost_blend_state *blend = ctx->blend;
+        assert(blend);
+
+        struct panfrost_blend_rt *rt = &blend->rt[rti];
+
+        struct panfrost_blend_final final;
+
+        /* First, we'll try a fixed function path */
+        if (rt->has_fixed_function && panfrost_can_fixed_blend(fmt)) {
+                if (panfrost_blend_constant(
+                                        &final.equation.constant,
+                                        ctx->blend_color.color,
+                                        rt->constant_mask))
+                {
+                        /* There's an equation and suitable constant, so we're good to go */
+                        final.is_shader = false;
+                        final.equation.equation = &rt->equation;
+                        return final;
+                }
+        }
+
+        /* Otherwise, we need to grab a shader */
+        struct panfrost_blend_shader *shader = panfrost_get_blend_shader(ctx, blend, fmt, rti);
+        final.is_shader = true;
+        final.shader.work_count = shader->work_count;
+
+        if (shader->patch_index) {
+                /* We have to specialize the blend shader to use constants, so
+                 * patch in the current constants and upload to transient
+                 * memory */
+                
+                float *patch = (float *) (shader->shader.cpu + shader->patch_index);
+                memcpy(patch, ctx->blend_color.color, sizeof(float) * 4);
+
+                final.shader.gpu = panfrost_upload_transient(
+                                ctx, shader->shader.cpu, shader->size);
+        } else {
+                /* No need to specialize further, use the preuploaded */
+                final.shader.gpu = shader->shader.gpu;
+        }
+
+        final.shader.gpu |= shader->first_tag;
+        return final;
+}
+
+void
+panfrost_blend_context_init(struct pipe_context *pipe)
+{
+        pipe->create_blend_state = panfrost_create_blend_state;
+        pipe->bind_blend_state   = panfrost_bind_blend_state;
+        pipe->delete_blend_state = panfrost_delete_blend_state;
+
+        pipe->set_blend_color = panfrost_set_blend_color;
+}
diff --git a/src/gallium/drivers/panfrost/pan_blend_shaders.c b/src/gallium/drivers/panfrost/pan_blend_shaders.c
index 2b6206545b3..993b612ee50 100644
--- a/src/gallium/drivers/panfrost/pan_blend_shaders.c
+++ b/src/gallium/drivers/panfrost/pan_blend_shaders.c
@@ -29,6 +29,7 @@ 
 #include "compiler/nir/nir_builder.h"
 #include "midgard/nir_lower_blend.h"
 #include "gallium/auxiliary/util/u_blend.h"
+#include "util/u_memory.h"
 
 /*
  * Implements the command stream portion of programmatic blend shaders.
@@ -124,13 +125,14 @@  nir_make_options(const struct pipe_blend_state *blend, unsigned nr_cbufs)
         return options;
 }
 
-void
-panfrost_make_blend_shader(
+struct panfrost_blend_shader
+panfrost_compile_blend_shader(
                 struct panfrost_context *ctx,
-                struct panfrost_blend_state *cso,
-                const struct pipe_blend_color *blend_color,
+                struct pipe_blend_state *cso,
                 enum pipe_format format)
 {
+        struct panfrost_blend_shader res;
+
         /* Build the shader */
 
         nir_shader *shader = nir_shader_create(NULL, MESA_SHADER_FRAGMENT, &midgard_nir_options, NULL);
@@ -160,7 +162,7 @@  panfrost_make_blend_shader(
         nir_store_var(b, c_out, s_src, 0xFF);
 
         nir_lower_blend_options options =
-                nir_make_options(&cso->base, 1);
+                nir_make_options(cso, 1);
         NIR_PASS_V(shader, nir_lower_blend, options);
 
         NIR_PASS_V(shader, nir_lower_framebuffer, format);
@@ -175,20 +177,16 @@  panfrost_make_blend_shader(
         int size = program.compiled.size;
         uint8_t *dst = program.compiled.data;
 
-        /* Hot patch in constant color */
-
-        if (program.blend_patch_offset >= 0) {
-                float *hot_color = (float *) (dst + program.blend_patch_offset);
-
-                for (int c = 0; c < 4; ++c)
-                        hot_color[c] = blend_color->color[c];
-        }
+        res.shader.cpu = mem_dup(dst, size);
+        res.shader.gpu = panfrost_upload(&ctx->shaders, dst, size, true);
 
-        cso->blend_shader = panfrost_upload(&ctx->shaders, dst, size, true) | program.first_tag;
+        /* At least two work registers are needed due to an encoding quirk */
+        res.work_count = MAX2(program.work_register_count, 2);
 
-        /* We need to switch to shader mode */
-        cso->has_blend_shader = true;
+        /* Allow us to patch later */
+        res.patch_index = program.blend_patch_offset;
+        res.first_tag = program.first_tag;
+        res.size = size;
 
-        /* At least two work registers are needed due to an encoding quirk */
-        cso->blend_work_count = MAX2(program.work_register_count, 2);
+        return res;
 }
diff --git a/src/gallium/drivers/panfrost/pan_blend_shaders.h b/src/gallium/drivers/panfrost/pan_blend_shaders.h
index 23acd39581a..1d2f090c4cc 100644
--- a/src/gallium/drivers/panfrost/pan_blend_shaders.h
+++ b/src/gallium/drivers/panfrost/pan_blend_shaders.h
@@ -29,12 +29,12 @@ 
 #include "pipe/p_defines.h"
 #include <panfrost-job.h>
 #include "pan_context.h"
+#include "pan_blend.h"
 
-void
-panfrost_make_blend_shader(
+struct panfrost_blend_shader
+panfrost_compile_blend_shader(
                 struct panfrost_context *ctx,
-                struct panfrost_blend_state *cso,
-                const struct pipe_blend_color *blend_color,
+                struct pipe_blend_state *cso,
                 enum pipe_format format);
 
 #endif
diff --git a/src/gallium/drivers/panfrost/pan_blending.c b/src/gallium/drivers/panfrost/pan_blending.c
index 6bdc8395d18..72eeb59280c 100644
--- a/src/gallium/drivers/panfrost/pan_blending.c
+++ b/src/gallium/drivers/panfrost/pan_blending.c
@@ -101,8 +101,8 @@ 
 
 /* Not all formats can be blended by fixed-function hardware */
 
-static bool
-panfrost_can_blend(enum pipe_format format)
+bool
+panfrost_can_fixed_blend(enum pipe_format format)
 {
         /* Fixed-function can handle sRGB */
         format = util_format_linear(format);
@@ -318,53 +318,24 @@  panfrost_make_fixed_blend_part(unsigned func, unsigned src_factor, unsigned dst_
         return true;
 }
 
-/* We can upload a single constant for all of the factors. So, scan the factors
- * for constants used, and scan the constants for the constants used. If there
- * is a single unique constant, output that. If there are multiple,
- * fixed-function operation breaks down. */
+/* We can upload a single constant for all of the factors. So, scan
+ * the factors for constants used to create a mask to check later. */
 
-static bool
-panfrost_make_constant(unsigned *factors, unsigned num_factors, const struct pipe_blend_color *blend_color, void *out)
+static unsigned
+panfrost_constant_mask(unsigned *factors, unsigned num_factors)
 {
-        /* Color components used */
-        bool cc[4] = { false };
+        unsigned mask = 0;
 
         for (unsigned i = 0; i < num_factors; ++i) {
                 unsigned factor = uncomplement_factor(factors[i]);
 
                 if (factor == PIPE_BLENDFACTOR_CONST_COLOR)
-                        cc[0] = cc[1] = cc[2] = true;
+                        mask |= 0b0111; /* RGB */
                 else if (factor == PIPE_BLENDFACTOR_CONST_ALPHA)
-                        cc[3] = true;
-        }
-
-        /* Find the actual constant associated with the components used*/
-
-        float constant = 0.0;
-        bool has_constant = false;
-
-        for (unsigned i = 0; i < 4; ++i) {
-                /* If the component is unused, nothing to do */
-                if (!cc[i]) continue;
-
-                float value = blend_color->color[i];
-
-                /* Either there's a second constant, in which case we fail, or
-                 * there's no constant / a first constant, in which case we use
-                 * that constant */
-
-                if (has_constant && constant != value) {
-                        return false;
-                } else {
-                        has_constant = true;
-                        constant = value;
-                }
+                        mask |= 0b1000; /* A */
         }
 
-        /* We have the constant -- success! */
-
-        memcpy(out, &constant, sizeof(float));
-        return true;
+        return mask;
 }
 
 /* Create the descriptor for a fixed blend mode given the corresponding Gallium
@@ -376,19 +347,13 @@  panfrost_make_constant(unsigned *factors, unsigned num_factors, const struct pip
 bool
 panfrost_make_fixed_blend_mode(
                 const struct pipe_rt_blend_state *blend,
-                struct panfrost_blend_state *so,
-                unsigned colormask,
-                const struct pipe_blend_color *blend_color,
-                enum pipe_format format)
+                struct mali_blend_equation *out,
+                unsigned *constant_mask,
+                unsigned colormask)
 {
-        struct mali_blend_equation *out = &so->equation;
-
-        /* Check if the format supports fixed-function blending at all */
+        /* Gallium and Mali represent colour masks identically. XXX: Static
+         * assert for future proof */
 
-        if (!panfrost_can_blend(format))
-                return false;
-
-        /* Gallium and Mali represent colour masks identically. XXX: Static assert for future proof */
         out->color_mask = colormask;
 
         /* If no blending is enabled, default back on `replace` mode */
@@ -399,16 +364,18 @@  panfrost_make_fixed_blend_mode(
                 return true;
         }
 
-        /* We have room only for a single float32 constant between the four
-         * components. If we need more, spill to the programmable pipeline. */
+        /* At draw-time, we'll need to analyze the blend constant, so
+         * precompute a mask for it -- even if we don't end up able to use
+         * fixed-function blending */
 
         unsigned factors[] = {
                 blend->rgb_src_factor, blend->rgb_dst_factor,
                 blend->alpha_src_factor, blend->alpha_dst_factor,
         };
 
-        if (!panfrost_make_constant(factors, ARRAY_SIZE(factors), blend_color, &so->constant))
-                return false;
+        *constant_mask = panfrost_constant_mask(factors, ARRAY_SIZE(factors));
+
+        /* Try to compile the actual fixed-function blend */
 
         unsigned rgb_mode = 0;
         unsigned alpha_mode = 0;
diff --git a/src/gallium/drivers/panfrost/pan_blending.h b/src/gallium/drivers/panfrost/pan_blending.h
index 4be0c4d4385..163237eed5e 100644
--- a/src/gallium/drivers/panfrost/pan_blending.h
+++ b/src/gallium/drivers/panfrost/pan_blending.h
@@ -31,11 +31,14 @@ 
 
 struct panfrost_blend_state;
 
-bool panfrost_make_fixed_blend_mode(
+bool
+panfrost_make_fixed_blend_mode(
                 const struct pipe_rt_blend_state *blend,
-                struct panfrost_blend_state *so,
-                unsigned colormask,
-                const struct pipe_blend_color *blend_color,
-                enum pipe_format format);
+                struct mali_blend_equation *out,
+                unsigned *constant_mask,
+                unsigned colormask);
+
+bool
+panfrost_can_fixed_blend(enum pipe_format format);
 
 #endif
diff --git a/src/gallium/drivers/panfrost/pan_context.c b/src/gallium/drivers/panfrost/pan_context.c
index c26a6dbaabb..69d877277be 100644
--- a/src/gallium/drivers/panfrost/pan_context.c
+++ b/src/gallium/drivers/panfrost/pan_context.c
@@ -1071,9 +1071,14 @@  panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
                 COPY(midgard1.unknown2);
 
 #undef COPY
+
+                /* Get blending setup */
+                struct panfrost_blend_final blend =
+                        panfrost_get_blend_for_context(ctx, 0);
+
                 /* If there is a blend shader, work registers are shared */
 
-                if (ctx->blend->has_blend_shader)
+                if (blend.is_shader)
                         ctx->fragment_shader_core.midgard1.work_count = /*MAX2(ctx->fragment_shader_core.midgard1.work_count, ctx->blend->blend_work_count)*/16;
 
                 /* Set late due to depending on render state */
@@ -1112,18 +1117,19 @@  panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
 		/* Check if we're using the default blend descriptor (fast path) */
 
 		bool no_blending =
-			!ctx->blend->has_blend_shader &&
-			(ctx->blend->equation.rgb_mode == 0x122) &&
-			(ctx->blend->equation.alpha_mode == 0x122) &&
-			(ctx->blend->equation.color_mask == 0xf);
+			!blend.is_shader &&
+			(blend.equation.equation->rgb_mode == 0x122) &&
+			(blend.equation.equation->alpha_mode == 0x122) &&
+			(blend.equation.equation->color_mask == 0xf);
 
                 /* Even on MFBD, the shader descriptor gets blend shaders. It's
                  * *also* copied to the blend_meta appended (by convention),
                  * but this is the field actually read by the hardware. (Or
                  * maybe both are read...?) */
 
-                if (ctx->blend->has_blend_shader) {
-                        ctx->fragment_shader_core.blend.shader = ctx->blend->blend_shader;
+                if (blend.is_shader) {
+                        ctx->fragment_shader_core.blend.shader =
+                                blend.shader.gpu;
                 } else {
                         ctx->fragment_shader_core.blend.shader = 0;
                 }
@@ -1134,9 +1140,11 @@  panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
                          * additionally need to signal CAN_DISCARD for nontrivial blend
                          * modes (so we're able to read back the destination buffer) */
 
-                        if (!ctx->blend->has_blend_shader) {
-                                ctx->fragment_shader_core.blend.equation = ctx->blend->equation;
-                                ctx->fragment_shader_core.blend.constant = ctx->blend->constant;
+                        if (!blend.is_shader) {
+                                ctx->fragment_shader_core.blend.equation =
+                                        *blend.equation.equation;
+                                ctx->fragment_shader_core.blend.constant =
+                                        blend.equation.constant;
                         }
 
                         if (!no_blending) {
@@ -1155,13 +1163,13 @@  panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
 
                         unsigned blend_count = 0x200;
 
-                        if (ctx->blend->has_blend_shader) {
+                        if (blend.is_shader) {
                                 /* For a blend shader, the bottom nibble corresponds to
                                  * the number of work registers used, which signals the
                                  * -existence- of a blend shader */
 
-                                assert(ctx->blend->blend_work_count >= 2);
-                                blend_count |= MIN2(ctx->blend->blend_work_count, 3);
+                                assert(blend.shader.work_count >= 2);
+                                blend_count |= MIN2(blend.shader.work_count, 3);
                         } else {
                                 /* Otherwise, the bottom bit simply specifies if
                                  * blending (anything other than REPLACE) is enabled */
@@ -1191,13 +1199,13 @@  panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
                                  * native Midgard ops for helping here, but
                                  * they're not well-understood yet. */
 
-                                assert(!(is_srgb && ctx->blend->has_blend_shader));
+                                assert(!(is_srgb && blend.is_shader));
 
-                                if (ctx->blend->has_blend_shader) {
-                                        rts[i].blend.shader = ctx->blend->blend_shader;
+                                if (blend.is_shader) {
+                                        rts[i].blend.shader = blend.shader.gpu;
                                 } else {
-                                        rts[i].blend.equation = ctx->blend->equation;
-                                        rts[i].blend.constant = ctx->blend->constant;
+                                        rts[i].blend.equation = *blend.equation.equation;
+                                        rts[i].blend.constant = blend.equation.constant;
                                 }
                         }
 
@@ -2363,88 +2371,6 @@  panfrost_set_framebuffer_state(struct pipe_context *pctx,
         }
 }
 
-static void *
-panfrost_create_blend_state(struct pipe_context *pipe,
-                            const struct pipe_blend_state *blend)
-{
-        struct panfrost_context *ctx = pan_context(pipe);
-        struct panfrost_blend_state *so = rzalloc(ctx, struct panfrost_blend_state);
-        so->base = *blend;
-
-        /* TODO: The following features are not yet implemented */
-        assert(!blend->logicop_enable);
-        assert(!blend->alpha_to_coverage);
-        assert(!blend->alpha_to_one);
-
-        /* Compile the blend state, first as fixed-function if we can */
-
-        /* TODO: Key by format */
-        enum pipe_format format = ctx->pipe_framebuffer.nr_cbufs ?
-                ctx->pipe_framebuffer.cbufs[0]->format :
-                PIPE_FORMAT_R8G8B8A8_UNORM;
-
-        if (panfrost_make_fixed_blend_mode(&blend->rt[0], so, blend->rt[0].colormask, &ctx->blend_color, format))
-                return so;
-
-        /* If we can't, compile a blend shader instead */
-
-        panfrost_make_blend_shader(ctx, so, &ctx->blend_color, format);
-
-        return so;
-}
-
-static void
-panfrost_bind_blend_state(struct pipe_context *pipe,
-                          void *cso)
-{
-        struct panfrost_context *ctx = pan_context(pipe);
-        struct pipe_blend_state *blend = (struct pipe_blend_state *) cso;
-        struct panfrost_blend_state *pblend = (struct panfrost_blend_state *) cso;
-        ctx->blend = pblend;
-
-        if (!blend)
-                return;
-
-        SET_BIT(ctx->fragment_shader_core.unknown2_4, MALI_NO_DITHER, !blend->dither);
-
-        /* TODO: Attach color */
-
-        /* Shader itself is not dirty, but the shader core is */
-        ctx->dirty |= PAN_DIRTY_FS;
-}
-
-static void
-panfrost_delete_blend_state(struct pipe_context *pipe,
-                            void *blend)
-{
-        struct panfrost_blend_state *so = (struct panfrost_blend_state *) blend;
-
-        if (so->has_blend_shader) {
-                DBG("Deleting blend state leak blend shaders bytecode\n");
-        }
-
-        ralloc_free(blend);
-}
-
-static void
-panfrost_set_blend_color(struct pipe_context *pipe,
-                         const struct pipe_blend_color *blend_color)
-{
-        struct panfrost_context *ctx = pan_context(pipe);
-
-        /* If blend_color is we're unbinding, so ctx->blend_color is now undefined -> nothing to do */
-
-        if (blend_color) {
-                ctx->blend_color = *blend_color;
-
-                /* The blend mode depends on the blend constant color, due to the
-                 * fixed/programmable split. So, we're forced to regenerate the blend
-                 * equation */
-
-                /* TODO: Attach color */
-        }
-}
-
 static void *
 panfrost_create_depth_stencil_state(struct pipe_context *pipe,
                                     const struct pipe_depth_stencil_alpha_state *depth_stencil)
@@ -2795,12 +2721,6 @@  panfrost_create_context(struct pipe_screen *screen, void *priv, unsigned flags)
         gallium->delete_sampler_state = panfrost_generic_cso_delete;
         gallium->bind_sampler_states = panfrost_bind_sampler_states;
 
-        gallium->create_blend_state = panfrost_create_blend_state;
-        gallium->bind_blend_state   = panfrost_bind_blend_state;
-        gallium->delete_blend_state = panfrost_delete_blend_state;
-
-        gallium->set_blend_color = panfrost_set_blend_color;
-
         gallium->create_depth_stencil_alpha_state = panfrost_create_depth_stencil_state;
         gallium->bind_depth_stencil_alpha_state   = panfrost_bind_depth_stencil_state;
         gallium->delete_depth_stencil_alpha_state = panfrost_delete_depth_stencil_state;
@@ -2824,6 +2744,7 @@  panfrost_create_context(struct pipe_screen *screen, void *priv, unsigned flags)
         gallium->set_stream_output_targets = panfrost_set_stream_output_targets;
 
         panfrost_resource_context_init(gallium);
+        panfrost_blend_context_init(gallium);
 
         panfrost_drm_init_context(ctx);
 
diff --git a/src/gallium/drivers/panfrost/pan_context.h b/src/gallium/drivers/panfrost/pan_context.h
index a913c8581ef..9b977ed59ec 100644
--- a/src/gallium/drivers/panfrost/pan_context.h
+++ b/src/gallium/drivers/panfrost/pan_context.h
@@ -31,6 +31,7 @@ 
 #include <assert.h>
 #include "pan_resource.h"
 #include "pan_job.h"
+#include "pan_blend.h"
 
 #include "pipe/p_compiler.h"
 #include "pipe/p_config.h"
@@ -228,21 +229,6 @@  struct panfrost_rasterizer {
         unsigned tiler_gl_enables;
 };
 
-struct panfrost_blend_state {
-        struct pipe_blend_state base;
-
-        /* Whether a blend shader is in use */
-        bool has_blend_shader;
-
-        /* Compiled fixed function command */
-        struct mali_blend_equation equation;
-        float constant;
-
-        /* Compiled blend shader */
-        mali_ptr blend_shader;
-        int blend_work_count;
-};
-
 /* Variants bundle together to form the backing CSO, bundling multiple
  * shaders with varying emulated features baked in (alpha test
  * parameters, etc) */