llvmpipe: limit the number of fragment shader variants kept around
authorRoland Scheidegger <sroland@vmware.com>
Fri, 18 Jun 2010 12:52:17 +0000 (13:52 +0100)
committerRoland Scheidegger <sroland@vmware.com>
Fri, 18 Jun 2010 12:52:17 +0000 (13:52 +0100)
llvmpipe can create a large number of shader variants for a single shader
(which are quite big), and they were only ever deleted if the shader itself
was deleted. This is especially apparent in things like glean
blendFunc where a new variant is created for every different subtest, chewing
up all memory.
This change limits the numbers of fragment shader variants (for all shaders)
which are kept around to a fixed number. If that would be exceeded a fixed
portion of the cached variants is deleted (since without tracking the used
variants this involves flushing we don't want to delete only one).
Always the least recently used variants (from all shaders together) are
deleted.
For now this is all per-context.
Both the number of how many variants are cached (1024) as well as how many
will be deleted at once (1/4 of the cache size) are just rough guesses and
subject to further optimization.

src/gallium/drivers/llvmpipe/lp_context.c
src/gallium/drivers/llvmpipe/lp_context.h
src/gallium/drivers/llvmpipe/lp_limits.h
src/gallium/drivers/llvmpipe/lp_state_fs.c
src/gallium/drivers/llvmpipe/lp_state_fs.h

index 9e88a6e09f4c54c30506a41fdec99dace5a9875c..06689c20eb8e427c05b944dbc6eb6e3bf68a0ebe 100644 (file)
@@ -36,6 +36,7 @@
 #include "util/u_inlines.h"
 #include "util/u_math.h"
 #include "util/u_memory.h"
+#include "util/u_simple_list.h"
 #include "lp_clear.h"
 #include "lp_context.h"
 #include "lp_flush.h"
@@ -94,6 +95,8 @@ llvmpipe_create_context( struct pipe_screen *screen, void *priv )
 
    memset(llvmpipe, 0, sizeof *llvmpipe);
 
+   make_empty_list(&llvmpipe->fs_variants_list);
+
    llvmpipe->pipe.winsys = screen->winsys;
    llvmpipe->pipe.screen = screen;
    llvmpipe->pipe.priv = priv;
index cb04d4a4d54fa96e07051f37b7319b906f24405b..1bdd0f79e195e381be6b0f2d34172777e699d447 100644 (file)
@@ -38,6 +38,7 @@
 #include "lp_tex_sample.h"
 #include "lp_jit.h"
 #include "lp_setup.h"
+#include "lp_state_fs.h"
 
 
 struct llvmpipe_vbuf_render;
@@ -105,6 +106,8 @@ struct llvmpipe_context {
    unsigned tex_timestamp;
    boolean no_rast;
 
+   struct lp_fs_variant_list_item fs_variants_list;
+   unsigned nr_fs_variants;
 };
 
 
index 4102a9df67c36b4f8feba39a413cc93f843605b2..d1c431475d8f63cbf283612f1144d81a28701fc0 100644 (file)
  */
 #define LP_MAX_SCENE_SIZE (512 * 1024 * 1024)
 
+/**
+ * Max number of shader variants (for all shaders combined,
+ * per context) that will be kept around.
+ */
+#define LP_MAX_SHADER_VARIANTS 1024
 
 #endif /* LP_LIMITS_H */
index 2619e043fdf4a87c493362acf6868f27d3636fc2..48c7754e41acbc0070a39bf5d893f96c4af331f3 100644 (file)
@@ -68,6 +68,7 @@
 #include "util/u_format.h"
 #include "util/u_dump.h"
 #include "util/u_string.h"
+#include "util/u_simple_list.h"
 #include "os/os_time.h"
 #include "pipe/p_shader_tokens.h"
 #include "draw/draw_context.h"
@@ -95,6 +96,7 @@
 #include "lp_setup.h"
 #include "lp_state.h"
 #include "lp_tex_sample.h"
+#include "lp_flush.h"
 
 
 #include <llvm-c/Analysis.h>
@@ -936,7 +938,10 @@ generate_variant(struct llvmpipe_context *lp,
    if(!variant)
       return NULL;
 
-   variant->no = shader->variant_no++;
+   variant->lpfs = shader;
+   variant->list_item_global.base = variant;
+   variant->list_item_local.base = variant;
+   variant->no = shader->variants_created++;
 
    memcpy(&variant->key, key, sizeof *key);
 
@@ -962,10 +967,6 @@ generate_variant(struct llvmpipe_context *lp,
          !shader->info.uses_kill
          ? TRUE : FALSE;
 
-   /* insert new variant into linked list */
-   variant->next = shader->variants;
-   shader->variants = variant;
-
    return variant;
 }
 
@@ -981,6 +982,7 @@ llvmpipe_create_fs_state(struct pipe_context *pipe,
       return NULL;
 
    shader->no = fs_no++;
+   make_empty_list(&shader->variants);
 
    /* get/save the summary info for this shader */
    tgsi_scan_shader(templ->tokens, &shader->info);
@@ -1024,14 +1026,40 @@ llvmpipe_bind_fs_state(struct pipe_context *pipe, void *fs)
    llvmpipe->dirty |= LP_NEW_FS;
 }
 
+static void
+remove_shader_variant(struct llvmpipe_context *lp,
+                      struct lp_fragment_shader_variant *variant)
+{
+   struct llvmpipe_screen *screen = llvmpipe_screen(lp->pipe.screen);
+   unsigned i;
+
+   if (gallivm_debug & GALLIVM_DEBUG_IR) {
+      debug_printf("llvmpipe: del fs #%u var #%u v created #%u v cached #%u v total cached #%u\n",
+                    variant->lpfs->no, variant->no, variant->lpfs->variants_created,
+                    variant->lpfs->variants_cached, lp->nr_fs_variants);
+   }
+   for (i = 0; i < Elements(variant->function); i++) {
+      if (variant->function[i]) {
+         if (variant->jit_function[i])
+            LLVMFreeMachineCodeForFunction(screen->engine,
+                                           variant->function[i]);
+         LLVMDeleteFunction(variant->function[i]);
+      }
+   }
+   remove_from_list(&variant->list_item_local);
+   variant->lpfs->variants_cached--;
+   remove_from_list(&variant->list_item_global);
+   lp->nr_fs_variants--;
+   FREE(variant);
+}
 
 static void
 llvmpipe_delete_fs_state(struct pipe_context *pipe, void *fs)
 {
    struct llvmpipe_context *llvmpipe = llvmpipe_context(pipe);
-   struct llvmpipe_screen *screen = llvmpipe_screen(pipe->screen);
+   struct pipe_fence_handle *fence = NULL;
    struct lp_fragment_shader *shader = fs;
-   struct lp_fragment_shader_variant *variant;
+   struct lp_fs_variant_list_item *li;
 
    assert(fs != llvmpipe->fs);
    (void) llvmpipe;
@@ -1039,29 +1067,23 @@ llvmpipe_delete_fs_state(struct pipe_context *pipe, void *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.
     */
-   draw_flush(llvmpipe->draw);
-   lp_setup_flush(llvmpipe->setup, 0);
-
-   variant = shader->variants;
-   while(variant) {
-      struct lp_fragment_shader_variant *next = variant->next;
-      unsigned i;
 
-      for (i = 0; i < Elements(variant->function); i++) {
-         if (variant->function[i]) {
-            if (variant->jit_function[i])
-               LLVMFreeMachineCodeForFunction(screen->engine,
-                                              variant->function[i]);
-            LLVMDeleteFunction(variant->function[i]);
-         }
-      }
+   llvmpipe_flush(pipe, 0, &fence);
 
-      FREE(variant);
+   if (fence) {
+      pipe->screen->fence_finish(pipe->screen, fence, 0);
+      pipe->screen->fence_reference(pipe->screen, &fence, NULL);
+   }
 
-      variant = next;
+   li = first_elem(&shader->variants);
+   while(!at_end(&shader->variants, li)) {
+      remove_shader_variant(llvmpipe, li->base);
+      li = next_elem(li);
    }
 
+   assert(shader->variants_cached == 0);
    FREE((void *) shader->base.tokens);
    FREE(shader);
 }
@@ -1215,7 +1237,6 @@ make_variant_key(struct llvmpipe_context *lp,
          lp_sampler_static_state(&key->sampler[i], lp->fragment_sampler_views[i], lp->sampler[i]);
 }
 
-
 /**
  * Update fragment state.  This is called just prior to drawing
  * something when some fragment-related state has changed.
@@ -1225,21 +1246,47 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
 {
    struct lp_fragment_shader *shader = lp->fs;
    struct lp_fragment_shader_variant_key key;
-   struct lp_fragment_shader_variant *variant;
+   struct lp_fragment_shader_variant *variant = NULL;
+   struct lp_fs_variant_list_item *li;
 
    make_variant_key(lp, shader, &key);
 
-   variant = shader->variants;
-   while(variant) {
-      if(memcmp(&variant->key, &key, sizeof key) == 0)
+   li = first_elem(&shader->variants);
+   while(!at_end(&shader->variants, li)) {
+      if(memcmp(&li->base->key, &key, sizeof key) == 0) {
+         variant = li->base;
          break;
-
-      variant = variant->next;
+      }
+      li = next_elem(li);
    }
 
-   if (!variant) {
+   if (variant) {
+      move_to_head(&lp->fs_variants_list, &variant->list_item_global);
+   }
+   else {
       int64_t t0, t1;
       int64_t dt;
+      unsigned i;
+      if (lp->nr_fs_variants >= LP_MAX_SHADER_VARIANTS) {
+         struct pipe_context *pipe = &lp->pipe;
+         struct pipe_fence_handle *fence = NULL;
+
+         /*
+          * 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_flush(pipe, 0, &fence);
+
+         if (fence) {
+            pipe->screen->fence_finish(pipe->screen, fence, 0);
+            pipe->screen->fence_reference(pipe->screen, &fence, NULL);
+         }
+         for (i = 0; i < LP_MAX_SHADER_VARIANTS / 4; i++) {
+            struct lp_fs_variant_list_item *item = last_elem(&lp->fs_variants_list);
+            remove_shader_variant(lp, item->base);
+         }
+      }
       t0 = os_time_get();
 
       variant = generate_variant(lp, shader, &key);
@@ -1248,6 +1295,13 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
       dt = t1 - t0;
       LP_COUNT_ADD(llvm_compile_time, dt);
       LP_COUNT_ADD(nr_llvm_compiles, 2);  /* emit vs. omit in/out test */
+
+      if (variant) {
+         insert_at_head(&shader->variants, &variant->list_item_local);
+         insert_at_head(&lp->fs_variants_list, &variant->list_item_global);
+         lp->nr_fs_variants++;
+         shader->variants_cached++;
+      }
    }
 
    lp_setup_set_fs_variant(lp->setup, variant);
index 64ead2a99738fb23e4bf9ad66f74f6710d2c953e..272c9269eaef3d55ff586ab69211c7a7a612b1fb 100644 (file)
@@ -64,10 +64,16 @@ struct lp_fragment_shader_variant_key
    struct lp_sampler_static_state sampler[PIPE_MAX_SAMPLERS];
 };
 
+struct lp_fs_variant_list_item
+{
+   struct lp_fragment_shader_variant *base;
+   struct lp_fs_variant_list_item *next, *prev;
+};
 
 struct lp_fragment_shader_variant
 {
    struct lp_fragment_shader_variant_key key;
+   struct lp_fragment_shader *lpfs;
 
    boolean opaque;
 
@@ -75,7 +81,7 @@ struct lp_fragment_shader_variant
 
    lp_jit_frag_func jit_function[2];
 
-   struct lp_fragment_shader_variant *next;
+   struct lp_fs_variant_list_item list_item_global, list_item_local;
 
    /* For debugging/profiling purposes */
    unsigned no;
@@ -89,11 +95,12 @@ struct lp_fragment_shader
 
    struct tgsi_shader_info info;
 
-   struct lp_fragment_shader_variant *variants;
+   struct lp_fs_variant_list_item variants;
 
    /* For debugging/profiling purposes */
    unsigned no;
-   unsigned variant_no;
+   unsigned variants_created;
+   unsigned variants_cached;
 };