blob: 2324a4b5017335953e2512ff2b842011bc7624e1 [file] [log] [blame]
Geoff Lange8ebe7f2013-08-05 15:03:13 -04001#include "precompiled.h"
2//
Geoff Langcec35902014-04-16 10:52:36 -04003// Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved.
Geoff Lange8ebe7f2013-08-05 15:03:13 -04004// 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"
Jamie Madill26e91952014-03-05 15:01:27 -050011#include "libGLESv2/validationES2.h"
12#include "libGLESv2/validationES3.h"
Geoff Lange8ebe7f2013-08-05 15:03:13 -040013#include "libGLESv2/Context.h"
14#include "libGLESv2/Texture.h"
15#include "libGLESv2/Framebuffer.h"
Jamie Madille261b442014-06-25 12:42:21 -040016#include "libGLESv2/FramebufferAttachment.h"
Geoff Lange8ebe7f2013-08-05 15:03:13 -040017#include "libGLESv2/formatutils.h"
18#include "libGLESv2/main.h"
Jamie Madilldb2f14c2014-05-13 13:56:30 -040019#include "libGLESv2/Query.h"
Jamie Madill36398922014-05-20 14:51:53 -040020#include "libGLESv2/ProgramBinary.h"
Jamie Madill250d33f2014-06-06 17:09:03 -040021#include "libGLESv2/TransformFeedback.h"
Geoff Lange8ebe7f2013-08-05 15:03:13 -040022
23#include "common/mathutil.h"
24#include "common/utilities.h"
25
26namespace gl
27{
28
Geoff Lang0550d032014-01-30 11:29:07 -050029bool ValidCap(const Context *context, GLenum cap)
30{
31 switch (cap)
32 {
33 case GL_CULL_FACE:
34 case GL_POLYGON_OFFSET_FILL:
35 case GL_SAMPLE_ALPHA_TO_COVERAGE:
36 case GL_SAMPLE_COVERAGE:
37 case GL_SCISSOR_TEST:
38 case GL_STENCIL_TEST:
39 case GL_DEPTH_TEST:
40 case GL_BLEND:
41 case GL_DITHER:
42 return true;
43 case GL_PRIMITIVE_RESTART_FIXED_INDEX:
44 case GL_RASTERIZER_DISCARD:
45 return (context->getClientVersion() >= 3);
46 default:
47 return false;
48 }
49}
50
Jamie Madill1fc7e2c2014-01-21 16:47:10 -050051bool ValidTextureTarget(const Context *context, GLenum target)
Jamie Madill35d15012013-10-07 10:46:37 -040052{
Jamie Madilld7460c72014-01-21 16:38:14 -050053 switch (target)
Jamie Madill35d15012013-10-07 10:46:37 -040054 {
Jamie Madilld7460c72014-01-21 16:38:14 -050055 case GL_TEXTURE_2D:
56 case GL_TEXTURE_CUBE_MAP:
57 return true;
Jamie Madill35d15012013-10-07 10:46:37 -040058
Jamie Madilld7460c72014-01-21 16:38:14 -050059 case GL_TEXTURE_3D:
60 case GL_TEXTURE_2D_ARRAY:
61 return (context->getClientVersion() >= 3);
62
63 default:
64 return false;
65 }
Jamie Madill35d15012013-10-07 10:46:37 -040066}
67
Shannon Woods4dfed832014-03-17 20:03:39 -040068// This function differs from ValidTextureTarget in that the target must be
69// usable as the destination of a 2D operation-- so a cube face is valid, but
70// GL_TEXTURE_CUBE_MAP is not.
Jamie Madill560a8d82014-05-21 13:06:20 -040071// Note: duplicate of IsInternalTextureTarget
Shannon Woods4dfed832014-03-17 20:03:39 -040072bool ValidTexture2DDestinationTarget(const Context *context, GLenum target)
73{
74 switch (target)
75 {
76 case GL_TEXTURE_2D:
77 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
78 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
79 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
80 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
81 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
82 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
83 return true;
84 case GL_TEXTURE_2D_ARRAY:
85 case GL_TEXTURE_3D:
86 return (context->getClientVersion() >= 3);
87 default:
88 return false;
89 }
90}
91
Jamie Madill1fc7e2c2014-01-21 16:47:10 -050092bool ValidFramebufferTarget(GLenum target)
93{
94 META_ASSERT(GL_DRAW_FRAMEBUFFER_ANGLE == GL_DRAW_FRAMEBUFFER && GL_READ_FRAMEBUFFER_ANGLE == GL_READ_FRAMEBUFFER);
95
96 switch (target)
97 {
98 case GL_FRAMEBUFFER: return true;
99 case GL_READ_FRAMEBUFFER: return true;
100 case GL_DRAW_FRAMEBUFFER: return true;
101 default: return false;
102 }
103}
104
Jamie Madill8c96d582014-03-05 15:01:23 -0500105bool ValidBufferTarget(const Context *context, GLenum target)
106{
107 switch (target)
108 {
109 case GL_ARRAY_BUFFER:
110 case GL_ELEMENT_ARRAY_BUFFER:
111 return true;
112
Jamie Madill8c96d582014-03-05 15:01:23 -0500113 case GL_PIXEL_PACK_BUFFER:
114 case GL_PIXEL_UNPACK_BUFFER:
Geoff Langc0b9ef42014-07-02 10:02:37 -0400115 return context->getExtensions().pixelBufferObject;
Shannon Woods158c4382014-05-06 13:00:07 -0400116
Shannon Woodsb3801742014-03-27 14:59:19 -0400117 case GL_COPY_READ_BUFFER:
118 case GL_COPY_WRITE_BUFFER:
Jamie Madill8c96d582014-03-05 15:01:23 -0500119 case GL_TRANSFORM_FEEDBACK_BUFFER:
120 case GL_UNIFORM_BUFFER:
121 return (context->getClientVersion() >= 3);
122
123 default:
124 return false;
125 }
126}
127
Jamie Madill70656a62014-03-05 15:01:26 -0500128bool ValidBufferParameter(const Context *context, GLenum pname)
129{
130 switch (pname)
131 {
132 case GL_BUFFER_USAGE:
133 case GL_BUFFER_SIZE:
134 return true;
135
136 // GL_BUFFER_MAP_POINTER is a special case, and may only be
137 // queried with GetBufferPointerv
138 case GL_BUFFER_ACCESS_FLAGS:
139 case GL_BUFFER_MAPPED:
140 case GL_BUFFER_MAP_OFFSET:
141 case GL_BUFFER_MAP_LENGTH:
142 return (context->getClientVersion() >= 3);
143
144 default:
145 return false;
146 }
147}
148
Jamie Madill8c96d582014-03-05 15:01:23 -0500149bool ValidMipLevel(const Context *context, GLenum target, GLint level)
Geoff Langce635692013-09-24 13:56:32 -0400150{
Geoff Langaae65a42014-05-26 12:43:44 -0400151 size_t maxDimension = 0;
Geoff Langce635692013-09-24 13:56:32 -0400152 switch (target)
153 {
Geoff Langaae65a42014-05-26 12:43:44 -0400154 case GL_TEXTURE_2D: maxDimension = context->getCaps().max2DTextureSize; break;
Geoff Langce635692013-09-24 13:56:32 -0400155 case GL_TEXTURE_CUBE_MAP:
156 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
157 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
158 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
159 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
160 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
Geoff Langaae65a42014-05-26 12:43:44 -0400161 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: maxDimension = context->getCaps().maxCubeMapTextureSize; break;
162 case GL_TEXTURE_3D: maxDimension = context->getCaps().max3DTextureSize; break;
163 case GL_TEXTURE_2D_ARRAY: maxDimension = context->getCaps().max2DTextureSize; break;
Geoff Langce635692013-09-24 13:56:32 -0400164 default: UNREACHABLE();
165 }
166
Geoff Langaae65a42014-05-26 12:43:44 -0400167 return level <= gl::log2(maxDimension);
Geoff Langce635692013-09-24 13:56:32 -0400168}
169
Jamie Madill4fd75c12014-06-23 10:53:54 -0400170bool ValidImageSize(const gl::Context *context, GLenum target, GLint level,
171 GLsizei width, GLsizei height, GLsizei depth)
Geoff Langce635692013-09-24 13:56:32 -0400172{
173 if (level < 0 || width < 0 || height < 0 || depth < 0)
174 {
175 return false;
176 }
177
Geoff Langc0b9ef42014-07-02 10:02:37 -0400178 if (!context->getExtensions().textureNPOT &&
Jamie Madill4fd75c12014-06-23 10:53:54 -0400179 (level != 0 && (!gl::isPow2(width) || !gl::isPow2(height) || !gl::isPow2(depth))))
Geoff Langce635692013-09-24 13:56:32 -0400180 {
181 return false;
182 }
183
184 if (!ValidMipLevel(context, target, level))
185 {
186 return false;
187 }
188
189 return true;
190}
191
Geoff Lang005df412013-10-16 14:12:50 -0400192bool ValidCompressedImageSize(const gl::Context *context, GLenum internalFormat, GLsizei width, GLsizei height)
Geoff Langd4f180b2013-09-24 13:57:44 -0400193{
Geoff Lange4a492b2014-06-19 14:14:41 -0400194 if (!IsFormatCompressed(internalFormat))
Geoff Langd4f180b2013-09-24 13:57:44 -0400195 {
196 return false;
197 }
198
Geoff Lange4a492b2014-06-19 14:14:41 -0400199 GLint blockWidth = GetCompressedBlockWidth(internalFormat);
200 GLint blockHeight = GetCompressedBlockHeight(internalFormat);
Geoff Langd4f180b2013-09-24 13:57:44 -0400201 if (width < 0 || (width > blockWidth && width % blockWidth != 0) ||
202 height < 0 || (height > blockHeight && height % blockHeight != 0))
203 {
204 return false;
205 }
206
207 return true;
208}
209
Geoff Lang37dde692014-01-31 16:34:54 -0500210bool ValidQueryType(const Context *context, GLenum queryType)
211{
212 META_ASSERT(GL_ANY_SAMPLES_PASSED == GL_ANY_SAMPLES_PASSED_EXT);
213 META_ASSERT(GL_ANY_SAMPLES_PASSED_CONSERVATIVE == GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT);
214
215 switch (queryType)
216 {
217 case GL_ANY_SAMPLES_PASSED:
218 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
219 return true;
220 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
221 return (context->getClientVersion() >= 3);
222 default:
223 return false;
224 }
225}
226
Geoff Lang48dcae72014-02-05 16:28:24 -0500227bool ValidProgram(const Context *context, GLuint id)
228{
229 // ES3 spec (section 2.11.1) -- "Commands that accept shader or program object names will generate the
230 // error INVALID_VALUE if the provided name is not the name of either a shader or program object and
231 // INVALID_OPERATION if the provided name identifies an object that is not the expected type."
232
233 if (context->getProgram(id) != NULL)
234 {
235 return true;
236 }
237 else if (context->getShader(id) != NULL)
238 {
239 // ID is the wrong type
240 return gl::error(GL_INVALID_OPERATION, false);
241 }
242 else
243 {
244 // No shader/program object has this ID
245 return gl::error(GL_INVALID_VALUE, false);
246 }
247}
248
Jamie Madillb4472272014-07-03 10:38:55 -0400249bool ValidateAttachmentTarget(const gl::Context *context, GLenum attachment)
250{
251 if (attachment >= GL_COLOR_ATTACHMENT0_EXT && attachment <= GL_COLOR_ATTACHMENT15_EXT)
252 {
253 const unsigned int colorAttachment = (attachment - GL_COLOR_ATTACHMENT0_EXT);
254
Geoff Langaae65a42014-05-26 12:43:44 -0400255 if (colorAttachment >= context->getCaps().maxColorAttachments)
Jamie Madillb4472272014-07-03 10:38:55 -0400256 {
257 return gl::error(GL_INVALID_VALUE, false);
258 }
259 }
260 else
261 {
262 switch (attachment)
263 {
264 case GL_DEPTH_ATTACHMENT:
265 case GL_STENCIL_ATTACHMENT:
266 break;
267
268 case GL_DEPTH_STENCIL_ATTACHMENT:
269 if (context->getClientVersion() < 3)
270 {
271 return gl::error(GL_INVALID_ENUM, false);
272 }
273 break;
274
275 default:
276 return gl::error(GL_INVALID_ENUM, false);
277 }
278 }
279
280 return true;
281}
282
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400283bool ValidateRenderbufferStorageParameters(const gl::Context *context, GLenum target, GLsizei samples,
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400284 GLenum internalformat, GLsizei width, GLsizei height,
285 bool angleExtension)
286{
287 switch (target)
288 {
289 case GL_RENDERBUFFER:
290 break;
291 default:
292 return gl::error(GL_INVALID_ENUM, false);
293 }
294
295 if (width < 0 || height < 0 || samples < 0)
296 {
297 return gl::error(GL_INVALID_VALUE, false);
298 }
299
Geoff Langc0b9ef42014-07-02 10:02:37 -0400300 if (!gl::IsValidInternalFormat(internalformat, context->getExtensions(), context->getClientVersion()))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400301 {
302 return gl::error(GL_INVALID_ENUM, false);
303 }
304
305 // ANGLE_framebuffer_multisample does not explicitly state that the internal format must be
306 // sized but it does state that the format must be in the ES2.0 spec table 4.5 which contains
307 // only sized internal formats. The ES3 spec (section 4.4.2) does, however, state that the
308 // internal format must be sized and not an integer format if samples is greater than zero.
Geoff Lange4a492b2014-06-19 14:14:41 -0400309 if (!gl::IsSizedInternalFormat(internalformat))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400310 {
311 return gl::error(GL_INVALID_ENUM, false);
312 }
313
Geoff Lange4a492b2014-06-19 14:14:41 -0400314 GLenum componentType = gl::GetComponentType(internalformat);
Geoff Langb2f3d052013-08-13 12:49:27 -0400315 if ((componentType == GL_UNSIGNED_INT || componentType == GL_INT) && samples > 0)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400316 {
317 return gl::error(GL_INVALID_OPERATION, false);
318 }
319
Geoff Langc0b9ef42014-07-02 10:02:37 -0400320 const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
Geoff Langcec35902014-04-16 10:52:36 -0400321 if (!formatCaps.colorRendering && !formatCaps.depthRendering && !formatCaps.stencilRendering)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400322 {
323 return gl::error(GL_INVALID_ENUM, false);
324 }
325
Geoff Langaae65a42014-05-26 12:43:44 -0400326 if (static_cast<GLuint>(std::max(width, height)) > context->getCaps().maxRenderbufferSize)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400327 {
328 return gl::error(GL_INVALID_VALUE, false);
329 }
330
331 // ANGLE_framebuffer_multisample states that the value of samples must be less than or equal
332 // to MAX_SAMPLES_ANGLE (Context::getMaxSupportedSamples) while the ES3.0 spec (section 4.4.2)
333 // states that samples must be less than or equal to the maximum samples for the specified
334 // internal format.
335 if (angleExtension)
336 {
337 if (samples > context->getMaxSupportedSamples())
338 {
339 return gl::error(GL_INVALID_VALUE, false);
340 }
341 }
342 else
343 {
344 if (samples > context->getMaxSupportedFormatSamples(internalformat))
345 {
346 return gl::error(GL_INVALID_VALUE, false);
347 }
348 }
349
350 GLuint handle = context->getRenderbufferHandle();
351 if (handle == 0)
352 {
353 return gl::error(GL_INVALID_OPERATION, false);
354 }
355
356 return true;
357}
358
Jamie Madill1fc7e2c2014-01-21 16:47:10 -0500359bool ValidateFramebufferRenderbufferParameters(gl::Context *context, GLenum target, GLenum attachment,
360 GLenum renderbuffertarget, GLuint renderbuffer)
361{
362 gl::Framebuffer *framebuffer = context->getTargetFramebuffer(target);
363 GLuint framebufferHandle = context->getTargetFramebufferHandle(target);
364
365 if (!framebuffer || (framebufferHandle == 0 && renderbuffer != 0))
366 {
367 return gl::error(GL_INVALID_OPERATION, false);
368 }
369
Jamie Madillb4472272014-07-03 10:38:55 -0400370 if (!ValidateAttachmentTarget(context, attachment))
Jamie Madill1fc7e2c2014-01-21 16:47:10 -0500371 {
Jamie Madillb4472272014-07-03 10:38:55 -0400372 return false;
Jamie Madill1fc7e2c2014-01-21 16:47:10 -0500373 }
374
Jamie Madillab9d82c2014-01-21 16:38:14 -0500375 // [OpenGL ES 2.0.25] Section 4.4.3 page 112
376 // [OpenGL ES 3.0.2] Section 4.4.2 page 201
377 // 'renderbuffer' must be either zero or the name of an existing renderbuffer object of
378 // type 'renderbuffertarget', otherwise an INVALID_OPERATION error is generated.
379 if (renderbuffer != 0)
380 {
381 if (!context->getRenderbuffer(renderbuffer))
382 {
383 return gl::error(GL_INVALID_OPERATION, false);
384 }
385 }
386
Jamie Madill1fc7e2c2014-01-21 16:47:10 -0500387 return true;
388}
389
Jamie Madill3c7fa222014-06-05 13:08:51 -0400390static bool IsPartialBlit(gl::Context *context, gl::FramebufferAttachment *readBuffer, gl::FramebufferAttachment *writeBuffer,
Geoff Lang125deab2013-08-09 13:34:16 -0400391 GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
392 GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1)
393{
394 if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 ||
395 dstX1 != writeBuffer->getWidth() || dstY1 != writeBuffer->getHeight() ||
396 srcX1 != readBuffer->getWidth() || srcY1 != readBuffer->getHeight())
397 {
398 return true;
399 }
400 else if (context->isScissorTestEnabled())
401 {
402 int scissorX, scissorY, scissorWidth, scissorHeight;
403 context->getScissorParams(&scissorX, &scissorY, &scissorWidth, &scissorHeight);
404
405 return scissorX > 0 || scissorY > 0 ||
406 scissorWidth < writeBuffer->getWidth() ||
407 scissorHeight < writeBuffer->getHeight();
408 }
409 else
410 {
411 return false;
412 }
413}
414
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400415bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400416 GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask,
417 GLenum filter, bool fromAngleExtension)
418{
419 switch (filter)
420 {
421 case GL_NEAREST:
422 break;
423 case GL_LINEAR:
424 if (fromAngleExtension)
425 {
426 return gl::error(GL_INVALID_ENUM, false);
427 }
428 break;
429 default:
430 return gl::error(GL_INVALID_ENUM, false);
431 }
432
433 if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)) != 0)
434 {
435 return gl::error(GL_INVALID_VALUE, false);
436 }
437
438 if (mask == 0)
439 {
440 // ES3.0 spec, section 4.3.2 specifies that a mask of zero is valid and no
441 // buffers are copied.
442 return false;
443 }
444
445 if (fromAngleExtension && (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0))
446 {
447 ERR("Scaling and flipping in BlitFramebufferANGLE not supported by this implementation.");
448 return gl::error(GL_INVALID_OPERATION, false);
449 }
450
451 // ES3.0 spec, section 4.3.2 states that linear filtering is only available for the
452 // color buffer, leaving only nearest being unfiltered from above
453 if ((mask & ~GL_COLOR_BUFFER_BIT) != 0 && filter != GL_NEAREST)
454 {
455 return gl::error(GL_INVALID_OPERATION, false);
456 }
457
458 if (context->getReadFramebufferHandle() == context->getDrawFramebufferHandle())
459 {
460 if (fromAngleExtension)
461 {
462 ERR("Blits with the same source and destination framebuffer are not supported by this "
463 "implementation.");
464 }
465 return gl::error(GL_INVALID_OPERATION, false);
466 }
467
468 gl::Framebuffer *readFramebuffer = context->getReadFramebuffer();
469 gl::Framebuffer *drawFramebuffer = context->getDrawFramebuffer();
470 if (!readFramebuffer || readFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE ||
471 !drawFramebuffer || drawFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
472 {
473 return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
474 }
475
476 if (drawFramebuffer->getSamples() != 0)
477 {
478 return gl::error(GL_INVALID_OPERATION, false);
479 }
480
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400481 bool sameBounds = srcX0 == dstX0 && srcY0 == dstY0 && srcX1 == dstX1 && srcY1 == dstY1;
482
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400483 if (mask & GL_COLOR_BUFFER_BIT)
484 {
Jamie Madill3c7fa222014-06-05 13:08:51 -0400485 gl::FramebufferAttachment *readColorBuffer = readFramebuffer->getReadColorbuffer();
486 gl::FramebufferAttachment *drawColorBuffer = drawFramebuffer->getFirstColorbuffer();
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400487
488 if (readColorBuffer && drawColorBuffer)
489 {
Geoff Lang005df412013-10-16 14:12:50 -0400490 GLenum readInternalFormat = readColorBuffer->getActualFormat();
Geoff Lange4a492b2014-06-19 14:14:41 -0400491 GLenum readComponentType = gl::GetComponentType(readInternalFormat);
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400492
493 for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; i++)
494 {
495 if (drawFramebuffer->isEnabledColorAttachment(i))
496 {
Geoff Lang005df412013-10-16 14:12:50 -0400497 GLenum drawInternalFormat = drawFramebuffer->getColorbuffer(i)->getActualFormat();
Geoff Lange4a492b2014-06-19 14:14:41 -0400498 GLenum drawComponentType = gl::GetComponentType(drawInternalFormat);
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400499
Geoff Langb2f3d052013-08-13 12:49:27 -0400500 // The GL ES 3.0.2 spec (pg 193) states that:
501 // 1) If the read buffer is fixed point format, the draw buffer must be as well
502 // 2) If the read buffer is an unsigned integer format, the draw buffer must be as well
503 // 3) If the read buffer is a signed integer format, the draw buffer must be as well
504 if ( (readComponentType == GL_UNSIGNED_NORMALIZED || readComponentType == GL_SIGNED_NORMALIZED) &&
505 !(drawComponentType == GL_UNSIGNED_NORMALIZED || drawComponentType == GL_SIGNED_NORMALIZED))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400506 {
507 return gl::error(GL_INVALID_OPERATION, false);
508 }
509
Geoff Langb2f3d052013-08-13 12:49:27 -0400510 if (readComponentType == GL_UNSIGNED_INT && drawComponentType != GL_UNSIGNED_INT)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400511 {
512 return gl::error(GL_INVALID_OPERATION, false);
513 }
514
Geoff Langb2f3d052013-08-13 12:49:27 -0400515 if (readComponentType == GL_INT && drawComponentType != GL_INT)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400516 {
517 return gl::error(GL_INVALID_OPERATION, false);
518 }
519
Geoff Langb2f3d052013-08-13 12:49:27 -0400520 if (readColorBuffer->getSamples() > 0 && (readInternalFormat != drawInternalFormat || !sameBounds))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400521 {
522 return gl::error(GL_INVALID_OPERATION, false);
523 }
524 }
525 }
526
Geoff Langb2f3d052013-08-13 12:49:27 -0400527 if ((readComponentType == GL_INT || readComponentType == GL_UNSIGNED_INT) && filter == GL_LINEAR)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400528 {
529 return gl::error(GL_INVALID_OPERATION, false);
530 }
531
532 if (fromAngleExtension)
533 {
534 const GLenum readColorbufferType = readFramebuffer->getReadColorbufferType();
535 if (readColorbufferType != GL_TEXTURE_2D && readColorbufferType != GL_RENDERBUFFER)
536 {
537 return gl::error(GL_INVALID_OPERATION, false);
538 }
539
540 for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
541 {
542 if (drawFramebuffer->isEnabledColorAttachment(colorAttachment))
543 {
Jamie Madille92a3542014-07-03 10:38:58 -0400544 FramebufferAttachment *attachment = drawFramebuffer->getColorbuffer(colorAttachment);
545 ASSERT(attachment);
546
547 if (attachment->type() != GL_TEXTURE_2D && attachment->type() != GL_RENDERBUFFER)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400548 {
549 return gl::error(GL_INVALID_OPERATION, false);
550 }
551
Jamie Madille92a3542014-07-03 10:38:58 -0400552 if (attachment->getActualFormat() != readColorBuffer->getActualFormat())
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400553 {
554 return gl::error(GL_INVALID_OPERATION, false);
555 }
556 }
557 }
Geoff Lang125deab2013-08-09 13:34:16 -0400558 if (readFramebuffer->getSamples() != 0 && IsPartialBlit(context, readColorBuffer, drawColorBuffer,
559 srcX0, srcY0, srcX1, srcY1,
560 dstX0, dstY0, dstX1, dstY1))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400561 {
562 return gl::error(GL_INVALID_OPERATION, false);
563 }
564 }
565 }
566 }
567
568 if (mask & GL_DEPTH_BUFFER_BIT)
569 {
Jamie Madill3c7fa222014-06-05 13:08:51 -0400570 gl::FramebufferAttachment *readDepthBuffer = readFramebuffer->getDepthbuffer();
571 gl::FramebufferAttachment *drawDepthBuffer = drawFramebuffer->getDepthbuffer();
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400572
573 if (readDepthBuffer && drawDepthBuffer)
574 {
575 if (readDepthBuffer->getActualFormat() != drawDepthBuffer->getActualFormat())
576 {
577 return gl::error(GL_INVALID_OPERATION, false);
578 }
579
580 if (readDepthBuffer->getSamples() > 0 && !sameBounds)
581 {
582 return gl::error(GL_INVALID_OPERATION, false);
583 }
584
585 if (fromAngleExtension)
586 {
Geoff Lang125deab2013-08-09 13:34:16 -0400587 if (IsPartialBlit(context, readDepthBuffer, drawDepthBuffer,
588 srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400589 {
590 ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
591 return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted
592 }
593
594 if (readDepthBuffer->getSamples() != 0 || drawDepthBuffer->getSamples() != 0)
595 {
596 return gl::error(GL_INVALID_OPERATION, false);
597 }
598 }
599 }
600 }
601
602 if (mask & GL_STENCIL_BUFFER_BIT)
603 {
Jamie Madill3c7fa222014-06-05 13:08:51 -0400604 gl::FramebufferAttachment *readStencilBuffer = readFramebuffer->getStencilbuffer();
605 gl::FramebufferAttachment *drawStencilBuffer = drawFramebuffer->getStencilbuffer();
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400606
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400607 if (readStencilBuffer && drawStencilBuffer)
608 {
609 if (readStencilBuffer->getActualFormat() != drawStencilBuffer->getActualFormat())
610 {
611 return gl::error(GL_INVALID_OPERATION, false);
612 }
613
614 if (readStencilBuffer->getSamples() > 0 && !sameBounds)
615 {
616 return gl::error(GL_INVALID_OPERATION, false);
617 }
618
619 if (fromAngleExtension)
620 {
Geoff Lang125deab2013-08-09 13:34:16 -0400621 if (IsPartialBlit(context, readStencilBuffer, drawStencilBuffer,
622 srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400623 {
624 ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
625 return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted
626 }
627
628 if (readStencilBuffer->getSamples() != 0 || drawStencilBuffer->getSamples() != 0)
629 {
630 return gl::error(GL_INVALID_OPERATION, false);
631 }
632 }
633 }
634 }
635
636 return true;
637}
638
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400639bool ValidateGetVertexAttribParameters(GLenum pname, int clientVersion)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400640{
641 switch (pname)
642 {
643 case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
644 case GL_VERTEX_ATTRIB_ARRAY_SIZE:
645 case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
646 case GL_VERTEX_ATTRIB_ARRAY_TYPE:
647 case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
648 case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
649 case GL_CURRENT_VERTEX_ATTRIB:
650 return true;
651
652 case GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
653 // Don't verify ES3 context because GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE uses
654 // the same constant.
655 META_ASSERT(GL_VERTEX_ATTRIB_ARRAY_DIVISOR == GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE);
656 return true;
657
658 case GL_VERTEX_ATTRIB_ARRAY_INTEGER:
659 return ((clientVersion >= 3) ? true : gl::error(GL_INVALID_ENUM, false));
660
661 default:
662 return gl::error(GL_INVALID_ENUM, false);
663 }
664}
665
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400666bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400667{
668 switch (pname)
669 {
670 case GL_TEXTURE_WRAP_R:
671 case GL_TEXTURE_SWIZZLE_R:
672 case GL_TEXTURE_SWIZZLE_G:
673 case GL_TEXTURE_SWIZZLE_B:
674 case GL_TEXTURE_SWIZZLE_A:
675 case GL_TEXTURE_BASE_LEVEL:
676 case GL_TEXTURE_MAX_LEVEL:
677 case GL_TEXTURE_COMPARE_MODE:
678 case GL_TEXTURE_COMPARE_FUNC:
679 case GL_TEXTURE_MIN_LOD:
680 case GL_TEXTURE_MAX_LOD:
681 if (context->getClientVersion() < 3)
682 {
683 return gl::error(GL_INVALID_ENUM, false);
684 }
685 break;
686
687 default: break;
688 }
689
690 switch (pname)
691 {
692 case GL_TEXTURE_WRAP_S:
693 case GL_TEXTURE_WRAP_T:
694 case GL_TEXTURE_WRAP_R:
695 switch (param)
696 {
697 case GL_REPEAT:
698 case GL_CLAMP_TO_EDGE:
699 case GL_MIRRORED_REPEAT:
700 return true;
701 default:
702 return gl::error(GL_INVALID_ENUM, false);
703 }
704
705 case GL_TEXTURE_MIN_FILTER:
706 switch (param)
707 {
708 case GL_NEAREST:
709 case GL_LINEAR:
710 case GL_NEAREST_MIPMAP_NEAREST:
711 case GL_LINEAR_MIPMAP_NEAREST:
712 case GL_NEAREST_MIPMAP_LINEAR:
713 case GL_LINEAR_MIPMAP_LINEAR:
714 return true;
715 default:
716 return gl::error(GL_INVALID_ENUM, false);
717 }
718 break;
719
720 case GL_TEXTURE_MAG_FILTER:
721 switch (param)
722 {
723 case GL_NEAREST:
724 case GL_LINEAR:
725 return true;
726 default:
727 return gl::error(GL_INVALID_ENUM, false);
728 }
729 break;
730
731 case GL_TEXTURE_USAGE_ANGLE:
732 switch (param)
733 {
734 case GL_NONE:
735 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE:
736 return true;
737 default:
738 return gl::error(GL_INVALID_ENUM, false);
739 }
740 break;
741
742 case GL_TEXTURE_MAX_ANISOTROPY_EXT:
Geoff Langc0b9ef42014-07-02 10:02:37 -0400743 if (!context->getExtensions().textureFilterAnisotropic)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400744 {
745 return gl::error(GL_INVALID_ENUM, false);
746 }
747
748 // we assume the parameter passed to this validation method is truncated, not rounded
749 if (param < 1)
750 {
751 return gl::error(GL_INVALID_VALUE, false);
752 }
753 return true;
754
755 case GL_TEXTURE_MIN_LOD:
756 case GL_TEXTURE_MAX_LOD:
757 // any value is permissible
758 return true;
759
760 case GL_TEXTURE_COMPARE_MODE:
Geoff Lang63b5f1f2013-09-23 14:52:14 -0400761 // Acceptable mode parameters from GLES 3.0.2 spec, table 3.17
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400762 switch (param)
763 {
764 case GL_NONE:
765 case GL_COMPARE_REF_TO_TEXTURE:
766 return true;
767 default:
768 return gl::error(GL_INVALID_ENUM, false);
769 }
770 break;
771
772 case GL_TEXTURE_COMPARE_FUNC:
Geoff Lang63b5f1f2013-09-23 14:52:14 -0400773 // Acceptable function parameters from GLES 3.0.2 spec, table 3.17
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400774 switch (param)
775 {
776 case GL_LEQUAL:
777 case GL_GEQUAL:
778 case GL_LESS:
779 case GL_GREATER:
780 case GL_EQUAL:
781 case GL_NOTEQUAL:
782 case GL_ALWAYS:
783 case GL_NEVER:
784 return true;
785 default:
786 return gl::error(GL_INVALID_ENUM, false);
787 }
788 break;
789
790 case GL_TEXTURE_SWIZZLE_R:
791 case GL_TEXTURE_SWIZZLE_G:
792 case GL_TEXTURE_SWIZZLE_B:
793 case GL_TEXTURE_SWIZZLE_A:
Geoff Langbc90a482013-09-17 16:51:27 -0400794 switch (param)
795 {
796 case GL_RED:
797 case GL_GREEN:
798 case GL_BLUE:
799 case GL_ALPHA:
800 case GL_ZERO:
801 case GL_ONE:
802 return true;
803 default:
804 return gl::error(GL_INVALID_ENUM, false);
805 }
806 break;
807
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400808 case GL_TEXTURE_BASE_LEVEL:
809 case GL_TEXTURE_MAX_LEVEL:
Nicolas Capens8de68282014-04-04 11:10:27 -0400810 if (param < 0)
811 {
812 return gl::error(GL_INVALID_VALUE, false);
813 }
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400814 return true;
815
816 default:
817 return gl::error(GL_INVALID_ENUM, false);
818 }
819}
820
Geoff Lang34dbb6f2013-08-05 15:05:47 -0400821bool ValidateSamplerObjectParameter(GLenum pname)
Geoff Lange8ebe7f2013-08-05 15:03:13 -0400822{
823 switch (pname)
824 {
825 case GL_TEXTURE_MIN_FILTER:
826 case GL_TEXTURE_MAG_FILTER:
827 case GL_TEXTURE_WRAP_S:
828 case GL_TEXTURE_WRAP_T:
829 case GL_TEXTURE_WRAP_R:
830 case GL_TEXTURE_MIN_LOD:
831 case GL_TEXTURE_MAX_LOD:
832 case GL_TEXTURE_COMPARE_MODE:
833 case GL_TEXTURE_COMPARE_FUNC:
834 return true;
835
836 default:
837 return gl::error(GL_INVALID_ENUM, false);
838 }
839}
840
Jamie Madill26e91952014-03-05 15:01:27 -0500841bool ValidateReadPixelsParameters(gl::Context *context, GLint x, GLint y, GLsizei width, GLsizei height,
842 GLenum format, GLenum type, GLsizei *bufSize, GLvoid *pixels)
843{
844 gl::Framebuffer *framebuffer = context->getReadFramebuffer();
Jamie Madill893ab082014-05-16 16:56:10 -0400845 ASSERT(framebuffer);
Jamie Madill26e91952014-03-05 15:01:27 -0500846
847 if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
848 {
849 return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
850 }
851
852 if (context->getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0)
853 {
854 return gl::error(GL_INVALID_OPERATION, false);
855 }
856
Jamie Madill893ab082014-05-16 16:56:10 -0400857 if (!framebuffer->getReadColorbuffer())
858 {
859 return gl::error(GL_INVALID_OPERATION, false);
860 }
861
Jamie Madill26e91952014-03-05 15:01:27 -0500862 GLenum currentInternalFormat, currentFormat, currentType;
Geoff Lange4a492b2014-06-19 14:14:41 -0400863 GLuint clientVersion = context->getClientVersion();
Jamie Madill26e91952014-03-05 15:01:27 -0500864
Jamie Madill893ab082014-05-16 16:56:10 -0400865 context->getCurrentReadFormatType(&currentInternalFormat, &currentFormat, &currentType);
Jamie Madill26e91952014-03-05 15:01:27 -0500866
Geoff Langbdc9b2f2014-04-16 14:41:54 -0400867 bool validReadFormat = (clientVersion < 3) ? ValidES2ReadFormatType(context, format, type) :
868 ValidES3ReadFormatType(context, currentInternalFormat, format, type);
Jamie Madill26e91952014-03-05 15:01:27 -0500869
870 if (!(currentFormat == format && currentType == type) && !validReadFormat)
871 {
872 return gl::error(GL_INVALID_OPERATION, false);
873 }
874
Geoff Lange4a492b2014-06-19 14:14:41 -0400875 GLenum sizedInternalFormat = IsSizedInternalFormat(format) ? format
876 : GetSizedInternalFormat(format, type);
Jamie Madill26e91952014-03-05 15:01:27 -0500877
Geoff Lange4a492b2014-06-19 14:14:41 -0400878 GLsizei outputPitch = GetRowPitch(sizedInternalFormat, type, width, context->getPackAlignment());
Jamie Madill26e91952014-03-05 15:01:27 -0500879 // sized query sanity check
880 if (bufSize)
881 {
882 int requiredSize = outputPitch * height;
883 if (requiredSize > *bufSize)
884 {
885 return gl::error(GL_INVALID_OPERATION, false);
886 }
887 }
888
889 return true;
890}
891
Jamie Madilldb2f14c2014-05-13 13:56:30 -0400892bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id)
893{
894 if (!ValidQueryType(context, target))
895 {
896 return gl::error(GL_INVALID_ENUM, false);
897 }
898
899 if (id == 0)
900 {
901 return gl::error(GL_INVALID_OPERATION, false);
902 }
903
904 // From EXT_occlusion_query_boolean: If BeginQueryEXT is called with an <id>
905 // of zero, if the active query object name for <target> is non-zero (for the
906 // targets ANY_SAMPLES_PASSED_EXT and ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if
907 // the active query for either target is non-zero), if <id> is the name of an
908 // existing query object whose type does not match <target>, or if <id> is the
909 // active query object name for any query type, the error INVALID_OPERATION is
910 // generated.
911
912 // Ensure no other queries are active
913 // NOTE: If other queries than occlusion are supported, we will need to check
914 // separately that:
915 // a) The query ID passed is not the current active query for any target/type
916 // b) There are no active queries for the requested target (and in the case
917 // of GL_ANY_SAMPLES_PASSED_EXT and GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT,
918 // no query may be active for either if glBeginQuery targets either.
919 if (context->isQueryActive())
920 {
921 return gl::error(GL_INVALID_OPERATION, false);
922 }
923
924 Query *queryObject = context->getQuery(id, true, target);
925
926 // check that name was obtained with glGenQueries
927 if (!queryObject)
928 {
929 return gl::error(GL_INVALID_OPERATION, false);
930 }
931
932 // check for type mismatch
933 if (queryObject->getType() != target)
934 {
935 return gl::error(GL_INVALID_OPERATION, false);
936 }
937
938 return true;
939}
940
Jamie Madill45c785d2014-05-13 14:09:34 -0400941bool ValidateEndQuery(gl::Context *context, GLenum target)
942{
943 if (!ValidQueryType(context, target))
944 {
945 return gl::error(GL_INVALID_ENUM, false);
946 }
947
948 const Query *queryObject = context->getActiveQuery(target);
949
950 if (queryObject == NULL)
951 {
952 return gl::error(GL_INVALID_OPERATION, false);
953 }
954
955 if (!queryObject->isStarted())
956 {
957 return gl::error(GL_INVALID_OPERATION, false);
958 }
959
960 return true;
961}
962
Jamie Madill36398922014-05-20 14:51:53 -0400963static bool ValidateUniformCommonBase(gl::Context *context, GLenum targetUniformType,
964 GLint location, GLsizei count, LinkedUniform **uniformOut)
Jamie Madilld7c7bb22014-05-20 10:55:54 -0400965{
966 if (count < 0)
967 {
968 return gl::error(GL_INVALID_VALUE, false);
969 }
970
Jamie Madilld7c7bb22014-05-20 10:55:54 -0400971 gl::ProgramBinary *programBinary = context->getCurrentProgramBinary();
972 if (!programBinary)
973 {
974 return gl::error(GL_INVALID_OPERATION, false);
975 }
976
977 if (location == -1)
978 {
979 // Silently ignore the uniform command
980 return false;
981 }
982
Jamie Madill36398922014-05-20 14:51:53 -0400983 if (!programBinary->isValidUniformLocation(location))
984 {
985 return gl::error(GL_INVALID_OPERATION, false);
986 }
987
988 LinkedUniform *uniform = programBinary->getUniformByLocation(location);
989
990 // attempting to write an array to a non-array uniform is an INVALID_OPERATION
991 if (uniform->elementCount() == 1 && count > 1)
992 {
993 return gl::error(GL_INVALID_OPERATION, false);
994 }
995
996 *uniformOut = uniform;
Jamie Madilld7c7bb22014-05-20 10:55:54 -0400997 return true;
998}
999
Jamie Madillaa981bd2014-05-20 10:55:55 -04001000bool ValidateUniform(gl::Context *context, GLenum uniformType, GLint location, GLsizei count)
1001{
1002 // Check for ES3 uniform entry points
Jamie Madillf2575982014-06-25 16:04:54 -04001003 if (VariableComponentType(uniformType) == GL_UNSIGNED_INT && context->getClientVersion() < 3)
Jamie Madillaa981bd2014-05-20 10:55:55 -04001004 {
1005 return gl::error(GL_INVALID_OPERATION, false);
1006 }
1007
Jamie Madill36398922014-05-20 14:51:53 -04001008 LinkedUniform *uniform = NULL;
1009 if (!ValidateUniformCommonBase(context, uniformType, location, count, &uniform))
1010 {
1011 return false;
1012 }
1013
Jamie Madillf2575982014-06-25 16:04:54 -04001014 GLenum targetBoolType = VariableBoolVectorType(uniformType);
Jamie Madill36398922014-05-20 14:51:53 -04001015 bool samplerUniformCheck = (IsSampler(uniform->type) && uniformType == GL_INT);
1016 if (!samplerUniformCheck && uniformType != uniform->type && targetBoolType != uniform->type)
1017 {
1018 return gl::error(GL_INVALID_OPERATION, false);
1019 }
1020
1021 return true;
Jamie Madillaa981bd2014-05-20 10:55:55 -04001022}
1023
1024bool ValidateUniformMatrix(gl::Context *context, GLenum matrixType, GLint location, GLsizei count,
1025 GLboolean transpose)
1026{
1027 // Check for ES3 uniform entry points
1028 int rows = VariableRowCount(matrixType);
1029 int cols = VariableColumnCount(matrixType);
1030 if (rows != cols && context->getClientVersion() < 3)
1031 {
1032 return gl::error(GL_INVALID_OPERATION, false);
1033 }
1034
1035 if (transpose != GL_FALSE && context->getClientVersion() < 3)
1036 {
1037 return gl::error(GL_INVALID_VALUE, false);
1038 }
1039
Jamie Madill36398922014-05-20 14:51:53 -04001040 LinkedUniform *uniform = NULL;
1041 if (!ValidateUniformCommonBase(context, matrixType, location, count, &uniform))
1042 {
1043 return false;
1044 }
1045
1046 if (uniform->type != matrixType)
1047 {
1048 return gl::error(GL_INVALID_OPERATION, false);
1049 }
1050
1051 return true;
Jamie Madillaa981bd2014-05-20 10:55:55 -04001052}
1053
Jamie Madill893ab082014-05-16 16:56:10 -04001054bool ValidateStateQuery(gl::Context *context, GLenum pname, GLenum *nativeType, unsigned int *numParams)
1055{
1056 if (!context->getQueryParameterInfo(pname, nativeType, numParams))
1057 {
1058 return gl::error(GL_INVALID_ENUM, false);
1059 }
1060
1061 if (pname >= GL_DRAW_BUFFER0 && pname <= GL_DRAW_BUFFER15)
1062 {
1063 unsigned int colorAttachment = (pname - GL_DRAW_BUFFER0);
1064
Geoff Langaae65a42014-05-26 12:43:44 -04001065 if (colorAttachment >= context->getCaps().maxDrawBuffers)
Jamie Madill893ab082014-05-16 16:56:10 -04001066 {
1067 return gl::error(GL_INVALID_OPERATION, false);
1068 }
1069 }
1070
1071 switch (pname)
1072 {
1073 case GL_TEXTURE_BINDING_2D:
1074 case GL_TEXTURE_BINDING_CUBE_MAP:
1075 case GL_TEXTURE_BINDING_3D:
1076 case GL_TEXTURE_BINDING_2D_ARRAY:
1077 if (context->getActiveSampler() >= context->getMaximumCombinedTextureImageUnits())
1078 {
1079 return gl::error(GL_INVALID_OPERATION, false);
1080 }
1081 break;
1082
1083 case GL_IMPLEMENTATION_COLOR_READ_TYPE:
1084 case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
1085 {
1086 Framebuffer *framebuffer = context->getReadFramebuffer();
1087 ASSERT(framebuffer);
1088 if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
1089 {
1090 return gl::error(GL_INVALID_OPERATION, false);
1091 }
1092
Jamie Madill3c7fa222014-06-05 13:08:51 -04001093 FramebufferAttachment *attachment = framebuffer->getReadColorbuffer();
1094 if (!attachment)
Jamie Madill893ab082014-05-16 16:56:10 -04001095 {
1096 return gl::error(GL_INVALID_OPERATION, false);
1097 }
1098 }
1099 break;
1100
1101 default:
1102 break;
1103 }
1104
1105 // pname is valid, but there are no parameters to return
1106 if (numParams == 0)
1107 {
1108 return false;
1109 }
1110
1111 return true;
1112}
1113
Jamie Madill560a8d82014-05-21 13:06:20 -04001114bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLint level, GLenum internalformat, bool isSubImage,
1115 GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height,
1116 GLint border, GLenum *textureFormatOut)
1117{
1118
1119 if (!ValidTexture2DDestinationTarget(context, target))
1120 {
1121 return gl::error(GL_INVALID_ENUM, false);
1122 }
1123
1124 if (level < 0 || xoffset < 0 || yoffset < 0 || zoffset < 0 || width < 0 || height < 0)
1125 {
1126 return gl::error(GL_INVALID_VALUE, false);
1127 }
1128
1129 if (std::numeric_limits<GLsizei>::max() - xoffset < width || std::numeric_limits<GLsizei>::max() - yoffset < height)
1130 {
1131 return gl::error(GL_INVALID_VALUE, false);
1132 }
1133
1134 if (border != 0)
1135 {
1136 return gl::error(GL_INVALID_VALUE, false);
1137 }
1138
1139 if (!ValidMipLevel(context, target, level))
1140 {
1141 return gl::error(GL_INVALID_VALUE, false);
1142 }
1143
1144 gl::Framebuffer *framebuffer = context->getReadFramebuffer();
1145 if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
1146 {
1147 return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
1148 }
1149
1150 if (context->getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0)
1151 {
1152 return gl::error(GL_INVALID_OPERATION, false);
1153 }
1154
Geoff Langaae65a42014-05-26 12:43:44 -04001155 const gl::Caps &caps = context->getCaps();
1156
Jamie Madill560a8d82014-05-21 13:06:20 -04001157 gl::Texture *texture = NULL;
1158 GLenum textureInternalFormat = GL_NONE;
1159 bool textureCompressed = false;
1160 bool textureIsDepth = false;
1161 GLint textureLevelWidth = 0;
1162 GLint textureLevelHeight = 0;
1163 GLint textureLevelDepth = 0;
Geoff Langaae65a42014-05-26 12:43:44 -04001164 GLuint maxDimension = 0;
Jamie Madill560a8d82014-05-21 13:06:20 -04001165
1166 switch (target)
1167 {
1168 case GL_TEXTURE_2D:
1169 {
1170 gl::Texture2D *texture2d = context->getTexture2D();
1171 if (texture2d)
1172 {
1173 textureInternalFormat = texture2d->getInternalFormat(level);
1174 textureCompressed = texture2d->isCompressed(level);
1175 textureIsDepth = texture2d->isDepth(level);
1176 textureLevelWidth = texture2d->getWidth(level);
1177 textureLevelHeight = texture2d->getHeight(level);
1178 textureLevelDepth = 1;
1179 texture = texture2d;
Geoff Langaae65a42014-05-26 12:43:44 -04001180 maxDimension = caps.max2DTextureSize;
Jamie Madill560a8d82014-05-21 13:06:20 -04001181 }
1182 }
1183 break;
1184
1185 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
1186 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
1187 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
1188 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
1189 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
1190 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
1191 {
1192 gl::TextureCubeMap *textureCube = context->getTextureCubeMap();
1193 if (textureCube)
1194 {
1195 textureInternalFormat = textureCube->getInternalFormat(target, level);
1196 textureCompressed = textureCube->isCompressed(target, level);
1197 textureIsDepth = false;
1198 textureLevelWidth = textureCube->getWidth(target, level);
1199 textureLevelHeight = textureCube->getHeight(target, level);
1200 textureLevelDepth = 1;
1201 texture = textureCube;
Geoff Langaae65a42014-05-26 12:43:44 -04001202 maxDimension = caps.maxCubeMapTextureSize;
Jamie Madill560a8d82014-05-21 13:06:20 -04001203 }
1204 }
1205 break;
1206
1207 case GL_TEXTURE_2D_ARRAY:
1208 {
1209 gl::Texture2DArray *texture2dArray = context->getTexture2DArray();
1210 if (texture2dArray)
1211 {
1212 textureInternalFormat = texture2dArray->getInternalFormat(level);
1213 textureCompressed = texture2dArray->isCompressed(level);
1214 textureIsDepth = texture2dArray->isDepth(level);
1215 textureLevelWidth = texture2dArray->getWidth(level);
1216 textureLevelHeight = texture2dArray->getHeight(level);
1217 textureLevelDepth = texture2dArray->getLayers(level);
1218 texture = texture2dArray;
Geoff Langaae65a42014-05-26 12:43:44 -04001219 maxDimension = caps.max2DTextureSize;
Jamie Madill560a8d82014-05-21 13:06:20 -04001220 }
1221 }
1222 break;
1223
1224 case GL_TEXTURE_3D:
1225 {
1226 gl::Texture3D *texture3d = context->getTexture3D();
1227 if (texture3d)
1228 {
1229 textureInternalFormat = texture3d->getInternalFormat(level);
1230 textureCompressed = texture3d->isCompressed(level);
1231 textureIsDepth = texture3d->isDepth(level);
1232 textureLevelWidth = texture3d->getWidth(level);
1233 textureLevelHeight = texture3d->getHeight(level);
1234 textureLevelDepth = texture3d->getDepth(level);
1235 texture = texture3d;
Geoff Langaae65a42014-05-26 12:43:44 -04001236 maxDimension = caps.max3DTextureSize;
Jamie Madill560a8d82014-05-21 13:06:20 -04001237 }
1238 }
1239 break;
1240
1241 default:
1242 return gl::error(GL_INVALID_ENUM, false);
1243 }
1244
1245 if (!texture)
1246 {
1247 return gl::error(GL_INVALID_OPERATION, false);
1248 }
1249
1250 if (texture->isImmutable() && !isSubImage)
1251 {
1252 return gl::error(GL_INVALID_OPERATION, false);
1253 }
1254
1255 if (textureIsDepth)
1256 {
1257 return gl::error(GL_INVALID_OPERATION, false);
1258 }
1259
1260 if (textureCompressed)
1261 {
Geoff Lange4a492b2014-06-19 14:14:41 -04001262 GLint blockWidth = GetCompressedBlockWidth(textureInternalFormat);
1263 GLint blockHeight = GetCompressedBlockHeight(textureInternalFormat);
Jamie Madill560a8d82014-05-21 13:06:20 -04001264
1265 if (((width % blockWidth) != 0 && width != textureLevelWidth) ||
1266 ((height % blockHeight) != 0 && height != textureLevelHeight))
1267 {
1268 return gl::error(GL_INVALID_OPERATION, false);
1269 }
1270 }
1271
1272 if (isSubImage)
1273 {
1274 if (xoffset + width > textureLevelWidth ||
1275 yoffset + height > textureLevelHeight ||
1276 zoffset >= textureLevelDepth)
1277 {
1278 return gl::error(GL_INVALID_VALUE, false);
1279 }
1280 }
Jamie Madill6f38f822014-06-06 17:12:20 -04001281 else
1282 {
1283 if (IsCubemapTextureTarget(target) && width != height)
1284 {
1285 return gl::error(GL_INVALID_VALUE, false);
1286 }
1287
Geoff Langc0b9ef42014-07-02 10:02:37 -04001288 if (!IsValidInternalFormat(internalformat, context->getExtensions(), context->getClientVersion()))
Jamie Madill6f38f822014-06-06 17:12:20 -04001289 {
1290 return gl::error(GL_INVALID_ENUM, false);
1291 }
1292
1293 int maxLevelDimension = (maxDimension >> level);
1294 if (static_cast<int>(width) > maxLevelDimension || static_cast<int>(height) > maxLevelDimension)
1295 {
1296 return gl::error(GL_INVALID_VALUE, false);
1297 }
1298 }
Jamie Madill560a8d82014-05-21 13:06:20 -04001299
1300 *textureFormatOut = textureInternalFormat;
1301 return true;
1302}
1303
Jamie Madill1aeb1312014-06-20 13:21:25 -04001304static bool ValidateDrawBase(const gl::Context *context, GLenum mode, GLsizei count)
Jamie Madill250d33f2014-06-06 17:09:03 -04001305{
Jamie Madill1aeb1312014-06-20 13:21:25 -04001306 switch (mode)
1307 {
1308 case GL_POINTS:
1309 case GL_LINES:
1310 case GL_LINE_LOOP:
1311 case GL_LINE_STRIP:
1312 case GL_TRIANGLES:
1313 case GL_TRIANGLE_STRIP:
1314 case GL_TRIANGLE_FAN:
1315 break;
1316 default:
1317 return gl::error(GL_INVALID_ENUM, false);
1318 }
1319
Jamie Madill250d33f2014-06-06 17:09:03 -04001320 if (count < 0)
1321 {
1322 return gl::error(GL_INVALID_VALUE, false);
1323 }
1324
Jamie Madill250d33f2014-06-06 17:09:03 -04001325 // Check for mapped buffers
Jamie Madillfd716582014-06-06 17:09:04 -04001326 if (context->hasMappedBuffer(GL_ARRAY_BUFFER))
Jamie Madill250d33f2014-06-06 17:09:03 -04001327 {
1328 return gl::error(GL_INVALID_OPERATION, false);
1329 }
1330
Jamie Madillac528012014-06-20 13:21:23 -04001331 const gl::DepthStencilState &depthStencilState = context->getDepthStencilState();
1332 if (depthStencilState.stencilWritemask != depthStencilState.stencilBackWritemask ||
1333 context->getStencilRef() != context->getStencilBackRef() ||
1334 depthStencilState.stencilMask != depthStencilState.stencilBackMask)
1335 {
1336 // Note: these separate values are not supported in WebGL, due to D3D's limitations.
1337 // See Section 6.10 of the WebGL 1.0 spec
1338 ERR("This ANGLE implementation does not support separate front/back stencil "
1339 "writemasks, reference values, or stencil mask values.");
1340 return gl::error(GL_INVALID_OPERATION, false);
1341 }
1342
Jamie Madill13f7d7d2014-06-20 13:21:27 -04001343 const gl::Framebuffer *fbo = context->getDrawFramebuffer();
1344 if (!fbo || fbo->completeness() != GL_FRAMEBUFFER_COMPLETE)
1345 {
1346 return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
1347 }
1348
Jamie Madill250d33f2014-06-06 17:09:03 -04001349 // No-op if zero count
1350 return (count > 0);
1351}
1352
Jamie Madillfd716582014-06-06 17:09:04 -04001353bool ValidateDrawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count)
Jamie Madill250d33f2014-06-06 17:09:03 -04001354{
Jamie Madillfd716582014-06-06 17:09:04 -04001355 if (first < 0)
Jamie Madill250d33f2014-06-06 17:09:03 -04001356 {
1357 return gl::error(GL_INVALID_VALUE, false);
1358 }
1359
Jamie Madillfd716582014-06-06 17:09:04 -04001360 gl::TransformFeedback *curTransformFeedback = context->getCurrentTransformFeedback();
1361 if (curTransformFeedback && curTransformFeedback->isStarted() && !curTransformFeedback->isPaused() &&
1362 curTransformFeedback->getDrawMode() != mode)
1363 {
1364 // It is an invalid operation to call DrawArrays or DrawArraysInstanced with a draw mode
1365 // that does not match the current transform feedback object's draw mode (if transform feedback
1366 // is active), (3.0.2, section 2.14, pg 86)
1367 return gl::error(GL_INVALID_OPERATION, false);
1368 }
1369
Jamie Madill1aeb1312014-06-20 13:21:25 -04001370 if (!ValidateDrawBase(context, mode, count))
Jamie Madillfd716582014-06-06 17:09:04 -04001371 {
1372 return false;
1373 }
1374
1375 return true;
1376}
1377
1378bool ValidateDrawArraysInstanced(const gl::Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount)
1379{
1380 if (primcount < 0)
1381 {
1382 return gl::error(GL_INVALID_VALUE, false);
1383 }
1384
1385 if (!ValidateDrawArrays(context, mode, first, count))
1386 {
1387 return false;
1388 }
1389
1390 // No-op if zero primitive count
1391 return (primcount > 0);
1392}
1393
1394bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)
1395{
Jamie Madill250d33f2014-06-06 17:09:03 -04001396 switch (type)
1397 {
1398 case GL_UNSIGNED_BYTE:
1399 case GL_UNSIGNED_SHORT:
1400 break;
1401 case GL_UNSIGNED_INT:
Geoff Langc0b9ef42014-07-02 10:02:37 -04001402 if (!context->getExtensions().elementIndexUint)
Jamie Madill250d33f2014-06-06 17:09:03 -04001403 {
1404 return gl::error(GL_INVALID_ENUM, false);
1405 }
1406 break;
1407 default:
1408 return gl::error(GL_INVALID_ENUM, false);
1409 }
1410
1411 gl::TransformFeedback *curTransformFeedback = context->getCurrentTransformFeedback();
1412 if (curTransformFeedback && curTransformFeedback->isStarted() && !curTransformFeedback->isPaused())
1413 {
1414 // It is an invalid operation to call DrawElements, DrawRangeElements or DrawElementsInstanced
1415 // while transform feedback is active, (3.0.2, section 2.14, pg 86)
1416 return gl::error(GL_INVALID_OPERATION, false);
1417 }
1418
1419 // Check for mapped buffers
Jamie Madillfd716582014-06-06 17:09:04 -04001420 if (context->hasMappedBuffer(GL_ELEMENT_ARRAY_BUFFER))
Jamie Madill250d33f2014-06-06 17:09:03 -04001421 {
1422 return gl::error(GL_INVALID_OPERATION, false);
1423 }
1424
Jamie Madill1aeb1312014-06-20 13:21:25 -04001425 if (!ValidateDrawBase(context, mode, count))
Jamie Madillfd716582014-06-06 17:09:04 -04001426 {
1427 return false;
1428 }
1429
1430 return true;
1431}
1432
1433bool ValidateDrawElementsInstanced(const gl::Context *context, GLenum mode, GLsizei count, GLenum type,
1434 const GLvoid *indices, GLsizei primcount)
1435{
1436 if (primcount < 0)
1437 {
1438 return gl::error(GL_INVALID_VALUE, false);
1439 }
1440
1441 if (!ValidateDrawElements(context, mode, count, type, indices))
1442 {
1443 return false;
1444 }
1445
1446 // No-op zero primitive count
1447 return (primcount > 0);
Jamie Madill250d33f2014-06-06 17:09:03 -04001448}
1449
Jamie Madill55ec3b12014-07-03 10:38:57 -04001450bool ValidateFramebufferTextureBase(const gl::Context *context, GLenum target, GLenum attachment,
1451 GLuint texture, GLint level)
Jamie Madill570f7c82014-07-03 10:38:54 -04001452{
Jamie Madill55ec3b12014-07-03 10:38:57 -04001453 if (!ValidFramebufferTarget(target))
1454 {
1455 return gl::error(GL_INVALID_ENUM, false);
1456 }
1457
1458 if (!ValidateAttachmentTarget(context, attachment))
Jamie Madill570f7c82014-07-03 10:38:54 -04001459 {
1460 return false;
1461 }
1462
Jamie Madill55ec3b12014-07-03 10:38:57 -04001463 if (texture != 0)
1464 {
1465 gl::Texture *tex = context->getTexture(texture);
1466
1467 if (tex == NULL)
1468 {
1469 return gl::error(GL_INVALID_OPERATION, false);
1470 }
1471
1472 if (level < 0)
1473 {
1474 return gl::error(GL_INVALID_VALUE, false);
1475 }
1476 }
1477
1478 const gl::Framebuffer *framebuffer = context->getTargetFramebuffer(target);
1479 GLuint framebufferHandle = context->getTargetFramebufferHandle(target);
1480
1481 if (framebufferHandle == 0 || !framebuffer)
1482 {
1483 return gl::error(GL_INVALID_OPERATION, false);
1484 }
1485
1486 return true;
1487}
1488
1489bool ValidateFramebufferTexture2D(const gl::Context *context, GLenum target, GLenum attachment,
1490 GLenum textarget, GLuint texture, GLint level)
1491{
1492 // Attachments are required to be bound to level 0 in ES2
1493 if (context->getClientVersion() < 3 && level != 0)
1494 {
1495 return gl::error(GL_INVALID_VALUE, false);
1496 }
1497
1498 if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
Jamie Madill570f7c82014-07-03 10:38:54 -04001499 {
1500 return false;
1501 }
1502
Jamie Madill55ec3b12014-07-03 10:38:57 -04001503 if (texture != 0)
1504 {
1505 gl::Texture *tex = context->getTexture(texture);
1506 ASSERT(tex);
1507
Jamie Madill2a6564e2014-07-11 09:53:19 -04001508 const gl::Caps &caps = context->getCaps();
1509
Jamie Madill55ec3b12014-07-03 10:38:57 -04001510 switch (textarget)
1511 {
1512 case GL_TEXTURE_2D:
1513 {
Jamie Madill2a6564e2014-07-11 09:53:19 -04001514 if (level > gl::log2(caps.max2DTextureSize))
Jamie Madill55ec3b12014-07-03 10:38:57 -04001515 {
1516 return gl::error(GL_INVALID_VALUE, false);
1517 }
1518 if (tex->getTarget() != GL_TEXTURE_2D)
1519 {
1520 return gl::error(GL_INVALID_OPERATION, false);
1521 }
1522 gl::Texture2D *tex2d = static_cast<gl::Texture2D *>(tex);
1523 if (tex2d->isCompressed(level))
1524 {
1525 return gl::error(GL_INVALID_OPERATION, false);
1526 }
1527 }
1528 break;
1529
1530 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
1531 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
1532 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
1533 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
1534 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
1535 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
1536 {
Jamie Madill2a6564e2014-07-11 09:53:19 -04001537 if (level > gl::log2(caps.maxCubeMapTextureSize))
Jamie Madill55ec3b12014-07-03 10:38:57 -04001538 {
1539 return gl::error(GL_INVALID_VALUE, false);
1540 }
1541 if (tex->getTarget() != GL_TEXTURE_CUBE_MAP)
1542 {
1543 return gl::error(GL_INVALID_OPERATION, false);
1544 }
1545 gl::TextureCubeMap *texcube = static_cast<gl::TextureCubeMap *>(tex);
1546 if (texcube->isCompressed(textarget, level))
1547 {
1548 return gl::error(GL_INVALID_OPERATION, false);
1549 }
1550 }
1551 break;
1552
1553 default:
1554 return gl::error(GL_INVALID_ENUM, false);
1555 }
1556 }
1557
Jamie Madill570f7c82014-07-03 10:38:54 -04001558 return true;
1559}
1560
Geoff Lange8ebe7f2013-08-05 15:03:13 -04001561}