101e04c3fSmrg/*
201e04c3fSmrg * Mesa 3-D graphics library
301e04c3fSmrg *
401e04c3fSmrg * Copyright 2003 VMware, Inc.
501e04c3fSmrg * Copyright 2009 VMware, Inc.
601e04c3fSmrg * All Rights Reserved.
701e04c3fSmrg * Copyright (C) 2016 Advanced Micro Devices, Inc.
801e04c3fSmrg *
901e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
1001e04c3fSmrg * copy of this software and associated documentation files (the "Software"),
1101e04c3fSmrg * to deal in the Software without restriction, including without limitation
1201e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1301e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the
1401e04c3fSmrg * Software is furnished to do so, subject to the following conditions:
1501e04c3fSmrg *
1601e04c3fSmrg * The above copyright notice and this permission notice (including the next
1701e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the
1801e04c3fSmrg * Software.
1901e04c3fSmrg *
2001e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2101e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2201e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
2301e04c3fSmrg * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
2401e04c3fSmrg * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2501e04c3fSmrg * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2601e04c3fSmrg * USE OR OTHER DEALINGS IN THE SOFTWARE.
2701e04c3fSmrg */
2801e04c3fSmrg
2901e04c3fSmrg#include "main/glheader.h"
3001e04c3fSmrg#include "main/context.h"
3101e04c3fSmrg#include "main/varray.h"
3201e04c3fSmrg#include "main/macros.h"
3301e04c3fSmrg#include "main/sse_minmax.h"
3401e04c3fSmrg#include "x86/common_x86_asm.h"
3501e04c3fSmrg#include "util/hash_table.h"
367ec681f3Smrg#include "util/u_memory.h"
377ec681f3Smrg#include "pipe/p_state.h"
3801e04c3fSmrg
3901e04c3fSmrg
4001e04c3fSmrgstruct minmax_cache_key {
4101e04c3fSmrg   GLintptr offset;
4201e04c3fSmrg   GLuint count;
4301e04c3fSmrg   unsigned index_size;
4401e04c3fSmrg};
4501e04c3fSmrg
4601e04c3fSmrg
4701e04c3fSmrgstruct minmax_cache_entry {
4801e04c3fSmrg   struct minmax_cache_key key;
4901e04c3fSmrg   GLuint min;
5001e04c3fSmrg   GLuint max;
5101e04c3fSmrg};
5201e04c3fSmrg
5301e04c3fSmrg
5401e04c3fSmrgstatic uint32_t
5501e04c3fSmrgvbo_minmax_cache_hash(const struct minmax_cache_key *key)
5601e04c3fSmrg{
5701e04c3fSmrg   return _mesa_hash_data(key, sizeof(*key));
5801e04c3fSmrg}
5901e04c3fSmrg
6001e04c3fSmrg
6101e04c3fSmrgstatic bool
6201e04c3fSmrgvbo_minmax_cache_key_equal(const struct minmax_cache_key *a,
6301e04c3fSmrg                           const struct minmax_cache_key *b)
6401e04c3fSmrg{
6501e04c3fSmrg   return (a->offset == b->offset) && (a->count == b->count) &&
6601e04c3fSmrg          (a->index_size == b->index_size);
6701e04c3fSmrg}
6801e04c3fSmrg
6901e04c3fSmrg
7001e04c3fSmrgstatic void
7101e04c3fSmrgvbo_minmax_cache_delete_entry(struct hash_entry *entry)
7201e04c3fSmrg{
7301e04c3fSmrg   free(entry->data);
7401e04c3fSmrg}
7501e04c3fSmrg
7601e04c3fSmrg
7701e04c3fSmrgstatic GLboolean
7801e04c3fSmrgvbo_use_minmax_cache(struct gl_buffer_object *bufferObj)
7901e04c3fSmrg{
8001e04c3fSmrg   if (bufferObj->UsageHistory & (USAGE_TEXTURE_BUFFER |
8101e04c3fSmrg                                  USAGE_ATOMIC_COUNTER_BUFFER |
8201e04c3fSmrg                                  USAGE_SHADER_STORAGE_BUFFER |
8301e04c3fSmrg                                  USAGE_TRANSFORM_FEEDBACK_BUFFER |
8401e04c3fSmrg                                  USAGE_PIXEL_PACK_BUFFER |
8501e04c3fSmrg                                  USAGE_DISABLE_MINMAX_CACHE))
8601e04c3fSmrg      return GL_FALSE;
8701e04c3fSmrg
8801e04c3fSmrg   if ((bufferObj->Mappings[MAP_USER].AccessFlags &
8901e04c3fSmrg        (GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT)) ==
9001e04c3fSmrg       (GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT))
9101e04c3fSmrg      return GL_FALSE;
9201e04c3fSmrg
9301e04c3fSmrg   return GL_TRUE;
9401e04c3fSmrg}
9501e04c3fSmrg
9601e04c3fSmrg
9701e04c3fSmrgvoid
9801e04c3fSmrgvbo_delete_minmax_cache(struct gl_buffer_object *bufferObj)
9901e04c3fSmrg{
10001e04c3fSmrg   _mesa_hash_table_destroy(bufferObj->MinMaxCache, vbo_minmax_cache_delete_entry);
10101e04c3fSmrg   bufferObj->MinMaxCache = NULL;
10201e04c3fSmrg}
10301e04c3fSmrg
10401e04c3fSmrg
10501e04c3fSmrgstatic GLboolean
10601e04c3fSmrgvbo_get_minmax_cached(struct gl_buffer_object *bufferObj,
10701e04c3fSmrg                      unsigned index_size, GLintptr offset, GLuint count,
10801e04c3fSmrg                      GLuint *min_index, GLuint *max_index)
10901e04c3fSmrg{
11001e04c3fSmrg   GLboolean found = GL_FALSE;
11101e04c3fSmrg   struct minmax_cache_key key;
11201e04c3fSmrg   uint32_t hash;
11301e04c3fSmrg   struct hash_entry *result;
11401e04c3fSmrg
11501e04c3fSmrg   if (!bufferObj->MinMaxCache)
11601e04c3fSmrg      return GL_FALSE;
11701e04c3fSmrg   if (!vbo_use_minmax_cache(bufferObj))
11801e04c3fSmrg      return GL_FALSE;
11901e04c3fSmrg
12001e04c3fSmrg   simple_mtx_lock(&bufferObj->MinMaxCacheMutex);
12101e04c3fSmrg
12201e04c3fSmrg   if (bufferObj->MinMaxCacheDirty) {
12301e04c3fSmrg      /* Disable the cache permanently for this BO if the number of hits
12401e04c3fSmrg       * is asymptotically less than the number of misses. This happens when
12501e04c3fSmrg       * applications use the BO for streaming.
12601e04c3fSmrg       *
12701e04c3fSmrg       * However, some initial optimism allows applications that interleave
12801e04c3fSmrg       * draw calls with glBufferSubData during warmup.
12901e04c3fSmrg       */
13001e04c3fSmrg      unsigned optimism = bufferObj->Size;
13101e04c3fSmrg      if (bufferObj->MinMaxCacheMissIndices > optimism &&
13201e04c3fSmrg          bufferObj->MinMaxCacheHitIndices < bufferObj->MinMaxCacheMissIndices - optimism) {
13301e04c3fSmrg         bufferObj->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
13401e04c3fSmrg         vbo_delete_minmax_cache(bufferObj);
13501e04c3fSmrg         goto out_disable;
13601e04c3fSmrg      }
13701e04c3fSmrg
13801e04c3fSmrg      _mesa_hash_table_clear(bufferObj->MinMaxCache, vbo_minmax_cache_delete_entry);
13901e04c3fSmrg      bufferObj->MinMaxCacheDirty = false;
14001e04c3fSmrg      goto out_invalidate;
14101e04c3fSmrg   }
14201e04c3fSmrg
14301e04c3fSmrg   key.index_size = index_size;
14401e04c3fSmrg   key.offset = offset;
14501e04c3fSmrg   key.count = count;
14601e04c3fSmrg   hash = vbo_minmax_cache_hash(&key);
14701e04c3fSmrg   result = _mesa_hash_table_search_pre_hashed(bufferObj->MinMaxCache, hash, &key);
14801e04c3fSmrg   if (result) {
14901e04c3fSmrg      struct minmax_cache_entry *entry = result->data;
15001e04c3fSmrg      *min_index = entry->min;
15101e04c3fSmrg      *max_index = entry->max;
15201e04c3fSmrg      found = GL_TRUE;
15301e04c3fSmrg   }
15401e04c3fSmrg
15501e04c3fSmrgout_invalidate:
15601e04c3fSmrg   if (found) {
15701e04c3fSmrg      /* The hit counter saturates so that we don't accidently disable the
15801e04c3fSmrg       * cache in a long-running program.
15901e04c3fSmrg       */
16001e04c3fSmrg      unsigned new_hit_count = bufferObj->MinMaxCacheHitIndices + count;
16101e04c3fSmrg
16201e04c3fSmrg      if (new_hit_count >= bufferObj->MinMaxCacheHitIndices)
16301e04c3fSmrg         bufferObj->MinMaxCacheHitIndices = new_hit_count;
16401e04c3fSmrg      else
16501e04c3fSmrg         bufferObj->MinMaxCacheHitIndices = ~(unsigned)0;
16601e04c3fSmrg   } else {
16701e04c3fSmrg      bufferObj->MinMaxCacheMissIndices += count;
16801e04c3fSmrg   }
16901e04c3fSmrg
17001e04c3fSmrgout_disable:
17101e04c3fSmrg   simple_mtx_unlock(&bufferObj->MinMaxCacheMutex);
17201e04c3fSmrg   return found;
17301e04c3fSmrg}
17401e04c3fSmrg
17501e04c3fSmrg
17601e04c3fSmrgstatic void
17701e04c3fSmrgvbo_minmax_cache_store(struct gl_context *ctx,
17801e04c3fSmrg                       struct gl_buffer_object *bufferObj,
17901e04c3fSmrg                       unsigned index_size, GLintptr offset, GLuint count,
18001e04c3fSmrg                       GLuint min, GLuint max)
18101e04c3fSmrg{
18201e04c3fSmrg   struct minmax_cache_entry *entry;
18301e04c3fSmrg   struct hash_entry *table_entry;
18401e04c3fSmrg   uint32_t hash;
18501e04c3fSmrg
18601e04c3fSmrg   if (!vbo_use_minmax_cache(bufferObj))
18701e04c3fSmrg      return;
18801e04c3fSmrg
18901e04c3fSmrg   simple_mtx_lock(&bufferObj->MinMaxCacheMutex);
19001e04c3fSmrg
19101e04c3fSmrg   if (!bufferObj->MinMaxCache) {
19201e04c3fSmrg      bufferObj->MinMaxCache =
19301e04c3fSmrg         _mesa_hash_table_create(NULL,
19401e04c3fSmrg                                 (uint32_t (*)(const void *))vbo_minmax_cache_hash,
19501e04c3fSmrg                                 (bool (*)(const void *, const void *))vbo_minmax_cache_key_equal);
19601e04c3fSmrg      if (!bufferObj->MinMaxCache)
19701e04c3fSmrg         goto out;
19801e04c3fSmrg   }
19901e04c3fSmrg
20001e04c3fSmrg   entry = MALLOC_STRUCT(minmax_cache_entry);
20101e04c3fSmrg   if (!entry)
20201e04c3fSmrg      goto out;
20301e04c3fSmrg
20401e04c3fSmrg   entry->key.offset = offset;
20501e04c3fSmrg   entry->key.count = count;
20601e04c3fSmrg   entry->key.index_size = index_size;
20701e04c3fSmrg   entry->min = min;
20801e04c3fSmrg   entry->max = max;
20901e04c3fSmrg   hash = vbo_minmax_cache_hash(&entry->key);
21001e04c3fSmrg
21101e04c3fSmrg   table_entry = _mesa_hash_table_search_pre_hashed(bufferObj->MinMaxCache,
21201e04c3fSmrg                                                    hash, &entry->key);
21301e04c3fSmrg   if (table_entry) {
21401e04c3fSmrg      /* It seems like this could happen when two contexts are rendering using
21501e04c3fSmrg       * the same buffer object from multiple threads.
21601e04c3fSmrg       */
21701e04c3fSmrg      _mesa_debug(ctx, "duplicate entry in minmax cache\n");
21801e04c3fSmrg      free(entry);
21901e04c3fSmrg      goto out;
22001e04c3fSmrg   }
22101e04c3fSmrg
22201e04c3fSmrg   table_entry = _mesa_hash_table_insert_pre_hashed(bufferObj->MinMaxCache,
22301e04c3fSmrg                                                    hash, &entry->key, entry);
22401e04c3fSmrg   if (!table_entry)
22501e04c3fSmrg      free(entry);
22601e04c3fSmrg
22701e04c3fSmrgout:
22801e04c3fSmrg   simple_mtx_unlock(&bufferObj->MinMaxCacheMutex);
22901e04c3fSmrg}
23001e04c3fSmrg
23101e04c3fSmrg
2327ec681f3Smrgvoid
2337ec681f3Smrgvbo_get_minmax_index_mapped(unsigned count, unsigned index_size,
2347ec681f3Smrg                            unsigned restartIndex, bool restart,
2357ec681f3Smrg                            const void *indices,
2367ec681f3Smrg                            unsigned *min_index, unsigned *max_index)
23701e04c3fSmrg{
2387ec681f3Smrg   switch (index_size) {
23901e04c3fSmrg   case 4: {
24001e04c3fSmrg      const GLuint *ui_indices = (const GLuint *)indices;
24101e04c3fSmrg      GLuint max_ui = 0;
24201e04c3fSmrg      GLuint min_ui = ~0U;
24301e04c3fSmrg      if (restart) {
2447ec681f3Smrg         for (unsigned i = 0; i < count; i++) {
24501e04c3fSmrg            if (ui_indices[i] != restartIndex) {
24601e04c3fSmrg               if (ui_indices[i] > max_ui) max_ui = ui_indices[i];
24701e04c3fSmrg               if (ui_indices[i] < min_ui) min_ui = ui_indices[i];
24801e04c3fSmrg            }
24901e04c3fSmrg         }
25001e04c3fSmrg      }
25101e04c3fSmrg      else {
25201e04c3fSmrg#if defined(USE_SSE41)
25301e04c3fSmrg         if (cpu_has_sse4_1) {
25401e04c3fSmrg            _mesa_uint_array_min_max(ui_indices, &min_ui, &max_ui, count);
25501e04c3fSmrg         }
25601e04c3fSmrg         else
25701e04c3fSmrg#endif
2587ec681f3Smrg            for (unsigned i = 0; i < count; i++) {
25901e04c3fSmrg               if (ui_indices[i] > max_ui) max_ui = ui_indices[i];
26001e04c3fSmrg               if (ui_indices[i] < min_ui) min_ui = ui_indices[i];
26101e04c3fSmrg            }
26201e04c3fSmrg      }
26301e04c3fSmrg      *min_index = min_ui;
26401e04c3fSmrg      *max_index = max_ui;
26501e04c3fSmrg      break;
26601e04c3fSmrg   }
26701e04c3fSmrg   case 2: {
26801e04c3fSmrg      const GLushort *us_indices = (const GLushort *)indices;
26901e04c3fSmrg      GLuint max_us = 0;
27001e04c3fSmrg      GLuint min_us = ~0U;
27101e04c3fSmrg      if (restart) {
2727ec681f3Smrg         for (unsigned i = 0; i < count; i++) {
27301e04c3fSmrg            if (us_indices[i] != restartIndex) {
27401e04c3fSmrg               if (us_indices[i] > max_us) max_us = us_indices[i];
27501e04c3fSmrg               if (us_indices[i] < min_us) min_us = us_indices[i];
27601e04c3fSmrg            }
27701e04c3fSmrg         }
27801e04c3fSmrg      }
27901e04c3fSmrg      else {
2807ec681f3Smrg         for (unsigned i = 0; i < count; i++) {
28101e04c3fSmrg            if (us_indices[i] > max_us) max_us = us_indices[i];
28201e04c3fSmrg            if (us_indices[i] < min_us) min_us = us_indices[i];
28301e04c3fSmrg         }
28401e04c3fSmrg      }
28501e04c3fSmrg      *min_index = min_us;
28601e04c3fSmrg      *max_index = max_us;
28701e04c3fSmrg      break;
28801e04c3fSmrg   }
28901e04c3fSmrg   case 1: {
29001e04c3fSmrg      const GLubyte *ub_indices = (const GLubyte *)indices;
29101e04c3fSmrg      GLuint max_ub = 0;
29201e04c3fSmrg      GLuint min_ub = ~0U;
29301e04c3fSmrg      if (restart) {
2947ec681f3Smrg         for (unsigned i = 0; i < count; i++) {
29501e04c3fSmrg            if (ub_indices[i] != restartIndex) {
29601e04c3fSmrg               if (ub_indices[i] > max_ub) max_ub = ub_indices[i];
29701e04c3fSmrg               if (ub_indices[i] < min_ub) min_ub = ub_indices[i];
29801e04c3fSmrg            }
29901e04c3fSmrg         }
30001e04c3fSmrg      }
30101e04c3fSmrg      else {
3027ec681f3Smrg         for (unsigned i = 0; i < count; i++) {
30301e04c3fSmrg            if (ub_indices[i] > max_ub) max_ub = ub_indices[i];
30401e04c3fSmrg            if (ub_indices[i] < min_ub) min_ub = ub_indices[i];
30501e04c3fSmrg         }
30601e04c3fSmrg      }
30701e04c3fSmrg      *min_index = min_ub;
30801e04c3fSmrg      *max_index = max_ub;
30901e04c3fSmrg      break;
31001e04c3fSmrg   }
31101e04c3fSmrg   default:
31201e04c3fSmrg      unreachable("not reached");
31301e04c3fSmrg   }
3147ec681f3Smrg}
3157ec681f3Smrg
3167ec681f3Smrg
3177ec681f3Smrg/**
3187ec681f3Smrg * Compute min and max elements by scanning the index buffer for
3197ec681f3Smrg * glDraw[Range]Elements() calls.
3207ec681f3Smrg * If primitive restart is enabled, we need to ignore restart
3217ec681f3Smrg * indexes when computing min/max.
3227ec681f3Smrg */
3237ec681f3Smrgstatic void
3247ec681f3Smrgvbo_get_minmax_index(struct gl_context *ctx, struct gl_buffer_object *obj,
3257ec681f3Smrg                     const void *ptr, GLintptr offset, unsigned count,
3267ec681f3Smrg                     unsigned index_size, bool primitive_restart,
3277ec681f3Smrg                     unsigned restart_index, GLuint *min_index,
3287ec681f3Smrg                     GLuint *max_index)
3297ec681f3Smrg{
3307ec681f3Smrg   const char *indices;
3317ec681f3Smrg
3327ec681f3Smrg   if (!obj) {
3337ec681f3Smrg      indices = (const char *)ptr + offset;
3347ec681f3Smrg   } else {
3357ec681f3Smrg      GLsizeiptr size = MIN2((GLsizeiptr)count * index_size, obj->Size);
3367ec681f3Smrg
3377ec681f3Smrg      if (vbo_get_minmax_cached(obj, index_size, offset, count, min_index,
3387ec681f3Smrg                                max_index))
3397ec681f3Smrg         return;
3407ec681f3Smrg
3417ec681f3Smrg      indices = ctx->Driver.MapBufferRange(ctx, offset, size, GL_MAP_READ_BIT,
3427ec681f3Smrg                                           obj, MAP_INTERNAL);
3437ec681f3Smrg   }
3447ec681f3Smrg
3457ec681f3Smrg   vbo_get_minmax_index_mapped(count, index_size, restart_index,
3467ec681f3Smrg                               primitive_restart, indices,
3477ec681f3Smrg                               min_index, max_index);
34801e04c3fSmrg
3497ec681f3Smrg   if (obj) {
3507ec681f3Smrg      vbo_minmax_cache_store(ctx, obj, index_size, offset, count, *min_index,
3517ec681f3Smrg                             *max_index);
3527ec681f3Smrg      ctx->Driver.UnmapBuffer(ctx, obj, MAP_INTERNAL);
35301e04c3fSmrg   }
35401e04c3fSmrg}
35501e04c3fSmrg
35601e04c3fSmrg/**
35701e04c3fSmrg * Compute min and max elements for nr_prims
35801e04c3fSmrg */
35901e04c3fSmrgvoid
36001e04c3fSmrgvbo_get_minmax_indices(struct gl_context *ctx,
36101e04c3fSmrg                       const struct _mesa_prim *prims,
36201e04c3fSmrg                       const struct _mesa_index_buffer *ib,
36301e04c3fSmrg                       GLuint *min_index,
36401e04c3fSmrg                       GLuint *max_index,
3657ec681f3Smrg                       GLuint nr_prims,
3667ec681f3Smrg                       bool primitive_restart,
3677ec681f3Smrg                       unsigned restart_index)
36801e04c3fSmrg{
36901e04c3fSmrg   GLuint tmp_min, tmp_max;
37001e04c3fSmrg   GLuint i;
37101e04c3fSmrg   GLuint count;
37201e04c3fSmrg
37301e04c3fSmrg   *min_index = ~0;
37401e04c3fSmrg   *max_index = 0;
37501e04c3fSmrg
37601e04c3fSmrg   for (i = 0; i < nr_prims; i++) {
37701e04c3fSmrg      const struct _mesa_prim *start_prim;
37801e04c3fSmrg
37901e04c3fSmrg      start_prim = &prims[i];
38001e04c3fSmrg      count = start_prim->count;
38101e04c3fSmrg      /* Do combination if possible to reduce map/unmap count */
38201e04c3fSmrg      while ((i + 1 < nr_prims) &&
38301e04c3fSmrg             (prims[i].start + prims[i].count == prims[i+1].start)) {
38401e04c3fSmrg         count += prims[i+1].count;
38501e04c3fSmrg         i++;
38601e04c3fSmrg      }
3877ec681f3Smrg      vbo_get_minmax_index(ctx, ib->obj, ib->ptr,
3887ec681f3Smrg                           (ib->obj ? (GLintptr)ib->ptr : 0) +
3897ec681f3Smrg                           (start_prim->start << ib->index_size_shift),
3907ec681f3Smrg                           count, 1 << ib->index_size_shift,
3917ec681f3Smrg                           primitive_restart, restart_index,
3927ec681f3Smrg                           &tmp_min, &tmp_max);
39301e04c3fSmrg      *min_index = MIN2(*min_index, tmp_min);
39401e04c3fSmrg      *max_index = MAX2(*max_index, tmp_max);
39501e04c3fSmrg   }
39601e04c3fSmrg}
3977ec681f3Smrg
3987ec681f3Smrg/**
3997ec681f3Smrg * Same as vbo_get_minmax_index, but using gallium draw structures.
4007ec681f3Smrg */
4017ec681f3Smrgbool
4027ec681f3Smrgvbo_get_minmax_indices_gallium(struct gl_context *ctx,
4037ec681f3Smrg                               struct pipe_draw_info *info,
4047ec681f3Smrg                               const struct pipe_draw_start_count_bias *draws,
4057ec681f3Smrg                               unsigned num_draws)
4067ec681f3Smrg{
4077ec681f3Smrg   info->min_index = ~0;
4087ec681f3Smrg   info->max_index = 0;
4097ec681f3Smrg
4107ec681f3Smrg   for (unsigned i = 0; i < num_draws; i++) {
4117ec681f3Smrg      struct pipe_draw_start_count_bias draw = draws[i];
4127ec681f3Smrg
4137ec681f3Smrg      /* Do combination if possible to reduce map/unmap count */
4147ec681f3Smrg      while ((i + 1 < num_draws) &&
4157ec681f3Smrg             (draws[i].start + draws[i].count == draws[i+1].start)) {
4167ec681f3Smrg         draw.count += draws[i+1].count;
4177ec681f3Smrg         i++;
4187ec681f3Smrg      }
4197ec681f3Smrg
4207ec681f3Smrg      if (!draw.count)
4217ec681f3Smrg         continue;
4227ec681f3Smrg
4237ec681f3Smrg      unsigned tmp_min, tmp_max;
4247ec681f3Smrg      vbo_get_minmax_index(ctx, info->has_user_indices ?
4257ec681f3Smrg                              NULL : info->index.gl_bo,
4267ec681f3Smrg                           info->index.user,
4277ec681f3Smrg                           (GLintptr)draw.start * info->index_size,
4287ec681f3Smrg                           draw.count, info->index_size,
4297ec681f3Smrg                           info->primitive_restart, info->restart_index,
4307ec681f3Smrg                           &tmp_min, &tmp_max);
4317ec681f3Smrg      info->min_index = MIN2(info->min_index, tmp_min);
4327ec681f3Smrg      info->max_index = MAX2(info->max_index, tmp_max);
4337ec681f3Smrg   }
4347ec681f3Smrg
4357ec681f3Smrg   return info->min_index <= info->max_index;
4367ec681f3Smrg}
437