2
* Mesa 3-D graphics library
4
* Copyright 2003 VMware, Inc.
5
* Copyright 2009 VMware, Inc.
7
* Copyright (C) 2016 Advanced Micro Devices, Inc.
9
* Permission is hereby granted, free of charge, to any person obtaining a
10
* copy of this software and associated documentation files (the "Software"),
11
* to deal in the Software without restriction, including without limitation
12
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
13
* and/or sell copies of the Software, and to permit persons to whom the
14
* Software is furnished to do so, subject to the following conditions:
16
* The above copyright notice and this permission notice (including the next
17
* paragraph) shall be included in all copies or substantial portions of the
20
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
23
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
24
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
25
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
26
* USE OR OTHER DEALINGS IN THE SOFTWARE.
29
#include "main/glheader.h"
30
#include "main/context.h"
31
#include "main/varray.h"
32
#include "main/macros.h"
33
#include "main/sse_minmax.h"
34
#include "x86/common_x86_asm.h"
35
#include "util/hash_table.h"
36
#include "util/u_memory.h"
37
#include "pipe/p_state.h"
39
struct minmax_cache_key {
46
struct minmax_cache_entry {
47
struct minmax_cache_key key;
54
vbo_minmax_cache_hash(const struct minmax_cache_key *key)
56
return _mesa_hash_data(key, sizeof(*key));
61
vbo_minmax_cache_key_equal(const struct minmax_cache_key *a,
62
const struct minmax_cache_key *b)
64
return (a->offset == b->offset) && (a->count == b->count) &&
65
(a->index_size == b->index_size);
70
vbo_minmax_cache_delete_entry(struct hash_entry *entry)
77
vbo_use_minmax_cache(struct gl_buffer_object *bufferObj)
79
if (bufferObj->UsageHistory & (USAGE_TEXTURE_BUFFER |
80
USAGE_ATOMIC_COUNTER_BUFFER |
81
USAGE_SHADER_STORAGE_BUFFER |
82
USAGE_TRANSFORM_FEEDBACK_BUFFER |
83
USAGE_PIXEL_PACK_BUFFER |
84
USAGE_DISABLE_MINMAX_CACHE))
87
if ((bufferObj->Mappings[MAP_USER].AccessFlags &
88
(GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT)) ==
89
(GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT))
97
vbo_delete_minmax_cache(struct gl_buffer_object *bufferObj)
99
_mesa_hash_table_destroy(bufferObj->MinMaxCache, vbo_minmax_cache_delete_entry);
100
bufferObj->MinMaxCache = NULL;
105
vbo_get_minmax_cached(struct gl_buffer_object *bufferObj,
106
unsigned index_size, GLintptr offset, GLuint count,
107
GLuint *min_index, GLuint *max_index)
109
GLboolean found = GL_FALSE;
110
struct minmax_cache_key key;
112
struct hash_entry *result;
114
if (!bufferObj->MinMaxCache)
116
if (!vbo_use_minmax_cache(bufferObj))
119
simple_mtx_lock(&bufferObj->MinMaxCacheMutex);
121
if (bufferObj->MinMaxCacheDirty) {
122
/* Disable the cache permanently for this BO if the number of hits
123
* is asymptotically less than the number of misses. This happens when
124
* applications use the BO for streaming.
126
* However, some initial optimism allows applications that interleave
127
* draw calls with glBufferSubData during warmup.
129
unsigned optimism = bufferObj->Size;
130
if (bufferObj->MinMaxCacheMissIndices > optimism &&
131
bufferObj->MinMaxCacheHitIndices < bufferObj->MinMaxCacheMissIndices - optimism) {
132
bufferObj->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
133
vbo_delete_minmax_cache(bufferObj);
137
_mesa_hash_table_clear(bufferObj->MinMaxCache, vbo_minmax_cache_delete_entry);
138
bufferObj->MinMaxCacheDirty = false;
142
key.index_size = index_size;
145
hash = vbo_minmax_cache_hash(&key);
146
result = _mesa_hash_table_search_pre_hashed(bufferObj->MinMaxCache, hash, &key);
148
struct minmax_cache_entry *entry = result->data;
149
*min_index = entry->min;
150
*max_index = entry->max;
156
/* The hit counter saturates so that we don't accidently disable the
157
* cache in a long-running program.
159
unsigned new_hit_count = bufferObj->MinMaxCacheHitIndices + count;
161
if (new_hit_count >= bufferObj->MinMaxCacheHitIndices)
162
bufferObj->MinMaxCacheHitIndices = new_hit_count;
164
bufferObj->MinMaxCacheHitIndices = ~(unsigned)0;
166
bufferObj->MinMaxCacheMissIndices += count;
170
simple_mtx_unlock(&bufferObj->MinMaxCacheMutex);
176
vbo_minmax_cache_store(struct gl_context *ctx,
177
struct gl_buffer_object *bufferObj,
178
unsigned index_size, GLintptr offset, GLuint count,
179
GLuint min, GLuint max)
181
struct minmax_cache_entry *entry;
182
struct hash_entry *table_entry;
185
if (!vbo_use_minmax_cache(bufferObj))
188
simple_mtx_lock(&bufferObj->MinMaxCacheMutex);
190
if (!bufferObj->MinMaxCache) {
191
bufferObj->MinMaxCache =
192
_mesa_hash_table_create(NULL,
193
(uint32_t (*)(const void *))vbo_minmax_cache_hash,
194
(bool (*)(const void *, const void *))vbo_minmax_cache_key_equal);
195
if (!bufferObj->MinMaxCache)
199
entry = MALLOC_STRUCT(minmax_cache_entry);
203
entry->key.offset = offset;
204
entry->key.count = count;
205
entry->key.index_size = index_size;
208
hash = vbo_minmax_cache_hash(&entry->key);
210
table_entry = _mesa_hash_table_search_pre_hashed(bufferObj->MinMaxCache,
213
/* It seems like this could happen when two contexts are rendering using
214
* the same buffer object from multiple threads.
216
_mesa_debug(ctx, "duplicate entry in minmax cache\n");
221
table_entry = _mesa_hash_table_insert_pre_hashed(bufferObj->MinMaxCache,
222
hash, &entry->key, entry);
227
simple_mtx_unlock(&bufferObj->MinMaxCacheMutex);
232
vbo_get_minmax_index_mapped(unsigned count, unsigned index_size,
233
unsigned restartIndex, bool restart,
235
unsigned *min_index, unsigned *max_index)
237
switch (index_size) {
239
const GLuint *ui_indices = (const GLuint *)indices;
243
for (unsigned i = 0; i < count; i++) {
244
if (ui_indices[i] != restartIndex) {
245
if (ui_indices[i] > max_ui) max_ui = ui_indices[i];
246
if (ui_indices[i] < min_ui) min_ui = ui_indices[i];
251
#if defined(USE_SSE41)
252
if (cpu_has_sse4_1) {
253
_mesa_uint_array_min_max(ui_indices, &min_ui, &max_ui, count);
257
for (unsigned i = 0; i < count; i++) {
258
if (ui_indices[i] > max_ui) max_ui = ui_indices[i];
259
if (ui_indices[i] < min_ui) min_ui = ui_indices[i];
267
const GLushort *us_indices = (const GLushort *)indices;
271
for (unsigned i = 0; i < count; i++) {
272
if (us_indices[i] != restartIndex) {
273
if (us_indices[i] > max_us) max_us = us_indices[i];
274
if (us_indices[i] < min_us) min_us = us_indices[i];
279
for (unsigned i = 0; i < count; i++) {
280
if (us_indices[i] > max_us) max_us = us_indices[i];
281
if (us_indices[i] < min_us) min_us = us_indices[i];
289
const GLubyte *ub_indices = (const GLubyte *)indices;
293
for (unsigned i = 0; i < count; i++) {
294
if (ub_indices[i] != restartIndex) {
295
if (ub_indices[i] > max_ub) max_ub = ub_indices[i];
296
if (ub_indices[i] < min_ub) min_ub = ub_indices[i];
301
for (unsigned i = 0; i < count; i++) {
302
if (ub_indices[i] > max_ub) max_ub = ub_indices[i];
303
if (ub_indices[i] < min_ub) min_ub = ub_indices[i];
311
unreachable("not reached");
317
* Compute min and max elements by scanning the index buffer for
318
* glDraw[Range]Elements() calls.
319
* If primitive restart is enabled, we need to ignore restart
320
* indexes when computing min/max.
323
vbo_get_minmax_index(struct gl_context *ctx, struct gl_buffer_object *obj,
324
const void *ptr, GLintptr offset, unsigned count,
325
unsigned index_size, bool primitive_restart,
326
unsigned restart_index, GLuint *min_index,
332
indices = (const char *)ptr + offset;
334
GLsizeiptr size = MIN2((GLsizeiptr)count * index_size, obj->Size);
336
if (vbo_get_minmax_cached(obj, index_size, offset, count, min_index,
340
indices = _mesa_bufferobj_map_range(ctx, offset, size, GL_MAP_READ_BIT,
344
vbo_get_minmax_index_mapped(count, index_size, restart_index,
345
primitive_restart, indices,
346
min_index, max_index);
349
vbo_minmax_cache_store(ctx, obj, index_size, offset, count, *min_index,
351
_mesa_bufferobj_unmap(ctx, obj, MAP_INTERNAL);
356
* Compute min and max elements for nr_prims
359
vbo_get_minmax_indices(struct gl_context *ctx,
360
const struct _mesa_prim *prims,
361
const struct _mesa_index_buffer *ib,
365
bool primitive_restart,
366
unsigned restart_index)
368
GLuint tmp_min, tmp_max;
375
for (i = 0; i < nr_prims; i++) {
376
const struct _mesa_prim *start_prim;
378
start_prim = &prims[i];
379
count = start_prim->count;
380
/* Do combination if possible to reduce map/unmap count */
381
while ((i + 1 < nr_prims) &&
382
(prims[i].start + prims[i].count == prims[i+1].start)) {
383
count += prims[i+1].count;
386
vbo_get_minmax_index(ctx, ib->obj, ib->ptr,
387
(ib->obj ? (GLintptr)ib->ptr : 0) +
388
(start_prim->start << ib->index_size_shift),
389
count, 1 << ib->index_size_shift,
390
primitive_restart, restart_index,
392
*min_index = MIN2(*min_index, tmp_min);
393
*max_index = MAX2(*max_index, tmp_max);
398
* Same as vbo_get_minmax_index, but using gallium draw structures.
401
vbo_get_minmax_indices_gallium(struct gl_context *ctx,
402
struct pipe_draw_info *info,
403
const struct pipe_draw_start_count_bias *draws,
406
info->min_index = ~0;
409
for (unsigned i = 0; i < num_draws; i++) {
410
struct pipe_draw_start_count_bias draw = draws[i];
412
/* Do combination if possible to reduce map/unmap count */
413
while ((i + 1 < num_draws) &&
414
(draws[i].start + draws[i].count == draws[i+1].start)) {
415
draw.count += draws[i+1].count;
422
unsigned tmp_min, tmp_max;
423
vbo_get_minmax_index(ctx, info->has_user_indices ?
424
NULL : info->index.gl_bo,
426
(GLintptr)draw.start * info->index_size,
427
draw.count, info->index_size,
428
info->primitive_restart, info->restart_index,
430
info->min_index = MIN2(info->min_index, tmp_min);
431
info->max_index = MAX2(info->max_index, tmp_max);
434
return info->min_index <= info->max_index;