mesa: Refactor _mesa_valid_prim_mode to use a switch-statement
[mesa.git] / src / mesa / main / api_validate.c
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 */
41 static GLsizei
42 index_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 */
60 GLuint
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 */
105 static GLboolean
106 check_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 */
169 static GLboolean
170 check_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 */
210 GLboolean
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 */
293 static bool
294 valid_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 */
314 GLboolean
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 */
365 GLboolean
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 */
431 GLboolean
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 */
488 GLboolean
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
517 GLboolean
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
560 GLboolean
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
618 GLboolean
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