blob: f5e447c27fd571ab520236f581f58e637460d939 [file] [log] [blame]
Geoff Lange8ebe7f2013-08-05 15:03:13 -04001#include "precompiled.h"
2//
3// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6//
7
8// validationES.h: Validation functions for generic OpenGL ES entry point parameters
9
10#include "libGLESv2/validationES.h"
11#include "libGLESv2/Context.h"
12#include "libGLESv2/Texture.h"
13#include "libGLESv2/Framebuffer.h"
14#include "libGLESv2/Renderbuffer.h"
15#include "libGLESv2/formatutils.h"
16#include "libGLESv2/main.h"
17
18#include "common/mathutil.h"
19#include "common/utilities.h"
20
21namespace gl
22{
23
Geoff Langce635692013-09-24 13:56:32 -040024bool ValidMipLevel(const gl::Context *context, GLenum target, GLint level)
25{
26 int maxLevel = 0;
27 switch (target)
28 {
29 case GL_TEXTURE_2D: maxLevel = context->getMaximum2DTextureLevel(); break;
30 case GL_TEXTURE_CUBE_MAP:
31 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
32 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
33 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
34 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
35 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
36 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: maxLevel = context->getMaximumCubeTextureLevel(); break;
37 case GL_TEXTURE_3D: maxLevel = context->getMaximum3DTextureLevel(); break;
38 case GL_TEXTURE_2D_ARRAY: maxLevel = context->getMaximum2DArrayTextureLevel(); break;
39 default: UNREACHABLE();
40 }
41
42 return level < maxLevel;
43}
44
45bool ValidImageSize(const gl::Context *context, GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth)
46{
47 if (level < 0 || width < 0 || height < 0 || depth < 0)
48 {
49 return false;
50 }
51
52 if (!context->supportsNonPower2Texture() && (level != 0 || !gl::isPow2(width) || !gl::isPow2(height) || !gl::isPow2(depth)))
53 {
54 return false;
55 }
56
57 if (!ValidMipLevel(context, target, level))
58 {
59 return false;
60 }
61
62 return true;
63}
64
Geoff Lang34dbb6f2013-08-05 15:05:47 -040065bool ValidateRenderbufferStorageParameters(const gl::Context *context, GLenum target, GLsizei samples,
Geoff Lange8ebe7f2013-08-05 15:03:13 -040066 GLenum internalformat, GLsizei width, GLsizei height,
67 bool angleExtension)
68{
69 switch (target)
70 {
71 case GL_RENDERBUFFER:
72 break;
73 default:
74 return gl::error(GL_INVALID_ENUM, false);
75 }
76
77 if (width < 0 || height < 0 || samples < 0)
78 {
79 return gl::error(GL_INVALID_VALUE, false);
80 }
81
82 if (!gl::IsValidInternalFormat(internalformat, context))
83 {
84 return gl::error(GL_INVALID_ENUM, false);
85 }
86
87 // ANGLE_framebuffer_multisample does not explicitly state that the internal format must be
88 // sized but it does state that the format must be in the ES2.0 spec table 4.5 which contains
89 // only sized internal formats. The ES3 spec (section 4.4.2) does, however, state that the
90 // internal format must be sized and not an integer format if samples is greater than zero.
91 if (!gl::IsSizedInternalFormat(internalformat, context->getClientVersion()))
92 {
93 return gl::error(GL_INVALID_ENUM, false);
94 }
95
Geoff Langb2f3d052013-08-13 12:49:27 -040096 GLenum componentType = gl::GetComponentType(internalformat, context->getClientVersion());
97 if ((componentType == GL_UNSIGNED_INT || componentType == GL_INT) && samples > 0)
Geoff Lange8ebe7f2013-08-05 15:03:13 -040098 {
99 return gl::error(GL_INVALID_OPERATION, false);
100 }
101
102 if (!gl::IsColorRenderingSupported(internalformat, context) &&
103 !gl::IsDepthRenderingSupported(internalformat, context) &&
104 !gl::IsStencilRenderingSupported(internalformat, context))
105 {
106 return gl::error(GL_INVALID_ENUM, false);
107 }
108
109 if (std::max(width, height) > context->getMaximumRenderbufferDimension())
110 {
111 return gl::error(GL_INVALID_VALUE, false);
112 }
113
114 // ANGLE_framebuffer_multisample states that the value of samples must be less than or equal
115 // to MAX_SAMPLES_ANGLE (Context::getMaxSupportedSamples) while the ES3.0 spec (section 4.4.2)
116 // states that samples must be less than or equal to the maximum samples for the specified
117 // internal format.
118 if (angleExtension)
119 {
120 if (samples > context->getMaxSupportedSamples())
121 {
122 return gl::error(GL_INVALID_VALUE, false);
123 }
124 }
125 else
126 {
127 if (samples > context->getMaxSupportedFormatSamples(internalformat))
128 {
129 return gl::error(GL_INVALID_VALUE, false);
130 }
131 }
132
133 GLuint handle = context->getRenderbufferHandle();
134 if (handle == 0)
135 {
136 return gl::error(GL_INVALID_OPERATION, false);
137 }
138
139 return true;
140}
141
Geoff Lang125deab2013-08-09 13:34:16 -0400142static bool IsPartialBlit(gl::Context *context, gl::Renderbuffer *readBuffer, gl::Renderbuffer *writeBuffer,
143 GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
144 GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1)
145{
146 if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 ||
147 dstX1 != writeBuffer->getWidth() || dstY1 != writeBuffer->getHeight() ||
148 srcX1 != readBuffer->getWidth() || srcY1 != readBuffer->getHeight())
149 {
150 return true;
151 }
152 else if (context->isScissorTestEnabled())
153 {
154 int scissorX, scissorY, scissorWidth, scissorHeight;
155 context->getScissorParams(&scissorX, &scissorY, &scissorWidth, &scissorHeight);
156
157 return scissorX > 0 || scissorY > 0 ||
158 scissorWidth < writeBuffer->getWidth() ||
159 scissorHeight < writeBuffer->getHeight();
160 }
161 else
162 {
163 return false;
164 }
165}
166
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400167bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400168 GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask,
169 GLenum filter, bool fromAngleExtension)
170{
171 switch (filter)
172 {
173 case GL_NEAREST:
174 break;
175 case GL_LINEAR:
176 if (fromAngleExtension)
177 {
178 return gl::error(GL_INVALID_ENUM, false);
179 }
180 break;
181 default:
182 return gl::error(GL_INVALID_ENUM, false);
183 }
184
185 if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)) != 0)
186 {
187 return gl::error(GL_INVALID_VALUE, false);
188 }
189
190 if (mask == 0)
191 {
192 // ES3.0 spec, section 4.3.2 specifies that a mask of zero is valid and no
193 // buffers are copied.
194 return false;
195 }
196
197 if (fromAngleExtension && (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0))
198 {
199 ERR("Scaling and flipping in BlitFramebufferANGLE not supported by this implementation.");
200 return gl::error(GL_INVALID_OPERATION, false);
201 }
202
203 // ES3.0 spec, section 4.3.2 states that linear filtering is only available for the
204 // color buffer, leaving only nearest being unfiltered from above
205 if ((mask & ~GL_COLOR_BUFFER_BIT) != 0 && filter != GL_NEAREST)
206 {
207 return gl::error(GL_INVALID_OPERATION, false);
208 }
209
210 if (context->getReadFramebufferHandle() == context->getDrawFramebufferHandle())
211 {
212 if (fromAngleExtension)
213 {
214 ERR("Blits with the same source and destination framebuffer are not supported by this "
215 "implementation.");
216 }
217 return gl::error(GL_INVALID_OPERATION, false);
218 }
219
220 gl::Framebuffer *readFramebuffer = context->getReadFramebuffer();
221 gl::Framebuffer *drawFramebuffer = context->getDrawFramebuffer();
222 if (!readFramebuffer || readFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE ||
223 !drawFramebuffer || drawFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
224 {
225 return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
226 }
227
228 if (drawFramebuffer->getSamples() != 0)
229 {
230 return gl::error(GL_INVALID_OPERATION, false);
231 }
232
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400233 bool sameBounds = srcX0 == dstX0 && srcY0 == dstY0 && srcX1 == dstX1 && srcY1 == dstY1;
234
235 GLuint clientVersion = context->getClientVersion();
236
237 if (mask & GL_COLOR_BUFFER_BIT)
238 {
239 gl::Renderbuffer *readColorBuffer = readFramebuffer->getReadColorbuffer();
240 gl::Renderbuffer *drawColorBuffer = drawFramebuffer->getFirstColorbuffer();
241
242 if (readColorBuffer && drawColorBuffer)
243 {
244 GLint readInternalFormat = readColorBuffer->getActualFormat();
Geoff Langb2f3d052013-08-13 12:49:27 -0400245 GLenum readComponentType = gl::GetComponentType(readInternalFormat, clientVersion);
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400246
247 for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; i++)
248 {
249 if (drawFramebuffer->isEnabledColorAttachment(i))
250 {
Geoff Langb2f3d052013-08-13 12:49:27 -0400251 GLint drawInternalFormat = drawFramebuffer->getColorbuffer(i)->getActualFormat();
252 GLenum drawComponentType = gl::GetComponentType(drawInternalFormat, clientVersion);
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400253
Geoff Langb2f3d052013-08-13 12:49:27 -0400254 // The GL ES 3.0.2 spec (pg 193) states that:
255 // 1) If the read buffer is fixed point format, the draw buffer must be as well
256 // 2) If the read buffer is an unsigned integer format, the draw buffer must be as well
257 // 3) If the read buffer is a signed integer format, the draw buffer must be as well
258 if ( (readComponentType == GL_UNSIGNED_NORMALIZED || readComponentType == GL_SIGNED_NORMALIZED) &&
259 !(drawComponentType == GL_UNSIGNED_NORMALIZED || drawComponentType == GL_SIGNED_NORMALIZED))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400260 {
261 return gl::error(GL_INVALID_OPERATION, false);
262 }
263
Geoff Langb2f3d052013-08-13 12:49:27 -0400264 if (readComponentType == GL_UNSIGNED_INT && drawComponentType != GL_UNSIGNED_INT)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400265 {
266 return gl::error(GL_INVALID_OPERATION, false);
267 }
268
Geoff Langb2f3d052013-08-13 12:49:27 -0400269 if (readComponentType == GL_INT && drawComponentType != GL_INT)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400270 {
271 return gl::error(GL_INVALID_OPERATION, false);
272 }
273
Geoff Langb2f3d052013-08-13 12:49:27 -0400274 if (readColorBuffer->getSamples() > 0 && (readInternalFormat != drawInternalFormat || !sameBounds))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400275 {
276 return gl::error(GL_INVALID_OPERATION, false);
277 }
278 }
279 }
280
Geoff Langb2f3d052013-08-13 12:49:27 -0400281 if ((readComponentType == GL_INT || readComponentType == GL_UNSIGNED_INT) && filter == GL_LINEAR)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400282 {
283 return gl::error(GL_INVALID_OPERATION, false);
284 }
285
286 if (fromAngleExtension)
287 {
288 const GLenum readColorbufferType = readFramebuffer->getReadColorbufferType();
289 if (readColorbufferType != GL_TEXTURE_2D && readColorbufferType != GL_RENDERBUFFER)
290 {
291 return gl::error(GL_INVALID_OPERATION, false);
292 }
293
294 for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
295 {
296 if (drawFramebuffer->isEnabledColorAttachment(colorAttachment))
297 {
298 if (drawFramebuffer->getColorbufferType(colorAttachment) != GL_TEXTURE_2D &&
299 drawFramebuffer->getColorbufferType(colorAttachment) != GL_RENDERBUFFER)
300 {
301 return gl::error(GL_INVALID_OPERATION, false);
302 }
303
304 if (drawFramebuffer->getColorbuffer(colorAttachment)->getActualFormat() != readColorBuffer->getActualFormat())
305 {
306 return gl::error(GL_INVALID_OPERATION, false);
307 }
308 }
309 }
Geoff Lang125deab2013-08-09 13:34:16 -0400310 if (readFramebuffer->getSamples() != 0 && IsPartialBlit(context, readColorBuffer, drawColorBuffer,
311 srcX0, srcY0, srcX1, srcY1,
312 dstX0, dstY0, dstX1, dstY1))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400313 {
314 return gl::error(GL_INVALID_OPERATION, false);
315 }
316 }
317 }
318 }
319
320 if (mask & GL_DEPTH_BUFFER_BIT)
321 {
322 gl::Renderbuffer *readDepthBuffer = readFramebuffer->getDepthbuffer();
323 gl::Renderbuffer *drawDepthBuffer = drawFramebuffer->getDepthbuffer();
324
325 if (readDepthBuffer && drawDepthBuffer)
326 {
327 if (readDepthBuffer->getActualFormat() != drawDepthBuffer->getActualFormat())
328 {
329 return gl::error(GL_INVALID_OPERATION, false);
330 }
331
332 if (readDepthBuffer->getSamples() > 0 && !sameBounds)
333 {
334 return gl::error(GL_INVALID_OPERATION, false);
335 }
336
337 if (fromAngleExtension)
338 {
Geoff Lang125deab2013-08-09 13:34:16 -0400339 if (IsPartialBlit(context, readDepthBuffer, drawDepthBuffer,
340 srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400341 {
342 ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
343 return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted
344 }
345
346 if (readDepthBuffer->getSamples() != 0 || drawDepthBuffer->getSamples() != 0)
347 {
348 return gl::error(GL_INVALID_OPERATION, false);
349 }
350 }
351 }
352 }
353
354 if (mask & GL_STENCIL_BUFFER_BIT)
355 {
356 gl::Renderbuffer *readStencilBuffer = readFramebuffer->getStencilbuffer();
357 gl::Renderbuffer *drawStencilBuffer = drawFramebuffer->getStencilbuffer();
358
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400359 if (readStencilBuffer && drawStencilBuffer)
360 {
361 if (readStencilBuffer->getActualFormat() != drawStencilBuffer->getActualFormat())
362 {
363 return gl::error(GL_INVALID_OPERATION, false);
364 }
365
366 if (readStencilBuffer->getSamples() > 0 && !sameBounds)
367 {
368 return gl::error(GL_INVALID_OPERATION, false);
369 }
370
371 if (fromAngleExtension)
372 {
Geoff Lang125deab2013-08-09 13:34:16 -0400373 if (IsPartialBlit(context, readStencilBuffer, drawStencilBuffer,
374 srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400375 {
376 ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
377 return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted
378 }
379
380 if (readStencilBuffer->getSamples() != 0 || drawStencilBuffer->getSamples() != 0)
381 {
382 return gl::error(GL_INVALID_OPERATION, false);
383 }
384 }
385 }
386 }
387
388 return true;
389}
390
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400391bool ValidateGetVertexAttribParameters(GLenum pname, int clientVersion)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400392{
393 switch (pname)
394 {
395 case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
396 case GL_VERTEX_ATTRIB_ARRAY_SIZE:
397 case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
398 case GL_VERTEX_ATTRIB_ARRAY_TYPE:
399 case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
400 case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
401 case GL_CURRENT_VERTEX_ATTRIB:
402 return true;
403
404 case GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
405 // Don't verify ES3 context because GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE uses
406 // the same constant.
407 META_ASSERT(GL_VERTEX_ATTRIB_ARRAY_DIVISOR == GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE);
408 return true;
409
410 case GL_VERTEX_ATTRIB_ARRAY_INTEGER:
411 return ((clientVersion >= 3) ? true : gl::error(GL_INVALID_ENUM, false));
412
413 default:
414 return gl::error(GL_INVALID_ENUM, false);
415 }
416}
417
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400418bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400419{
420 switch (pname)
421 {
422 case GL_TEXTURE_WRAP_R:
423 case GL_TEXTURE_SWIZZLE_R:
424 case GL_TEXTURE_SWIZZLE_G:
425 case GL_TEXTURE_SWIZZLE_B:
426 case GL_TEXTURE_SWIZZLE_A:
427 case GL_TEXTURE_BASE_LEVEL:
428 case GL_TEXTURE_MAX_LEVEL:
429 case GL_TEXTURE_COMPARE_MODE:
430 case GL_TEXTURE_COMPARE_FUNC:
431 case GL_TEXTURE_MIN_LOD:
432 case GL_TEXTURE_MAX_LOD:
433 if (context->getClientVersion() < 3)
434 {
435 return gl::error(GL_INVALID_ENUM, false);
436 }
437 break;
438
439 default: break;
440 }
441
442 switch (pname)
443 {
444 case GL_TEXTURE_WRAP_S:
445 case GL_TEXTURE_WRAP_T:
446 case GL_TEXTURE_WRAP_R:
447 switch (param)
448 {
449 case GL_REPEAT:
450 case GL_CLAMP_TO_EDGE:
451 case GL_MIRRORED_REPEAT:
452 return true;
453 default:
454 return gl::error(GL_INVALID_ENUM, false);
455 }
456
457 case GL_TEXTURE_MIN_FILTER:
458 switch (param)
459 {
460 case GL_NEAREST:
461 case GL_LINEAR:
462 case GL_NEAREST_MIPMAP_NEAREST:
463 case GL_LINEAR_MIPMAP_NEAREST:
464 case GL_NEAREST_MIPMAP_LINEAR:
465 case GL_LINEAR_MIPMAP_LINEAR:
466 return true;
467 default:
468 return gl::error(GL_INVALID_ENUM, false);
469 }
470 break;
471
472 case GL_TEXTURE_MAG_FILTER:
473 switch (param)
474 {
475 case GL_NEAREST:
476 case GL_LINEAR:
477 return true;
478 default:
479 return gl::error(GL_INVALID_ENUM, false);
480 }
481 break;
482
483 case GL_TEXTURE_USAGE_ANGLE:
484 switch (param)
485 {
486 case GL_NONE:
487 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE:
488 return true;
489 default:
490 return gl::error(GL_INVALID_ENUM, false);
491 }
492 break;
493
494 case GL_TEXTURE_MAX_ANISOTROPY_EXT:
495 if (!context->supportsTextureFilterAnisotropy())
496 {
497 return gl::error(GL_INVALID_ENUM, false);
498 }
499
500 // we assume the parameter passed to this validation method is truncated, not rounded
501 if (param < 1)
502 {
503 return gl::error(GL_INVALID_VALUE, false);
504 }
505 return true;
506
507 case GL_TEXTURE_MIN_LOD:
508 case GL_TEXTURE_MAX_LOD:
509 // any value is permissible
510 return true;
511
512 case GL_TEXTURE_COMPARE_MODE:
513 switch (param)
514 {
515 case GL_NONE:
516 case GL_COMPARE_REF_TO_TEXTURE:
517 return true;
518 default:
519 return gl::error(GL_INVALID_ENUM, false);
520 }
521 break;
522
523 case GL_TEXTURE_COMPARE_FUNC:
524 switch (param)
525 {
526 case GL_LEQUAL:
527 case GL_GEQUAL:
528 case GL_LESS:
529 case GL_GREATER:
530 case GL_EQUAL:
531 case GL_NOTEQUAL:
532 case GL_ALWAYS:
533 case GL_NEVER:
534 return true;
535 default:
536 return gl::error(GL_INVALID_ENUM, false);
537 }
538 break;
539
540 case GL_TEXTURE_SWIZZLE_R:
541 case GL_TEXTURE_SWIZZLE_G:
542 case GL_TEXTURE_SWIZZLE_B:
543 case GL_TEXTURE_SWIZZLE_A:
544 case GL_TEXTURE_BASE_LEVEL:
545 case GL_TEXTURE_MAX_LEVEL:
546 UNIMPLEMENTED();
547 return true;
548
549 default:
550 return gl::error(GL_INVALID_ENUM, false);
551 }
552}
553
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400554bool ValidateSamplerObjectParameter(GLenum pname)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400555{
556 switch (pname)
557 {
558 case GL_TEXTURE_MIN_FILTER:
559 case GL_TEXTURE_MAG_FILTER:
560 case GL_TEXTURE_WRAP_S:
561 case GL_TEXTURE_WRAP_T:
562 case GL_TEXTURE_WRAP_R:
563 case GL_TEXTURE_MIN_LOD:
564 case GL_TEXTURE_MAX_LOD:
565 case GL_TEXTURE_COMPARE_MODE:
566 case GL_TEXTURE_COMPARE_FUNC:
567 return true;
568
569 default:
570 return gl::error(GL_INVALID_ENUM, false);
571 }
572}
573
574}