api_validate.c revision 56f0c00f125ee75caeadc1c9e8cab8a488635e5e
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 "vbo/vbo.h" 33 34 35/** 36 * \return number of bytes in array [count] of type. 37 */ 38static GLsizei 39index_bytes(GLenum type, GLsizei count) 40{ 41 if (type == GL_UNSIGNED_INT) { 42 return count * sizeof(GLuint); 43 } 44 else if (type == GL_UNSIGNED_BYTE) { 45 return count * sizeof(GLubyte); 46 } 47 else { 48 ASSERT(type == GL_UNSIGNED_SHORT); 49 return count * sizeof(GLushort); 50 } 51} 52 53 54/** 55 * Find the max index in the given element/index buffer 56 */ 57GLuint 58_mesa_max_buffer_index(struct gl_context *ctx, GLuint count, GLenum type, 59 const void *indices, 60 struct gl_buffer_object *elementBuf) 61{ 62 const GLubyte *map = NULL; 63 GLuint max = 0; 64 GLuint i; 65 66 if (_mesa_is_bufferobj(elementBuf)) { 67 /* elements are in a user-defined buffer object. need to map it */ 68 map = ctx->Driver.MapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER, 69 GL_READ_ONLY, elementBuf); 70 /* Actual address is the sum of pointers */ 71 indices = (const GLvoid *) ADD_POINTERS(map, (const GLubyte *) indices); 72 } 73 74 if (type == GL_UNSIGNED_INT) { 75 for (i = 0; i < count; i++) 76 if (((GLuint *) indices)[i] > max) 77 max = ((GLuint *) indices)[i]; 78 } 79 else if (type == GL_UNSIGNED_SHORT) { 80 for (i = 0; i < count; i++) 81 if (((GLushort *) indices)[i] > max) 82 max = ((GLushort *) indices)[i]; 83 } 84 else { 85 ASSERT(type == GL_UNSIGNED_BYTE); 86 for (i = 0; i < count; i++) 87 if (((GLubyte *) indices)[i] > max) 88 max = ((GLubyte *) indices)[i]; 89 } 90 91 if (map) { 92 ctx->Driver.UnmapBuffer(ctx, elementBuf); 93 } 94 95 return max; 96} 97 98 99/** 100 * Check if OK to draw arrays/elements. 101 */ 102static GLboolean 103check_valid_to_render(struct gl_context *ctx, const char *function) 104{ 105 if (!_mesa_valid_to_render(ctx, function)) { 106 return GL_FALSE; 107 } 108 109 switch (ctx->API) { 110#if FEATURE_es2_glsl 111 case API_OPENGLES2: 112 /* For ES2, we can draw if any vertex array is enabled (and we 113 * should always have a vertex program/shader). */ 114 if (ctx->Array.ArrayObj->_Enabled == 0x0 || !ctx->VertexProgram._Current) 115 return GL_FALSE; 116 break; 117#endif 118 119#if FEATURE_ES1 120 case API_OPENGLES: 121 /* For OpenGL ES, only draw if we have vertex positions 122 */ 123 if (!ctx->Array.ArrayObj->Vertex.Enabled) 124 return GL_FALSE; 125 break; 126#endif 127 128#if FEATURE_GL 129 case API_OPENGL: 130 { 131 const struct gl_shader_program *vsProg = 132 ctx->Shader.CurrentVertexProgram; 133 GLboolean haveVertexShader = (vsProg && vsProg->LinkStatus); 134 GLboolean haveVertexProgram = ctx->VertexProgram._Enabled; 135 if (haveVertexShader || haveVertexProgram) { 136 /* Draw regardless of whether or not we have any vertex arrays. 137 * (Ex: could draw a point using a constant vertex pos) 138 */ 139 return GL_TRUE; 140 } 141 else { 142 /* Draw if we have vertex positions (GL_VERTEX_ARRAY or generic 143 * array [0]). 144 */ 145 return (ctx->Array.ArrayObj->Vertex.Enabled || 146 ctx->Array.ArrayObj->VertexAttrib[0].Enabled); 147 } 148 } 149 break; 150#endif 151 152 default: 153 ASSERT_NO_FEATURE(); 154 } 155 156 return GL_TRUE; 157} 158 159 160/** 161 * Do bounds checking on array element indexes. Check that the vertices 162 * pointed to by the indices don't lie outside buffer object bounds. 163 * \return GL_TRUE if OK, GL_FALSE if any indexed vertex goes is out of bounds 164 */ 165static GLboolean 166check_index_bounds(struct gl_context *ctx, GLsizei count, GLenum type, 167 const GLvoid *indices, GLint basevertex) 168{ 169 struct _mesa_prim prim; 170 struct _mesa_index_buffer ib; 171 GLuint min, max; 172 173 /* Only the X Server needs to do this -- otherwise, accessing outside 174 * array/BO bounds allows application termination. 175 */ 176 if (!ctx->Const.CheckArrayBounds) 177 return GL_TRUE; 178 179 memset(&prim, 0, sizeof(prim)); 180 prim.count = count; 181 182 memset(&ib, 0, sizeof(ib)); 183 ib.type = type; 184 ib.ptr = indices; 185 ib.obj = ctx->Array.ElementArrayBufferObj; 186 187 vbo_get_minmax_index(ctx, &prim, &ib, &min, &max); 188 189 if ((int)(min + basevertex) < 0 || 190 max + basevertex > ctx->Array.ArrayObj->_MaxElement) { 191 /* the max element is out of bounds of one or more enabled arrays */ 192 _mesa_warning(ctx, "glDrawElements() index=%u is out of bounds (max=%u)", 193 max, ctx->Array.ArrayObj->_MaxElement); 194 return GL_FALSE; 195 } 196 197 return GL_TRUE; 198} 199 200 201/** 202 * Error checking for glDrawElements(). Includes parameter checking 203 * and VBO bounds checking. 204 * \return GL_TRUE if OK to render, GL_FALSE if error found 205 */ 206GLboolean 207_mesa_validate_DrawElements(struct gl_context *ctx, 208 GLenum mode, GLsizei count, GLenum type, 209 const GLvoid *indices, GLint basevertex) 210{ 211 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 212 213 if (count <= 0) { 214 if (count < 0) 215 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawElements(count)" ); 216 return GL_FALSE; 217 } 218 219 if (mode > GL_TRIANGLE_STRIP_ADJACENCY_ARB) { 220 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(mode)" ); 221 return GL_FALSE; 222 } 223 224 if (type != GL_UNSIGNED_INT && 225 type != GL_UNSIGNED_BYTE && 226 type != GL_UNSIGNED_SHORT) 227 { 228 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)" ); 229 return GL_FALSE; 230 } 231 232 if (!check_valid_to_render(ctx, "glDrawElements")) 233 return GL_FALSE; 234 235 /* Vertex buffer object tests */ 236 if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { 237 /* use indices in the buffer object */ 238 /* make sure count doesn't go outside buffer bounds */ 239 if (index_bytes(type, count) > ctx->Array.ElementArrayBufferObj->Size) { 240 _mesa_warning(ctx, "glDrawElements index out of buffer bounds"); 241 return GL_FALSE; 242 } 243 } 244 else { 245 /* not using a VBO */ 246 if (!indices) 247 return GL_FALSE; 248 } 249 250 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 251 return GL_FALSE; 252 253 return GL_TRUE; 254} 255 256 257/** 258 * Error checking for glDrawRangeElements(). Includes parameter checking 259 * and VBO bounds checking. 260 * \return GL_TRUE if OK to render, GL_FALSE if error found 261 */ 262GLboolean 263_mesa_validate_DrawRangeElements(struct gl_context *ctx, GLenum mode, 264 GLuint start, GLuint end, 265 GLsizei count, GLenum type, 266 const GLvoid *indices, GLint basevertex) 267{ 268 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 269 270 if (count <= 0) { 271 if (count < 0) 272 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(count)" ); 273 return GL_FALSE; 274 } 275 276 if (mode > GL_TRIANGLE_STRIP_ADJACENCY_ARB) { 277 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(mode)" ); 278 return GL_FALSE; 279 } 280 281 if (end < start) { 282 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(end<start)"); 283 return GL_FALSE; 284 } 285 286 if (type != GL_UNSIGNED_INT && 287 type != GL_UNSIGNED_BYTE && 288 type != GL_UNSIGNED_SHORT) { 289 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(type)" ); 290 return GL_FALSE; 291 } 292 293 if (!check_valid_to_render(ctx, "glDrawRangeElements")) 294 return GL_FALSE; 295 296 /* Vertex buffer object tests */ 297 if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { 298 /* use indices in the buffer object */ 299 /* make sure count doesn't go outside buffer bounds */ 300 if (index_bytes(type, count) > ctx->Array.ElementArrayBufferObj->Size) { 301 _mesa_warning(ctx, "glDrawRangeElements index out of buffer bounds"); 302 return GL_FALSE; 303 } 304 } 305 else { 306 /* not using a VBO */ 307 if (!indices) 308 return GL_FALSE; 309 } 310 311 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 312 return GL_FALSE; 313 314 return GL_TRUE; 315} 316 317 318/** 319 * Called from the tnl module to error check the function parameters and 320 * verify that we really can draw something. 321 * \return GL_TRUE if OK to render, GL_FALSE if error found 322 */ 323GLboolean 324_mesa_validate_DrawArrays(struct gl_context *ctx, 325 GLenum mode, GLint start, GLsizei count) 326{ 327 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 328 329 if (count <= 0) { 330 if (count < 0) 331 _mesa_error(ctx, GL_INVALID_VALUE, "glDrawArrays(count)" ); 332 return GL_FALSE; 333 } 334 335 if (mode > GL_TRIANGLE_STRIP_ADJACENCY_ARB) { 336 _mesa_error(ctx, GL_INVALID_ENUM, "glDrawArrays(mode)" ); 337 return GL_FALSE; 338 } 339 340 if (!check_valid_to_render(ctx, "glDrawArrays")) 341 return GL_FALSE; 342 343 if (ctx->Const.CheckArrayBounds) { 344 if (start + count > (GLint) ctx->Array.ArrayObj->_MaxElement) 345 return GL_FALSE; 346 } 347 348 return GL_TRUE; 349} 350 351 352GLboolean 353_mesa_validate_DrawArraysInstanced(struct gl_context *ctx, GLenum mode, GLint first, 354 GLsizei count, GLsizei numInstances) 355{ 356 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 357 358 if (count <= 0) { 359 if (count < 0) 360 _mesa_error(ctx, GL_INVALID_VALUE, 361 "glDrawArraysInstanced(count=%d)", count); 362 return GL_FALSE; 363 } 364 365 if (mode > GL_TRIANGLE_STRIP_ADJACENCY_ARB) { 366 _mesa_error(ctx, GL_INVALID_ENUM, 367 "glDrawArraysInstanced(mode=0x%x)", mode); 368 return GL_FALSE; 369 } 370 371 if (numInstances <= 0) { 372 if (numInstances < 0) 373 _mesa_error(ctx, GL_INVALID_VALUE, 374 "glDrawArraysInstanced(numInstances=%d)", numInstances); 375 return GL_FALSE; 376 } 377 378 if (!check_valid_to_render(ctx, "glDrawArraysInstanced(invalid to render)")) 379 return GL_FALSE; 380 381 if (ctx->CompileFlag) { 382 _mesa_error(ctx, GL_INVALID_OPERATION, 383 "glDrawArraysInstanced(display list"); 384 return GL_FALSE; 385 } 386 387 if (ctx->Const.CheckArrayBounds) { 388 if (first + count > (GLint) ctx->Array.ArrayObj->_MaxElement) 389 return GL_FALSE; 390 } 391 392 return GL_TRUE; 393} 394 395 396GLboolean 397_mesa_validate_DrawElementsInstanced(struct gl_context *ctx, 398 GLenum mode, GLsizei count, GLenum type, 399 const GLvoid *indices, GLsizei numInstances, 400 GLint basevertex) 401{ 402 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 403 404 if (count <= 0) { 405 if (count < 0) 406 _mesa_error(ctx, GL_INVALID_VALUE, 407 "glDrawElementsInstanced(count=%d)", count); 408 return GL_FALSE; 409 } 410 411 if (mode > GL_TRIANGLE_STRIP_ADJACENCY_ARB) { 412 _mesa_error(ctx, GL_INVALID_ENUM, 413 "glDrawElementsInstanced(mode = 0x%x)", mode); 414 return GL_FALSE; 415 } 416 417 if (type != GL_UNSIGNED_INT && 418 type != GL_UNSIGNED_BYTE && 419 type != GL_UNSIGNED_SHORT) { 420 _mesa_error(ctx, GL_INVALID_ENUM, 421 "glDrawElementsInstanced(type=0x%x)", type); 422 return GL_FALSE; 423 } 424 425 if (numInstances <= 0) { 426 if (numInstances < 0) 427 _mesa_error(ctx, GL_INVALID_VALUE, 428 "glDrawElementsInstanced(numInstances=%d)", numInstances); 429 return GL_FALSE; 430 } 431 432 if (!check_valid_to_render(ctx, "glDrawElementsInstanced")) 433 return GL_FALSE; 434 435 /* Vertex buffer object tests */ 436 if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { 437 /* use indices in the buffer object */ 438 /* make sure count doesn't go outside buffer bounds */ 439 if (index_bytes(type, count) > ctx->Array.ElementArrayBufferObj->Size) { 440 _mesa_warning(ctx, 441 "glDrawElementsInstanced index out of buffer bounds"); 442 return GL_FALSE; 443 } 444 } 445 else { 446 /* not using a VBO */ 447 if (!indices) 448 return GL_FALSE; 449 } 450 451 if (!check_index_bounds(ctx, count, type, indices, basevertex)) 452 return GL_FALSE; 453 454 return GL_TRUE; 455} 456