vbo_exec_array.c revision 3bfe312d0136c95b2a8518d65fa32c89ed474987
1/************************************************************************** 2 * 3 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28#include "main/glheader.h" 29#include "main/context.h" 30#include "main/state.h" 31#include "main/api_validate.h" 32#include "main/api_noop.h" 33#include "main/varray.h" 34#include "main/bufferobj.h" 35#include "glapi/dispatch.h" 36 37#include "vbo_context.h" 38 39/** 40 * Compute min and max elements for glDraw[Range]Elements() calls. 41 */ 42static void get_minmax_index( GLuint count, GLuint type, 43 const GLvoid *indices, 44 GLuint *min_index, 45 GLuint *max_index) 46{ 47 GLuint i; 48 49 switch(type) { 50 case GL_UNSIGNED_INT: { 51 const GLuint *ui_indices = (const GLuint *)indices; 52 GLuint max_ui = ui_indices[count-1]; 53 GLuint min_ui = ui_indices[0]; 54 for (i = 0; i < count; i++) { 55 if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; 56 if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; 57 } 58 *min_index = min_ui; 59 *max_index = max_ui; 60 break; 61 } 62 case GL_UNSIGNED_SHORT: { 63 const GLushort *us_indices = (const GLushort *)indices; 64 GLuint max_us = us_indices[count-1]; 65 GLuint min_us = us_indices[0]; 66 for (i = 0; i < count; i++) { 67 if (us_indices[i] > max_us) max_us = us_indices[i]; 68 if (us_indices[i] < min_us) min_us = us_indices[i]; 69 } 70 *min_index = min_us; 71 *max_index = max_us; 72 break; 73 } 74 case GL_UNSIGNED_BYTE: { 75 const GLubyte *ub_indices = (const GLubyte *)indices; 76 GLuint max_ub = ub_indices[count-1]; 77 GLuint min_ub = ub_indices[0]; 78 for (i = 0; i < count; i++) { 79 if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; 80 if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; 81 } 82 *min_index = min_ub; 83 *max_index = max_ub; 84 break; 85 } 86 default: 87 assert(0); 88 break; 89 } 90} 91 92 93/** 94 * Just translate the arrayobj into a sane layout. 95 */ 96static void bind_array_obj( GLcontext *ctx ) 97{ 98 struct vbo_context *vbo = vbo_context(ctx); 99 struct vbo_exec_context *exec = &vbo->exec; 100 struct gl_array_object *arrayObj = ctx->Array.ArrayObj; 101 GLuint i; 102 103 /* TODO: Fix the ArrayObj struct to keep legacy arrays in an array 104 * rather than as individual named arrays. Then this function can 105 * go away. 106 */ 107 exec->array.legacy_array[VERT_ATTRIB_POS] = &arrayObj->Vertex; 108 exec->array.legacy_array[VERT_ATTRIB_WEIGHT] = &arrayObj->Weight; 109 exec->array.legacy_array[VERT_ATTRIB_NORMAL] = &arrayObj->Normal; 110 exec->array.legacy_array[VERT_ATTRIB_COLOR0] = &arrayObj->Color; 111 exec->array.legacy_array[VERT_ATTRIB_COLOR1] = &arrayObj->SecondaryColor; 112 exec->array.legacy_array[VERT_ATTRIB_FOG] = &arrayObj->FogCoord; 113 exec->array.legacy_array[VERT_ATTRIB_COLOR_INDEX] = &arrayObj->Index; 114 if (arrayObj->PointSize.Enabled) { 115 /* this aliases COLOR_INDEX */ 116 exec->array.legacy_array[VERT_ATTRIB_POINT_SIZE] = &arrayObj->PointSize; 117 } 118 exec->array.legacy_array[VERT_ATTRIB_EDGEFLAG] = &arrayObj->EdgeFlag; 119 120 for (i = 0; i < MAX_TEXTURE_COORD_UNITS; i++) 121 exec->array.legacy_array[VERT_ATTRIB_TEX0 + i] = &arrayObj->TexCoord[i]; 122 123 for (i = 0; i < MAX_VERTEX_ATTRIBS; i++) { 124 assert(i < Elements(arrayObj->VertexAttrib)); 125 assert(i < Elements(exec->array.generic_array)); 126 exec->array.generic_array[i] = &arrayObj->VertexAttrib[i]; 127 } 128 129 exec->array.array_obj = arrayObj->Name; 130} 131 132 133static void recalculate_input_bindings( GLcontext *ctx ) 134{ 135 struct vbo_context *vbo = vbo_context(ctx); 136 struct vbo_exec_context *exec = &vbo->exec; 137 const struct gl_client_array **inputs = &exec->array.inputs[0]; 138 GLbitfield const_inputs = 0x0; 139 GLuint i; 140 141 exec->array.program_mode = get_program_mode(ctx); 142 exec->array.enabled_flags = ctx->Array.ArrayObj->_Enabled; 143 144 switch (exec->array.program_mode) { 145 case VP_NONE: 146 /* When no vertex program is active (or the vertex program is generated 147 * from fixed-function state). We put the material values into the 148 * generic slots. This is the only situation where material values 149 * are available as per-vertex attributes. 150 */ 151 for (i = 0; i <= VERT_ATTRIB_TEX7; i++) { 152 if (exec->array.legacy_array[i]->Enabled) 153 inputs[i] = exec->array.legacy_array[i]; 154 else { 155 inputs[i] = &vbo->legacy_currval[i]; 156 const_inputs |= 1 << i; 157 } 158 } 159 160 for (i = 0; i < MAT_ATTRIB_MAX; i++) { 161 inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->mat_currval[i]; 162 const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); 163 } 164 165 /* Could use just about anything, just to fill in the empty 166 * slots: 167 */ 168 for (i = MAT_ATTRIB_MAX; i < VERT_ATTRIB_MAX - VERT_ATTRIB_GENERIC0; i++) { 169 inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->generic_currval[i]; 170 const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); 171 } 172 break; 173 174 case VP_NV: 175 /* NV_vertex_program - attribute arrays alias and override 176 * conventional, legacy arrays. No materials, and the generic 177 * slots are vacant. 178 */ 179 for (i = 0; i <= VERT_ATTRIB_TEX7; i++) { 180 if (exec->array.generic_array[i]->Enabled) 181 inputs[i] = exec->array.generic_array[i]; 182 else if (exec->array.legacy_array[i]->Enabled) 183 inputs[i] = exec->array.legacy_array[i]; 184 else { 185 inputs[i] = &vbo->legacy_currval[i]; 186 const_inputs |= 1 << i; 187 } 188 } 189 190 /* Could use just about anything, just to fill in the empty 191 * slots: 192 */ 193 for (i = VERT_ATTRIB_GENERIC0; i < VERT_ATTRIB_MAX; i++) { 194 inputs[i] = &vbo->generic_currval[i - VERT_ATTRIB_GENERIC0]; 195 const_inputs |= 1 << i; 196 } 197 break; 198 199 case VP_ARB: 200 /* GL_ARB_vertex_program or GLSL vertex shader - Only the generic[0] 201 * attribute array aliases and overrides the legacy position array. 202 * 203 * Otherwise, legacy attributes available in the legacy slots, 204 * generic attributes in the generic slots and materials are not 205 * available as per-vertex attributes. 206 */ 207 if (exec->array.generic_array[0]->Enabled) 208 inputs[0] = exec->array.generic_array[0]; 209 else if (exec->array.legacy_array[0]->Enabled) 210 inputs[0] = exec->array.legacy_array[0]; 211 else { 212 inputs[0] = &vbo->legacy_currval[0]; 213 const_inputs |= 1 << 0; 214 } 215 216 for (i = 1; i <= VERT_ATTRIB_TEX7; i++) { 217 if (exec->array.legacy_array[i]->Enabled) 218 inputs[i] = exec->array.legacy_array[i]; 219 else { 220 inputs[i] = &vbo->legacy_currval[i]; 221 const_inputs |= 1 << i; 222 } 223 } 224 225 for (i = 0; i < MAX_VERTEX_ATTRIBS; i++) { 226 if (exec->array.generic_array[i]->Enabled) 227 inputs[VERT_ATTRIB_GENERIC0 + i] = exec->array.generic_array[i]; 228 else { 229 inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->generic_currval[i]; 230 const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); 231 } 232 233 } 234 break; 235 } 236 237 _mesa_set_varying_vp_inputs( ctx, ~const_inputs ); 238} 239 240 241static void bind_arrays( GLcontext *ctx ) 242{ 243#if 0 244 if (ctx->Array.ArrayObj.Name != exec->array.array_obj) { 245 bind_array_obj(ctx); 246 recalculate_input_bindings(ctx); 247 } 248 else if (exec->array.program_mode != get_program_mode(ctx) || 249 exec->array.enabled_flags != ctx->Array.ArrayObj->_Enabled) { 250 251 recalculate_input_bindings(ctx); 252 } 253#else 254 bind_array_obj(ctx); 255 recalculate_input_bindings(ctx); 256#endif 257} 258 259 260 261/*********************************************************************** 262 * API functions. 263 */ 264 265static void GLAPIENTRY 266vbo_exec_DrawArrays(GLenum mode, GLint start, GLsizei count) 267{ 268 GET_CURRENT_CONTEXT(ctx); 269 struct vbo_context *vbo = vbo_context(ctx); 270 struct vbo_exec_context *exec = &vbo->exec; 271 struct _mesa_prim prim[1]; 272 273 if (!_mesa_validate_DrawArrays( ctx, mode, start, count )) 274 return; 275 276 FLUSH_CURRENT( ctx, 0 ); 277 278 if (ctx->NewState) 279 _mesa_update_state( ctx ); 280 281 if (!vbo_validate_shaders(ctx)) { 282 _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawArrays(bad shader)"); 283 return; 284 } 285 286 bind_arrays( ctx ); 287 288 /* Again... because we may have changed the bitmask of per-vertex varying 289 * attributes. If we regenerate the fixed-function vertex program now 290 * we may be able to prune down the number of vertex attributes which we 291 * need in the shader. 292 */ 293 if (ctx->NewState) 294 _mesa_update_state( ctx ); 295 296 prim[0].begin = 1; 297 prim[0].end = 1; 298 prim[0].weak = 0; 299 prim[0].pad = 0; 300 prim[0].mode = mode; 301 prim[0].start = start; 302 prim[0].count = count; 303 prim[0].indexed = 0; 304 305 vbo->draw_prims( ctx, exec->array.inputs, prim, 1, NULL, 306 start, start + count - 1 ); 307 308#if 0 309 { 310 int i; 311 312 _mesa_printf("vbo_exec_DrawArrays(mode 0x%x, start %d, count %d):\n", 313 mode, start, count); 314 315 for (i = 0; i < 32; i++) { 316 GLuint bufName = exec->array.inputs[i]->BufferObj->Name; 317 GLint stride = exec->array.inputs[i]->Stride; 318 _mesa_printf("attr %2d: size %d stride %d enabled %d " 319 "ptr %p Bufobj %u\n", 320 i, 321 exec->array.inputs[i]->Size, 322 stride, 323 /*exec->array.inputs[i]->Enabled,*/ 324 exec->array.legacy_array[i]->Enabled, 325 exec->array.inputs[i]->Ptr, 326 bufName); 327 328 if (bufName) { 329 struct gl_buffer_object *buf = _mesa_lookup_bufferobj(ctx, bufName); 330 GLubyte *p = ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER_ARB, 331 GL_READ_ONLY_ARB, buf); 332 int offset = (int) exec->array.inputs[i]->Ptr; 333 float *f = (float *) (p + offset); 334 int *k = (int *) f; 335 int i; 336 int n = (count * stride) / 4; 337 if (n > 32) 338 n = 32; 339 _mesa_printf(" Data at offset %d:\n", offset); 340 for (i = 0; i < n; i++) { 341 _mesa_printf(" float[%d] = 0x%08x %f\n", i, k[i], f[i]); 342 } 343 ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, buf); 344 } 345 } 346 } 347#endif 348} 349 350 351static void GLAPIENTRY 352vbo_exec_DrawRangeElements(GLenum mode, 353 GLuint start, GLuint end, 354 GLsizei count, GLenum type, const GLvoid *indices) 355{ 356 GET_CURRENT_CONTEXT(ctx); 357 struct vbo_context *vbo = vbo_context(ctx); 358 struct vbo_exec_context *exec = &vbo->exec; 359 struct _mesa_index_buffer ib; 360 struct _mesa_prim prim[1]; 361 362 if (!_mesa_validate_DrawRangeElements( ctx, mode, start, end, count, type, indices )) 363 return; 364 365 if (end >= ctx->Array.ArrayObj->_MaxElement) { 366 /* the max element is out of bounds of one or more enabled arrays */ 367 _mesa_warning(ctx, "glDraw[Range]Elements(start %u, end %u, count %d, " 368 "type 0x%x) index=%u is out of bounds (max=%u)", 369 start, end, count, type, end, 370 if (0) 371 _mesa_print_arrays(ctx); 372 return; 373 } 374 375 FLUSH_CURRENT( ctx, 0 ); 376 377 if (ctx->NewState) 378 _mesa_update_state( ctx ); 379 380 if (!vbo_validate_shaders(ctx)) { 381 _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawRangeElements(bad shader)"); 382 return; 383 } 384 385 bind_arrays( ctx ); 386 387 if (ctx->NewState) 388 _mesa_update_state( ctx ); 389 390 ib.count = count; 391 ib.type = type; 392 ib.obj = ctx->Array.ElementArrayBufferObj; 393 ib.ptr = indices; 394 395 prim[0].begin = 1; 396 prim[0].end = 1; 397 prim[0].weak = 0; 398 prim[0].pad = 0; 399 prim[0].mode = mode; 400 prim[0].start = 0; 401 prim[0].count = count; 402 prim[0].indexed = 1; 403 404 /* Need to give special consideration to rendering a range of 405 * indices starting somewhere above zero. Typically the 406 * application is issuing multiple DrawRangeElements() to draw 407 * successive primitives layed out linearly in the vertex arrays. 408 * Unless the vertex arrays are all in a VBO (or locked as with 409 * CVA), the OpenGL semantics imply that we need to re-read or 410 * re-upload the vertex data on each draw call. 411 * 412 * In the case of hardware tnl, we want to avoid starting the 413 * upload at zero, as it will mean every draw call uploads an 414 * increasing amount of not-used vertex data. Worse - in the 415 * software tnl module, all those vertices might be transformed and 416 * lit but never rendered. 417 * 418 * If we just upload or transform the vertices in start..end, 419 * however, the indices will be incorrect. 420 * 421 * At this level, we don't know exactly what the requirements of 422 * the backend are going to be, though it will likely boil down to 423 * either: 424 * 425 * 1) Do nothing, everything is in a VBO and is processed once 426 * only. 427 * 428 * 2) Adjust the indices and vertex arrays so that start becomes 429 * zero. 430 * 431 * Rather than doing anything here, I'll provide a helper function 432 * for the latter case elsewhere. 433 */ 434 435 vbo->draw_prims( ctx, exec->array.inputs, prim, 1, &ib, start, end ); 436} 437 438 439static void GLAPIENTRY 440vbo_exec_DrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) 441{ 442 GET_CURRENT_CONTEXT(ctx); 443 GLuint min_index = 0; 444 GLuint max_index = 0; 445 446 if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices )) 447 return; 448 449 if (!vbo_validate_shaders(ctx)) { 450 _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawElements(bad shader)"); 451 return; 452 } 453 454 if (ctx->Array.ElementArrayBufferObj->Name) { 455 const GLvoid *map = ctx->Driver.MapBuffer(ctx, 456 GL_ELEMENT_ARRAY_BUFFER_ARB, 457 GL_READ_ONLY, 458 ctx->Array.ElementArrayBufferObj); 459 460 get_minmax_index(count, type, ADD_POINTERS(map, indices), &min_index, &max_index); 461 462 ctx->Driver.UnmapBuffer(ctx, 463 GL_ELEMENT_ARRAY_BUFFER_ARB, 464 ctx->Array.ElementArrayBufferObj); 465 } 466 else { 467 get_minmax_index(count, type, indices, &min_index, &max_index); 468 } 469 470 vbo_exec_DrawRangeElements(mode, min_index, max_index, count, type, indices); 471} 472 473 474/*********************************************************************** 475 * Initialization 476 */ 477 478void vbo_exec_array_init( struct vbo_exec_context *exec ) 479{ 480#if 1 481 exec->vtxfmt.DrawArrays = vbo_exec_DrawArrays; 482 exec->vtxfmt.DrawElements = vbo_exec_DrawElements; 483 exec->vtxfmt.DrawRangeElements = vbo_exec_DrawRangeElements; 484#else 485 exec->vtxfmt.DrawArrays = _mesa_noop_DrawArrays; 486 exec->vtxfmt.DrawElements = _mesa_noop_DrawElements; 487 exec->vtxfmt.DrawRangeElements = _mesa_noop_DrawRangeElements; 488#endif 489} 490 491 492void vbo_exec_array_destroy( struct vbo_exec_context *exec ) 493{ 494 /* nothing to do */ 495} 496 497 498/* This API entrypoint is not ordinarily used */ 499void GLAPIENTRY 500_mesa_DrawArrays(GLenum mode, GLint first, GLsizei count) 501{ 502 vbo_exec_DrawArrays(mode, first, count); 503} 504 505 506/* This API entrypoint is not ordinarily used */ 507void GLAPIENTRY 508_mesa_DrawElements(GLenum mode, GLsizei count, GLenum type, 509 const GLvoid *indices) 510{ 511 vbo_exec_DrawElements(mode, count, type, indices); 512} 513 514 515/* This API entrypoint is not ordinarily used */ 516void GLAPIENTRY 517_mesa_DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, 518 GLenum type, const GLvoid *indices) 519{ 520 vbo_exec_DrawRangeElements(mode, start, end, count, type, indices); 521} 522