• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2012 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  * Authors:
24  *    Jordan Justen <jordan.l.justen@intel.com>
25  *
26  */
27 
28 #include "main/bufferobj.h"
29 #include "main/varray.h"
30 #include "vbo/vbo.h"
31 
32 #include "brw_context.h"
33 #include "brw_defines.h"
34 #include "brw_draw.h"
35 
36 #include "brw_batch.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 static void
vbo_sw_primitive_restart_common_start(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,bool primitive_restart,unsigned restart_index)163 vbo_sw_primitive_restart_common_start(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,
168                                       GLuint base_instance,
169                                       struct gl_buffer_object *indirect,
170                                       GLsizeiptr indirect_offset,
171                                       bool primitive_restart,
172                                       unsigned restart_index)
173 {
174    GLuint prim_num;
175    struct _mesa_prim new_prim;
176    struct _mesa_index_buffer new_ib;
177    struct sub_primitive *sub_prims;
178    struct sub_primitive *sub_prim;
179    GLuint num_sub_prims;
180    GLuint sub_prim_num;
181    GLuint end_index;
182    GLuint sub_end_index;
183    struct _mesa_prim temp_prim;
184    GLboolean map_ib = ib->obj && !ib->obj->Mappings[MAP_INTERNAL].Pointer;
185    const void *ptr;
186 
187    /* If there is an indirect buffer, map it and extract the draw params */
188    if (indirect) {
189       const uint32_t *indirect_params;
190       if (!ctx->Driver.MapBufferRange(ctx, 0, indirect->Size, GL_MAP_READ_BIT,
191                                       indirect, MAP_INTERNAL)) {
192 
193          /* something went wrong with mapping, give up */
194          _mesa_error(ctx, GL_OUT_OF_MEMORY,
195                      "failed to map indirect buffer for sw primitive restart");
196          return;
197       }
198 
199       assert(nr_prims == 1);
200       new_prim = prims[0];
201       indirect_params = (const uint32_t *)
202                         ADD_POINTERS(indirect->Mappings[MAP_INTERNAL].Pointer,
203                                      indirect_offset);
204 
205       new_prim.count = indirect_params[0];
206       new_prim.start = indirect_params[2];
207       new_prim.basevertex = indirect_params[3];
208 
209       num_instances = indirect_params[1];
210       base_instance = indirect_params[4];
211 
212       new_ib = *ib;
213       new_ib.count = new_prim.count;
214 
215       prims = &new_prim;
216       ib = &new_ib;
217 
218       ctx->Driver.UnmapBuffer(ctx, indirect, MAP_INTERNAL);
219    }
220 
221    /* Find the sub-primitives. These are regions in the index buffer which
222     * are split based on the primitive restart index value.
223     */
224    if (map_ib) {
225       ctx->Driver.MapBufferRange(ctx, 0, ib->obj->Size, GL_MAP_READ_BIT,
226                                  ib->obj, MAP_INTERNAL);
227    }
228 
229    if (ib->obj)
230       ptr = ADD_POINTERS(ib->obj->Mappings[MAP_INTERNAL].Pointer, ib->ptr);
231    else
232       ptr = ib->ptr;
233 
234    sub_prims = find_sub_primitives(ptr, 1 << ib->index_size_shift,
235                                    prims[0].start, prims[0].start + ib->count,
236                                    restart_index, &num_sub_prims);
237 
238    if (map_ib) {
239       ctx->Driver.UnmapBuffer(ctx, ib->obj, MAP_INTERNAL);
240    }
241 
242    /* Loop over the primitives, and use the located sub-primitives to draw
243     * each primitive with a break to implement each primitive restart.
244     */
245    for (prim_num = 0; prim_num < nr_prims; prim_num++) {
246       end_index = prims[prim_num].start + prims[prim_num].count;
247       memcpy(&temp_prim, &prims[prim_num], sizeof (temp_prim));
248       /* Loop over the sub-primitives drawing sub-ranges of the primitive. */
249       for (sub_prim_num = 0; sub_prim_num < num_sub_prims; sub_prim_num++) {
250          sub_prim = &sub_prims[sub_prim_num];
251          sub_end_index = sub_prim->start + sub_prim->count;
252          if (prims[prim_num].start <= sub_prim->start) {
253             temp_prim.start = MAX2(prims[prim_num].start, sub_prim->start);
254             temp_prim.count = MIN2(sub_end_index, end_index) - temp_prim.start;
255             if ((temp_prim.start == sub_prim->start) &&
256                 (temp_prim.count == sub_prim->count)) {
257                ctx->Driver.Draw(ctx, &temp_prim, 1, ib, true, false, 0,
258                                 sub_prim->min_index, sub_prim->max_index,
259                                 num_instances, base_instance);
260             } else {
261                ctx->Driver.Draw(ctx, &temp_prim, 1, ib,
262                                 false, false, 0, -1, -1,
263                                 num_instances, base_instance);
264             }
265          }
266          if (sub_end_index >= end_index) {
267             break;
268          }
269       }
270    }
271 
272    free(sub_prims);
273 }
274 
275 static 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,bool primitive_restart,unsigned restart_index)276 vbo_sw_primitive_restart(struct gl_context *ctx,
277                          const struct _mesa_prim *prims,
278                          GLuint nr_prims,
279                          const struct _mesa_index_buffer *ib,
280                          GLuint num_instances,
281                          GLuint base_instance,
282                          struct gl_buffer_object *indirect,
283                          GLsizeiptr indirect_offset,
284                          bool primitive_restart,
285                          unsigned restart_index)
286 {
287    unsigned i;
288    for (i = 1; i < nr_prims; i++) {
289       if (prims[i].start != prims[0].start)
290          break;
291    }
292 
293    vbo_sw_primitive_restart_common_start(ctx, &prims[0], i, ib,
294                                          num_instances, base_instance,
295                                          indirect, indirect_offset,
296                                          primitive_restart,
297                                          restart_index);
298    if (i != nr_prims) {
299       vbo_sw_primitive_restart(ctx, &prims[i], nr_prims - i, ib,
300                                num_instances, base_instance,
301                                indirect, indirect_offset,
302                                primitive_restart,
303                                restart_index);
304    }
305 }
306 
307 /**
308  * Check if the hardware's cut index support can handle the primitive
309  * restart index value (pre-Haswell only).
310  */
311 static bool
can_cut_index_handle_restart_index(struct gl_context * ctx,const struct _mesa_index_buffer * ib,unsigned restart_index)312 can_cut_index_handle_restart_index(struct gl_context *ctx,
313                                    const struct _mesa_index_buffer *ib,
314                                    unsigned restart_index)
315 {
316    /* The FixedIndex variant means 0xFF, 0xFFFF, or 0xFFFFFFFF based on
317     * the index buffer type, which corresponds exactly to the hardware.
318     */
319    if (ctx->Array.PrimitiveRestartFixedIndex)
320       return true;
321 
322    bool cut_index_will_work;
323 
324    switch (ib->index_size_shift) {
325    case 0:
326       cut_index_will_work = restart_index == 0xff;
327       break;
328    case 1:
329       cut_index_will_work = restart_index == 0xffff;
330       break;
331    case 2:
332       cut_index_will_work = restart_index == 0xffffffff;
333       break;
334    default:
335       unreachable("not reached");
336    }
337 
338    return cut_index_will_work;
339 }
340 
341 /**
342  * Check if the hardware's cut index support can handle the primitive
343  * restart case.
344  */
345 static bool
can_cut_index_handle_prims(struct gl_context * ctx,const struct _mesa_prim * prim,GLuint nr_prims,const struct _mesa_index_buffer * ib,unsigned restart_index)346 can_cut_index_handle_prims(struct gl_context *ctx,
347                            const struct _mesa_prim *prim,
348                            GLuint nr_prims,
349                            const struct _mesa_index_buffer *ib,
350                            unsigned restart_index)
351 {
352    struct brw_context *brw = brw_context(ctx);
353    const struct intel_device_info *devinfo = &brw->screen->devinfo;
354 
355    /* Otherwise Haswell can do it all. */
356    if (devinfo->verx10 >= 75)
357       return true;
358 
359    if (!can_cut_index_handle_restart_index(ctx, ib, restart_index)) {
360       /* The primitive restart index can't be handled, so take
361        * the software path
362        */
363       return false;
364    }
365 
366    for (unsigned i = 0; i < nr_prims; i++) {
367       switch (prim[i].mode) {
368       case GL_POINTS:
369       case GL_LINES:
370       case GL_LINE_STRIP:
371       case GL_TRIANGLES:
372       case GL_TRIANGLE_STRIP:
373       case GL_LINES_ADJACENCY:
374       case GL_LINE_STRIP_ADJACENCY:
375       case GL_TRIANGLES_ADJACENCY:
376       case GL_TRIANGLE_STRIP_ADJACENCY:
377          /* Cut index supports these primitive types */
378          break;
379       default:
380          /* Cut index does not support these primitive types */
381       //case GL_LINE_LOOP:
382       //case GL_TRIANGLE_FAN:
383       //case GL_QUADS:
384       //case GL_QUAD_STRIP:
385       //case GL_POLYGON:
386          return false;
387       }
388    }
389 
390    return true;
391 }
392 
393 /**
394  * Check if primitive restart is enabled, and if so, handle it properly.
395  *
396  * In some cases the support will be handled in software. When available
397  * hardware will handle primitive restart.
398  */
399 GLboolean
brw_handle_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,bool primitive_restart,unsigned restart_index)400 brw_handle_primitive_restart(struct gl_context *ctx,
401                              const struct _mesa_prim *prims,
402                              GLuint nr_prims,
403                              const struct _mesa_index_buffer *ib,
404                              GLuint num_instances, GLuint base_instance,
405                              bool primitive_restart,
406                              unsigned restart_index)
407 {
408    struct brw_context *brw = brw_context(ctx);
409 
410    /* We only need to handle cases where there is an index buffer. */
411    if (ib == NULL) {
412       return GL_FALSE;
413    }
414 
415    /* If we have set the in_progress flag, then we are in the middle
416     * of handling the primitive restart draw.
417     */
418    if (brw->prim_restart.in_progress) {
419       return GL_FALSE;
420    }
421 
422    /* If PrimitiveRestart is not enabled, then we aren't concerned about
423     * handling this draw.
424     */
425    if (!primitive_restart) {
426       return GL_FALSE;
427    }
428 
429    /* Signal that we are in the process of handling the
430     * primitive restart draw
431     */
432    brw->prim_restart.in_progress = true;
433 
434    if (can_cut_index_handle_prims(ctx, prims, nr_prims, ib, restart_index)) {
435       /* Cut index should work for primitive restart, so use it
436        */
437       brw->prim_restart.enable_cut_index = true;
438       brw->prim_restart.restart_index = restart_index;
439       brw_draw_prims(ctx, prims, nr_prims, ib, false, primitive_restart,
440                      restart_index, -1, -1,
441                      num_instances, base_instance);
442       brw->prim_restart.enable_cut_index = false;
443    } else {
444       /* Not all the primitive draw modes are supported by the cut index,
445        * so take the software path
446        */
447       struct gl_buffer_object *indirect_data = brw->draw.draw_indirect_data;
448 
449       /* Clear this to make the draw direct. */
450       brw->draw.draw_indirect_data = NULL;
451 
452       vbo_sw_primitive_restart(ctx, prims, nr_prims, ib, num_instances,
453                                base_instance, indirect_data,
454                                brw->draw.draw_indirect_offset,
455                                primitive_restart, restart_index);
456    }
457 
458    brw->prim_restart.in_progress = false;
459 
460    /* The primitive restart draw was completed, so return true. */
461    return GL_TRUE;
462 }
463