api_validate.c revision 09714c09a40501d82823e42f7461d7b8d7bf11c0
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 25#include "glheader.h" 26#include "api_validate.h" 27#include "bufferobj.h" 28#include "context.h" 29#include "imports.h" 30#include "mfeatures.h" 31#include "mtypes.h" 32#include "enums.h" 33#include "vbo/vbo.h" 34 35 36/** 37 * \return number of bytes in array [count] of type. 38 */ 39static GLsizei 40index_bytes(GLenum type, GLsizei count) 41{ 42 if (type == GL_UNSIGNED_INT) { 43 return count * sizeof(GLuint); 44 } 45 else if (type == GL_UNSIGNED_BYTE) { 46 return count * sizeof(GLubyte); 47 } 48 else { 49 ASSERT(type == GL_UNSIGNED_SHORT); 50 return count * sizeof(GLushort); 51 } 52} 53 54 55/** 56 * Find the max index in the given element/index buffer 57 */ 58GLuint 59_mesa_max_buffer_index(struct gl_context *ctx, GLuint count, GLenum type, 60 const void *indices, 61 struct gl_buffer_object *elementBuf) 62{ 63 const GLubyte *map = NULL; 64 GLuint max = 0; 65 GLuint i; 66 67 if (_mesa_is_bufferobj(elementBuf)) { 68 /* elements are in a user-defined buffer object. need to map it */ 69 map = ctx->Driver.MapBufferRange(ctx, 0, elementBuf->Size, 70 GL_MAP_READ_BIT, elementBuf); 71 /* Actual address is the sum of pointers */ 72 indices = (const GLvoid *) ADD_POINTERS(map, (const GLubyte *) indices); 73 } 74 75 if (type == GL_UNSIGNED_INT) { 76 for (i = 0; i < count; i++) 77 if (((GLuint *) indices)[i] > max) 78 max = ((GLuint *) indices)[i]; 79 } 80 else if (type == GL_UNSIGNED_SHORT) { 81 for (i = 0; i < count; i++) 82 if (((GLushort *) indices)[i] > max) 83 max = ((GLushort *) indices)[i]; 84 } 85 else { 86 ASSERT(type == GL_UNSIGNED_BYTE); 87 for (i = 0; i < count; i++) 88 if (((GLubyte *) indices)[i] > max) 89 max = ((GLubyte *) indices)[i]; 90 } 91 92 if (map) { 93 ctx->Driver.UnmapBuffer(ctx, elementBuf); 94 } 95 96 return max; 97} 98 99 100/** 101 * Check if OK to draw arrays/elements. 102 */ 103static GLboolean 104check_valid_to_render(struct gl_context *ctx, const char *function) 105{ 106 if (!_mesa_valid_to_render(ctx, function)) { 107 return GL_FALSE; 108 } 109 110 switch (ctx->API) { 111#if FEATURE_es2_glsl 112 case API_OPENGLES2: 113 /* For ES2, we can draw if any vertex array is enabled (and we 114 * should always have a vertex program/shader). */ 115 if (ctx->Array.ArrayObj->_Enabled == 0x0 || !ctx->VertexProgram._Current) 116 return GL_FALSE; 117 break; 118#endif 119 120#if FEATURE_ES1 121 case API_OPENGLES: 122 /* For OpenGL ES, only draw if we have vertex positions 123 */ 124 if (!ctx->Array.ArrayObj->VertexAttrib[VERT_ATTRIB_POS].Enabled) 125 return GL_FALSE; 126 break; 127#endif 128 129#if FEATURE_GL 130 case API_OPENGL: 131 case API_OPENGL_CORE: 132 { 133 const struct gl_shader_program *vsProg = 134 ctx->Shader.CurrentVertexProgram; 135 GLboolean haveVertexShader = (vsProg && vsProg->LinkStatus); 136 GLboolean haveVertexProgram = ctx->VertexProgram._Enabled; 137 if (haveVertexShader || haveVertexProgram) { 138 /* Draw regardless of whether or not we have any vertex arrays. 139 * (Ex: could draw a point using a constant vertex pos) 140 */ 141 return GL_TRUE; 142 } 143 else { 144 /* Draw if we have vertex positions (GL_VERTEX_ARRAY or generic 145 * array [0]). 146 */ 147 return (ctx->Array.ArrayObj->VertexAttrib[VERT_ATTRIB_POS].Enabled || 148 ctx->Array.ArrayObj->VertexAttrib[VERT_ATTRIB_GENERIC0].Enabled); 149 } 150 } 151 break; 152#endif 153 154 default: 155 ASSERT_NO_FEATURE(); 156 } 157 158 return GL_TRUE; 159} 160 161 162/** 163 * Do bounds checking on array element indexes. Check that the vertices 164 * pointed to by the indices don't lie outside buffer object bounds. 165 * \return GL_TRUE if OK, GL_FALSE if any indexed vertex goes is out of bounds 166 */ 167static GLboolean 168check_index_bounds(struct gl_context *ctx, GLsizei count, GLenum type, 169 const GLvoid *indices, GLint basevertex) 170{ 171 struct _mesa_prim prim; 172 struct _mesa_index_buffer ib; 173 GLuint min, max; 174 175 /* Only the X Server needs to do this -- otherwise, accessing outside 176 * array/BO bounds allows application termination. 177 */ 178 if (!ctx->Const.CheckArrayBounds) 179 return GL_TRUE; 180 181 memset(&prim, 0, sizeof(prim)); 182 prim.count = count; 183 184 memset(&ib, 0, sizeof(ib)); 185 ib.type = type; 186 ib.ptr = indices; 187 ib.obj = ctx->Array.ArrayObj->ElementArrayBufferObj; 188 189 vbo_get_minmax_indices(ctx, &prim, &ib, &min, &max, 1); 190 191 if ((int)(min + basevertex) < 0 || 192 max + basevertex >= ctx->Array.ArrayObj->_MaxElement) { 193 /* the max element is out of bounds of one or more enabled arrays */ 194 _mesa_warning(ctx, "glDrawElements() index=%u is out of bounds (max=%u)", 195 max, ctx->Array.ArrayObj->_MaxElement); 196 return GL_FALSE; 197 } 198 199 return GL_TRUE; 200} 201 202 203/** 204 * Is 'mode' a valid value for glBegin(), glDrawArrays(), glDrawElements(), 205 * etc? The set of legal values depends on whether geometry shaders/programs 206 * are supported. 207 */ 208GLboolean 209_mesa_valid_prim_mode(struct gl_context *ctx, GLenum mode, const char *name) 210{ 211 if (ctx->Extensions.ARB_geometry_shader4 && 212 mode > GL_TRIANGLE_STRIP_ADJACENCY_ARB) { 213 _mesa_error(ctx, GL_INVALID_ENUM, "%s(mode=%x)", name, mode); 214 return GL_FALSE; 215 } 216 else if (mode > GL_POLYGON) { 217 _mesa_error(ctx, GL_INVALID_ENUM, "%s(mode=%x)", name, mode); 218 return GL_FALSE; 219 } 220 221 /* From the GL_EXT_transform_feedback spec: 222 * 223 * "The error INVALID_OPERATION is generated if Begin, or any command 224 * that performs an explicit Begin, is called when: 225 * 226 * * a geometry shader is not active and <mode> does not match the 227 * allowed begin modes for the current transform feedback state as 228 * given by table X.1. 229 * 230 * * a geometry shader is active and the output primitive type of the 231 * geometry shader does not match the allowed begin modes for the 232 * current transform feedback state as given by table X.1. 233 * 234 */ 235 if (ctx->TransformFeedback.CurrentObject->Active && 236 !ctx->TransformFeedback.CurrentObject->Paused) { 237 GLboolean pass = GL_TRUE; 238 239 switch (mode) { 240 case GL_POINTS: 241 pass = ctx->TransformFeedback.Mode == GL_POINTS; 242 break; 243 case GL_LINES: 244 case GL_LINE_STRIP: 245 case GL_LINE_LOOP: 246 pass = ctx->TransformFeedback.Mode == GL_LINES; 247 break; 248 default: 249 pass = ctx->TransformFeedback.Mode == GL_TRIANGLES; 250 break; 251 } 252 if (!pass) { 253 _mesa_error(ctx, GL_INVALID_OPERATION, 254 "%s(mode=%s vs transform feedback %s)", 255 name, 256 _mesa_lookup_prim_by_nr(mode), 257 _mesa_lookup_prim_by_nr(ctx->TransformFeedback.Mode)); 258 return GL_FALSE; 259 } 260 } 261 262 return GL_TRUE; 263} 264 265 266/** 267 * Error checking for glDrawElements(). Includes parameter checking 268 * and VBO bounds checking. 269 * \return GL_TRUE if OK to render, GL_FALSE if error found 270 */ 271GLboolean 272_mesa_validate_DrawElements(struct gl_context *ctx, 273 GLenum mode, GLsizei count, GLenum type, 274 const GLvoid *indices, GLint basevertex) 275{ 276 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 277 FLUSH_CURRENT(ctx, 0); 278 279 if (count <= 0) { 280 if (count < 0) 281 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawElements(count)" ); 282 return GL_FALSE; 283 } 284 285 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawElements")) { 286 return GL_FALSE; 287 } 288 289 if (type != GL_UNSIGNED_INT && 290 type != GL_UNSIGNED_BYTE && 291 type != GL_UNSIGNED_SHORT) 292 { 293 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)" ); 294 return GL_FALSE; 295 } 296 297 if (!check_valid_to_render(ctx, "glDrawElements")) 298 return GL_FALSE; 299 300 /* Vertex buffer object tests */ 301 if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { 302 /* use indices in the buffer object */ 303 /* make sure count doesn't go outside buffer bounds */ 304 if (index_bytes(type, count) > ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { 305 _mesa_warning(ctx, "glDrawElements index out of buffer bounds"); 306 return GL_FALSE; 307 } 308 } 309 else { 310 /* not using a VBO */ 311 if (!indices) 312 return GL_FALSE; 313 } 314 315 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 316 return GL_FALSE; 317 318 return GL_TRUE; 319} 320 321 322/** 323 * Error checking for glMultiDrawElements(). Includes parameter checking 324 * and VBO bounds checking. 325 * \return GL_TRUE if OK to render, GL_FALSE if error found 326 */ 327GLboolean 328_mesa_validate_MultiDrawElements(struct gl_context *ctx, 329 GLenum mode, const GLsizei *count, 330 GLenum type, const GLvoid * const *indices, 331 GLuint primcount, const GLint *basevertex) 332{ 333 unsigned i; 334 335 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 336 FLUSH_CURRENT(ctx, 0); 337 338 for (i = 0; i < primcount; i++) { 339 if (count[i] <= 0) { 340 if (count[i] < 0) 341 _mesa_error(ctx, GL_INVALID_VALUE, 342 "glMultiDrawElements(count)" ); 343 return GL_FALSE; 344 } 345 } 346 347 if (!_mesa_valid_prim_mode(ctx, mode, "glMultiDrawElements")) { 348 return GL_FALSE; 349 } 350 351 if (type != GL_UNSIGNED_INT && 352 type != GL_UNSIGNED_BYTE && 353 type != GL_UNSIGNED_SHORT) 354 { 355 _mesa_error(ctx, GL_INVALID_ENUM, "glMultiDrawElements(type)" ); 356 return GL_FALSE; 357 } 358 359 if (!check_valid_to_render(ctx, "glMultiDrawElements")) 360 return GL_FALSE; 361 362 /* Vertex buffer object tests */ 363 if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { 364 /* use indices in the buffer object */ 365 /* make sure count doesn't go outside buffer bounds */ 366 for (i = 0; i < primcount; i++) { 367 if (index_bytes(type, count[i]) > 368 ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { 369 _mesa_warning(ctx, 370 "glMultiDrawElements index out of buffer bounds"); 371 return GL_FALSE; 372 } 373 } 374 } 375 else { 376 /* not using a VBO */ 377 for (i = 0; i < primcount; i++) { 378 if (!indices[i]) 379 return GL_FALSE; 380 } 381 } 382 383 for (i = 0; i < primcount; i++) { 384 if (!check_index_bounds(ctx, count[i], type, indices[i], 385 basevertex ? basevertex[i] : 0)) 386 return GL_FALSE; 387 } 388 389 return GL_TRUE; 390} 391 392 393/** 394 * Error checking for glDrawRangeElements(). Includes parameter checking 395 * and VBO bounds checking. 396 * \return GL_TRUE if OK to render, GL_FALSE if error found 397 */ 398GLboolean 399_mesa_validate_DrawRangeElements(struct gl_context *ctx, GLenum mode, 400 GLuint start, GLuint end, 401 GLsizei count, GLenum type, 402 const GLvoid *indices, GLint basevertex) 403{ 404 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 405 FLUSH_CURRENT(ctx, 0); 406 407 if (count <= 0) { 408 if (count < 0) 409 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(count)" ); 410 return GL_FALSE; 411 } 412 413 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawRangeElements")) { 414 return GL_FALSE; 415 } 416 417 if (end < start) { 418 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(end<start)"); 419 return GL_FALSE; 420 } 421 422 if (type != GL_UNSIGNED_INT && 423 type != GL_UNSIGNED_BYTE && 424 type != GL_UNSIGNED_SHORT) { 425 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(type)" ); 426 return GL_FALSE; 427 } 428 429 if (!check_valid_to_render(ctx, "glDrawRangeElements")) 430 return GL_FALSE; 431 432 /* Vertex buffer object tests */ 433 if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { 434 /* use indices in the buffer object */ 435 /* make sure count doesn't go outside buffer bounds */ 436 if (index_bytes(type, count) > ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { 437 _mesa_warning(ctx, "glDrawRangeElements index out of buffer bounds"); 438 return GL_FALSE; 439 } 440 } 441 else { 442 /* not using a VBO */ 443 if (!indices) 444 return GL_FALSE; 445 } 446 447 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 448 return GL_FALSE; 449 450 return GL_TRUE; 451} 452 453 454/** 455 * Called from the tnl module to error check the function parameters and 456 * verify that we really can draw something. 457 * \return GL_TRUE if OK to render, GL_FALSE if error found 458 */ 459GLboolean 460_mesa_validate_DrawArrays(struct gl_context *ctx, 461 GLenum mode, GLint start, GLsizei count) 462{ 463 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 464 FLUSH_CURRENT(ctx, 0); 465 466 if (count <= 0) { 467 if (count < 0) 468 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawArrays(count)" ); 469 return GL_FALSE; 470 } 471 472 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawArrays")) { 473 return GL_FALSE; 474 } 475 476 if (!check_valid_to_render(ctx, "glDrawArrays")) 477 return GL_FALSE; 478 479 if (ctx->Const.CheckArrayBounds) { 480 if (start + count > (GLint) ctx->Array.ArrayObj->_MaxElement) 481 return GL_FALSE; 482 } 483 484 return GL_TRUE; 485} 486 487 488GLboolean 489_mesa_validate_DrawArraysInstanced(struct gl_context *ctx, GLenum mode, GLint first, 490 GLsizei count, GLsizei numInstances) 491{ 492 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 493 FLUSH_CURRENT(ctx, 0); 494 495 if (count <= 0) { 496 if (count < 0) 497 _mesa_error(ctx, GL_INVALID_VALUE, 498 "glDrawArraysInstanced(count=%d)", count); 499 return GL_FALSE; 500 } 501 502 if (first < 0) { 503 _mesa_error(ctx, GL_INVALID_VALUE, 504 "glDrawArraysInstanced(start=%d)", first); 505 return GL_FALSE; 506 } 507 508 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawArraysInstanced")) { 509 return GL_FALSE; 510 } 511 512 if (numInstances <= 0) { 513 if (numInstances < 0) 514 _mesa_error(ctx, GL_INVALID_VALUE, 515 "glDrawArraysInstanced(numInstances=%d)", numInstances); 516 return GL_FALSE; 517 } 518 519 if (!check_valid_to_render(ctx, "glDrawArraysInstanced(invalid to render)")) 520 return GL_FALSE; 521 522 if (ctx->Const.CheckArrayBounds) { 523 if (first + count > (GLint) ctx->Array.ArrayObj->_MaxElement) 524 return GL_FALSE; 525 } 526 527 return GL_TRUE; 528} 529 530 531GLboolean 532_mesa_validate_DrawElementsInstanced(struct gl_context *ctx, 533 GLenum mode, GLsizei count, GLenum type, 534 const GLvoid *indices, GLsizei numInstances, 535 GLint basevertex) 536{ 537 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 538 FLUSH_CURRENT(ctx, 0); 539 540 if (count <= 0) { 541 if (count < 0) 542 _mesa_error(ctx, GL_INVALID_VALUE, 543 "glDrawElementsInstanced(count=%d)", count); 544 return GL_FALSE; 545 } 546 547 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawElementsInstanced")) { 548 return GL_FALSE; 549 } 550 551 if (type != GL_UNSIGNED_INT && 552 type != GL_UNSIGNED_BYTE && 553 type != GL_UNSIGNED_SHORT) { 554 _mesa_error(ctx, GL_INVALID_ENUM, 555 "glDrawElementsInstanced(type=0x%x)", type); 556 return GL_FALSE; 557 } 558 559 if (numInstances <= 0) { 560 if (numInstances < 0) 561 _mesa_error(ctx, GL_INVALID_VALUE, 562 "glDrawElementsInstanced(numInstances=%d)", numInstances); 563 return GL_FALSE; 564 } 565 566 if (!check_valid_to_render(ctx, "glDrawElementsInstanced")) 567 return GL_FALSE; 568 569 /* Vertex buffer object tests */ 570 if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { 571 /* use indices in the buffer object */ 572 /* make sure count doesn't go outside buffer bounds */ 573 if (index_bytes(type, count) > ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { 574 _mesa_warning(ctx, 575 "glDrawElementsInstanced index out of buffer bounds"); 576 return GL_FALSE; 577 } 578 } 579 else { 580 /* not using a VBO */ 581 if (!indices) 582 return GL_FALSE; 583 } 584 585 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 586 return GL_FALSE; 587 588 return GL_TRUE; 589} 590 591 592#if FEATURE_EXT_transform_feedback 593 594GLboolean 595_mesa_validate_DrawTransformFeedback(struct gl_context *ctx, 596 GLenum mode, 597 struct gl_transform_feedback_object *obj, 598 GLuint stream, 599 GLsizei numInstances) 600{ 601 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 602 FLUSH_CURRENT(ctx, 0); 603 604 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawTransformFeedback*(mode)")) { 605 return GL_FALSE; 606 } 607 608 if (!obj) { 609 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedback*(name)"); 610 return GL_FALSE; 611 } 612 613 if (!obj->EndedAnytime) { 614 _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawTransformFeedback*"); 615 return GL_FALSE; 616 } 617 618 if (stream >= ctx->Const.MaxVertexStreams) { 619 _mesa_error(ctx, GL_INVALID_VALUE, 620 "glDrawTransformFeedbackStream*(index>=MaxVertexStream)"); 621 return GL_FALSE; 622 } 623 624 if (numInstances <= 0) { 625 if (numInstances < 0) 626 _mesa_error(ctx, GL_INVALID_VALUE, 627 "glDrawTransformFeedback*Instanced(numInstances=%d)", 628 numInstances); 629 return GL_FALSE; 630 } 631 632 if (!check_valid_to_render(ctx, "glDrawTransformFeedback*")) { 633 return GL_FALSE; 634 } 635 636 return GL_TRUE; 637} 638 639#endif 640