fix refcounting bugs in tnl/tex program caches
authorBrian Paul <brian.paul@tungstengraphics.com>
Thu, 8 May 2008 00:51:44 +0000 (18:51 -0600)
committerBrian Paul <brian.paul@tungstengraphics.com>
Thu, 8 May 2008 00:51:44 +0000 (18:51 -0600)
src/mesa/main/mtypes.h
src/mesa/main/texenvprogram.c
src/mesa/shader/program.c
src/mesa/tnl/t_context.h
src/mesa/tnl/t_vp_build.c

index 98fab69fd82856adfda29e60781cb3a719c8afb2..001240a14f59e94a8896c771b068df061d378876 100644 (file)
@@ -1544,19 +1544,17 @@ struct gl_texture_unit
    /*@}*/
 };
 
-struct texenvprog_cache_item {
-   GLuint hash;
-   void *key;
-   struct gl_fragment_program *data;
-   struct texenvprog_cache_item *next;
-};
 
-struct texenvprog_cache {
+struct texenvprog_cache_item;
+
+struct texenvprog_cache
+{
    struct texenvprog_cache_item **items;
    GLuint size, n_items;
    GLcontext *ctx;
 };
 
+
 /**
  * Texture attribute group (GL_TEXTURE_BIT).
  */
index fb68bf0720e04385a7c55d2b3cfc7dc1e9159876..68a4db919749a8082e8945d55febb629f0786cd6 100644 (file)
 #include "glheader.h"
 #include "macros.h"
 #include "enums.h"
+#include "shader/program.h"
 #include "shader/prog_parameter.h"
 #include "shader/prog_instruction.h"
 #include "shader/prog_print.h"
 #include "shader/prog_statevars.h"
 #include "texenvprogram.h"
 
+
+struct texenvprog_cache_item
+{
+   GLuint hash;
+   void *key;
+   struct gl_fragment_program *data;
+   struct texenvprog_cache_item *next;
+};
+
+
 /**
  * This MAX is probably a bit generous, but that's OK.  There can be
  * up to four instructions per texture unit (TEX + 3 for combine),
@@ -1133,7 +1144,7 @@ search_cache(const struct texenvprog_cache *cache,
 
    for (c = cache->items[hash % cache->size]; c; c = c->next) {
       if (c->hash == hash && memcmp(c->key, key, keysize) == 0)
-        return (struct gl_fragment_program *) c->data;
+        return c->data;
    }
 
    return NULL;
@@ -1161,7 +1172,7 @@ static void rehash( struct texenvprog_cache *cache )
    cache->size = size;
 }
 
-static void clear_cache( struct texenvprog_cache *cache )
+static void clear_cache(GLcontext *ctx, struct texenvprog_cache *cache)
 {
    struct texenvprog_cache_item *c, *next;
    GLuint i;
@@ -1170,8 +1181,7 @@ static void clear_cache( struct texenvprog_cache *cache )
       for (c = cache->items[i]; c; c = next) {
         next = c->next;
         _mesa_free(c->key);
-        cache->ctx->Driver.DeleteProgram(cache->ctx,
-                                          (struct gl_program *) c->data);
+         _mesa_reference_fragprog(ctx, &c->data, NULL);
         _mesa_free(c);
       }
       cache->items[i] = NULL;
@@ -1182,25 +1192,25 @@ static void clear_cache( struct texenvprog_cache *cache )
 }
 
 
-static void cache_item( struct texenvprog_cache *cache,
+static void cache_item( GLcontext *ctx,
+                        struct texenvprog_cache *cache,
                        GLuint hash,
                        const struct state_key *key,
-                       void *data )
+                        struct gl_fragment_program *prog)
 {
-   struct texenvprog_cache_item *c
-      = (struct texenvprog_cache_item *) MALLOC(sizeof(*c));
+   struct texenvprog_cache_item *c = CALLOC_STRUCT(texenvprog_cache_item);
    c->hash = hash;
 
    c->key = _mesa_malloc(sizeof(*key));
    memcpy(c->key, key, sizeof(*key));
 
-   c->data = (struct gl_fragment_program *) data;
+   _mesa_reference_fragprog(ctx, &c->data, prog);
 
    if (cache->n_items > cache->size * 1.5) {
       if (cache->size < 1000)
         rehash(cache);
       else 
-        clear_cache(cache);
+        clear_cache(ctx, cache);
    }
 
    cache->n_items++;
@@ -1243,32 +1253,29 @@ _mesa_UpdateTexEnvProgram( GLcontext *ctx )
    /* If a conventional fragment program/shader isn't in effect... */
    if (!ctx->FragmentProgram._Enabled &&
        (!ctx->Shader.CurrentProgram || !ctx->Shader.CurrentProgram->FragmentProgram)) {
+      struct gl_fragment_program *newProg;
+
       make_state_key(ctx, &key);
       hash = hash_key(&key);
       
-      ctx->FragmentProgram._Current =
-      ctx->FragmentProgram._TexEnvProgram =
-         search_cache(&ctx->Texture.env_fp_cache, hash, &key, sizeof(key));
+      newProg = search_cache(&ctx->Texture.env_fp_cache, hash, &key, sizeof(key));
+
+      if (!newProg) {
+         /* create new tex env program */
 
-      if (!ctx->FragmentProgram._TexEnvProgram) {
          if (0)
             _mesa_printf("Building new texenv proggy for key %x\n", hash);
 
-         /* create new tex env program */
-        ctx->FragmentProgram._Current =
-         ctx->FragmentProgram._TexEnvProgram =
-            (struct gl_fragment_program *) 
+         newProg = (struct gl_fragment_program *) 
             ctx->Driver.NewProgram(ctx, GL_FRAGMENT_PROGRAM_ARB, 0);
 
-         create_new_program(ctx, &key, ctx->FragmentProgram._TexEnvProgram);
+         create_new_program(ctx, &key, newProg);
 
-         cache_item(&ctx->Texture.env_fp_cache, hash, &key,
-                    ctx->FragmentProgram._TexEnvProgram);
-      }
-      else {
-         if (0)
-            _mesa_printf("Found existing texenv program for key %x\n", hash);
+         cache_item(ctx, &ctx->Texture.env_fp_cache, hash, &key, newProg);
       }
+
+      _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._Current, newProg);
+      _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._TexEnvProgram, newProg);
    } 
    else {
       /* _Current pointer has been updated in update_program */
@@ -1298,6 +1305,6 @@ void _mesa_TexEnvProgramCacheInit( GLcontext *ctx )
 
 void _mesa_TexEnvProgramCacheDestroy( GLcontext *ctx )
 {
-   clear_cache(&ctx->Texture.env_fp_cache);
+   clear_cache(ctx, &ctx->Texture.env_fp_cache);
    _mesa_free(ctx->Texture.env_fp_cache.items);
 }
index 8166e7e935c28cf22fdbc4a7e139010885dfec70..39784a1416142dc0d0589dffcb4d7927c4ef5a72 100644 (file)
@@ -303,7 +303,7 @@ void
 _mesa_delete_program(GLcontext *ctx, struct gl_program *prog)
 {
    (void) ctx;
-   ASSERT(prog);
+   ASSERT(prog);   assert(prog->RefCount==0);
 
    if (prog == &_mesa_DummyProgram)
       return;
@@ -378,7 +378,7 @@ _mesa_reference_program(GLcontext *ctx,
       GLboolean deleteFlag;
 
       /*_glthread_LOCK_MUTEX((*ptr)->Mutex);*/
-#if 0
+#if 01
       printf("Program %p %u 0x%x  Refcount-- to %d\n",
              *ptr, (*ptr)->Id, (*ptr)->Target, (*ptr)->RefCount - 1);
 #endif
@@ -400,7 +400,7 @@ _mesa_reference_program(GLcontext *ctx,
    if (prog) {
       /*_glthread_LOCK_MUTEX(prog->Mutex);*/
       prog->RefCount++;
-#if 0
+#if 01
       printf("Program %p %u 0x%x  Refcount++ to %d\n",
              prog, prog->Id, prog->Target, prog->RefCount);
 #endif
index baf283ef0f61fbd1e865c8c74015d1690bae3300..1ac508f0330fd8e8b0b1f1cea9519aaee8d7b653 100644 (file)
@@ -388,7 +388,7 @@ struct tnl_clipspace
 struct tnl_cache_item {
    GLuint hash;
    void *key;
-   void *data;
+   struct gl_vertex_program *prog;
    struct tnl_cache_item *next;
 };
 
index f254a4d7a4d472db0215cb6f1d4e823dd56b51bc..2b1eefe80988563991c74f894ac3019c7605062e 100644 (file)
@@ -1464,21 +1464,22 @@ create_new_program( const struct state_key *key,
    build_tnl_program( &p );
 }
 
-static void *search_cache( struct tnl_cache *cache,
-                          GLuint hash,
-                          const void *key,
-                          GLuint keysize)
+
+static struct gl_vertex_program *
+search_cache(struct tnl_cache *cache, GLuint hash,
+             const void *key, GLuint keysize)
 {
    struct tnl_cache_item *c;
 
    for (c = cache->items[hash % cache->size]; c; c = c->next) {
       if (c->hash == hash && _mesa_memcmp(c->key, key, keysize) == 0)
-        return c->data;
+        return c->prog;
    }
 
    return NULL;
 }
 
+
 static void rehash( struct tnl_cache *cache )
 {
    struct tnl_cache_item **items;
@@ -1501,15 +1502,16 @@ static void rehash( struct tnl_cache *cache )
    cache->size = size;
 }
 
-static void cache_item( struct tnl_cache *cache,
+static void cache_item( GLcontext *ctx,
+                        struct tnl_cache *cache,
                        GLuint hash,
                        void *key,
-                       void *data )
+                       struct gl_vertex_program *prog )
 {
-   struct tnl_cache_item *c = (struct tnl_cache_item*) _mesa_malloc(sizeof(*c));
+   struct tnl_cache_item *c = CALLOC_STRUCT(tnl_cache_item);
    c->hash = hash;
    c->key = key;
-   c->data = data;
+   _mesa_reference_vertprog(ctx, &c->prog, prog);
 
    if (++cache->n_items > cache->size * 1.5)
       rehash(cache);
@@ -1540,6 +1542,8 @@ void _tnl_UpdateFixedFunctionProgram( GLcontext *ctx )
 
    if (!ctx->VertexProgram._Current ||
        ctx->VertexProgram._Current == ctx->VertexProgram._TnlProgram) {
+      struct gl_vertex_program *newProg;
+
       /* Grab all the relevent state and put it in a single structure:
        */
       key = make_state_key(ctx);
@@ -1547,34 +1551,31 @@ void _tnl_UpdateFixedFunctionProgram( GLcontext *ctx )
 
       /* Look for an already-prepared program for this state:
        */
-      ctx->VertexProgram._TnlProgram = (struct gl_vertex_program *)
-        search_cache( tnl->vp_cache, hash, key, sizeof(*key) );
+      newProg = search_cache( tnl->vp_cache, hash, key, sizeof(*key));
    
       /* OK, we'll have to build a new one:
        */
-      if (!ctx->VertexProgram._TnlProgram) {
+      if (!newProg) {
+
         if (0)
            _mesa_printf("Build new TNL program\n");
         
-        ctx->VertexProgram._TnlProgram = (struct gl_vertex_program *)
+        newProg = (struct gl_vertex_program *)
            ctx->Driver.NewProgram(ctx, GL_VERTEX_PROGRAM_ARB, 0); 
 
-        create_new_program( key, ctx->VertexProgram._TnlProgram, 
-                            ctx->Const.VertexProgram.MaxTemps );
+        create_new_program( key, newProg, ctx->Const.VertexProgram.MaxTemps );
 
         if (ctx->Driver.ProgramStringNotify)
            ctx->Driver.ProgramStringNotify( ctx, GL_VERTEX_PROGRAM_ARB, 
-                                       &ctx->VertexProgram._TnlProgram->Base );
+                                             &newProg->Base );
 
-        cache_item(tnl->vp_cache, hash, key, ctx->VertexProgram._TnlProgram );
-      }
-      else {
-        FREE(key);
-        if (0) 
-           _mesa_printf("Found existing TNL program for key %x\n", hash);
+        cache_item(ctx, tnl->vp_cache, hash, key, newProg);
+
+         _mesa_reference_vertprog(ctx, &ctx->VertexProgram._TnlProgram, newProg);
       }
-      _mesa_reference_vertprog(ctx, &ctx->VertexProgram._Current,
-                               ctx->VertexProgram._TnlProgram);
+
+      _mesa_reference_vertprog(ctx, &ctx->VertexProgram._TnlProgram, newProg);
+      _mesa_reference_vertprog(ctx, &ctx->VertexProgram._Current, newProg);
    }
 
    /* Tell the driver about the change.  Could define a new target for
@@ -1607,7 +1608,7 @@ void _tnl_ProgramCacheDestroy( GLcontext *ctx )
       for (c = tnl->vp_cache->items[i]; c; c = next) {
         next = c->next;
         FREE(c->key);
-        FREE(c->data);
+        _mesa_reference_vertprog(ctx, &c->prog, NULL);
         FREE(c);
       }