panfrost: Fix blend leak for render targets 5-8
[mesa.git] / src / gallium / drivers / panfrost / pan_blend_cso.c
index a1b8111190f54ab24fdbc57842ddaa3a8dd4b129..ffda1196d046dedbfc6fc4b23aeb4e62ead7d9c3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 Collabora
+ * Copyright (C) 2019 Collabora, Ltd.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -29,6 +29,8 @@
 #include "util/u_memory.h"
 #include "pan_blend_shaders.h"
 #include "pan_blending.h"
+#include "pan_bo.h"
+#include "panfrost-quirks.h"
 
 /* A given Gallium blend state can be encoded to the hardware in numerous,
  * dramatically divergent ways due to the interactions of blending with
  * befast, suitable for calling every draw to avoid wacky dirty
  * tracking paths. If the cache hits, boom, done. */
 
-static struct panfrost_blend_shader *
+struct panfrost_blend_shader *
 panfrost_get_blend_shader(
-                struct panfrost_context *ctx,
-                struct panfrost_blend_state *blend,
-                enum pipe_format fmt,
-                unsigned rt)
+        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 */
+        /* Check the cache. Key by the RT and format */
         struct hash_table_u64 *shaders = blend->rt[rt].shaders;
+        unsigned key = (fmt << 3) | rt;
 
         struct panfrost_blend_shader *shader =
-                _mesa_hash_table_u64_search(shaders, fmt);
+                _mesa_hash_table_u64_search(shaders, key);
 
         if (shader)
                 return shader;
@@ -85,10 +88,10 @@ panfrost_get_blend_shader(
         /* Cache miss. Build one instead, cache it, and go */
 
         struct panfrost_blend_shader generated =
-                panfrost_compile_blend_shader(ctx, &blend->base, fmt);
+                panfrost_compile_blend_shader(ctx, &blend->base, fmt, rt);
 
         shader = mem_dup(&generated, sizeof(generated));
-        _mesa_hash_table_u64_insert(shaders, fmt, shader);
+        _mesa_hash_table_u64_insert(shaders, key, shader);
         return  shader;
 }
 
@@ -104,27 +107,32 @@ panfrost_create_blend_state(struct pipe_context *pipe,
         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) {
+        for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++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],
+                /* Without indep blending, the first RT settings replicate */
+
+                if (!blend->logicop_enable) {
+                        unsigned g =
+                                blend->independent_blend_enable ? c : 0;
+
+                        rt->has_fixed_function =
+                                panfrost_make_fixed_blend_mode(
+                                        &blend->rt[g],
                                         &rt->equation,
                                         &rt->constant_mask,
-                                        blend->rt[c].colormask);
+                                        blend->rt[g].colormask);
+                }
 
                 /* Regardless if that works, we also need to initialize
                  * the blend shaders */
 
-                rt->shaders = _mesa_hash_table_u64_create(NULL);
+                rt->shaders = _mesa_hash_table_u64_create(so);
         }
 
         return so;
@@ -141,19 +149,26 @@ panfrost_bind_blend_state(struct pipe_context *pipe,
 
         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_shader(struct hash_entry *entry)
+{
+        struct panfrost_blend_shader *shader = (struct panfrost_blend_shader *)entry->data;
+        free(shader->buffer);
+        free(shader);
 }
 
 static void
 panfrost_delete_blend_state(struct pipe_context *pipe,
-                            void *blend)
+                            void *cso)
 {
-        /* TODO: leaks internally? */
+        struct panfrost_blend_state *blend = (struct panfrost_blend_state *) cso;
 
+        for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++c) {
+                struct panfrost_blend_rt *rt = &blend->rt[c];
+                _mesa_hash_table_u64_clear(rt->shaders, panfrost_delete_blend_shader);
+        }
         ralloc_free(blend);
 }
 
@@ -173,13 +188,10 @@ panfrost_set_blend_color(struct pipe_context *pipe,
 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 there is no components used, it automatically works */
 
-        if (!mask) {
-                *out = 0.0;
+        if (!mask)
                 return true;
-        }
 
         /* Find some starter mask */
         unsigned first = ffs(mask) - 1;
@@ -190,10 +202,8 @@ panfrost_blend_constant(float *out, float *in, unsigned mask)
         while (mask) {
                 unsigned i = u_bit_scan(&mask);
 
-                if (in[i] != cons) {
-                        *out = 0.0;
+                if (in[i] != cons)
                         return false;
-                }
         }
 
         /* Otherwise, we're good to go */
@@ -206,54 +216,68 @@ panfrost_blend_constant(float *out, float *in, unsigned mask)
 struct panfrost_blend_final
 panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rti)
 {
-        /* Grab the format */
+        struct panfrost_batch *batch = panfrost_get_batch_for_fbo(ctx);
         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 */
+        /* First, we'll try fixed function, matching equationn and constant */
         if (rt->has_fixed_function && panfrost_can_fixed_blend(fmt)) {
+                float constant = 0.0;
+
                 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;
+                            &constant,
+                            ctx->blend_color.color,
+                            rt->constant_mask)) {
+                        bool no_blending =
+                                (rt->equation.rgb_mode == 0x122) &&
+                                (rt->equation.alpha_mode == 0x122) &&
+                                (rt->equation.color_mask == 0xf);
+
+                        struct panfrost_blend_final final = {
+                                .equation = {
+                                        .equation = &rt->equation,
+                                        .constant = constant
+                                },
+                                .no_blending = no_blending,
+                                .no_colour = (rt->equation.color_mask == 0x0)
+                        };
+
                         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;
+
+        struct panfrost_bo *bo = panfrost_batch_create_bo(batch, shader->size,
+                   PAN_BO_EXECUTE,
+                   PAN_BO_ACCESS_PRIVATE |
+                   PAN_BO_ACCESS_READ |
+                   PAN_BO_ACCESS_FRAGMENT);
+
+        memcpy(bo->cpu, shader->buffer, shader->size);
 
         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);
+                 * patch in the current constants */
 
-                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;
+                float *patch = (float *) (bo->cpu + shader->patch_index);
+                memcpy(patch, ctx->blend_color.color, sizeof(float) * 4);
         }
 
-        final.shader.gpu |= shader->first_tag;
+        struct panfrost_blend_final final = {
+                .is_shader = true,
+                .shader = {
+                        .work_count = shader->work_count,
+                        .first_tag = shader->first_tag,
+                        .gpu = bo->gpu,
+                }
+        };
+
         return final;
 }