1 /* 2 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas. 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 (at) intel.com> 28 * 29 */ 30 31 #include "main/imports.h" 32 #include "main/bufferobj.h" 33 #include "main/macros.h" 34 35 #include "vbo.h" 36 #include "vbo_context.h" 37 38 #define UPDATE_MIN2(a, b) (a) = MIN2((a), (b)) 39 #define UPDATE_MAX2(a, b) (a) = MAX2((a), (b)) 40 41 /* 42 * Notes on primitive restart: 43 * The code below is used when the driver does not support primitive 44 * restart itself. (ctx->Const.PrimitiveRestartInSoftware == GL_TRUE) 45 * 46 * We map the index buffer, find the restart indexes, unmap 47 * the index buffer then draw the sub-primitives delineated by the restarts. 48 * 49 * A couple possible optimizations: 50 * 1. Save the list of sub-primitive (start, count) values in a list attached 51 * to the index buffer for re-use in subsequent draws. The list would be 52 * invalidated when the contents of the buffer changed. 53 * 2. If drawing triangle strips or quad strips, create a new index buffer 54 * that uses duplicated vertices to render the disjoint strips as one 55 * long strip. We'd have to be careful to avoid using too much memory 56 * for this. 57 * 58 * Finally, some apps might perform better if they don't use primitive restart 59 * at all rather than this fallback path. Set MESA_EXTENSION_OVERRIDE to 60 * "-GL_NV_primitive_restart" to test that. 61 */ 62 63 64 struct sub_primitive 65 { 66 GLuint start; 67 GLuint count; 68 GLuint min_index; 69 GLuint max_index; 70 }; 71 72 73 /** 74 * Scan the elements array to find restart indexes. Return an array 75 * of struct sub_primitive to indicate how to draw the sub-primitives 76 * are delineated by the restart index. 77 */ 78 static struct sub_primitive * 79 find_sub_primitives(const void *elements, unsigned element_size, 80 unsigned start, unsigned end, unsigned restart_index, 81 unsigned *num_sub_prims) 82 { 83 const unsigned max_prims = end - start; 84 struct sub_primitive *sub_prims; 85 unsigned i, cur_start, cur_count; 86 GLuint scan_index; 87 unsigned scan_num; 88 89 sub_prims = (struct sub_primitive *) 90 malloc(max_prims * sizeof(struct sub_primitive)); 91 92 if (!sub_prims) { 93 *num_sub_prims = 0; 94 return NULL; 95 } 96 97 cur_start = start; 98 cur_count = 0; 99 scan_num = 0; 100 101 #define IB_INDEX_READ(TYPE, INDEX) (((const GL##TYPE *) elements)[INDEX]) 102 103 #define SCAN_ELEMENTS(TYPE) \ 104 sub_prims[scan_num].min_index = (GL##TYPE) 0xffffffff; \ 105 sub_prims[scan_num].max_index = 0; \ 106 for (i = start; i < end; i++) { \ 107 scan_index = IB_INDEX_READ(TYPE, i); \ 108 if (scan_index == restart_index) { \ 109 if (cur_count > 0) { \ 110 assert(scan_num < max_prims); \ 111 sub_prims[scan_num].start = cur_start; \ 112 sub_prims[scan_num].count = cur_count; \ 113 scan_num++; \ 114 sub_prims[scan_num].min_index = (GL##TYPE) 0xffffffff; \ 115 sub_prims[scan_num].max_index = 0; \ 116 } \ 117 cur_start = i + 1; \ 118 cur_count = 0; \ 119 } \ 120 else { \ 121 UPDATE_MIN2(sub_prims[scan_num].min_index, scan_index); \ 122 UPDATE_MAX2(sub_prims[scan_num].max_index, scan_index); \ 123 cur_count++; \ 124 } \ 125 } \ 126 if (cur_count > 0) { \ 127 assert(scan_num < max_prims); \ 128 sub_prims[scan_num].start = cur_start; \ 129 sub_prims[scan_num].count = cur_count; \ 130 scan_num++; \ 131 } 132 133 switch (element_size) { 134 case 1: 135 SCAN_ELEMENTS(ubyte); 136 break; 137 case 2: 138 SCAN_ELEMENTS(ushort); 139 break; 140 case 4: 141 SCAN_ELEMENTS(uint); 142 break; 143 default: 144 assert(0 && "bad index_size in find_sub_primitives()"); 145 } 146 147 #undef SCAN_ELEMENTS 148 149 *num_sub_prims = scan_num; 150 151 return sub_prims; 152 } 153 154 155 /** 156 * Handle primitive restart in software. 157 * 158 * This function breaks up calls into the driver so primitive restart 159 * support is not required in the driver. 160 */ 161 void 162 vbo_sw_primitive_restart(struct gl_context *ctx, 163 const struct _mesa_prim *prims, 164 GLuint nr_prims, 165 const struct _mesa_index_buffer *ib) 166 { 167 GLuint prim_num; 168 struct sub_primitive *sub_prims; 169 struct sub_primitive *sub_prim; 170 GLuint num_sub_prims; 171 GLuint sub_prim_num; 172 GLuint end_index; 173 GLuint sub_end_index; 174 GLuint restart_index = ctx->Array.RestartIndex; 175 struct _mesa_prim temp_prim; 176 struct vbo_context *vbo = vbo_context(ctx); 177 vbo_draw_func draw_prims_func = vbo->draw_prims; 178 GLboolean map_ib = ib->obj->Name && !ib->obj->Pointer; 179 void *ptr; 180 181 /* Find the sub-primitives. These are regions in the index buffer which 182 * are split based on the primitive restart index value. 183 */ 184 if (map_ib) { 185 ctx->Driver.MapBufferRange(ctx, 0, ib->obj->Size, GL_MAP_READ_BIT, 186 ib->obj); 187 } 188 189 ptr = ADD_POINTERS(ib->obj->Pointer, ib->ptr); 190 191 sub_prims = find_sub_primitives(ptr, vbo_sizeof_ib_type(ib->type), 192 0, ib->count, restart_index, 193 &num_sub_prims); 194 195 if (map_ib) { 196 ctx->Driver.UnmapBuffer(ctx, ib->obj); 197 } 198 199 /* Loop over the primitives, and use the located sub-primitives to draw 200 * each primitive with a break to implement each primitive restart. 201 */ 202 for (prim_num = 0; prim_num < nr_prims; prim_num++) { 203 end_index = prims[prim_num].start + prims[prim_num].count; 204 memcpy(&temp_prim, &prims[prim_num], sizeof (temp_prim)); 205 /* Loop over the sub-primitives drawing sub-ranges of the primitive. */ 206 for (sub_prim_num = 0; sub_prim_num < num_sub_prims; sub_prim_num++) { 207 sub_prim = &sub_prims[sub_prim_num]; 208 sub_end_index = sub_prim->start + sub_prim->count; 209 if (prims[prim_num].start <= sub_prim->start) { 210 temp_prim.start = MAX2(prims[prim_num].start, sub_prim->start); 211 temp_prim.count = MIN2(sub_end_index, end_index) - temp_prim.start; 212 if ((temp_prim.start == sub_prim->start) && 213 (temp_prim.count == sub_prim->count)) { 214 draw_prims_func(ctx, &temp_prim, 1, ib, 215 GL_TRUE, sub_prim->min_index, sub_prim->max_index, 216 NULL); 217 } else { 218 draw_prims_func(ctx, &temp_prim, 1, ib, 219 GL_FALSE, -1, -1, 220 NULL); 221 } 222 } 223 if (sub_end_index >= end_index) { 224 break; 225 } 226 } 227 } 228 229 if (sub_prims) { 230 free(sub_prims); 231 } 232 } 233 234