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