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