1 2/* 3 * Mesa 3-D graphics library 4 * 5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. 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 shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 * OTHER DEALINGS IN THE SOFTWARE. 24 * 25 * Authors: 26 * Keith Whitwell <keithw@vmware.com> 27 */ 28 29/* Helper for drivers which find themselves rendering a range of 30 * indices starting somewhere above zero. Typically the application 31 * is issuing multiple DrawArrays() or DrawElements() to draw 32 * successive primitives layed out linearly in the vertex arrays. 33 * Unless the vertex arrays are all in a VBO, the OpenGL semantics 34 * imply that we need to re-upload the vertex data on each draw call. 35 * In that case, we want to avoid starting the upload at zero, as it 36 * will mean every draw call uploads an increasing amount of not-used 37 * vertex data. Worse - in the software tnl module, all those 38 * vertices will be transformed and lit. 39 * 40 * If we just upload the new data, however, the indices will be 41 * incorrect as we tend to upload each set of vertex data to a new 42 * region. 43 * 44 * This file provides a helper to adjust the arrays, primitives and 45 * indices of a draw call so that it can be re-issued with a min_index 46 * of zero. 47 */ 48 49#include <stdio.h> 50#include "main/bufferobj.h" 51#include "main/errors.h" 52#include "main/glheader.h" 53#include "main/imports.h" 54#include "main/mtypes.h" 55#include "vbo/vbo.h" 56 57#include "t_rebase.h" 58 59 60#define REBASE(TYPE) \ 61static void *rebase_##TYPE( const void *ptr, \ 62 GLuint count, \ 63 TYPE min_index ) \ 64{ \ 65 GLuint i; \ 66 const TYPE *in = (TYPE *)ptr; \ 67 TYPE *tmp_indices = malloc(count * sizeof(TYPE)); \ 68 \ 69 if (tmp_indices == NULL) { \ 70 _mesa_error_no_memory(__func__); \ 71 return NULL; \ 72 } \ 73 \ 74 for (i = 0; i < count; i++) \ 75 tmp_indices[i] = in[i] - min_index; \ 76 \ 77 return (void *)tmp_indices; \ 78} 79 80 81REBASE(GLuint) 82REBASE(GLushort) 83REBASE(GLubyte) 84 85 86 87/* Adjust primitives, indices and vertex definitions so that min_index 88 * becomes zero. There are lots of reasons for wanting to do this, eg: 89 * 90 * Software tnl: 91 * - any time min_index != 0, otherwise unused vertices lower than 92 * min_index will be transformed. 93 * 94 * Hardware tnl: 95 * - if ib != NULL and min_index != 0, otherwise vertices lower than 96 * min_index will be uploaded. Requires adjusting index values. 97 * 98 * - if ib == NULL and min_index != 0, just for convenience so this doesn't 99 * have to be handled within the driver. 100 * 101 * Hardware tnl with VBO support: 102 * - as above, but only when vertices are not (all?) in VBO's. 103 * - can't save time by trying to upload half a vbo - typically it is 104 * all or nothing. 105 */ 106void t_rebase_prims( struct gl_context *ctx, 107 const struct tnl_vertex_array *arrays, 108 const struct _mesa_prim *prim, 109 GLuint nr_prims, 110 const struct _mesa_index_buffer *ib, 111 GLuint min_index, 112 GLuint max_index, 113 tnl_draw_func draw ) 114{ 115 struct gl_array_attributes tmp_attribs[VERT_ATTRIB_MAX]; 116 struct tnl_vertex_array tmp_arrays[VERT_ATTRIB_MAX]; 117 118 struct _mesa_index_buffer tmp_ib; 119 struct _mesa_prim *tmp_prims = NULL; 120 void *tmp_indices = NULL; 121 GLuint i; 122 123 assert(min_index != 0); 124 125 if (0) 126 printf("%s %d..%d\n", __func__, min_index, max_index); 127 128 129 /* XXX this path is disabled for now. 130 * There's rendering corruption in some apps when it's enabled. 131 */ 132 if (0 && ib && ctx->Extensions.ARB_draw_elements_base_vertex) { 133 /* If we can just tell the hardware or the TNL to interpret our 134 * indices with a different base, do so. 135 */ 136 tmp_prims = malloc(sizeof(*prim) * nr_prims); 137 138 if (tmp_prims == NULL) { 139 _mesa_error_no_memory(__func__); 140 return; 141 } 142 143 for (i = 0; i < nr_prims; i++) { 144 tmp_prims[i] = prim[i]; 145 tmp_prims[i].basevertex -= min_index; 146 } 147 148 prim = tmp_prims; 149 } else if (ib) { 150 /* Unfortunately need to adjust each index individually. 151 */ 152 GLboolean map_ib = ib->obj->Name && 153 !ib->obj->Mappings[MAP_INTERNAL].Pointer; 154 void *ptr; 155 156 if (map_ib) 157 ctx->Driver.MapBufferRange(ctx, 0, ib->obj->Size, GL_MAP_READ_BIT, 158 ib->obj, MAP_INTERNAL); 159 160 161 ptr = ADD_POINTERS(ib->obj->Mappings[MAP_INTERNAL].Pointer, ib->ptr); 162 163 /* Some users might prefer it if we translated elements to 164 * GLuints here. Others wouldn't... 165 */ 166 switch (ib->index_size) { 167 case 4: 168 tmp_indices = rebase_GLuint( ptr, ib->count, min_index ); 169 break; 170 case 2: 171 tmp_indices = rebase_GLushort( ptr, ib->count, min_index ); 172 break; 173 case 1: 174 tmp_indices = rebase_GLubyte( ptr, ib->count, min_index ); 175 break; 176 } 177 178 if (map_ib) 179 ctx->Driver.UnmapBuffer(ctx, ib->obj, MAP_INTERNAL); 180 181 if (tmp_indices == NULL) { 182 return; 183 } 184 185 tmp_ib.obj = ctx->Shared->NullBufferObj; 186 tmp_ib.ptr = tmp_indices; 187 tmp_ib.count = ib->count; 188 tmp_ib.index_size = ib->index_size; 189 190 ib = &tmp_ib; 191 } 192 else { 193 /* Otherwise the primitives need adjustment. 194 */ 195 tmp_prims = malloc(sizeof(*prim) * nr_prims); 196 197 if (tmp_prims == NULL) { 198 _mesa_error_no_memory(__func__); 199 return; 200 } 201 202 for (i = 0; i < nr_prims; i++) { 203 /* If this fails, it could indicate an application error: 204 */ 205 assert(prim[i].start >= min_index); 206 207 tmp_prims[i] = prim[i]; 208 tmp_prims[i].start -= min_index; 209 } 210 211 prim = tmp_prims; 212 } 213 214 /* Just need to adjust the pointer values on each incoming array. 215 * This works for VBO and non-vbo rendering and shouldn't pesimize 216 * VBO-based upload schemes. However this may still not be a fast 217 * path for hardware tnl for VBO based rendering as most machines 218 * will be happier if you just specify a starting vertex value in 219 * each primitive. 220 * 221 * For drivers with hardware tnl, you only want to do this if you 222 * are forced to, eg non-VBO indexed rendering with start != 0. 223 */ 224 for (i = 0; i < VERT_ATTRIB_MAX; i++) { 225 tmp_attribs[i] = *(arrays[i].VertexAttrib); 226 tmp_arrays[i].BufferBinding = arrays[i].BufferBinding; 227 tmp_arrays[i].VertexAttrib = &tmp_attribs[i]; 228 if (_mesa_is_bufferobj(arrays[i].BufferBinding->BufferObj)) 229 tmp_attribs[i].RelativeOffset += 230 min_index * arrays[i].BufferBinding->Stride; 231 else 232 tmp_attribs[i].Ptr += min_index * arrays[i].BufferBinding->Stride; 233 } 234 235 /* Re-issue the draw call. 236 */ 237 draw( ctx, 238 tmp_arrays, 239 prim, 240 nr_prims, 241 ib, 242 GL_TRUE, 243 0, 244 max_index - min_index, 245 NULL, 0, NULL ); 246 247 free(tmp_indices); 248 249 free(tmp_prims); 250} 251 252 253 254