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