llvmpipe: add reference counting to fragment shaders.
authorDave Airlie <airlied@redhat.com>
Mon, 6 Jul 2020 05:32:19 +0000 (15:32 +1000)
committerDave Airlie <airlied@redhat.com>
Mon, 31 Aug 2020 00:02:22 +0000 (10:02 +1000)
Currently llvmpipe calls finish on the context when a shader
variant has to be destroyed just in case the variant is currently
in use by the setup engine.

Fix this by reference counting the shaders, and reference counting
the shader variants.

Whenever a shader is used in the rasteriser backend, it is added
to a reference list and removed when the rasterizer is finished with it.

Reviewed-by: Roland Scheidegger <sroland@vmware.com>
Reviewed-by: Jose Fonseca <jfonseca@vmware.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6341>

src/gallium/drivers/llvmpipe/lp_scene.c
src/gallium/drivers/llvmpipe/lp_scene.h
src/gallium/drivers/llvmpipe/lp_setup.c
src/gallium/drivers/llvmpipe/lp_state_fs.c
src/gallium/drivers/llvmpipe/lp_state_fs.h

index 59eed4145504942a08f6250f37cb72dd2af5ebb3..539b84c65256794d56b8547113d15a3709e2d02b 100644 (file)
@@ -34,6 +34,8 @@
 #include "lp_scene.h"
 #include "lp_fence.h"
 #include "lp_debug.h"
+#include "lp_context.h"
+#include "lp_state_fs.h"
 
 
 #define RESOURCE_REF_SZ 32
@@ -45,6 +47,14 @@ struct resource_ref {
    struct resource_ref *next;
 };
 
+#define SHADER_REF_SZ 32
+/** List of shader variant references */
+struct shader_ref {
+   struct lp_fragment_shader_variant *variant[SHADER_REF_SZ];
+   int count;
+   struct shader_ref *next;
+};
+
 
 /**
  * Create a new scene object.
@@ -281,6 +291,22 @@ lp_scene_end_rasterization(struct lp_scene *scene )
                       j, scene->resource_reference_size);
    }
 
+   /* Decrement shader variant ref counts
+    */
+   {
+      struct shader_ref *ref;
+      int i, j = 0;
+
+      for (ref = scene->frag_shaders; ref; ref = ref->next) {
+         for (i = 0; i < ref->count; i++) {
+            if (LP_DEBUG & DEBUG_SETUP)
+               debug_printf("shader %d: %p\n", j, (void *) ref->variant[i]);
+            j++;
+            lp_fs_variant_reference(llvmpipe_context(scene->pipe), &ref->variant[i], NULL);
+         }
+      }
+   }
+
    /* Free all scene data blocks:
     */
    {
@@ -299,6 +325,7 @@ lp_scene_end_rasterization(struct lp_scene *scene )
    lp_fence_reference(&scene->fence, NULL);
 
    scene->resources = NULL;
+   scene->frag_shaders = NULL;
    scene->scene_size = 0;
    scene->resource_reference_size = 0;
 
@@ -434,6 +461,53 @@ lp_scene_add_resource_reference(struct lp_scene *scene,
 }
 
 
+/**
+ * Add a reference to a fragment shader variant
+ */
+boolean
+lp_scene_add_frag_shader_reference(struct lp_scene *scene,
+                                   struct lp_fragment_shader_variant *variant)
+{
+   struct shader_ref *ref, **last = &scene->frag_shaders;
+   int i;
+
+   /* Look at existing resource blocks:
+    */
+   for (ref = scene->frag_shaders; ref; ref = ref->next) {
+      last = &ref->next;
+
+      /* Search for this resource:
+       */
+      for (i = 0; i < ref->count; i++)
+         if (ref->variant[i] == variant)
+            return TRUE;
+
+      if (ref->count < SHADER_REF_SZ) {
+         /* If the block is half-empty, then append the reference here.
+          */
+         break;
+      }
+   }
+
+   /* Create a new block if no half-empty block was found.
+    */
+   if (!ref) {
+      assert(*last == NULL);
+      *last = lp_scene_alloc(scene, sizeof *ref);
+      if (*last == NULL)
+          return FALSE;
+
+      ref = *last;
+      memset(ref, 0, sizeof *ref);
+   }
+
+   /* Append the reference to the reference block.
+    */
+   lp_fs_variant_reference(llvmpipe_context(scene->pipe), &ref->variant[ref->count++], variant);
+
+   return TRUE;
+}
+
 /**
  * Does this scene have a reference to the given resource?
  */
index 679e2c07b28c1c0382c7c524a94c79a2720cac23..ba6b201395a716a5cd4229483f34636f4f7984e7 100644 (file)
@@ -117,6 +117,8 @@ struct data_block_list {
 
 struct resource_ref;
 
+struct shader_ref;
+
 /**
  * All bins and bin data are contained here.
  * Per-bin data goes into the 'tile' bins.
@@ -162,6 +164,9 @@ struct lp_scene {
    /** list of resources referenced by the scene commands */
    struct resource_ref *resources;
 
+   /** list of frag shaders referenced by the scene commands */
+   struct shader_ref *frag_shaders;
+
    /** Total memory used by the scene (in bytes).  This sums all the
     * data blocks and counts all bins, state, resource references and
     * other random allocations within the scene.
@@ -209,6 +214,10 @@ boolean lp_scene_add_resource_reference(struct lp_scene *scene,
 boolean lp_scene_is_resource_referenced(const struct lp_scene *scene,
                                         const struct pipe_resource *resource );
 
+boolean lp_scene_add_frag_shader_reference(struct lp_scene *scene,
+                                           struct lp_fragment_shader_variant *variant);
+
+
 
 /**
  * Allocate space for a command/data in the bin's data buffer.
index 550062ffcfce3664057f7654480f518459d8b086..57d0acb41b15d654e3cd444f12a18865b4af0e78 100644 (file)
@@ -128,6 +128,7 @@ void lp_setup_reset( struct lp_setup_context *setup )
       setup->constants[i].stored_size = 0;
       setup->constants[i].stored_data = NULL;
    }
+
    setup->fs.stored = NULL;
    setup->dirty = ~0;
 
@@ -624,7 +625,6 @@ lp_setup_set_fs_variant( struct lp_setup_context *setup,
 {
    LP_DBG(DEBUG_SETUP, "%s %p\n", __FUNCTION__,
           variant);
-   /* FIXME: reference count */
 
    setup->fs.current.variant = variant;
    setup->dirty |= LP_SETUP_NEW_FS;
@@ -1280,9 +1280,14 @@ try_update_scene_state( struct lp_setup_context *setup )
             return FALSE;
          }
 
-         memcpy(stored,
-                &setup->fs.current,
-                sizeof setup->fs.current);
+         memcpy(&stored->jit_context,
+                &setup->fs.current.jit_context,
+                sizeof setup->fs.current.jit_context);
+         stored->variant = setup->fs.current.variant;
+
+         if (!lp_scene_add_frag_shader_reference(scene,
+                                                 setup->fs.current.variant))
+            return FALSE;
          setup->fs.stored = stored;
          
          /* The scene now references the textures in the rasterization
index 5166238a118fe8d8a3a30336278698ac1c62950d..efb6bee25117cee68a96d9852717d544245e72e5 100644 (file)
@@ -3481,7 +3481,9 @@ generate_variant(struct llvmpipe_context *lp,
    snprintf(module_name, sizeof(module_name), "fs%u_variant%u",
             shader->no, shader->variants_created);
 
-   variant->shader = shader;
+   pipe_reference_init(&variant->reference, 1);
+   lp_fs_reference(lp, &variant->shader, shader);
+
    memcpy(&variant->key, key, shader->variant_key_size);
 
    if (shader->base.ir.nir) {
@@ -3588,6 +3590,7 @@ llvmpipe_create_fs_state(struct pipe_context *pipe,
    if (!shader)
       return NULL;
 
+   pipe_reference_init(&shader->reference, 1);
    shader->no = fs_no++;
    make_empty_list(&shader->variants);
 
@@ -3687,8 +3690,10 @@ llvmpipe_bind_fs_state(struct pipe_context *pipe, void *fs)
    draw_bind_fragment_shader(llvmpipe->draw,
                              (lp_fs ? lp_fs->draw_data : NULL));
 
-   llvmpipe->fs = lp_fs;
+   lp_fs_reference(llvmpipe, &llvmpipe->fs, lp_fs);
 
+   /* invalidate the setup link, NEW_FS will make it update */
+   lp_setup_set_fs_variant(llvmpipe->setup, NULL);
    llvmpipe->dirty |= LP_NEW_FS;
 }
 
@@ -3697,9 +3702,10 @@ llvmpipe_bind_fs_state(struct pipe_context *pipe, void *fs)
  * Remove shader variant from two lists: the shader's variant list
  * and the context's variant list.
  */
-static void
-llvmpipe_remove_shader_variant(struct llvmpipe_context *lp,
-                               struct lp_fragment_shader_variant *variant)
+
+static
+void llvmpipe_remove_shader_variant(struct llvmpipe_context *lp,
+                                    struct lp_fragment_shader_variant *variant)
 {
    if ((LP_DEBUG & DEBUG_FS) || (gallivm_debug & GALLIVM_DEBUG_IR)) {
       debug_printf("llvmpipe: del fs #%u var %u v created %u v cached %u "
@@ -3710,8 +3716,6 @@ llvmpipe_remove_shader_variant(struct llvmpipe_context *lp,
                    lp->nr_fs_variants, variant->nr_instrs, lp->nr_fs_instrs);
    }
 
-   gallivm_destroy(variant->gallivm);
-
    /* remove from shader's list */
    remove_from_list(&variant->list_item_local);
    variant->shader->variants_cached--;
@@ -3720,10 +3724,32 @@ llvmpipe_remove_shader_variant(struct llvmpipe_context *lp,
    remove_from_list(&variant->list_item_global);
    lp->nr_fs_variants--;
    lp->nr_fs_instrs -= variant->nr_instrs;
+}
+
+void
+llvmpipe_destroy_shader_variant(struct llvmpipe_context *lp,
+                               struct lp_fragment_shader_variant *variant)
+{
+   gallivm_destroy(variant->gallivm);
+
+   lp_fs_reference(lp, &variant->shader, NULL);
 
    FREE(variant);
 }
 
+void
+llvmpipe_destroy_fs(struct llvmpipe_context *llvmpipe,
+                    struct lp_fragment_shader *shader)
+{
+   /* Delete draw module's data */
+   draw_delete_fragment_shader(llvmpipe->draw, shader->draw_data);
+
+   if (shader->base.ir.nir)
+      ralloc_free(shader->base.ir.nir);
+   assert(shader->variants_cached == 0);
+   FREE((void *) shader->base.tokens);
+   FREE(shader);
+}
 
 static void
 llvmpipe_delete_fs_state(struct pipe_context *pipe, void *fs)
@@ -3732,35 +3758,20 @@ llvmpipe_delete_fs_state(struct pipe_context *pipe, void *fs)
    struct lp_fragment_shader *shader = fs;
    struct lp_fs_variant_list_item *li;
 
-   assert(fs != llvmpipe->fs);
-
-   /*
-    * XXX: we need to flush the context until we have some sort of reference
-    * counting in fragment shaders as they may still be binned
-    * Flushing alone might not sufficient we need to wait on it too.
-    */
-   llvmpipe_finish(pipe, __FUNCTION__);
-
    /* Delete all the variants */
    li = first_elem(&shader->variants);
    while(!at_end(&shader->variants, li)) {
       struct lp_fs_variant_list_item *next = next_elem(li);
+      struct lp_fragment_shader_variant *variant;
+      variant = li->base;
       llvmpipe_remove_shader_variant(llvmpipe, li->base);
+      lp_fs_variant_reference(llvmpipe, &variant, NULL);
       li = next;
    }
 
-   /* Delete draw module's data */
-   draw_delete_fragment_shader(llvmpipe->draw, shader->draw_data);
-
-   if (shader->base.ir.nir)
-      ralloc_free(shader->base.ir.nir);
-   assert(shader->variants_cached == 0);
-   FREE((void *) shader->base.tokens);
-   FREE(shader);
+   lp_fs_reference(llvmpipe, &shader, NULL);
 }
 
-
-
 static void
 llvmpipe_set_constant_buffer(struct pipe_context *pipe,
                              enum pipe_shader_type shader, uint index,
@@ -4181,8 +4192,6 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
 
       if (variants_to_cull ||
           lp->nr_fs_instrs >= LP_MAX_SHADER_INSTRUCTIONS) {
-         struct pipe_context *pipe = &lp->pipe;
-
          if (gallivm_debug & GALLIVM_DEBUG_PERF) {
             debug_printf("Evicting FS: %u fs variants,\t%u total variants,"
                          "\t%u instrs,\t%u instrs/variant\n",
@@ -4191,13 +4200,6 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
                          lp->nr_fs_instrs / lp->nr_fs_variants);
          }
 
-         /*
-          * XXX: we need to flush the context until we have some sort of
-          * reference counting in fragment shaders as they may still be binned
-          * Flushing alone might not be sufficient we need to wait on it too.
-          */
-         llvmpipe_finish(pipe, __FUNCTION__);
-
          /*
           * We need to re-check lp->nr_fs_variants because an arbitrarliy large
           * number of shader variants (potentially all of them) could be
@@ -4213,6 +4215,7 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
             assert(item);
             assert(item->base);
             llvmpipe_remove_shader_variant(lp, item->base);
+            lp_fs_variant_reference(lp, &item->base, NULL);
          }
       }
 
index 555bacff4ec60793a6bfdc17eb7ffbc25560758e..4dd48db356e75cb3517e580dc8e83d1e47ed22c5 100644 (file)
@@ -36,7 +36,7 @@
 #include "gallivm/lp_bld_sample.h" /* for struct lp_sampler_static_state */
 #include "gallivm/lp_bld_tgsi.h" /* for lp_tgsi_info */
 #include "lp_bld_interp.h" /* for struct lp_shader_input */
-
+#include "util/u_inlines.h"
 
 struct tgsi_token;
 struct lp_fragment_shader;
@@ -128,7 +128,7 @@ struct lp_fs_variant_list_item
 
 struct lp_fragment_shader_variant
 {
-
+   struct pipe_reference reference;
    boolean opaque;
 
    struct gallivm_state *gallivm;
@@ -160,6 +160,7 @@ struct lp_fragment_shader
 {
    struct pipe_shader_state base;
 
+   struct pipe_reference reference;
    struct lp_tgsi_info info;
 
    struct lp_fs_variant_list_item variants;
@@ -180,4 +181,36 @@ struct lp_fragment_shader
 void
 lp_debug_fs_variant(struct lp_fragment_shader_variant *variant);
 
+void
+llvmpipe_destroy_fs(struct llvmpipe_context *llvmpipe,
+                    struct lp_fragment_shader *shader);
+
+static inline void
+lp_fs_reference(struct llvmpipe_context *llvmpipe,
+                struct lp_fragment_shader **ptr,
+                struct lp_fragment_shader *shader)
+{
+   struct lp_fragment_shader *old_ptr = *ptr;
+   if (pipe_reference(old_ptr ? &(*ptr)->reference : NULL, shader ? &shader->reference : NULL)) {
+      llvmpipe_destroy_fs(llvmpipe, old_ptr);
+   }
+   *ptr = shader;
+}
+
+void
+llvmpipe_destroy_shader_variant(struct llvmpipe_context *lp,
+                                struct lp_fragment_shader_variant *variant);
+
+static inline void
+lp_fs_variant_reference(struct llvmpipe_context *llvmpipe,
+                        struct lp_fragment_shader_variant **ptr,
+                        struct lp_fragment_shader_variant *variant)
+{
+   struct lp_fragment_shader_variant *old_ptr = *ptr;
+   if (pipe_reference(old_ptr ? &(*ptr)->reference : NULL, variant ? &variant->reference : NULL)) {
+      llvmpipe_destroy_shader_variant(llvmpipe, old_ptr);
+   }
+   *ptr = variant;
+}
+
 #endif /* LP_STATE_FS_H_ */