mesa: add _mesa_program_state_value_size() helper
[mesa.git] / src / mesa / vbo / vbo_minmax_index.c
index 47b0d9cf2a37aef3023994ecc3e117e7472bd48a..62c8b6466f6f151b05b66539fc907f891b783cd1 100644 (file)
 #include "main/sse_minmax.h"
 #include "x86/common_x86_asm.h"
 #include "util/hash_table.h"
+#include "util/u_memory.h"
 
 
 struct minmax_cache_key {
    GLintptr offset;
    GLuint count;
-   GLenum type;
+   unsigned index_size;
 };
 
 
@@ -60,7 +61,8 @@ 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->type == b->type);
+   return (a->offset == b->offset) && (a->count == b->count) &&
+          (a->index_size == b->index_size);
 }
 
 
@@ -101,7 +103,7 @@ vbo_delete_minmax_cache(struct gl_buffer_object *bufferObj)
 
 static GLboolean
 vbo_get_minmax_cached(struct gl_buffer_object *bufferObj,
-                      GLenum type, GLintptr offset, GLuint count,
+                      unsigned index_size, GLintptr offset, GLuint count,
                       GLuint *min_index, GLuint *max_index)
 {
    GLboolean found = GL_FALSE;
@@ -114,15 +116,30 @@ vbo_get_minmax_cached(struct gl_buffer_object *bufferObj,
    if (!vbo_use_minmax_cache(bufferObj))
       return GL_FALSE;
 
-   mtx_lock(&bufferObj->Mutex);
+   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;
+      goto out_invalidate;
    }
 
-   key.type = type;
+   key.index_size = index_size;
    key.offset = offset;
    key.count = count;
    hash = vbo_minmax_cache_hash(&key);
@@ -134,8 +151,23 @@ vbo_get_minmax_cached(struct gl_buffer_object *bufferObj,
       found = GL_TRUE;
    }
 
-out:
-   mtx_unlock(&bufferObj->Mutex);
+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;
 }
 
@@ -143,7 +175,7 @@ out:
 static void
 vbo_minmax_cache_store(struct gl_context *ctx,
                        struct gl_buffer_object *bufferObj,
-                       GLenum type, GLintptr offset, GLuint count,
+                       unsigned index_size, GLintptr offset, GLuint count,
                        GLuint min, GLuint max)
 {
    struct minmax_cache_entry *entry;
@@ -153,7 +185,7 @@ vbo_minmax_cache_store(struct gl_context *ctx,
    if (!vbo_use_minmax_cache(bufferObj))
       return;
 
-   mtx_lock(&bufferObj->Mutex);
+   simple_mtx_lock(&bufferObj->MinMaxCacheMutex);
 
    if (!bufferObj->MinMaxCache) {
       bufferObj->MinMaxCache =
@@ -170,7 +202,7 @@ vbo_minmax_cache_store(struct gl_context *ctx,
 
    entry->key.offset = offset;
    entry->key.count = count;
-   entry->key.type = type;
+   entry->key.index_size = index_size;
    entry->min = min;
    entry->max = max;
    hash = vbo_minmax_cache_hash(&entry->key);
@@ -192,49 +224,23 @@ vbo_minmax_cache_store(struct gl_context *ctx,
       free(entry);
 
 out:
-   mtx_unlock(&bufferObj->Mutex);
+   simple_mtx_unlock(&bufferObj->MinMaxCacheMutex);
 }
 
 
-/**
- * Compute min and max elements by scanning the index buffer for
- * glDraw[Range]Elements() calls.
- * If primitive restart is enabled, we need to ignore restart
- * indexes when computing min/max.
- */
-static void
-vbo_get_minmax_index(struct gl_context *ctx,
-                     const struct _mesa_prim *prim,
-                     const struct _mesa_index_buffer *ib,
-                     GLuint *min_index, GLuint *max_index,
-                     const GLuint count)
+void
+vbo_get_minmax_index_mapped(unsigned count, unsigned index_size,
+                            unsigned restartIndex, bool restart,
+                            const void *indices,
+                            unsigned *min_index, unsigned *max_index)
 {
-   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 char *indices;
-   GLuint i;
-
-   indices = (char *) ib->ptr + prim->start * index_size;
-   if (_mesa_is_bufferobj(ib->obj)) {
-      GLsizeiptr size = MIN2(count * index_size, ib->obj->Size);
-
-      if (vbo_get_minmax_cached(ib->obj, ib->type, (GLintptr) indices, count,
-                                min_index, max_index))
-         return;
-
-      indices = ctx->Driver.MapBufferRange(ctx, (GLintptr) indices, size,
-                                           GL_MAP_READ_BIT, ib->obj,
-                                           MAP_INTERNAL);
-   }
-
-   switch (ib->type) {
-   case GL_UNSIGNED_INT: {
+   switch (index_size) {
+   case 4: {
       const GLuint *ui_indices = (const GLuint *)indices;
       GLuint max_ui = 0;
       GLuint min_ui = ~0U;
       if (restart) {
-         for (i = 0; i < count; i++) {
+         for (unsigned i = 0; i < count; i++) {
             if (ui_indices[i] != restartIndex) {
                if (ui_indices[i] > max_ui) max_ui = ui_indices[i];
                if (ui_indices[i] < min_ui) min_ui = ui_indices[i];
@@ -248,7 +254,7 @@ vbo_get_minmax_index(struct gl_context *ctx,
          }
          else
 #endif
-            for (i = 0; i < count; i++) {
+            for (unsigned i = 0; i < count; i++) {
                if (ui_indices[i] > max_ui) max_ui = ui_indices[i];
                if (ui_indices[i] < min_ui) min_ui = ui_indices[i];
             }
@@ -257,12 +263,12 @@ vbo_get_minmax_index(struct gl_context *ctx,
       *max_index = max_ui;
       break;
    }
-   case GL_UNSIGNED_SHORT: {
+   case 2: {
       const GLushort *us_indices = (const GLushort *)indices;
       GLuint max_us = 0;
       GLuint min_us = ~0U;
       if (restart) {
-         for (i = 0; i < count; i++) {
+         for (unsigned i = 0; i < count; i++) {
             if (us_indices[i] != restartIndex) {
                if (us_indices[i] > max_us) max_us = us_indices[i];
                if (us_indices[i] < min_us) min_us = us_indices[i];
@@ -270,7 +276,7 @@ vbo_get_minmax_index(struct gl_context *ctx,
          }
       }
       else {
-         for (i = 0; i < count; i++) {
+         for (unsigned i = 0; i < count; i++) {
             if (us_indices[i] > max_us) max_us = us_indices[i];
             if (us_indices[i] < min_us) min_us = us_indices[i];
          }
@@ -279,12 +285,12 @@ vbo_get_minmax_index(struct gl_context *ctx,
       *max_index = max_us;
       break;
    }
-   case GL_UNSIGNED_BYTE: {
+   case 1: {
       const GLubyte *ub_indices = (const GLubyte *)indices;
       GLuint max_ub = 0;
       GLuint min_ub = ~0U;
       if (restart) {
-         for (i = 0; i < count; i++) {
+         for (unsigned i = 0; i < count; i++) {
             if (ub_indices[i] != restartIndex) {
                if (ub_indices[i] > max_ub) max_ub = ub_indices[i];
                if (ub_indices[i] < min_ub) min_ub = ub_indices[i];
@@ -292,7 +298,7 @@ vbo_get_minmax_index(struct gl_context *ctx,
          }
       }
       else {
-         for (i = 0; i < count; i++) {
+         for (unsigned i = 0; i < count; i++) {
             if (ub_indices[i] > max_ub) max_ub = ub_indices[i];
             if (ub_indices[i] < min_ub) min_ub = ub_indices[i];
          }
@@ -304,10 +310,48 @@ vbo_get_minmax_index(struct gl_context *ctx,
    default:
       unreachable("not reached");
    }
+}
+
+
+/**
+ * Compute min and max elements by scanning the index buffer for
+ * glDraw[Range]Elements() calls.
+ * If primitive restart is enabled, we need to ignore restart
+ * indexes when computing min/max.
+ */
+static void
+vbo_get_minmax_index(struct gl_context *ctx,
+                     const struct _mesa_prim *prim,
+                     const struct _mesa_index_buffer *ib,
+                     GLuint *min_index, GLuint *max_index,
+                     const GLuint count)
+{
+   const GLboolean restart = ctx->Array._PrimitiveRestart;
+   const GLuint restartIndex =
+      ctx->Array._RestartIndex[(1 << ib->index_size_shift) - 1];
+   const char *indices;
+   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;
+
+      offset = (GLintptr) indices;
+      indices = ctx->Driver.MapBufferRange(ctx, offset, size,
+                                           GL_MAP_READ_BIT, ib->obj,
+                                           MAP_INTERNAL);
+   }
+
+   vbo_get_minmax_index_mapped(count, 1 << ib->index_size_shift, restartIndex,
+                               restart, indices, min_index, max_index);
 
-   if (_mesa_is_bufferobj(ib->obj)) {
-      vbo_minmax_cache_store(ctx, ib->obj, ib->type, prim->start, count,
-                             *min_index, *max_index);
+   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);
    }
 }