api_validate.c revision 62b971673950148eb949ba23d7fdc47debea16f0
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 { 132 const struct gl_shader_program *vsProg = 133 ctx->Shader.CurrentVertexProgram; 134 GLboolean haveVertexShader = (vsProg && vsProg->LinkStatus); 135 GLboolean haveVertexProgram = ctx->VertexProgram._Enabled; 136 if (haveVertexShader || haveVertexProgram) { 137 /* Draw regardless of whether or not we have any vertex arrays. 138 * (Ex: could draw a point using a constant vertex pos) 139 */ 140 return GL_TRUE; 141 } 142 else { 143 /* Draw if we have vertex positions (GL_VERTEX_ARRAY or generic 144 * array [0]). 145 */ 146 return (ctx->Array.ArrayObj->VertexAttrib[VERT_ATTRIB_POS].Enabled || 147 ctx->Array.ArrayObj->VertexAttrib[VERT_ATTRIB_GENERIC0].Enabled); 148 } 149 } 150 break; 151#endif 152 153 default: 154 ASSERT_NO_FEATURE(); 155 } 156 157 return GL_TRUE; 158} 159 160 161/** 162 * Do bounds checking on array element indexes. Check that the vertices 163 * pointed to by the indices don't lie outside buffer object bounds. 164 * \return GL_TRUE if OK, GL_FALSE if any indexed vertex goes is out of bounds 165 */ 166static GLboolean 167check_index_bounds(struct gl_context *ctx, GLsizei count, GLenum type, 168 const GLvoid *indices, GLint basevertex) 169{ 170 struct _mesa_prim prim; 171 struct _mesa_index_buffer ib; 172 GLuint min, max; 173 174 /* Only the X Server needs to do this -- otherwise, accessing outside 175 * array/BO bounds allows application termination. 176 */ 177 if (!ctx->Const.CheckArrayBounds) 178 return GL_TRUE; 179 180 memset(&prim, 0, sizeof(prim)); 181 prim.count = count; 182 183 memset(&ib, 0, sizeof(ib)); 184 ib.type = type; 185 ib.ptr = indices; 186 ib.obj = ctx->Array.ArrayObj->ElementArrayBufferObj; 187 188 vbo_get_minmax_indices(ctx, &prim, &ib, &min, &max, 1); 189 190 if ((int)(min + basevertex) < 0 || 191 max + basevertex >= ctx->Array.ArrayObj->_MaxElement) { 192 /* the max element is out of bounds of one or more enabled arrays */ 193 _mesa_warning(ctx, "glDrawElements() index=%u is out of bounds (max=%u)", 194 max, ctx->Array.ArrayObj->_MaxElement); 195 return GL_FALSE; 196 } 197 198 return GL_TRUE; 199} 200 201 202/** 203 * Is 'mode' a valid value for glBegin(), glDrawArrays(), glDrawElements(), 204 * etc? The set of legal values depends on whether geometry shaders/programs 205 * are supported. 206 */ 207GLboolean 208_mesa_valid_prim_mode(struct gl_context *ctx, GLenum mode, const char *name) 209{ 210 if (ctx->Extensions.ARB_geometry_shader4 && 211 mode > GL_TRIANGLE_STRIP_ADJACENCY_ARB) { 212 _mesa_error(ctx, GL_INVALID_ENUM, "%s(mode=%x)", name, mode); 213 return GL_FALSE; 214 } 215 else if (mode > GL_POLYGON) { 216 _mesa_error(ctx, GL_INVALID_ENUM, "%s(mode=%x)", name, mode); 217 return GL_FALSE; 218 } 219 220 /* From the GL_EXT_transform_feedback spec: 221 * 222 * "The error INVALID_OPERATION is generated if Begin, or any command 223 * that performs an explicit Begin, is called when: 224 * 225 * * a geometry shader is not active and <mode> does not match the 226 * allowed begin modes for the current transform feedback state as 227 * given by table X.1. 228 * 229 * * a geometry shader is active and the output primitive type of the 230 * geometry shader does not match the allowed begin modes for the 231 * current transform feedback state as given by table X.1. 232 * 233 */ 234 if (ctx->TransformFeedback.CurrentObject->Active && 235 !ctx->TransformFeedback.CurrentObject->Paused) { 236 GLboolean pass = GL_TRUE; 237 238 switch (mode) { 239 case GL_POINTS: 240 pass = ctx->TransformFeedback.Mode == GL_POINTS; 241 break; 242 case GL_LINES: 243 case GL_LINE_STRIP: 244 case GL_LINE_LOOP: 245 pass = ctx->TransformFeedback.Mode == GL_LINES; 246 break; 247 default: 248 pass = ctx->TransformFeedback.Mode == GL_TRIANGLES; 249 break; 250 } 251 if (!pass) { 252 _mesa_error(ctx, GL_INVALID_OPERATION, 253 "%s(mode=%s vs transform feedback %s)", 254 name, 255 _mesa_lookup_prim_by_nr(mode), 256 _mesa_lookup_prim_by_nr(ctx->TransformFeedback.Mode)); 257 return GL_FALSE; 258 } 259 } 260 261 return GL_TRUE; 262} 263 264 265/** 266 * Error checking for glDrawElements(). Includes parameter checking 267 * and VBO bounds checking. 268 * \return GL_TRUE if OK to render, GL_FALSE if error found 269 */ 270GLboolean 271_mesa_validate_DrawElements(struct gl_context *ctx, 272 GLenum mode, GLsizei count, GLenum type, 273 const GLvoid *indices, GLint basevertex) 274{ 275 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, GL_FALSE); 276 277 if (count <= 0) { 278 if (count < 0) 279 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawElements(count)" ); 280 return GL_FALSE; 281 } 282 283 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawElements")) { 284 return GL_FALSE; 285 } 286 287 if (type != GL_UNSIGNED_INT && 288 type != GL_UNSIGNED_BYTE && 289 type != GL_UNSIGNED_SHORT) 290 { 291 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)" ); 292 return GL_FALSE; 293 } 294 295 if (!check_valid_to_render(ctx, "glDrawElements")) 296 return GL_FALSE; 297 298 /* Vertex buffer object tests */ 299 if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { 300 /* use indices in the buffer object */ 301 /* make sure count doesn't go outside buffer bounds */ 302 if (index_bytes(type, count) > ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { 303 _mesa_warning(ctx, "glDrawElements index out of buffer bounds"); 304 return GL_FALSE; 305 } 306 } 307 else { 308 /* not using a VBO */ 309 if (!indices) 310 return GL_FALSE; 311 } 312 313 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 314 return GL_FALSE; 315 316 return GL_TRUE; 317} 318 319 320/** 321 * Error checking for glDrawRangeElements(). Includes parameter checking 322 * and VBO bounds checking. 323 * \return GL_TRUE if OK to render, GL_FALSE if error found 324 */ 325GLboolean 326_mesa_validate_DrawRangeElements(struct gl_context *ctx, GLenum mode, 327 GLuint start, GLuint end, 328 GLsizei count, GLenum type, 329 const GLvoid *indices, GLint basevertex) 330{ 331 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, GL_FALSE); 332 333 if (count <= 0) { 334 if (count < 0) 335 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(count)" ); 336 return GL_FALSE; 337 } 338 339 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawRangeElements")) { 340 return GL_FALSE; 341 } 342 343 if (end < start) { 344 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(end<start)"); 345 return GL_FALSE; 346 } 347 348 if (type != GL_UNSIGNED_INT && 349 type != GL_UNSIGNED_BYTE && 350 type != GL_UNSIGNED_SHORT) { 351 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(type)" ); 352 return GL_FALSE; 353 } 354 355 if (!check_valid_to_render(ctx, "glDrawRangeElements")) 356 return GL_FALSE; 357 358 /* Vertex buffer object tests */ 359 if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { 360 /* use indices in the buffer object */ 361 /* make sure count doesn't go outside buffer bounds */ 362 if (index_bytes(type, count) > ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { 363 _mesa_warning(ctx, "glDrawRangeElements index out of buffer bounds"); 364 return GL_FALSE; 365 } 366 } 367 else { 368 /* not using a VBO */ 369 if (!indices) 370 return GL_FALSE; 371 } 372 373 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 374 return GL_FALSE; 375 376 return GL_TRUE; 377} 378 379 380/** 381 * Called from the tnl module to error check the function parameters and 382 * verify that we really can draw something. 383 * \return GL_TRUE if OK to render, GL_FALSE if error found 384 */ 385GLboolean 386_mesa_validate_DrawArrays(struct gl_context *ctx, 387 GLenum mode, GLint start, GLsizei count) 388{ 389 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, GL_FALSE); 390 391 if (count <= 0) { 392 if (count < 0) 393 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawArrays(count)" ); 394 return GL_FALSE; 395 } 396 397 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawArrays")) { 398 return GL_FALSE; 399 } 400 401 if (!check_valid_to_render(ctx, "glDrawArrays")) 402 return GL_FALSE; 403 404 if (ctx->Const.CheckArrayBounds) { 405 if (start + count > (GLint) ctx->Array.ArrayObj->_MaxElement) 406 return GL_FALSE; 407 } 408 409 return GL_TRUE; 410} 411 412 413GLboolean 414_mesa_validate_DrawArraysInstanced(struct gl_context *ctx, GLenum mode, GLint first, 415 GLsizei count, GLsizei numInstances) 416{ 417 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, GL_FALSE); 418 419 if (count <= 0) { 420 if (count < 0) 421 _mesa_error(ctx, GL_INVALID_VALUE, 422 "glDrawArraysInstanced(count=%d)", count); 423 return GL_FALSE; 424 } 425 426 if (first < 0) { 427 _mesa_error(ctx, GL_INVALID_VALUE, 428 "glDrawArraysInstanced(start=%d)", first); 429 return GL_FALSE; 430 } 431 432 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawArraysInstanced")) { 433 return GL_FALSE; 434 } 435 436 if (numInstances <= 0) { 437 if (numInstances < 0) 438 _mesa_error(ctx, GL_INVALID_VALUE, 439 "glDrawArraysInstanced(numInstances=%d)", numInstances); 440 return GL_FALSE; 441 } 442 443 if (!check_valid_to_render(ctx, "glDrawArraysInstanced(invalid to render)")) 444 return GL_FALSE; 445 446 if (ctx->Const.CheckArrayBounds) { 447 if (first + count > (GLint) ctx->Array.ArrayObj->_MaxElement) 448 return GL_FALSE; 449 } 450 451 return GL_TRUE; 452} 453 454 455GLboolean 456_mesa_validate_DrawElementsInstanced(struct gl_context *ctx, 457 GLenum mode, GLsizei count, GLenum type, 458 const GLvoid *indices, GLsizei numInstances, 459 GLint basevertex) 460{ 461 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, GL_FALSE); 462 463 if (count <= 0) { 464 if (count < 0) 465 _mesa_error(ctx, GL_INVALID_VALUE, 466 "glDrawElementsInstanced(count=%d)", count); 467 return GL_FALSE; 468 } 469 470 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawElementsInstanced")) { 471 return GL_FALSE; 472 } 473 474 if (type != GL_UNSIGNED_INT && 475 type != GL_UNSIGNED_BYTE && 476 type != GL_UNSIGNED_SHORT) { 477 _mesa_error(ctx, GL_INVALID_ENUM, 478 "glDrawElementsInstanced(type=0x%x)", type); 479 return GL_FALSE; 480 } 481 482 if (numInstances <= 0) { 483 if (numInstances < 0) 484 _mesa_error(ctx, GL_INVALID_VALUE, 485 "glDrawElementsInstanced(numInstances=%d)", numInstances); 486 return GL_FALSE; 487 } 488 489 if (!check_valid_to_render(ctx, "glDrawElementsInstanced")) 490 return GL_FALSE; 491 492 /* Vertex buffer object tests */ 493 if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { 494 /* use indices in the buffer object */ 495 /* make sure count doesn't go outside buffer bounds */ 496 if (index_bytes(type, count) > ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { 497 _mesa_warning(ctx, 498 "glDrawElementsInstanced index out of buffer bounds"); 499 return GL_FALSE; 500 } 501 } 502 else { 503 /* not using a VBO */ 504 if (!indices) 505 return GL_FALSE; 506 } 507 508 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 509 return GL_FALSE; 510 511 return GL_TRUE; 512} 513 514 515#if FEATURE_EXT_transform_feedback 516 517GLboolean 518_mesa_validate_DrawTransformFeedback(struct gl_context *ctx, 519 GLenum mode, 520 struct gl_transform_feedback_object *obj) 521{ 522 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, GL_FALSE); 523 524 if (!_mesa_valid_prim_mode(ctx, mode, "glDrawTransformFeedback")) { 525 return GL_FALSE; 526 } 527 528 if (!obj) { 529 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedback(name)"); 530 return GL_FALSE; 531 } 532 533 if (!obj->EndedAnytime) { 534 _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawTransformFeedback"); 535 return GL_FALSE; 536 } 537 538 if (!check_valid_to_render(ctx, "glDrawTransformFeedback")) { 539 return GL_FALSE; 540 } 541 542 return GL_TRUE; 543} 544 545#endif 546