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"
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
,
324 const struct _mesa_prim
*prim
,
325 const struct _mesa_index_buffer
*ib
,
326 GLuint
*min_index
, GLuint
*max_index
,
329 const GLboolean restart
= ctx
->Array
._PrimitiveRestart
;
330 const GLuint restartIndex
=
331 ctx
->Array
._RestartIndex
[(1 << ib
->index_size_shift
) - 1];
335 indices
= (char *) ib
->ptr
+ (prim
->start
<< ib
->index_size_shift
);
337 GLsizeiptr size
= MIN2(count
<< ib
->index_size_shift
, ib
->obj
->Size
);
339 if (vbo_get_minmax_cached(ib
->obj
, 1 << ib
->index_size_shift
, (GLintptr
) indices
,
340 count
, min_index
, max_index
))
343 offset
= (GLintptr
) indices
;
344 indices
= ctx
->Driver
.MapBufferRange(ctx
, offset
, size
,
345 GL_MAP_READ_BIT
, ib
->obj
,
349 vbo_get_minmax_index_mapped(count
, 1 << ib
->index_size_shift
, restartIndex
,
350 restart
, indices
, min_index
, max_index
);
353 vbo_minmax_cache_store(ctx
, ib
->obj
, 1 << ib
->index_size_shift
, offset
,
354 count
, *min_index
, *max_index
);
355 ctx
->Driver
.UnmapBuffer(ctx
, ib
->obj
, MAP_INTERNAL
);
360 * Compute min and max elements for nr_prims
363 vbo_get_minmax_indices(struct gl_context
*ctx
,
364 const struct _mesa_prim
*prims
,
365 const struct _mesa_index_buffer
*ib
,
370 GLuint tmp_min
, tmp_max
;
377 for (i
= 0; i
< nr_prims
; i
++) {
378 const struct _mesa_prim
*start_prim
;
380 start_prim
= &prims
[i
];
381 count
= start_prim
->count
;
382 /* Do combination if possible to reduce map/unmap count */
383 while ((i
+ 1 < nr_prims
) &&
384 (prims
[i
].start
+ prims
[i
].count
== prims
[i
+1].start
)) {
385 count
+= prims
[i
+1].count
;
388 vbo_get_minmax_index(ctx
, start_prim
, ib
, &tmp_min
, &tmp_max
, count
);
389 *min_index
= MIN2(*min_index
, tmp_min
);
390 *max_index
= MAX2(*max_index
, tmp_max
);