13464ebd5Sriastradh/**************************************************************************
27ec681f3Smrg *
3af69d88dSmrg * Copyright 2003 VMware, Inc.
43464ebd5Sriastradh * All Rights Reserved.
57ec681f3Smrg *
63464ebd5Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a
73464ebd5Sriastradh * copy of this software and associated documentation files (the
83464ebd5Sriastradh * "Software"), to deal in the Software without restriction, including
93464ebd5Sriastradh * without limitation the rights to use, copy, modify, merge, publish,
103464ebd5Sriastradh * distribute, sub license, and/or sell copies of the Software, and to
113464ebd5Sriastradh * permit persons to whom the Software is furnished to do so, subject to
123464ebd5Sriastradh * the following conditions:
137ec681f3Smrg *
143464ebd5Sriastradh * The above copyright notice and this permission notice (including the
153464ebd5Sriastradh * next paragraph) shall be included in all copies or substantial portions
163464ebd5Sriastradh * of the Software.
177ec681f3Smrg *
183464ebd5Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
193464ebd5Sriastradh * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
203464ebd5Sriastradh * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21af69d88dSmrg * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
223464ebd5Sriastradh * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
233464ebd5Sriastradh * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
243464ebd5Sriastradh * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
257ec681f3Smrg *
263464ebd5Sriastradh **************************************************************************/
273464ebd5Sriastradh
283464ebd5Sriastradh
293464ebd5Sriastradh#include "main/glheader.h"
303464ebd5Sriastradh#include "main/mtypes.h"
317ec681f3Smrg
323464ebd5Sriastradh#include "main/shaderobj.h"
333464ebd5Sriastradh#include "program/prog_cache.h"
343464ebd5Sriastradh#include "program/program.h"
357ec681f3Smrg#include "util/u_memory.h"
363464ebd5Sriastradh
373464ebd5Sriastradh
383464ebd5Sriastradhstruct cache_item
393464ebd5Sriastradh{
403464ebd5Sriastradh   GLuint hash;
41af69d88dSmrg   unsigned keysize;
423464ebd5Sriastradh   void *key;
433464ebd5Sriastradh   struct gl_program *program;
443464ebd5Sriastradh   struct cache_item *next;
453464ebd5Sriastradh};
463464ebd5Sriastradh
473464ebd5Sriastradhstruct gl_program_cache
483464ebd5Sriastradh{
493464ebd5Sriastradh   struct cache_item **items;
503464ebd5Sriastradh   struct cache_item *last;
513464ebd5Sriastradh   GLuint size, n_items;
523464ebd5Sriastradh};
533464ebd5Sriastradh
543464ebd5Sriastradh
553464ebd5Sriastradh
563464ebd5Sriastradh/**
573464ebd5Sriastradh * Compute hash index from state key.
583464ebd5Sriastradh */
593464ebd5Sriastradhstatic GLuint
603464ebd5Sriastradhhash_key(const void *key, GLuint key_size)
613464ebd5Sriastradh{
623464ebd5Sriastradh   const GLuint *ikey = (const GLuint *) key;
633464ebd5Sriastradh   GLuint hash = 0, i;
643464ebd5Sriastradh
653464ebd5Sriastradh   assert(key_size >= 4);
663464ebd5Sriastradh
673464ebd5Sriastradh   /* Make a slightly better attempt at a hash function:
683464ebd5Sriastradh    */
693464ebd5Sriastradh   for (i = 0; i < key_size / sizeof(*ikey); i++)
703464ebd5Sriastradh   {
713464ebd5Sriastradh      hash += ikey[i];
723464ebd5Sriastradh      hash += (hash << 10);
733464ebd5Sriastradh      hash ^= (hash >> 6);
743464ebd5Sriastradh   }
753464ebd5Sriastradh
763464ebd5Sriastradh   return hash;
773464ebd5Sriastradh}
783464ebd5Sriastradh
793464ebd5Sriastradh
803464ebd5Sriastradh/**
8101e04c3fSmrg * Rebuild/expand the hash table to accommodate more entries
823464ebd5Sriastradh */
833464ebd5Sriastradhstatic void
843464ebd5Sriastradhrehash(struct gl_program_cache *cache)
853464ebd5Sriastradh{
863464ebd5Sriastradh   struct cache_item **items;
873464ebd5Sriastradh   struct cache_item *c, *next;
883464ebd5Sriastradh   GLuint size, i;
893464ebd5Sriastradh
903464ebd5Sriastradh   cache->last = NULL;
913464ebd5Sriastradh
923464ebd5Sriastradh   size = cache->size * 3;
93af69d88dSmrg   items = malloc(size * sizeof(*items));
943464ebd5Sriastradh   memset(items, 0, size * sizeof(*items));
953464ebd5Sriastradh
963464ebd5Sriastradh   for (i = 0; i < cache->size; i++)
973464ebd5Sriastradh      for (c = cache->items[i]; c; c = next) {
983464ebd5Sriastradh	 next = c->next;
993464ebd5Sriastradh	 c->next = items[c->hash % size];
1003464ebd5Sriastradh	 items[c->hash % size] = c;
1013464ebd5Sriastradh      }
1023464ebd5Sriastradh
1033464ebd5Sriastradh   free(cache->items);
1043464ebd5Sriastradh   cache->items = items;
1053464ebd5Sriastradh   cache->size = size;
1063464ebd5Sriastradh}
1073464ebd5Sriastradh
1083464ebd5Sriastradh
1093464ebd5Sriastradhstatic void
1103464ebd5Sriastradhclear_cache(struct gl_context *ctx, struct gl_program_cache *cache,
1113464ebd5Sriastradh	    GLboolean shader)
1123464ebd5Sriastradh{
1133464ebd5Sriastradh   struct cache_item *c, *next;
1143464ebd5Sriastradh   GLuint i;
1157ec681f3Smrg
1163464ebd5Sriastradh   cache->last = NULL;
1173464ebd5Sriastradh
1183464ebd5Sriastradh   for (i = 0; i < cache->size; i++) {
1193464ebd5Sriastradh      for (c = cache->items[i]; c; c = next) {
1203464ebd5Sriastradh	 next = c->next;
1213464ebd5Sriastradh	 free(c->key);
1223464ebd5Sriastradh	 if (shader) {
1233464ebd5Sriastradh	    _mesa_reference_shader_program(ctx,
1243464ebd5Sriastradh					   (struct gl_shader_program **)&c->program,
1253464ebd5Sriastradh					   NULL);
1263464ebd5Sriastradh	 } else {
1273464ebd5Sriastradh	    _mesa_reference_program(ctx, &c->program, NULL);
1283464ebd5Sriastradh	 }
1293464ebd5Sriastradh	 free(c);
1303464ebd5Sriastradh      }
1313464ebd5Sriastradh      cache->items[i] = NULL;
1323464ebd5Sriastradh   }
1333464ebd5Sriastradh
1343464ebd5Sriastradh
1353464ebd5Sriastradh   cache->n_items = 0;
1363464ebd5Sriastradh}
1373464ebd5Sriastradh
1383464ebd5Sriastradh
1393464ebd5Sriastradh
1403464ebd5Sriastradhstruct gl_program_cache *
1413464ebd5Sriastradh_mesa_new_program_cache(void)
1423464ebd5Sriastradh{
1433464ebd5Sriastradh   struct gl_program_cache *cache = CALLOC_STRUCT(gl_program_cache);
1443464ebd5Sriastradh   if (cache) {
1453464ebd5Sriastradh      cache->size = 17;
146af69d88dSmrg      cache->items =
14701e04c3fSmrg         calloc(cache->size, sizeof(struct cache_item *));
1483464ebd5Sriastradh      if (!cache->items) {
1493464ebd5Sriastradh         free(cache);
1503464ebd5Sriastradh         return NULL;
1513464ebd5Sriastradh      }
1523464ebd5Sriastradh   }
1533464ebd5Sriastradh   return cache;
1543464ebd5Sriastradh}
1553464ebd5Sriastradh
1563464ebd5Sriastradh
1573464ebd5Sriastradhvoid
1583464ebd5Sriastradh_mesa_delete_program_cache(struct gl_context *ctx, struct gl_program_cache *cache)
1593464ebd5Sriastradh{
1603464ebd5Sriastradh   clear_cache(ctx, cache, GL_FALSE);
1613464ebd5Sriastradh   free(cache->items);
1623464ebd5Sriastradh   free(cache);
1633464ebd5Sriastradh}
1643464ebd5Sriastradh
1653464ebd5Sriastradhvoid
1663464ebd5Sriastradh_mesa_delete_shader_cache(struct gl_context *ctx,
1673464ebd5Sriastradh			  struct gl_program_cache *cache)
1683464ebd5Sriastradh{
1693464ebd5Sriastradh   clear_cache(ctx, cache, GL_TRUE);
1703464ebd5Sriastradh   free(cache->items);
1713464ebd5Sriastradh   free(cache);
1723464ebd5Sriastradh}
1733464ebd5Sriastradh
1743464ebd5Sriastradh
1753464ebd5Sriastradhstruct gl_program *
1763464ebd5Sriastradh_mesa_search_program_cache(struct gl_program_cache *cache,
1773464ebd5Sriastradh                           const void *key, GLuint keysize)
1783464ebd5Sriastradh{
179af69d88dSmrg   if (cache->last &&
180af69d88dSmrg       cache->last->keysize == keysize &&
1813464ebd5Sriastradh       memcmp(cache->last->key, key, keysize) == 0) {
1823464ebd5Sriastradh      return cache->last->program;
1833464ebd5Sriastradh   }
1843464ebd5Sriastradh   else {
1853464ebd5Sriastradh      const GLuint hash = hash_key(key, keysize);
1863464ebd5Sriastradh      struct cache_item *c;
1873464ebd5Sriastradh
1883464ebd5Sriastradh      for (c = cache->items[hash % cache->size]; c; c = c->next) {
189af69d88dSmrg         if (c->hash == hash &&
190af69d88dSmrg             c->keysize == keysize &&
191af69d88dSmrg             memcmp(c->key, key, keysize) == 0) {
192af69d88dSmrg
1933464ebd5Sriastradh            cache->last = c;
1943464ebd5Sriastradh            return c->program;
1953464ebd5Sriastradh         }
1963464ebd5Sriastradh      }
1973464ebd5Sriastradh
1983464ebd5Sriastradh      return NULL;
1993464ebd5Sriastradh   }
2003464ebd5Sriastradh}
2013464ebd5Sriastradh
2023464ebd5Sriastradh
2033464ebd5Sriastradhvoid
2043464ebd5Sriastradh_mesa_program_cache_insert(struct gl_context *ctx,
2053464ebd5Sriastradh                           struct gl_program_cache *cache,
2063464ebd5Sriastradh                           const void *key, GLuint keysize,
2073464ebd5Sriastradh                           struct gl_program *program)
2083464ebd5Sriastradh{
2093464ebd5Sriastradh   const GLuint hash = hash_key(key, keysize);
2103464ebd5Sriastradh   struct cache_item *c = CALLOC_STRUCT(cache_item);
2113464ebd5Sriastradh
2123464ebd5Sriastradh   c->hash = hash;
2133464ebd5Sriastradh
2143464ebd5Sriastradh   c->key = malloc(keysize);
2153464ebd5Sriastradh   memcpy(c->key, key, keysize);
216af69d88dSmrg   c->keysize = keysize;
2173464ebd5Sriastradh
2183464ebd5Sriastradh   c->program = program;  /* no refcount change */
2193464ebd5Sriastradh
2203464ebd5Sriastradh   if (cache->n_items > cache->size * 1.5) {
2213464ebd5Sriastradh      if (cache->size < 1000)
2223464ebd5Sriastradh	 rehash(cache);
2237ec681f3Smrg      else
2243464ebd5Sriastradh	 clear_cache(ctx, cache, GL_FALSE);
2253464ebd5Sriastradh   }
2263464ebd5Sriastradh
2273464ebd5Sriastradh   cache->n_items++;
2283464ebd5Sriastradh   c->next = cache->items[hash % cache->size];
2293464ebd5Sriastradh   cache->items[hash % cache->size] = c;
2303464ebd5Sriastradh}
2313464ebd5Sriastradh
2323464ebd5Sriastradhvoid
2333464ebd5Sriastradh_mesa_shader_cache_insert(struct gl_context *ctx,
2343464ebd5Sriastradh			  struct gl_program_cache *cache,
2353464ebd5Sriastradh			  const void *key, GLuint keysize,
2363464ebd5Sriastradh			  struct gl_shader_program *program)
2373464ebd5Sriastradh{
2383464ebd5Sriastradh   const GLuint hash = hash_key(key, keysize);
2393464ebd5Sriastradh   struct cache_item *c = CALLOC_STRUCT(cache_item);
2403464ebd5Sriastradh
2413464ebd5Sriastradh   c->hash = hash;
2423464ebd5Sriastradh
2433464ebd5Sriastradh   c->key = malloc(keysize);
2443464ebd5Sriastradh   memcpy(c->key, key, keysize);
245af69d88dSmrg   c->keysize = keysize;
2463464ebd5Sriastradh
2473464ebd5Sriastradh   c->program = (struct gl_program *)program;  /* no refcount change */
2483464ebd5Sriastradh
2493464ebd5Sriastradh   if (cache->n_items > cache->size * 1.5) {
2503464ebd5Sriastradh      if (cache->size < 1000)
2513464ebd5Sriastradh	 rehash(cache);
2523464ebd5Sriastradh      else
2533464ebd5Sriastradh	 clear_cache(ctx, cache, GL_TRUE);
2543464ebd5Sriastradh   }
2553464ebd5Sriastradh
2563464ebd5Sriastradh   cache->n_items++;
2573464ebd5Sriastradh   c->next = cache->items[hash % cache->size];
2583464ebd5Sriastradh   cache->items[hash % cache->size] = c;
2593464ebd5Sriastradh}
260