t_draw.c revision c1f859d4
1/* 2 * Mesa 3-D graphics library 3 * Version: 7.1 4 * 5 * Copyright (C) 1999-2007 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 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Keith Whitwell <keith@tungstengraphics.com> 26 */ 27 28#include "main/glheader.h" 29#include "main/context.h" 30#include "main/imports.h" 31#include "main/state.h" 32#include "main/mtypes.h" 33#include "main/macros.h" 34#include "main/enums.h" 35 36#include "t_context.h" 37#include "t_pipeline.h" 38#include "t_vp_build.h" 39#include "t_vertex.h" 40#include "tnl.h" 41 42 43 44static GLubyte *get_space(GLcontext *ctx, GLuint bytes) 45{ 46 TNLcontext *tnl = TNL_CONTEXT(ctx); 47 GLubyte *space = _mesa_malloc(bytes); 48 49 tnl->block[tnl->nr_blocks++] = space; 50 return space; 51} 52 53 54static void free_space(GLcontext *ctx) 55{ 56 TNLcontext *tnl = TNL_CONTEXT(ctx); 57 GLuint i; 58 for (i = 0; i < tnl->nr_blocks; i++) 59 _mesa_free(tnl->block[i]); 60 tnl->nr_blocks = 0; 61} 62 63 64/* Convert the incoming array to GLfloats. Understands the 65 * array->Normalized flag and selects the correct conversion method. 66 */ 67#define CONVERT( TYPE, MACRO ) do { \ 68 GLuint i, j; \ 69 if (input->Normalized) { \ 70 for (i = 0; i < count; i++) { \ 71 const TYPE *in = (TYPE *)ptr; \ 72 for (j = 0; j < sz; j++) { \ 73 *fptr++ = MACRO(*in); \ 74 in++; \ 75 } \ 76 ptr += input->StrideB; \ 77 } \ 78 } else { \ 79 for (i = 0; i < count; i++) { \ 80 const TYPE *in = (TYPE *)ptr; \ 81 for (j = 0; j < sz; j++) { \ 82 *fptr++ = (GLfloat)(*in); \ 83 in++; \ 84 } \ 85 ptr += input->StrideB; \ 86 } \ 87 } \ 88} while (0) 89 90 91 92/* Adjust pointer to point at first requested element, convert to 93 * floating point, populate VB->AttribPtr[]. 94 */ 95static void _tnl_import_array( GLcontext *ctx, 96 GLuint attrib, 97 GLuint count, 98 const struct gl_client_array *input, 99 const GLubyte *ptr ) 100{ 101 TNLcontext *tnl = TNL_CONTEXT(ctx); 102 struct vertex_buffer *VB = &tnl->vb; 103 GLuint stride = input->StrideB; 104 105 if (input->Type != GL_FLOAT) { 106 const GLuint sz = input->Size; 107 GLubyte *buf = get_space(ctx, count * sz * sizeof(GLfloat)); 108 GLfloat *fptr = (GLfloat *)buf; 109 110 switch (input->Type) { 111 case GL_BYTE: 112 CONVERT(GLbyte, BYTE_TO_FLOAT); 113 break; 114 case GL_UNSIGNED_BYTE: 115 CONVERT(GLubyte, UBYTE_TO_FLOAT); 116 break; 117 case GL_SHORT: 118 CONVERT(GLshort, SHORT_TO_FLOAT); 119 break; 120 case GL_UNSIGNED_SHORT: 121 CONVERT(GLushort, USHORT_TO_FLOAT); 122 break; 123 case GL_INT: 124 CONVERT(GLint, INT_TO_FLOAT); 125 break; 126 case GL_UNSIGNED_INT: 127 CONVERT(GLuint, UINT_TO_FLOAT); 128 break; 129 case GL_DOUBLE: 130 CONVERT(GLdouble, (GLfloat)); 131 break; 132 default: 133 assert(0); 134 break; 135 } 136 137 ptr = buf; 138 stride = sz * sizeof(GLfloat); 139 } 140 141 VB->AttribPtr[attrib] = &tnl->tmp_inputs[attrib]; 142 VB->AttribPtr[attrib]->data = (GLfloat (*)[4])ptr; 143 VB->AttribPtr[attrib]->start = (GLfloat *)ptr; 144 VB->AttribPtr[attrib]->count = count; 145 VB->AttribPtr[attrib]->stride = stride; 146 VB->AttribPtr[attrib]->size = input->Size; 147 148 /* This should die, but so should the whole GLvector4f concept: 149 */ 150 VB->AttribPtr[attrib]->flags = (((1<<input->Size)-1) | 151 VEC_NOT_WRITEABLE | 152 (stride == 4*sizeof(GLfloat) ? 0 : VEC_BAD_STRIDE)); 153 154 VB->AttribPtr[attrib]->storage = NULL; 155} 156 157#define CLIPVERTS ((6 + MAX_CLIP_PLANES) * 2) 158 159 160static GLboolean *_tnl_import_edgeflag( GLcontext *ctx, 161 const GLvector4f *input, 162 GLuint count) 163{ 164 const GLubyte *ptr = (const GLubyte *)input->data; 165 const GLuint stride = input->stride; 166 GLboolean *space = (GLboolean *)get_space(ctx, count + CLIPVERTS); 167 GLboolean *bptr = space; 168 GLuint i; 169 170 for (i = 0; i < count; i++) { 171 *bptr++ = ((GLfloat *)ptr)[0] == 1.0; 172 ptr += stride; 173 } 174 175 return space; 176} 177 178 179static void bind_inputs( GLcontext *ctx, 180 const struct gl_client_array *inputs[], 181 GLint count, 182 struct gl_buffer_object **bo, 183 GLuint *nr_bo ) 184{ 185 TNLcontext *tnl = TNL_CONTEXT(ctx); 186 struct vertex_buffer *VB = &tnl->vb; 187 GLuint i; 188 189 /* Map all the VBOs 190 */ 191 for (i = 0; i < VERT_ATTRIB_MAX; i++) { 192 const void *ptr; 193 194 if (inputs[i]->BufferObj->Name) { 195 if (!inputs[i]->BufferObj->Pointer) { 196 bo[*nr_bo] = inputs[i]->BufferObj; 197 (*nr_bo)++; 198 ctx->Driver.MapBuffer(ctx, 199 GL_ARRAY_BUFFER, 200 GL_READ_ONLY_ARB, 201 inputs[i]->BufferObj); 202 203 assert(inputs[i]->BufferObj->Pointer); 204 } 205 206 ptr = ADD_POINTERS(inputs[i]->BufferObj->Pointer, 207 inputs[i]->Ptr); 208 } 209 else 210 ptr = inputs[i]->Ptr; 211 212 /* Just make sure the array is floating point, otherwise convert to 213 * temporary storage. 214 * 215 * XXX: remove the GLvector4f type at some stage and just use 216 * client arrays. 217 */ 218 _tnl_import_array(ctx, i, count, inputs[i], ptr); 219 } 220 221 /* We process only the vertices between min & max index: 222 */ 223 VB->Count = count; 224 225 226 /* Legacy pointers -- remove one day. 227 */ 228 VB->ObjPtr = VB->AttribPtr[_TNL_ATTRIB_POS]; 229 VB->NormalPtr = VB->AttribPtr[_TNL_ATTRIB_NORMAL]; 230 VB->ColorPtr[0] = VB->AttribPtr[_TNL_ATTRIB_COLOR0]; 231 VB->ColorPtr[1] = NULL; 232 VB->IndexPtr[0] = VB->AttribPtr[_TNL_ATTRIB_COLOR_INDEX]; 233 VB->IndexPtr[1] = NULL; 234 VB->SecondaryColorPtr[0] = VB->AttribPtr[_TNL_ATTRIB_COLOR1]; 235 VB->SecondaryColorPtr[1] = NULL; 236 VB->FogCoordPtr = VB->AttribPtr[_TNL_ATTRIB_FOG]; 237 238 for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) { 239 VB->TexCoordPtr[i] = VB->AttribPtr[_TNL_ATTRIB_TEX0 + i]; 240 } 241 242 /* Clipping and drawing code still requires this to be a packed 243 * array of ubytes which can be written into. TODO: Fix and 244 * remove. 245 */ 246 if (ctx->Polygon.FrontMode != GL_FILL || 247 ctx->Polygon.BackMode != GL_FILL) 248 { 249 VB->EdgeFlag = _tnl_import_edgeflag( ctx, 250 VB->AttribPtr[_TNL_ATTRIB_EDGEFLAG], 251 VB->Count ); 252 } 253 else { 254 /* the data previously pointed to by EdgeFlag may have been freed */ 255 VB->EdgeFlag = NULL; 256 } 257} 258 259 260/* Translate indices to GLuints and store in VB->Elts. 261 */ 262static void bind_indices( GLcontext *ctx, 263 const struct _mesa_index_buffer *ib, 264 struct gl_buffer_object **bo, 265 GLuint *nr_bo) 266{ 267 TNLcontext *tnl = TNL_CONTEXT(ctx); 268 struct vertex_buffer *VB = &tnl->vb; 269 GLuint i; 270 void *ptr; 271 272 if (!ib) { 273 VB->Elts = NULL; 274 return; 275 } 276 277 if (ib->obj->Name && !ib->obj->Pointer) { 278 bo[*nr_bo] = ib->obj; 279 (*nr_bo)++; 280 ctx->Driver.MapBuffer(ctx, 281 GL_ELEMENT_ARRAY_BUFFER, 282 GL_READ_ONLY_ARB, 283 ib->obj); 284 285 assert(ib->obj->Pointer); 286 } 287 288 ptr = ADD_POINTERS(ib->obj->Pointer, ib->ptr); 289 290 if (ib->type == GL_UNSIGNED_INT) { 291 VB->Elts = (GLuint *) ptr; 292 } 293 else { 294 GLuint *elts = (GLuint *)get_space(ctx, ib->count * sizeof(GLuint)); 295 VB->Elts = elts; 296 297 if (ib->type == GL_UNSIGNED_SHORT) { 298 const GLushort *in = (GLushort *)ptr; 299 for (i = 0; i < ib->count; i++) 300 *elts++ = (GLuint)(*in++); 301 } 302 else { 303 const GLubyte *in = (GLubyte *)ptr; 304 for (i = 0; i < ib->count; i++) 305 *elts++ = (GLuint)(*in++); 306 } 307 } 308} 309 310static void bind_prims( GLcontext *ctx, 311 const struct _mesa_prim *prim, 312 GLuint nr_prims ) 313{ 314 TNLcontext *tnl = TNL_CONTEXT(ctx); 315 struct vertex_buffer *VB = &tnl->vb; 316 317 VB->Primitive = prim; 318 VB->PrimitiveCount = nr_prims; 319} 320 321static void unmap_vbos( GLcontext *ctx, 322 struct gl_buffer_object **bo, 323 GLuint nr_bo ) 324{ 325 GLuint i; 326 for (i = 0; i < nr_bo; i++) { 327 ctx->Driver.UnmapBuffer(ctx, 328 0, /* target -- I don't see why this would be needed */ 329 bo[i]); 330 } 331} 332 333 334 335/* This is the main entrypoint into the slimmed-down software tnl 336 * module. In a regular swtnl driver, this can be plugged straight 337 * into the vbo->Driver.DrawPrims() callback. 338 */ 339void _tnl_draw_prims( GLcontext *ctx, 340 const struct gl_client_array *arrays[], 341 const struct _mesa_prim *prim, 342 GLuint nr_prims, 343 const struct _mesa_index_buffer *ib, 344 GLuint min_index, 345 GLuint max_index) 346{ 347 TNLcontext *tnl = TNL_CONTEXT(ctx); 348 const GLuint TEST_SPLIT = 0; 349 const GLint max = TEST_SPLIT ? 8 : tnl->vb.Size - MAX_CLIPPED_VERTICES; 350 351 if (0) 352 { 353 GLuint i; 354 _mesa_printf("%s %d..%d\n", __FUNCTION__, min_index, max_index); 355 for (i = 0; i < nr_prims; i++) 356 _mesa_printf("prim %d: %s start %d count %d\n", i, 357 _mesa_lookup_enum_by_nr(prim[i].mode), 358 prim[i].start, 359 prim[i].count); 360 } 361 362 if (min_index) { 363 /* We always translate away calls with min_index != 0. 364 */ 365 vbo_rebase_prims( ctx, arrays, prim, nr_prims, ib, 366 min_index, max_index, 367 _tnl_draw_prims ); 368 return; 369 } 370 else if (max_index > max) { 371 /* The software TNL pipeline has a fixed amount of storage for 372 * vertices and it is necessary to split incoming drawing commands 373 * if they exceed that limit. 374 */ 375 struct split_limits limits; 376 limits.max_verts = max; 377 limits.max_vb_size = ~0; 378 limits.max_indices = ~0; 379 380 /* This will split the buffers one way or another and 381 * recursively call back into this function. 382 */ 383 vbo_split_prims( ctx, arrays, prim, nr_prims, ib, 384 0, max_index, 385 _tnl_draw_prims, 386 &limits ); 387 } 388 else { 389 /* May need to map a vertex buffer object for every attribute plus 390 * one for the index buffer. 391 */ 392 struct gl_buffer_object *bo[VERT_ATTRIB_MAX + 1]; 393 GLuint nr_bo = 0; 394 395 /* Binding inputs may imply mapping some vertex buffer objects. 396 * They will need to be unmapped below. 397 */ 398 bind_inputs(ctx, arrays, max_index+1, bo, &nr_bo); 399 bind_indices(ctx, ib, bo, &nr_bo); 400 bind_prims(ctx, prim, nr_prims ); 401 402 TNL_CONTEXT(ctx)->Driver.RunPipeline(ctx); 403 404 unmap_vbos(ctx, bo, nr_bo); 405 free_space(ctx); 406 } 407} 408 409