fix refcounting bugs in tnl/tex program caches
This commit is contained in:
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -28,12 +28,23 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
+26
-25
@@ -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 );
|
||||
cache_item(ctx, tnl->vp_cache, hash, key, newProg);
|
||||
|
||||
_mesa_reference_vertprog(ctx, &ctx->VertexProgram._TnlProgram, newProg);
|
||||
}
|
||||
else {
|
||||
FREE(key);
|
||||
if (0)
|
||||
_mesa_printf("Found existing TNL program for key %x\n", hash);
|
||||
}
|
||||
_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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user