• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2007 VMware, Inc.
3  * All Rights Reserved.
4  *
5  * Copyright © 2012 Intel Corporation
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the next
15  * paragraph) shall be included in all copies or substantial portions of the
16  * Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24  * IN THE SOFTWARE.
25  *
26  * Authors:
27  *    Jordan Justen <jordan.l.justen@intel.com>
28  *
29  */
30 
31 #include "main/errors.h"
32 
33 #include "main/macros.h"
34 #include "main/varray.h"
35 
36 #include "vbo.h"
37 
38 
39 #define UPDATE_MIN2(a, b) (a) = MIN2((a), (b))
40 #define UPDATE_MAX2(a, b) (a) = MAX2((a), (b))
41 
42 /*
43  * Notes on primitive restart:
44  * The code below is used when the driver does not fully support primitive
45  * restart (for example, if it only does restart index of ~0).
46  *
47  * We map the index buffer, find the restart indexes, unmap
48  * the index buffer then draw the sub-primitives delineated by the restarts.
49  *
50  * A couple possible optimizations:
51  * 1. Save the list of sub-primitive (start, count) values in a list attached
52  *    to the index buffer for re-use in subsequent draws.  The list would be
53  *    invalidated when the contents of the buffer changed.
54  * 2. If drawing triangle strips or quad strips, create a new index buffer
55  *    that uses duplicated vertices to render the disjoint strips as one
56  *    long strip.  We'd have to be careful to avoid using too much memory
57  *    for this.
58  *
59  * Finally, some apps might perform better if they don't use primitive restart
60  * at all rather than this fallback path.  Set MESA_EXTENSION_OVERRIDE to
61  * "-GL_NV_primitive_restart" to test that.
62  */
63 
64 
65 struct sub_primitive
66 {
67    GLuint start;
68    GLuint count;
69    GLuint min_index;
70    GLuint max_index;
71 };
72 
73 
74 /**
75  * Scan the elements array to find restart indexes.  Return an array
76  * of struct sub_primitive to indicate how to draw the sub-primitives
77  * are delineated by the restart index.
78  */
79 static struct sub_primitive *
find_sub_primitives(const void * elements,unsigned element_size,unsigned start,unsigned end,unsigned restart_index,unsigned * num_sub_prims)80 find_sub_primitives(const void *elements, unsigned element_size,
81                     unsigned start, unsigned end, unsigned restart_index,
82                     unsigned *num_sub_prims)
83 {
84    const unsigned max_prims = end - start;
85    struct sub_primitive *sub_prims;
86    unsigned i, cur_start, cur_count;
87    GLuint scan_index;
88    unsigned scan_num;
89 
90    sub_prims =
91       malloc(max_prims * sizeof(struct sub_primitive));
92 
93    if (!sub_prims) {
94       *num_sub_prims = 0;
95       return NULL;
96    }
97 
98    cur_start = start;
99    cur_count = 0;
100    scan_num = 0;
101 
102 #define IB_INDEX_READ(TYPE, INDEX) (((const GL##TYPE *) elements)[INDEX])
103 
104 #define SCAN_ELEMENTS(TYPE) \
105    sub_prims[scan_num].min_index = (GL##TYPE) 0xffffffff; \
106    sub_prims[scan_num].max_index = 0; \
107    for (i = start; i < end; i++) { \
108       scan_index = IB_INDEX_READ(TYPE, i); \
109       if (scan_index == restart_index) { \
110          if (cur_count > 0) { \
111             assert(scan_num < max_prims); \
112             sub_prims[scan_num].start = cur_start; \
113             sub_prims[scan_num].count = cur_count; \
114             scan_num++; \
115             sub_prims[scan_num].min_index = (GL##TYPE) 0xffffffff; \
116             sub_prims[scan_num].max_index = 0; \
117          } \
118          cur_start = i + 1; \
119          cur_count = 0; \
120       } \
121       else { \
122          UPDATE_MIN2(sub_prims[scan_num].min_index, scan_index); \
123          UPDATE_MAX2(sub_prims[scan_num].max_index, scan_index); \
124          cur_count++; \
125       } \
126    } \
127    if (cur_count > 0) { \
128       assert(scan_num < max_prims); \
129       sub_prims[scan_num].start = cur_start; \
130       sub_prims[scan_num].count = cur_count; \
131       scan_num++; \
132    }
133 
134    switch (element_size) {
135    case 1:
136       SCAN_ELEMENTS(ubyte);
137       break;
138    case 2:
139       SCAN_ELEMENTS(ushort);
140       break;
141    case 4:
142       SCAN_ELEMENTS(uint);
143       break;
144    default:
145       assert(0 && "bad index_size in find_sub_primitives()");
146    }
147 
148 #undef SCAN_ELEMENTS
149 
150    *num_sub_prims = scan_num;
151 
152    return sub_prims;
153 }
154 
155 
156 /**
157  * Handle primitive restart in software.
158  *
159  * This function breaks up calls into the driver so primitive restart
160  * support is not required in the driver.
161  */
162 void
vbo_sw_primitive_restart(struct gl_context * ctx,const struct _mesa_prim * prims,GLuint nr_prims,const struct _mesa_index_buffer * ib,GLuint num_instances,GLuint base_instance,struct gl_buffer_object * indirect,GLsizeiptr indirect_offset)163 vbo_sw_primitive_restart(struct gl_context *ctx,
164                          const struct _mesa_prim *prims,
165                          GLuint nr_prims,
166                          const struct _mesa_index_buffer *ib,
167                          GLuint num_instances, GLuint base_instance,
168                          struct gl_buffer_object *indirect,
169                          GLsizeiptr indirect_offset)
170 {
171    GLuint prim_num;
172    struct _mesa_prim new_prim;
173    struct _mesa_index_buffer new_ib;
174    struct sub_primitive *sub_prims;
175    struct sub_primitive *sub_prim;
176    GLuint num_sub_prims;
177    GLuint sub_prim_num;
178    GLuint end_index;
179    GLuint sub_end_index;
180    GLuint restart_index = ctx->Array._RestartIndex[(1 << ib->index_size_shift) - 1];
181    struct _mesa_prim temp_prim;
182    GLboolean map_ib = ib->obj && !ib->obj->Mappings[MAP_INTERNAL].Pointer;
183    const void *ptr;
184 
185    /* If there is an indirect buffer, map it and extract the draw params */
186    if (indirect) {
187       const uint32_t *indirect_params;
188       if (!ctx->Driver.MapBufferRange(ctx, 0, indirect->Size, GL_MAP_READ_BIT,
189                                       indirect, MAP_INTERNAL)) {
190 
191          /* something went wrong with mapping, give up */
192          _mesa_error(ctx, GL_OUT_OF_MEMORY,
193                      "failed to map indirect buffer for sw primitive restart");
194          return;
195       }
196 
197       assert(nr_prims == 1);
198       new_prim = prims[0];
199       indirect_params = (const uint32_t *)
200                         ADD_POINTERS(indirect->Mappings[MAP_INTERNAL].Pointer,
201                                      indirect_offset);
202 
203       new_prim.count = indirect_params[0];
204       new_prim.start = indirect_params[2];
205       new_prim.basevertex = indirect_params[3];
206 
207       num_instances = indirect_params[1];
208       base_instance = indirect_params[4];
209 
210       new_ib = *ib;
211       new_ib.count = new_prim.count;
212 
213       prims = &new_prim;
214       ib = &new_ib;
215 
216       ctx->Driver.UnmapBuffer(ctx, indirect, MAP_INTERNAL);
217    }
218 
219    /* Find the sub-primitives. These are regions in the index buffer which
220     * are split based on the primitive restart index value.
221     */
222    if (map_ib) {
223       ctx->Driver.MapBufferRange(ctx, 0, ib->obj->Size, GL_MAP_READ_BIT,
224                                  ib->obj, MAP_INTERNAL);
225    }
226 
227    if (ib->obj)
228       ptr = ADD_POINTERS(ib->obj->Mappings[MAP_INTERNAL].Pointer, ib->ptr);
229    else
230       ptr = ib->ptr;
231 
232    sub_prims = find_sub_primitives(ptr, 1 << ib->index_size_shift,
233                                    0, ib->count, restart_index,
234                                    &num_sub_prims);
235 
236    if (map_ib) {
237       ctx->Driver.UnmapBuffer(ctx, ib->obj, MAP_INTERNAL);
238    }
239 
240    /* Loop over the primitives, and use the located sub-primitives to draw
241     * each primitive with a break to implement each primitive restart.
242     */
243    for (prim_num = 0; prim_num < nr_prims; prim_num++) {
244       end_index = prims[prim_num].start + prims[prim_num].count;
245       memcpy(&temp_prim, &prims[prim_num], sizeof (temp_prim));
246       /* Loop over the sub-primitives drawing sub-ranges of the primitive. */
247       for (sub_prim_num = 0; sub_prim_num < num_sub_prims; sub_prim_num++) {
248          sub_prim = &sub_prims[sub_prim_num];
249          sub_end_index = sub_prim->start + sub_prim->count;
250          if (prims[prim_num].start <= sub_prim->start) {
251             temp_prim.start = MAX2(prims[prim_num].start, sub_prim->start);
252             temp_prim.count = MIN2(sub_end_index, end_index) - temp_prim.start;
253             if ((temp_prim.start == sub_prim->start) &&
254                 (temp_prim.count == sub_prim->count)) {
255                ctx->Driver.Draw(ctx, &temp_prim, 1, ib, GL_TRUE,
256                                 sub_prim->min_index, sub_prim->max_index,
257                                 num_instances, base_instance,
258                                 NULL, 0);
259             } else {
260                ctx->Driver.Draw(ctx, &temp_prim, 1, ib,
261                                 GL_FALSE, -1, -1,
262                                 num_instances, base_instance,
263                                 NULL, 0);
264             }
265          }
266          if (sub_end_index >= end_index) {
267             break;
268          }
269       }
270    }
271 
272    free(sub_prims);
273 }
274 
275