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