panfrost: Move the blend shader cache at the context level

Blend shaders can be shared among blend states, so let's move the blend
shader one level up so we don't have to re-create/re-compile shaders
when another blend state already asked for it.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7066>
diff --git a/src/gallium/drivers/panfrost/pan_blend.h b/src/gallium/drivers/panfrost/pan_blend.h
index 8e30d67..901d54a 100644
--- a/src/gallium/drivers/panfrost/pan_blend.h
+++ b/src/gallium/drivers/panfrost/pan_blend.h
@@ -33,19 +33,31 @@
 
 struct panfrost_bo;
 
+struct panfrost_blend_shader_key {
+        /* RT format */
+        enum pipe_format format;
+
+        /* Render target */
+        unsigned rt : 3;
+
+        /* Blend shader uses blend constants */
+        unsigned has_constants : 1;
+
+        /* Logic Op info */
+        unsigned logicop_enable : 1;
+        unsigned logicop_func:4;
+
+        struct pipe_rt_blend_state equation;
+};
+
 /* An internal blend shader descriptor, from the compiler */
 
 struct panfrost_blend_shader {
+        struct panfrost_blend_shader_key key;
         struct panfrost_context *ctx;
 
         nir_shader *nir;
 
-        /* Render target */
-        unsigned rt;
-
-        /* RT format */
-        enum pipe_format format;
-
         /* Blend constants */
         float constants[4];
 
@@ -92,11 +104,6 @@
 
         /* Properties of the blend mode */
         bool opaque, load_dest, no_colour;
-
-        /* 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 {
diff --git a/src/gallium/drivers/panfrost/pan_blend_cso.c b/src/gallium/drivers/panfrost/pan_blend_cso.c
index e1f279d..15308d2 100644
--- a/src/gallium/drivers/panfrost/pan_blend_cso.c
+++ b/src/gallium/drivers/panfrost/pan_blend_cso.c
@@ -77,16 +77,30 @@
         assert(fmt != 0);
 
         /* Check the cache. Key by the RT and format */
-        struct hash_table_u64 *shaders = blend->rt[rt].shaders;
-        unsigned key = (fmt << 4) | ((constants != NULL) << 3) | rt;
+        struct hash_table *shaders = ctx->blend_shaders;
+        struct panfrost_blend_shader_key key = {
+                .rt = rt,
+                .format = fmt,
+                .has_constants = constants != NULL,
+                .logicop_enable = blend->base.logicop_enable,
+        };
 
-        struct panfrost_blend_shader *shader =
-                _mesa_hash_table_u64_search(shaders, key);
+        if (blend->base.logicop_enable) {
+                key.logicop_func = blend->base.logicop_func;
+        } else {
+                unsigned idx = blend->base.independent_blend_enable ? rt : 0;
+
+                if (blend->base.rt[idx].blend_enable)
+                        key.equation = blend->base.rt[idx];
+        }
+
+        struct hash_entry *he = _mesa_hash_table_search(shaders, &key);
+        struct panfrost_blend_shader *shader = he ? he->data : NULL;
 
         if (!shader) {
                 /* Cache miss. Build one instead, cache it, and go */
-                shader = panfrost_create_blend_shader(ctx, blend, fmt, rt);
-                _mesa_hash_table_u64_insert(shaders, key, shader);
+                shader = panfrost_create_blend_shader(ctx, blend, &key);
+                _mesa_hash_table_insert(shaders, &shader->key, shader);
         }
 
         panfrost_compile_blend_shader(shader, constants);
@@ -114,7 +128,6 @@
                 struct pipe_rt_blend_state pipe = blend->rt[g];
 
                 struct panfrost_blend_rt *rt = &so->rt[c];
-                rt->shaders = _mesa_hash_table_u64_create(so);
 
                 /* Logic ops are always shader */
                 if (blend->logicop_enable) {
diff --git a/src/gallium/drivers/panfrost/pan_blend_shaders.c b/src/gallium/drivers/panfrost/pan_blend_shaders.c
index b0c6d27..fee3bbe 100644
--- a/src/gallium/drivers/panfrost/pan_blend_shaders.c
+++ b/src/gallium/drivers/panfrost/pan_blend_shaders.c
@@ -132,23 +132,21 @@
 struct panfrost_blend_shader *
 panfrost_create_blend_shader(struct panfrost_context *ctx,
                              struct panfrost_blend_state *state,
-                             enum pipe_format format,
-                             unsigned rt)
+                             const struct panfrost_blend_shader_key *key)
 {
-        struct panfrost_blend_shader *res = rzalloc(state, struct panfrost_blend_shader);
+        struct panfrost_blend_shader *res = rzalloc(ctx, struct panfrost_blend_shader);
 
         res->ctx = ctx;
-        res->rt = rt;
-        res->format = format;
+        res->key = *key;
 
         /* Build the shader */
 
-        nir_shader *shader = nir_shader_create(state, MESA_SHADER_FRAGMENT, &midgard_nir_options, NULL);
+        nir_shader *shader = nir_shader_create(ctx, MESA_SHADER_FRAGMENT, &midgard_nir_options, NULL);
         nir_function *fn = nir_function_create(shader, "main");
         nir_function_impl *impl = nir_function_impl_create(fn);
 
         const struct util_format_description *format_desc =
-                util_format_description(format);
+                util_format_description(key->format);
 
         nir_alu_type T = pan_unpacked_type_for_format(format_desc);
         enum glsl_base_type g =
@@ -201,8 +199,8 @@
         /* Build a trivial blend shader */
         nir_store_var(b, c_out, s_src[0], 0xFF);
 
-        nir_lower_blend_options options = nir_make_options(&state->base, rt);
-        options.format = format;
+        nir_lower_blend_options options = nir_make_options(&state->base, key->rt);
+        options.format = key->format;
         options.src1 = s_src[1];
 
         if (T == nir_type_float16)
@@ -235,8 +233,8 @@
         struct panfrost_compile_inputs inputs = {
                 .gpu_id = dev->gpu_id,
                 .is_blend = true,
-                .blend.rt = shader->rt,
-                .rt_formats = {shader->format},
+                .blend.rt = shader->key.rt,
+                .rt_formats = {shader->key.format},
         };
 
         if (constants)
diff --git a/src/gallium/drivers/panfrost/pan_blend_shaders.h b/src/gallium/drivers/panfrost/pan_blend_shaders.h
index 9200489..3c47f0b 100644
--- a/src/gallium/drivers/panfrost/pan_blend_shaders.h
+++ b/src/gallium/drivers/panfrost/pan_blend_shaders.h
@@ -34,8 +34,7 @@
 struct panfrost_blend_shader *
 panfrost_create_blend_shader(struct panfrost_context *ctx,
                              struct panfrost_blend_state *state,
-                             enum pipe_format format,
-                             unsigned rt);
+                             const struct panfrost_blend_shader_key *key);
 
 void
 panfrost_compile_blend_shader(struct panfrost_blend_shader *shader,
diff --git a/src/gallium/drivers/panfrost/pan_context.c b/src/gallium/drivers/panfrost/pan_context.c
index 06afce0..adc0cd8 100644
--- a/src/gallium/drivers/panfrost/pan_context.c
+++ b/src/gallium/drivers/panfrost/pan_context.c
@@ -1504,6 +1504,16 @@
         so->num_targets = num_targets;
 }
 
+static uint32_t panfrost_shader_key_hash(const void *key)
+{
+        return _mesa_hash_data(key, sizeof(struct panfrost_blend_shader_key));
+}
+
+static bool panfrost_shader_key_equal(const void *a, const void *b)
+{
+        return !memcmp(a, b, sizeof(struct panfrost_blend_shader_key));
+}
+
 struct pipe_context *
 panfrost_create_context(struct pipe_screen *screen, void *priv, unsigned flags)
 {
@@ -1607,11 +1617,11 @@
 
         panfrost_batch_init(ctx);
 
-        if (!(dev->quirks & IS_BIFROST)) {
-                ctx->blit_blend = rzalloc(ctx, struct panfrost_blend_state);
-                for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++c)
-                        ctx->blit_blend->rt[c].shaders = _mesa_hash_table_u64_create(ctx);
-        }
+        ctx->blit_blend = rzalloc(ctx, struct panfrost_blend_state);
+        ctx->blend_shaders =
+                _mesa_hash_table_create(ctx,
+                                        panfrost_shader_key_hash,
+                                        panfrost_shader_key_equal);
 
         /* By default mask everything on */
         ctx->sample_mask = ~0;
diff --git a/src/gallium/drivers/panfrost/pan_context.h b/src/gallium/drivers/panfrost/pan_context.h
index 130989f..f725113 100644
--- a/src/gallium/drivers/panfrost/pan_context.h
+++ b/src/gallium/drivers/panfrost/pan_context.h
@@ -179,6 +179,7 @@
         unsigned min_samples;
 
         struct panfrost_blend_state *blit_blend;
+        struct hash_table *blend_shaders;
 };
 
 /* Corresponds to the CSO */