#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;
};
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);
}
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;
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);
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;
}
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;
if (!vbo_use_minmax_cache(bufferObj))
return;
- mtx_lock(&bufferObj->Mutex);
+ simple_mtx_lock(&bufferObj->MinMaxCacheMutex);
if (!bufferObj->MinMaxCache) {
bufferObj->MinMaxCache =
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);
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];
}
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];
}
*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];
}
}
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];
}
*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];
}
}
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];
}
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);
}
}