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