mesa: remove no longer needed _mesa_is_bufferobj function
[mesa.git] / src / mesa / vbo / vbo_minmax_index.c
index b43ed98367655902713f4e8d13828ad5254ae919..f66ed3dee392b22fc36701b82418acca3e7b6907 100644 (file)
 #include "main/macros.h"
 #include "main/sse_minmax.h"
 #include "x86/common_x86_asm.h"
+#include "util/hash_table.h"
+
+
+struct minmax_cache_key {
+   GLintptr offset;
+   GLuint count;
+   unsigned index_size;
+};
+
+
+struct minmax_cache_entry {
+   struct minmax_cache_key key;
+   GLuint min;
+   GLuint max;
+};
+
+
+static uint32_t
+vbo_minmax_cache_hash(const struct minmax_cache_key *key)
+{
+   return _mesa_hash_data(key, sizeof(*key));
+}
+
+
+static bool
+vbo_minmax_cache_key_equal(const struct minmax_cache_key *a,
+                           const struct minmax_cache_key *b)
+{
+   return (a->offset == b->offset) && (a->count == b->count) &&
+          (a->index_size == b->index_size);
+}
+
+
+static void
+vbo_minmax_cache_delete_entry(struct hash_entry *entry)
+{
+   free(entry->data);
+}
+
+
+static GLboolean
+vbo_use_minmax_cache(struct gl_buffer_object *bufferObj)
+{
+   if (bufferObj->UsageHistory & (USAGE_TEXTURE_BUFFER |
+                                  USAGE_ATOMIC_COUNTER_BUFFER |
+                                  USAGE_SHADER_STORAGE_BUFFER |
+                                  USAGE_TRANSFORM_FEEDBACK_BUFFER |
+                                  USAGE_PIXEL_PACK_BUFFER |
+                                  USAGE_DISABLE_MINMAX_CACHE))
+      return GL_FALSE;
+
+   if ((bufferObj->Mappings[MAP_USER].AccessFlags &
+        (GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT)) ==
+       (GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT))
+      return GL_FALSE;
+
+   return GL_TRUE;
+}
+
+
+void
+vbo_delete_minmax_cache(struct gl_buffer_object *bufferObj)
+{
+   _mesa_hash_table_destroy(bufferObj->MinMaxCache, vbo_minmax_cache_delete_entry);
+   bufferObj->MinMaxCache = NULL;
+}
+
+
+static GLboolean
+vbo_get_minmax_cached(struct gl_buffer_object *bufferObj,
+                      unsigned index_size, GLintptr offset, GLuint count,
+                      GLuint *min_index, GLuint *max_index)
+{
+   GLboolean found = GL_FALSE;
+   struct minmax_cache_key key;
+   uint32_t hash;
+   struct hash_entry *result;
+
+   if (!bufferObj->MinMaxCache)
+      return GL_FALSE;
+   if (!vbo_use_minmax_cache(bufferObj))
+      return GL_FALSE;
+
+   simple_mtx_lock(&bufferObj->MinMaxCacheMutex);
+
+   if (bufferObj->MinMaxCacheDirty) {
+      /* Disable the cache permanently for this BO if the number of hits
+       * is asymptotically less than the number of misses. This happens when
+       * applications use the BO for streaming.
+       *
+       * However, some initial optimism allows applications that interleave
+       * draw calls with glBufferSubData during warmup.
+       */
+      unsigned optimism = bufferObj->Size;
+      if (bufferObj->MinMaxCacheMissIndices > optimism &&
+          bufferObj->MinMaxCacheHitIndices < bufferObj->MinMaxCacheMissIndices - optimism) {
+         bufferObj->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
+         vbo_delete_minmax_cache(bufferObj);
+         goto out_disable;
+      }
+
+      _mesa_hash_table_clear(bufferObj->MinMaxCache, vbo_minmax_cache_delete_entry);
+      bufferObj->MinMaxCacheDirty = false;
+      goto out_invalidate;
+   }
+
+   key.index_size = index_size;
+   key.offset = offset;
+   key.count = count;
+   hash = vbo_minmax_cache_hash(&key);
+   result = _mesa_hash_table_search_pre_hashed(bufferObj->MinMaxCache, hash, &key);
+   if (result) {
+      struct minmax_cache_entry *entry = result->data;
+      *min_index = entry->min;
+      *max_index = entry->max;
+      found = GL_TRUE;
+   }
+
+out_invalidate:
+   if (found) {
+      /* The hit counter saturates so that we don't accidently disable the
+       * cache in a long-running program.
+       */
+      unsigned new_hit_count = bufferObj->MinMaxCacheHitIndices + count;
+
+      if (new_hit_count >= bufferObj->MinMaxCacheHitIndices)
+         bufferObj->MinMaxCacheHitIndices = new_hit_count;
+      else
+         bufferObj->MinMaxCacheHitIndices = ~(unsigned)0;
+   } else {
+      bufferObj->MinMaxCacheMissIndices += count;
+   }
+
+out_disable:
+   simple_mtx_unlock(&bufferObj->MinMaxCacheMutex);
+   return found;
+}
+
+
+static void
+vbo_minmax_cache_store(struct gl_context *ctx,
+                       struct gl_buffer_object *bufferObj,
+                       unsigned index_size, GLintptr offset, GLuint count,
+                       GLuint min, GLuint max)
+{
+   struct minmax_cache_entry *entry;
+   struct hash_entry *table_entry;
+   uint32_t hash;
+
+   if (!vbo_use_minmax_cache(bufferObj))
+      return;
+
+   simple_mtx_lock(&bufferObj->MinMaxCacheMutex);
+
+   if (!bufferObj->MinMaxCache) {
+      bufferObj->MinMaxCache =
+         _mesa_hash_table_create(NULL,
+                                 (uint32_t (*)(const void *))vbo_minmax_cache_hash,
+                                 (bool (*)(const void *, const void *))vbo_minmax_cache_key_equal);
+      if (!bufferObj->MinMaxCache)
+         goto out;
+   }
+
+   entry = MALLOC_STRUCT(minmax_cache_entry);
+   if (!entry)
+      goto out;
+
+   entry->key.offset = offset;
+   entry->key.count = count;
+   entry->key.index_size = index_size;
+   entry->min = min;
+   entry->max = max;
+   hash = vbo_minmax_cache_hash(&entry->key);
+
+   table_entry = _mesa_hash_table_search_pre_hashed(bufferObj->MinMaxCache,
+                                                    hash, &entry->key);
+   if (table_entry) {
+      /* It seems like this could happen when two contexts are rendering using
+       * the same buffer object from multiple threads.
+       */
+      _mesa_debug(ctx, "duplicate entry in minmax cache\n");
+      free(entry);
+      goto out;
+   }
+
+   table_entry = _mesa_hash_table_insert_pre_hashed(bufferObj->MinMaxCache,
+                                                    hash, &entry->key, entry);
+   if (!table_entry)
+      free(entry);
+
+out:
+   simple_mtx_unlock(&bufferObj->MinMaxCacheMutex);
+}
 
 
 /**
@@ -48,21 +241,28 @@ vbo_get_minmax_index(struct gl_context *ctx,
                      const GLuint count)
 {
    const GLboolean restart = ctx->Array._PrimitiveRestart;
-   const GLuint restartIndex = _mesa_primitive_restart_index(ctx, ib->type);
-   const int index_size = vbo_sizeof_ib_type(ib->type);
+   const GLuint restartIndex =
+      _mesa_primitive_restart_index(ctx, 1 << ib->index_size_shift);
    const char *indices;
    GLuint i;
+   GLintptr offset = 0;
+
+   indices = (char *) ib->ptr + (prim->start << ib->index_size_shift);
+   if (ib->obj) {
+      GLsizeiptr size = MIN2(count << ib->index_size_shift, ib->obj->Size);
+
+      if (vbo_get_minmax_cached(ib->obj, 1 << ib->index_size_shift, (GLintptr) indices,
+                                count, min_index, max_index))
+         return;
 
-   indices = (char *) ib->ptr + prim->start * index_size;
-   if (_mesa_is_bufferobj(ib->obj)) {
-      GLsizeiptr size = MIN2(count * index_size, ib->obj->Size);
-      indices = ctx->Driver.MapBufferRange(ctx, (GLintptr) indices, size,
+      offset = (GLintptr) indices;
+      indices = ctx->Driver.MapBufferRange(ctx, offset, size,
                                            GL_MAP_READ_BIT, ib->obj,
                                            MAP_INTERNAL);
    }
 
-   switch (ib->type) {
-   case GL_UNSIGNED_INT: {
+   switch (ib->index_size_shift) {
+   case 2: {
       const GLuint *ui_indices = (const GLuint *)indices;
       GLuint max_ui = 0;
       GLuint min_ui = ~0U;
@@ -90,7 +290,7 @@ vbo_get_minmax_index(struct gl_context *ctx,
       *max_index = max_ui;
       break;
    }
-   case GL_UNSIGNED_SHORT: {
+   case 1: {
       const GLushort *us_indices = (const GLushort *)indices;
       GLuint max_us = 0;
       GLuint min_us = ~0U;
@@ -112,7 +312,7 @@ vbo_get_minmax_index(struct gl_context *ctx,
       *max_index = max_us;
       break;
    }
-   case GL_UNSIGNED_BYTE: {
+   case 0: {
       const GLubyte *ub_indices = (const GLubyte *)indices;
       GLuint max_ub = 0;
       GLuint min_ub = ~0U;
@@ -138,7 +338,9 @@ vbo_get_minmax_index(struct gl_context *ctx,
       unreachable("not reached");
    }
 
-   if (_mesa_is_bufferobj(ib->obj)) {
+   if (ib->obj) {
+      vbo_minmax_cache_store(ctx, ib->obj, 1 << ib->index_size_shift, offset,
+                             count, *min_index, *max_index);
       ctx->Driver.UnmapBuffer(ctx, ib->obj, MAP_INTERNAL);
    }
 }