summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/validationES3.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/validationES3.cpp')
-rw-r--r--gfx/angle/checkout/src/libANGLE/validationES3.cpp4330
1 files changed, 4330 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/validationES3.cpp b/gfx/angle/checkout/src/libANGLE/validationES3.cpp
new file mode 100644
index 0000000000..869a2033f4
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/validationES3.cpp
@@ -0,0 +1,4330 @@
+//
+// Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// validationES3.cpp: Validation functions for OpenGL ES 3.0 entry point parameters
+
+#include "libANGLE/validationES3_autogen.h"
+
+#include "anglebase/numerics/safe_conversions.h"
+#include "common/mathutil.h"
+#include "common/utilities.h"
+#include "libANGLE/Context.h"
+#include "libANGLE/ErrorStrings.h"
+#include "libANGLE/Framebuffer.h"
+#include "libANGLE/FramebufferAttachment.h"
+#include "libANGLE/Renderbuffer.h"
+#include "libANGLE/Texture.h"
+#include "libANGLE/VertexArray.h"
+#include "libANGLE/formatutils.h"
+#include "libANGLE/validationES.h"
+
+using namespace angle;
+
+namespace gl
+{
+using namespace err;
+
+namespace
+{
+bool ValidateFramebufferTextureMultiviewBaseANGLE(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLsizei numViews)
+{
+ if (!(context->getExtensions().multiview || context->getExtensions().multiview2))
+ {
+ context->validationError(GL_INVALID_OPERATION, kMultiviewNotAvailable);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ if (texture != 0 && numViews < 1)
+ {
+ context->validationError(GL_INVALID_VALUE, kMultiviewViewsTooSmall);
+ return false;
+ }
+
+ const Extensions &extensions = context->getExtensions();
+ if (static_cast<GLuint>(numViews) > extensions.maxViews)
+ {
+ context->validationError(GL_INVALID_VALUE, kMultiviewViewsTooLarge);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferTextureMultiviewLevelAndFormat(Context *context,
+ Texture *texture,
+ GLint level)
+{
+ TextureType type = texture->getType();
+ if (!ValidMipLevel(context, type, level))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ const auto &format = texture->getFormat(NonCubeTextureTypeToTarget(type), level);
+ if (format.info->compressed)
+ {
+ context->validationError(GL_INVALID_OPERATION, kCompressedTexturesNotAttachable);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateUniformES3(Context *context, GLenum uniformType, GLint location, GLint count)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateUniform(context, uniformType, location, count);
+}
+
+bool ValidateUniformMatrixES3(Context *context,
+ GLenum valueType,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose)
+{
+ // Check for ES3 uniform entry points
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateUniformMatrix(context, valueType, location, count, transpose);
+}
+
+bool ValidateGenOrDeleteES3(Context *context, GLint n)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenOrDeleteCountES3(Context *context, GLint count)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ if (count < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateCopyTexture3DCommon(Context *context,
+ const Texture *source,
+ GLint sourceLevel,
+ GLint srcInternalFormat,
+ const Texture *dest,
+ GLint destLevel,
+ GLint internalFormat,
+ TextureTarget destTarget)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!context->getExtensions().copyTexture3d)
+ {
+ context->validationError(GL_INVALID_OPERATION, kANGLECopyTexture3DUnavailable);
+ return false;
+ }
+
+ if (!ValidTexture3DTarget(context, source->getType()))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ // Table 1.1 from the ANGLE_copy_texture_3d spec
+ switch (GetUnsizedFormat(srcInternalFormat))
+ {
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ case GL_LUMINANCE_ALPHA:
+ case GL_RED:
+ case GL_RED_INTEGER:
+ case GL_RG:
+ case GL_RG_INTEGER:
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_STENCIL:
+ break;
+ default:
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+
+ if (!ValidTexture3DTarget(context, TextureTargetToType(destTarget)))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ // Table 1.0 from the ANGLE_copy_texture_3d spec
+ switch (internalFormat)
+ {
+ case GL_RGB:
+ case GL_RGBA:
+ case GL_LUMINANCE:
+ case GL_LUMINANCE_ALPHA:
+ case GL_ALPHA:
+ case GL_R8:
+ case GL_R8_SNORM:
+ case GL_R16F:
+ case GL_R32F:
+ case GL_R8UI:
+ case GL_R8I:
+ case GL_R16UI:
+ case GL_R16I:
+ case GL_R32UI:
+ case GL_R32I:
+ case GL_RG:
+ case GL_RG8:
+ case GL_RG8_SNORM:
+ case GL_RG16F:
+ case GL_RG32F:
+ case GL_RG8UI:
+ case GL_RG8I:
+ case GL_RG16UI:
+ case GL_RG16I:
+ case GL_RG32UI:
+ case GL_RG32I:
+ case GL_RGB8:
+ case GL_SRGB8:
+ case GL_RGB565:
+ case GL_RGB8_SNORM:
+ case GL_R11F_G11F_B10F:
+ case GL_RGB9_E5:
+ case GL_RGB16F:
+ case GL_RGB32F:
+ case GL_RGB8UI:
+ case GL_RGB8I:
+ case GL_RGB16UI:
+ case GL_RGB16I:
+ case GL_RGB32UI:
+ case GL_RGB32I:
+ case GL_RGBA8:
+ case GL_SRGB8_ALPHA8:
+ case GL_RGBA8_SNORM:
+ case GL_RGB5_A1:
+ case GL_RGBA4:
+ case GL_RGB10_A2:
+ case GL_RGBA16F:
+ case GL_RGBA32F:
+ case GL_RGBA8UI:
+ case GL_RGBA8I:
+ case GL_RGB10_A2UI:
+ case GL_RGBA16UI:
+ case GL_RGBA16I:
+ case GL_RGBA32I:
+ case GL_RGBA32UI:
+ break;
+ default:
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+
+ return true;
+}
+} // anonymous namespace
+
+static bool ValidateTexImageFormatCombination(gl::Context *context,
+ TextureType target,
+ GLenum internalFormat,
+ GLenum format,
+ GLenum type)
+{
+
+ // The type and format are valid if any supported internal format has that type and format
+ if (!ValidES3Format(format))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (!ValidES3Type(type))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidType);
+ return false;
+ }
+
+ // For historical reasons, glTexImage2D and glTexImage3D pass in their internal format as a
+ // GLint instead of a GLenum. Therefor an invalid internal format gives a GL_INVALID_VALUE
+ // error instead of a GL_INVALID_ENUM error. As this validation function is only called in
+ // the validation codepaths for glTexImage2D/3D, we record a GL_INVALID_VALUE error.
+ if (!ValidES3InternalFormat(internalFormat))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidInternalFormat);
+ return false;
+ }
+
+ // From the ES 3.0 spec section 3.8.3:
+ // Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL are supported by
+ // texture image specification commands only if target is TEXTURE_2D, TEXTURE_2D_ARRAY, or
+ // TEXTURE_CUBE_MAP.Using these formats in conjunction with any other target will result in an
+ // INVALID_OPERATION error.
+ if (target == TextureType::_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL))
+ {
+ context->validationError(GL_INVALID_OPERATION, k3DDepthStencil);
+ return false;
+ }
+
+ // Check if this is a valid format combination to load texture data
+ if (!ValidES3FormatCombination(format, type, internalFormat))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormatCombination);
+ return false;
+ }
+
+ const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type);
+ if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexImageParametersBase(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isCompressed,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ GLsizei imageSize,
+ const void *pixels)
+{
+ TextureType texType = TextureTargetToType(target);
+
+ // Validate image size
+ if (!ValidImageSizeParameters(context, texType, level, width, height, depth, isSubImage))
+ {
+ // Error already processed.
+ return false;
+ }
+
+ // Verify zero border
+ if (border != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidBorder);
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0 || zoffset < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (std::numeric_limits<GLsizei>::max() - xoffset < width ||
+ std::numeric_limits<GLsizei>::max() - yoffset < height ||
+ std::numeric_limits<GLsizei>::max() - zoffset < depth)
+ {
+ context->validationError(GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ const gl::Caps &caps = context->getCaps();
+
+ switch (texType)
+ {
+ case TextureType::_2D:
+ case TextureType::External:
+ if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ case TextureType::Rectangle:
+ ASSERT(level == 0);
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ if (isCompressed)
+ {
+ context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+ break;
+
+ case TextureType::CubeMap:
+ if (!isSubImage && width != height)
+ {
+ context->validationError(GL_INVALID_VALUE, kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level))
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ case TextureType::_3D:
+ if (static_cast<GLuint>(width) > (caps.max3DTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.max3DTextureSize >> level) ||
+ static_cast<GLuint>(depth) > (caps.max3DTextureSize >> level))
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ case TextureType::_2DArray:
+ if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.max2DTextureSize >> level) ||
+ static_cast<GLuint>(depth) > caps.maxArrayTextureLayers)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ gl::Texture *texture = context->getTextureByType(texType);
+ if (!texture)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMissingTexture);
+ return false;
+ }
+
+ if (texture->getImmutableFormat() && !isSubImage)
+ {
+ context->validationError(GL_INVALID_OPERATION, kTextureIsImmutable);
+ return false;
+ }
+
+ // Validate texture formats
+ GLenum actualInternalFormat =
+ isSubImage ? texture->getFormat(target, level).info->internalFormat : internalformat;
+ if (isSubImage && actualInternalFormat == GL_NONE)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidMipLevel);
+ return false;
+ }
+
+ const gl::InternalFormat &actualFormatInfo = isSubImage
+ ? *texture->getFormat(target, level).info
+ : GetInternalFormatInfo(internalformat, type);
+ if (isCompressed)
+ {
+ if (!actualFormatInfo.compressed)
+ {
+ context->validationError(GL_INVALID_ENUM, kCompressedMismatch);
+ return false;
+ }
+
+ if (isSubImage)
+ {
+ if (!ValidCompressedSubImageSize(
+ context, actualFormatInfo.internalFormat, xoffset, yoffset, zoffset, width,
+ height, depth, texture->getWidth(target, level),
+ texture->getHeight(target, level), texture->getDepth(target, level)))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidCompressedImageSize);
+ return false;
+ }
+
+ if (format != actualInternalFormat)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMismatchedFormat);
+ return false;
+ }
+
+ if (actualInternalFormat == GL_ETC1_RGB8_OES)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height,
+ depth))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidCompressedImageSize);
+ return false;
+ }
+ }
+
+ if (!actualFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (texType == TextureType::_3D)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget);
+ return false;
+ }
+
+ // Disallow 3D-only compressed formats from being set on 2D textures
+ if (actualFormatInfo.compressedBlockDepth > 1 && texType != TextureType::_2DArray)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidateTexImageFormatCombination(context, texType, actualInternalFormat, format,
+ type))
+ {
+ return false;
+ }
+ }
+
+ // Validate sub image parameters
+ if (isSubImage)
+ {
+ if (isCompressed != actualFormatInfo.compressed)
+ {
+ context->validationError(GL_INVALID_OPERATION, kCompressedMismatch);
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0 || zoffset < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (std::numeric_limits<GLsizei>::max() - xoffset < width ||
+ std::numeric_limits<GLsizei>::max() - yoffset < height ||
+ std::numeric_limits<GLsizei>::max() - zoffset < depth)
+ {
+ context->validationError(GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ if (static_cast<size_t>(xoffset + width) > texture->getWidth(target, level) ||
+ static_cast<size_t>(yoffset + height) > texture->getHeight(target, level) ||
+ static_cast<size_t>(zoffset + depth) > texture->getDepth(target, level))
+ {
+ context->validationError(GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ if (width > 0 && height > 0 && depth > 0 && pixels == nullptr &&
+ context->getState().getTargetBuffer(gl::BufferBinding::PixelUnpack) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kPixelDataNull);
+ return false;
+ }
+ }
+
+ GLenum sizeCheckFormat = isSubImage ? format : internalformat;
+ if (!ValidImageDataSize(context, texType, width, height, depth, sizeCheckFormat, type, pixels,
+ imageSize))
+ {
+ return false;
+ }
+
+ // Check for pixel unpack buffer related API errors
+ gl::Buffer *pixelUnpackBuffer = context->getState().getTargetBuffer(BufferBinding::PixelUnpack);
+ if (pixelUnpackBuffer != nullptr)
+ {
+ // ...data is not evenly divisible into the number of bytes needed to store in memory a
+ // datum
+ // indicated by type.
+ if (!isCompressed)
+ {
+ size_t offset = reinterpret_cast<size_t>(pixels);
+ size_t dataBytesPerPixel = static_cast<size_t>(gl::GetTypeInfo(type).bytes);
+
+ if ((offset % dataBytesPerPixel) != 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kDataTypeNotAligned);
+ return false;
+ }
+ }
+
+ // ...the buffer object's data store is currently mapped.
+ if (pixelUnpackBuffer->isMapped())
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferMapped);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateES3TexImage2DParameters(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isCompressed,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ GLsizei imageSize,
+ const void *pixels)
+{
+ if (!ValidTexture2DDestinationTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3TexImageParametersBase(context, target, level, internalformat, isCompressed,
+ isSubImage, xoffset, yoffset, zoffset, width, height,
+ depth, border, format, type, imageSize, pixels);
+}
+
+bool ValidateES3TexImage3DParameters(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isCompressed,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const void *pixels)
+{
+ if (!ValidTexture3DDestinationTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ if (IsETC2EACFormat(format) && target != TextureTarget::_2DArray)
+ {
+ // ES 3.1, Section 8.7, page 169.
+ context->validationError(GL_INVALID_OPERATION, kInternalFormatRequiresTexture2DArray);
+ return false;
+ }
+
+ return ValidateES3TexImageParametersBase(context, target, level, internalformat, isCompressed,
+ isSubImage, xoffset, yoffset, zoffset, width, height,
+ depth, border, format, type, bufSize, pixels);
+}
+
+struct EffectiveInternalFormatInfo
+{
+ GLenum effectiveFormat;
+ GLenum destFormat;
+ GLuint minRedBits;
+ GLuint maxRedBits;
+ GLuint minGreenBits;
+ GLuint maxGreenBits;
+ GLuint minBlueBits;
+ GLuint maxBlueBits;
+ GLuint minAlphaBits;
+ GLuint maxAlphaBits;
+};
+
+static bool QueryEffectiveFormatList(const InternalFormat &srcFormat,
+ GLenum targetFormat,
+ const EffectiveInternalFormatInfo *list,
+ size_t size,
+ GLenum *outEffectiveFormat)
+{
+ for (size_t curFormat = 0; curFormat < size; ++curFormat)
+ {
+ const EffectiveInternalFormatInfo &formatInfo = list[curFormat];
+ if ((formatInfo.destFormat == targetFormat) &&
+ (formatInfo.minRedBits <= srcFormat.redBits &&
+ formatInfo.maxRedBits >= srcFormat.redBits) &&
+ (formatInfo.minGreenBits <= srcFormat.greenBits &&
+ formatInfo.maxGreenBits >= srcFormat.greenBits) &&
+ (formatInfo.minBlueBits <= srcFormat.blueBits &&
+ formatInfo.maxBlueBits >= srcFormat.blueBits) &&
+ (formatInfo.minAlphaBits <= srcFormat.alphaBits &&
+ formatInfo.maxAlphaBits >= srcFormat.alphaBits))
+ {
+ *outEffectiveFormat = formatInfo.effectiveFormat;
+ return true;
+ }
+ }
+
+ *outEffectiveFormat = GL_NONE;
+ return false;
+}
+
+bool GetSizedEffectiveInternalFormatInfo(const InternalFormat &srcFormat,
+ GLenum *outEffectiveFormat)
+{
+ // OpenGL ES 3.0.3 Specification, Table 3.17, pg 141:
+ // Effective internal format coresponding to destination internal format and linear source
+ // buffer component sizes.
+ // | Source channel min/max sizes |
+ // Effective Internal Format | N/A | R | G | B | A |
+ // clang-format off
+ constexpr EffectiveInternalFormatInfo list[] = {
+ { GL_ALPHA8_EXT, GL_NONE, 0, 0, 0, 0, 0, 0, 1, 8 },
+ { GL_R8, GL_NONE, 1, 8, 0, 0, 0, 0, 0, 0 },
+ { GL_RG8, GL_NONE, 1, 8, 1, 8, 0, 0, 0, 0 },
+ { GL_RGB565, GL_NONE, 1, 5, 1, 6, 1, 5, 0, 0 },
+ { GL_RGB8, GL_NONE, 6, 8, 7, 8, 6, 8, 0, 0 },
+ { GL_RGBA4, GL_NONE, 1, 4, 1, 4, 1, 4, 1, 4 },
+ { GL_RGB5_A1, GL_NONE, 5, 5, 5, 5, 5, 5, 1, 1 },
+ { GL_RGBA8, GL_NONE, 5, 8, 5, 8, 5, 8, 2, 8 },
+ { GL_RGB10_A2, GL_NONE, 9, 10, 9, 10, 9, 10, 2, 2 },
+ };
+ // clang-format on
+
+ return QueryEffectiveFormatList(srcFormat, GL_NONE, list, ArraySize(list), outEffectiveFormat);
+}
+
+bool GetUnsizedEffectiveInternalFormatInfo(const InternalFormat &srcFormat,
+ const InternalFormat &destFormat,
+ GLenum *outEffectiveFormat)
+{
+ constexpr GLuint umax = UINT_MAX;
+
+ // OpenGL ES 3.0.3 Specification, Table 3.17, pg 141:
+ // Effective internal format coresponding to destination internal format andlinear source buffer
+ // component sizes.
+ // | Source channel min/max sizes |
+ // Effective Internal Format | Dest Format | R | G | B | A |
+ // clang-format off
+ constexpr EffectiveInternalFormatInfo list[] = {
+ { GL_ALPHA8_EXT, GL_ALPHA, 0, umax, 0, umax, 0, umax, 1, 8 },
+ { GL_LUMINANCE8_EXT, GL_LUMINANCE, 1, 8, 0, umax, 0, umax, 0, umax },
+ { GL_LUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, 1, 8, 0, umax, 0, umax, 1, 8 },
+ { GL_RGB565, GL_RGB, 1, 5, 1, 6, 1, 5, 0, umax },
+ { GL_RGB8, GL_RGB, 6, 8, 7, 8, 6, 8, 0, umax },
+ { GL_RGBA4, GL_RGBA, 1, 4, 1, 4, 1, 4, 1, 4 },
+ { GL_RGB5_A1, GL_RGBA, 5, 5, 5, 5, 5, 5, 1, 1 },
+ { GL_RGBA8, GL_RGBA, 5, 8, 5, 8, 5, 8, 5, 8 },
+ };
+ // clang-format on
+
+ return QueryEffectiveFormatList(srcFormat, destFormat.format, list, ArraySize(list),
+ outEffectiveFormat);
+}
+
+static bool GetEffectiveInternalFormat(const InternalFormat &srcFormat,
+ const InternalFormat &destFormat,
+ GLenum *outEffectiveFormat)
+{
+ if (destFormat.sized)
+ {
+ return GetSizedEffectiveInternalFormatInfo(srcFormat, outEffectiveFormat);
+ }
+ else
+ {
+ return GetUnsizedEffectiveInternalFormatInfo(srcFormat, destFormat, outEffectiveFormat);
+ }
+}
+
+static bool EqualOrFirstZero(GLuint first, GLuint second)
+{
+ return first == 0 || first == second;
+}
+
+static bool IsValidES3CopyTexImageCombination(const InternalFormat &textureFormatInfo,
+ const InternalFormat &framebufferFormatInfo,
+ GLuint readBufferHandle)
+{
+ if (!ValidES3CopyConversion(textureFormatInfo.format, framebufferFormatInfo.format))
+ {
+ return false;
+ }
+
+ // Section 3.8.5 of the GLES 3.0.3 spec states that source and destination formats
+ // must both be signed, unsigned, or fixed point and both source and destinations
+ // must be either both SRGB or both not SRGB. EXT_color_buffer_float adds allowed
+ // conversion between fixed and floating point.
+
+ if ((textureFormatInfo.colorEncoding == GL_SRGB) !=
+ (framebufferFormatInfo.colorEncoding == GL_SRGB))
+ {
+ return false;
+ }
+
+ if (((textureFormatInfo.componentType == GL_INT) !=
+ (framebufferFormatInfo.componentType == GL_INT)) ||
+ ((textureFormatInfo.componentType == GL_UNSIGNED_INT) !=
+ (framebufferFormatInfo.componentType == GL_UNSIGNED_INT)))
+ {
+ return false;
+ }
+
+ if ((textureFormatInfo.componentType == GL_UNSIGNED_NORMALIZED ||
+ textureFormatInfo.componentType == GL_SIGNED_NORMALIZED) &&
+ !(framebufferFormatInfo.componentType == GL_UNSIGNED_NORMALIZED ||
+ framebufferFormatInfo.componentType == GL_SIGNED_NORMALIZED))
+ {
+ return false;
+ }
+
+ // SNORM is not supported (e.g. is not in the tables of "effective internal format" that
+ // correspond to internal formats.
+ if (textureFormatInfo.componentType == GL_SIGNED_NORMALIZED)
+ {
+ return false;
+ }
+
+ // Section 3.8.5 of the GLES 3.0.3 (and section 8.6 of the GLES 3.2) spec has a caveat, that
+ // the KHR dEQP tests enforce:
+ //
+ // Note that the above rules disallow matches where some components sizes are smaller and
+ // others are larger (such as RGB10_A2).
+ if (!textureFormatInfo.sized && (framebufferFormatInfo.internalFormat == GL_RGB10_A2))
+ {
+ return false;
+ }
+
+ // GLES specification 3.0.3, sec 3.8.5, pg 139-140:
+ // The effective internal format of the source buffer is determined with the following rules
+ // applied in order:
+ // * If the source buffer is a texture or renderbuffer that was created with a sized internal
+ // format then the effective internal format is the source buffer's sized internal format.
+ // * If the source buffer is a texture that was created with an unsized base internal format,
+ // then the effective internal format is the source image array's effective internal
+ // format, as specified by table 3.12, which is determined from the <format> and <type>
+ // that were used when the source image array was specified by TexImage*.
+ // * Otherwise the effective internal format is determined by the row in table 3.17 or 3.18
+ // where Destination Internal Format matches internalformat and where the [source channel
+ // sizes] are consistent with the values of the source buffer's [channel sizes]. Table 3.17
+ // is used if the FRAMEBUFFER_ATTACHMENT_ENCODING is LINEAR and table 3.18 is used if the
+ // FRAMEBUFFER_ATTACHMENT_ENCODING is SRGB.
+ const InternalFormat *sourceEffectiveFormat = nullptr;
+ if (readBufferHandle != 0)
+ {
+ // Not the default framebuffer, therefore the read buffer must be a user-created texture or
+ // renderbuffer
+ if (framebufferFormatInfo.sized)
+ {
+ sourceEffectiveFormat = &framebufferFormatInfo;
+ }
+ else
+ {
+ // Renderbuffers cannot be created with an unsized internal format, so this must be an
+ // unsized-format texture. We can use the same table we use when creating textures to
+ // get its effective sized format.
+ sourceEffectiveFormat =
+ &GetSizedInternalFormatInfo(framebufferFormatInfo.sizedInternalFormat);
+ }
+ }
+ else
+ {
+ // The effective internal format must be derived from the source framebuffer's channel
+ // sizes. This is done in GetEffectiveInternalFormat for linear buffers (table 3.17)
+ if (framebufferFormatInfo.colorEncoding == GL_LINEAR)
+ {
+ GLenum effectiveFormat;
+ if (GetEffectiveInternalFormat(framebufferFormatInfo, textureFormatInfo,
+ &effectiveFormat))
+ {
+ sourceEffectiveFormat = &GetSizedInternalFormatInfo(effectiveFormat);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (framebufferFormatInfo.colorEncoding == GL_SRGB)
+ {
+ // SRGB buffers can only be copied to sized format destinations according to table 3.18
+ if (textureFormatInfo.sized &&
+ (framebufferFormatInfo.redBits >= 1 && framebufferFormatInfo.redBits <= 8) &&
+ (framebufferFormatInfo.greenBits >= 1 && framebufferFormatInfo.greenBits <= 8) &&
+ (framebufferFormatInfo.blueBits >= 1 && framebufferFormatInfo.blueBits <= 8) &&
+ (framebufferFormatInfo.alphaBits >= 1 && framebufferFormatInfo.alphaBits <= 8))
+ {
+ sourceEffectiveFormat = &GetSizedInternalFormatInfo(GL_SRGB8_ALPHA8);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ UNREACHABLE();
+ return false;
+ }
+ }
+
+ if (textureFormatInfo.sized)
+ {
+ // Section 3.8.5 of the GLES 3.0.3 spec, pg 139, requires that, if the destination format is
+ // sized, component sizes of the source and destination formats must exactly match if the
+ // destination format exists.
+ if (!EqualOrFirstZero(textureFormatInfo.redBits, sourceEffectiveFormat->redBits) ||
+ !EqualOrFirstZero(textureFormatInfo.greenBits, sourceEffectiveFormat->greenBits) ||
+ !EqualOrFirstZero(textureFormatInfo.blueBits, sourceEffectiveFormat->blueBits) ||
+ !EqualOrFirstZero(textureFormatInfo.alphaBits, sourceEffectiveFormat->alphaBits))
+ {
+ return false;
+ }
+ }
+
+ return true; // A conversion function exists, and no rule in the specification has precluded
+ // conversion between these formats.
+}
+
+bool ValidateES3CopyTexImageParametersBase(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border)
+{
+ Format textureFormat = Format::Invalid();
+ if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
+ xoffset, yoffset, zoffset, x, y, width, height, border,
+ &textureFormat))
+ {
+ return false;
+ }
+ ASSERT(textureFormat.valid() || !isSubImage);
+
+ const auto &state = context->getState();
+ gl::Framebuffer *framebuffer = state.getReadFramebuffer();
+ GLuint readFramebufferID = framebuffer->id();
+
+ if (!ValidateFramebufferComplete(context, framebuffer))
+ {
+ return false;
+ }
+
+ if (readFramebufferID != 0 && !ValidateFramebufferNotMultisampled(context, framebuffer))
+ {
+ return false;
+ }
+
+ const FramebufferAttachment *source = framebuffer->getReadColorAttachment();
+
+ // According to ES 3.x spec, if the internalformat of the texture
+ // is RGB9_E5 and copy to such a texture, generate INVALID_OPERATION.
+ if (textureFormat.info->internalFormat == GL_RGB9_E5)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+
+ if (isSubImage)
+ {
+ if (!IsValidES3CopyTexImageCombination(*textureFormat.info, *source->getFormat().info,
+ readFramebufferID))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidCopyCombination);
+ return false;
+ }
+ }
+ else
+ {
+ // Use format/type from the source FBO. (Might not be perfect for all cases?)
+ const InternalFormat &framebufferFormat = *source->getFormat().info;
+ const InternalFormat &copyFormat = GetInternalFormatInfo(internalformat, GL_UNSIGNED_BYTE);
+ if (!IsValidES3CopyTexImageCombination(copyFormat, framebufferFormat, readFramebufferID))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidCopyCombination);
+ return false;
+ }
+ }
+
+ // If width or height is zero, it is a no-op. Return false without setting an error.
+ return (width > 0 && height > 0);
+}
+
+bool ValidateES3CopyTexImage2DParameters(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border)
+{
+ if (!ValidTexture2DDestinationTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3CopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
+ xoffset, yoffset, zoffset, x, y, width, height,
+ border);
+}
+
+bool ValidateES3CopyTexImage3DParameters(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border)
+{
+ if (!ValidTexture3DDestinationTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3CopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
+ xoffset, yoffset, zoffset, x, y, width, height,
+ border);
+}
+
+bool ValidateES3TexStorageParametersBase(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (width < 1 || height < 1 || depth < 1 || levels < 1)
+ {
+ context->validationError(GL_INVALID_VALUE, kTextureSizeTooSmall);
+ return false;
+ }
+
+ GLsizei maxDim = std::max(width, height);
+ if (target != TextureType::_2DArray)
+ {
+ maxDim = std::max(maxDim, depth);
+ }
+
+ if (levels > gl::log2(maxDim) + 1)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels);
+ return false;
+ }
+
+ const gl::Caps &caps = context->getCaps();
+
+ switch (target)
+ {
+ case TextureType::_2D:
+ {
+ if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
+ static_cast<GLuint>(height) > caps.max2DTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::Rectangle:
+ {
+ if (levels != 1)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevels);
+ return false;
+ }
+
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::CubeMap:
+ {
+ if (width != height)
+ {
+ context->validationError(GL_INVALID_VALUE, kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (static_cast<GLuint>(width) > caps.maxCubeMapTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::_3D:
+ {
+ if (static_cast<GLuint>(width) > caps.max3DTextureSize ||
+ static_cast<GLuint>(height) > caps.max3DTextureSize ||
+ static_cast<GLuint>(depth) > caps.max3DTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::_2DArray:
+ {
+ if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
+ static_cast<GLuint>(height) > caps.max2DTextureSize ||
+ static_cast<GLuint>(depth) > caps.maxArrayTextureLayers)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ return false;
+ }
+
+ gl::Texture *texture = context->getTextureByType(target);
+ if (!texture || texture->id() == 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMissingTexture);
+ return false;
+ }
+
+ if (texture->getImmutableFormat())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTextureIsImmutable);
+ return false;
+ }
+
+ const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalformat);
+ if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (!formatInfo.sized)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (formatInfo.compressed && target == TextureType::Rectangle)
+ {
+ context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+
+ if (formatInfo.compressed && target == TextureType::_3D)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexStorage2DParameters(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (!ValidTexture2DTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3TexStorageParametersBase(context, target, levels, internalformat, width,
+ height, depth);
+}
+
+bool ValidateES3TexStorage3DParameters(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (!ValidTexture3DTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3TexStorageParametersBase(context, target, levels, internalformat, width,
+ height, depth);
+}
+
+bool ValidateBeginQuery(gl::Context *context, QueryType target, GLuint id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateBeginQueryBase(context, target, id);
+}
+
+bool ValidateEndQuery(gl::Context *context, QueryType target)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateEndQueryBase(context, target);
+}
+
+bool ValidateGetQueryiv(Context *context, QueryType target, GLenum pname, GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetQueryivBase(context, target, pname, nullptr);
+}
+
+bool ValidateGetQueryObjectuiv(Context *context, GLuint id, GLenum pname, GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetQueryObjectValueBase(context, id, pname, nullptr);
+}
+
+bool ValidateFramebufferTextureLayer(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLint layer)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ const gl::Caps &caps = context->getCaps();
+ if (texture != 0)
+ {
+ if (layer < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeLayer);
+ return false;
+ }
+
+ gl::Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ switch (tex->getType())
+ {
+ case TextureType::_2DArray:
+ {
+ if (level > gl::log2(caps.max2DTextureSize))
+ {
+ context->validationError(GL_INVALID_VALUE, kFramebufferTextureInvalidMipLevel);
+ return false;
+ }
+
+ if (static_cast<GLuint>(layer) >= caps.maxArrayTextureLayers)
+ {
+ context->validationError(GL_INVALID_VALUE, kFramebufferTextureInvalidLayer);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::_3D:
+ {
+ if (level > gl::log2(caps.max3DTextureSize))
+ {
+ context->validationError(GL_INVALID_VALUE, kFramebufferTextureInvalidMipLevel);
+ return false;
+ }
+
+ if (static_cast<GLuint>(layer) >= caps.max3DTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kFramebufferTextureInvalidLayer);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::_2DMultisampleArray:
+ {
+ if (level != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kFramebufferTextureInvalidMipLevel);
+ return false;
+ }
+
+ if (static_cast<GLuint>(layer) >= caps.maxArrayTextureLayers)
+ {
+ context->validationError(GL_INVALID_VALUE, kFramebufferTextureInvalidLayer);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_OPERATION,
+ kFramebufferTextureLayerIncorrectTextureType);
+ return false;
+ }
+
+ const auto &format = tex->getFormat(NonCubeTextureTypeToTarget(tex->getType()), level);
+ if (format.info->compressed)
+ {
+ context->validationError(GL_INVALID_OPERATION, kCompressedTexturesNotAttachable);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateInvalidateFramebuffer(Context *context,
+ GLenum target,
+ GLsizei numAttachments,
+ const GLenum *attachments)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ bool defaultFramebuffer = false;
+
+ switch (target)
+ {
+ case GL_DRAW_FRAMEBUFFER:
+ case GL_FRAMEBUFFER:
+ defaultFramebuffer = context->getState().getDrawFramebuffer()->id() == 0;
+ break;
+ case GL_READ_FRAMEBUFFER:
+ defaultFramebuffer = context->getState().getReadFramebuffer()->id() == 0;
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidFramebufferTarget);
+ return false;
+ }
+
+ return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments,
+ defaultFramebuffer);
+}
+
+bool ValidateInvalidateSubFramebuffer(Context *context,
+ GLenum target,
+ GLsizei numAttachments,
+ const GLenum *attachments,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ if (width < 0 || height < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ return ValidateInvalidateFramebuffer(context, target, numAttachments, attachments);
+}
+
+bool ValidateClearBuffer(Context *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateFramebufferComplete(context, context->getState().getDrawFramebuffer()))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawRangeElements(Context *context,
+ PrimitiveMode mode,
+ GLuint start,
+ GLuint end,
+ GLsizei count,
+ DrawElementsType type,
+ const void *indices)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (end < start)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidElementRange);
+ return false;
+ }
+
+ if (!ValidateDrawElementsCommon(context, mode, count, type, indices, 0))
+ {
+ return false;
+ }
+
+ // Skip range checks for no-op calls.
+ if (count <= 0)
+ {
+ return true;
+ }
+
+ // Note that resolving the index range is a bit slow. We should probably optimize this.
+ IndexRange indexRange;
+ ANGLE_VALIDATION_TRY(context->getState().getVertexArray()->getIndexRange(context, type, count,
+ indices, &indexRange));
+
+ if (indexRange.end > end || indexRange.start < start)
+ {
+ // GL spec says that behavior in this case is undefined - generating an error is fine.
+ context->validationError(GL_INVALID_OPERATION, kExceedsElementRange);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateGetUniformuiv(Context *context, GLuint program, GLint location, GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetUniformBase(context, program, location);
+}
+
+bool ValidateReadBuffer(Context *context, GLenum src)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ const Framebuffer *readFBO = context->getState().getReadFramebuffer();
+
+ if (readFBO == nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoReadFramebuffer);
+ return false;
+ }
+
+ if (src == GL_NONE)
+ {
+ return true;
+ }
+
+ if (src != GL_BACK && (src < GL_COLOR_ATTACHMENT0 || src > GL_COLOR_ATTACHMENT31))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidReadBuffer);
+ return false;
+ }
+
+ if (readFBO->id() == 0)
+ {
+ if (src != GL_BACK)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidDefaultReadBuffer);
+ return false;
+ }
+ }
+ else
+ {
+ GLuint drawBuffer = static_cast<GLuint>(src - GL_COLOR_ATTACHMENT0);
+
+ if (drawBuffer >= context->getCaps().maxDrawBuffers)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExceedsMaxDrawBuffers);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexImage3D(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLsizei imageSize,
+ const void *data)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().texture3DOES)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidTextureTarget(context, TextureTargetToType(target)))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ // Validate image size
+ if (!ValidImageSizeParameters(context, TextureTargetToType(target), level, width, height, depth,
+ false))
+ {
+ // Error already generated.
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
+ if (!formatInfo.compressed)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidCompressedFormat);
+ return false;
+ }
+
+ GLuint blockSize = 0;
+ if (!formatInfo.computeCompressedImageSize(gl::Extents(width, height, depth), &blockSize))
+ {
+ context->validationError(GL_INVALID_VALUE, kIntegerOverflow);
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidCompressedImageSize);
+ return false;
+ }
+
+ // 3D texture target validation
+ if (target != TextureTarget::_3D && target != TextureTarget::_2DArray)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ // validateES3TexImageFormat sets the error code if there is an error
+ if (!ValidateES3TexImage3DParameters(context, target, level, internalformat, true, false, 0, 0,
+ 0, width, height, depth, border, GL_NONE, GL_NONE, -1,
+ data))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexImage3DRobustANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
+{
+ if (!ValidateRobustCompressedTexImageBase(context, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexImage3D(context, target, level, internalformat, width, height,
+ depth, border, imageSize, data);
+}
+
+bool ValidateBindVertexArray(Context *context, GLuint array)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateBindVertexArrayBase(context, array);
+}
+
+bool ValidateIsVertexArray(Context *context, GLuint array)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateBindBufferCommon(Context *context,
+ BufferBinding target,
+ GLuint index,
+ GLuint buffer,
+ GLintptr offset,
+ GLsizeiptr size)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (buffer != 0 && offset < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (!context->getState().isBindGeneratesResourceEnabled() &&
+ !context->isBufferGenerated(buffer))
+ {
+ context->validationError(GL_INVALID_OPERATION, kObjectNotGenerated);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ switch (target)
+ {
+ case BufferBinding::TransformFeedback:
+ {
+ if (index >= caps.maxTransformFeedbackSeparateAttributes)
+ {
+ context->validationError(GL_INVALID_VALUE,
+ kIndexExceedsTransformFeedbackBufferBindings);
+ return false;
+ }
+ if (buffer != 0 && ((offset % 4) != 0 || (size % 4) != 0))
+ {
+ context->validationError(GL_INVALID_VALUE, kOffsetAndSizeAlignment);
+ return false;
+ }
+
+ if (context->getState().isTransformFeedbackActive())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackTargetActive);
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::Uniform:
+ {
+ if (index >= caps.maxUniformBufferBindings)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxUniformBufferBindings);
+ return false;
+ }
+
+ ASSERT(caps.uniformBufferOffsetAlignment);
+ if (buffer != 0 && (offset % caps.uniformBufferOffsetAlignment) != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kUniformBufferOffsetAlignment);
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::AtomicCounter:
+ {
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= caps.maxAtomicCounterBufferBindings)
+ {
+ context->validationError(GL_INVALID_VALUE,
+ kIndexExceedsMaxAtomicCounterBufferBindings);
+ return false;
+ }
+ if (buffer != 0 && (offset % 4) != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kOffsetAlignment);
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::ShaderStorage:
+ {
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= caps.maxShaderStorageBufferBindings)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxShaderStorageBufferBindings);
+ return false;
+ }
+ ASSERT(caps.shaderStorageBufferOffsetAlignment);
+ if (buffer != 0 && (offset % caps.shaderStorageBufferOffsetAlignment) != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kShaderStorageBufferOffsetAlignment);
+ return false;
+ }
+ break;
+ }
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindBufferBase(Context *context, BufferBinding target, GLuint index, GLuint buffer)
+{
+ return ValidateBindBufferCommon(context, target, index, buffer, 0, 0);
+}
+
+bool ValidateBindBufferRange(Context *context,
+ BufferBinding target,
+ GLuint index,
+ GLuint buffer,
+ GLintptr offset,
+ GLsizeiptr size)
+{
+ if (buffer != 0 && size <= 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidBindBufferSize);
+ return false;
+ }
+ return ValidateBindBufferCommon(context, target, index, buffer, offset, size);
+}
+
+bool ValidateProgramBinary(Context *context,
+ GLuint program,
+ GLenum binaryFormat,
+ const void *binary,
+ GLint length)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateProgramBinaryBase(context, program, binaryFormat, binary, length);
+}
+
+bool ValidateGetProgramBinary(Context *context,
+ GLuint program,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLenum *binaryFormat,
+ void *binary)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary);
+}
+
+bool ValidateProgramParameteri(Context *context, GLuint program, GLenum pname, GLint value)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (GetValidProgram(context, program) == nullptr)
+ {
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
+ if (value != GL_FALSE && value != GL_TRUE)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidBooleanValue);
+ return false;
+ }
+ break;
+
+ case GL_PROGRAM_SEPARABLE:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(GL_INVALID_ENUM, kES31Required);
+ return false;
+ }
+
+ if (value != GL_FALSE && value != GL_TRUE)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidBooleanValue);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBlitFramebuffer(Context *context,
+ GLint srcX0,
+ GLint srcY0,
+ GLint srcX1,
+ GLint srcY1,
+ GLint dstX0,
+ GLint dstY0,
+ GLint dstX1,
+ GLint dstY1,
+ GLbitfield mask,
+ GLenum filter)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0,
+ dstX1, dstY1, mask, filter);
+}
+
+bool ValidateClearBufferiv(Context *context, GLenum buffer, GLint drawbuffer, const GLint *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 ||
+ static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxDrawBuffer);
+ return false;
+ }
+ if (context->getExtensions().webglCompatibility)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_INT};
+ if (!ValidateWebGLFramebufferAttachmentClearType(
+ context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
+ break;
+
+ case GL_STENCIL:
+ if (drawbuffer != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDepthStencilDrawBuffer);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return ValidateClearBuffer(context);
+}
+
+bool ValidateClearBufferuiv(Context *context, GLenum buffer, GLint drawbuffer, const GLuint *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 ||
+ static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxDrawBuffer);
+ return false;
+ }
+ if (context->getExtensions().webglCompatibility)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_UNSIGNED_INT};
+ if (!ValidateWebGLFramebufferAttachmentClearType(
+ context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return ValidateClearBuffer(context);
+}
+
+bool ValidateClearBufferfv(Context *context, GLenum buffer, GLint drawbuffer, const GLfloat *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 ||
+ static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxDrawBuffer);
+ return false;
+ }
+ if (context->getExtensions().webglCompatibility)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_FLOAT, GL_UNSIGNED_NORMALIZED,
+ GL_SIGNED_NORMALIZED};
+ if (!ValidateWebGLFramebufferAttachmentClearType(
+ context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
+ break;
+
+ case GL_DEPTH:
+ if (drawbuffer != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDepthStencilDrawBuffer);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return ValidateClearBuffer(context);
+}
+
+bool ValidateClearBufferfi(Context *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ GLfloat depth,
+ GLint stencil)
+{
+ switch (buffer)
+ {
+ case GL_DEPTH_STENCIL:
+ if (drawbuffer != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDepthStencilDrawBuffer);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return ValidateClearBuffer(context);
+}
+
+bool ValidateDrawBuffers(Context *context, GLsizei n, const GLenum *bufs)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateDrawBuffersBase(context, n, bufs);
+}
+
+bool ValidateCopyTexSubImage3D(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().texture3DOES)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateES3CopyTexImage3DParameters(context, target, level, GL_NONE, true, xoffset,
+ yoffset, zoffset, x, y, width, height, 0);
+}
+
+bool ValidateCopyTexture3DANGLE(Context *context,
+ GLuint sourceId,
+ GLint sourceLevel,
+ TextureTarget destTarget,
+ GLuint destId,
+ GLint destLevel,
+ GLint internalFormat,
+ GLenum destType,
+ GLboolean unpackFlipY,
+ GLboolean unpackPremultiplyAlpha,
+ GLboolean unpackUnmultiplyAlpha)
+{
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTexture);
+ return false;
+ }
+
+ TextureType sourceType = source->getType();
+ ASSERT(sourceType != TextureType::CubeMap);
+ TextureTarget sourceTarget = NonCubeTextureTypeToTarget(sourceType);
+ const Format &sourceFormat = source->getFormat(sourceTarget, sourceLevel);
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ if (!ValidateCopyTexture3DCommon(context, source, sourceLevel,
+ sourceFormat.info->internalFormat, dest, destLevel,
+ internalFormat, destTarget))
+ {
+ return false;
+ }
+
+ if (!ValidMipLevel(context, source->getType(), sourceLevel))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTextureLevel);
+ return false;
+ }
+
+ GLsizei sourceWidth = static_cast<GLsizei>(source->getWidth(sourceTarget, sourceLevel));
+ GLsizei sourceHeight = static_cast<GLsizei>(source->getHeight(sourceTarget, sourceLevel));
+ if (sourceWidth == 0 || sourceHeight == 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidSourceTextureSize);
+ return false;
+ }
+
+ if (dest->getImmutableFormat())
+ {
+ context->validationError(GL_INVALID_OPERATION, kDestinationImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCopySubTexture3DANGLE(Context *context,
+ GLuint sourceId,
+ GLint sourceLevel,
+ TextureTarget destTarget,
+ GLuint destId,
+ GLint destLevel,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLint z,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLboolean unpackFlipY,
+ GLboolean unpackPremultiplyAlpha,
+ GLboolean unpackUnmultiplyAlpha)
+{
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTexture);
+ return false;
+ }
+
+ TextureType sourceType = source->getType();
+ ASSERT(sourceType != TextureType::CubeMap);
+ TextureTarget sourceTarget = NonCubeTextureTypeToTarget(sourceType);
+ const Format &sourceFormat = source->getFormat(sourceTarget, sourceLevel);
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ const InternalFormat &destFormat = *dest->getFormat(destTarget, destLevel).info;
+
+ if (!ValidateCopyTexture3DCommon(context, source, sourceLevel,
+ sourceFormat.info->internalFormat, dest, destLevel,
+ destFormat.internalFormat, destTarget))
+ {
+ return false;
+ }
+
+ if (x < 0 || y < 0 || z < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeXYZ);
+ return false;
+ }
+
+ if (width < 0 || height < 0 || depth < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeHeightWidthDepth);
+ return false;
+ }
+
+ if (static_cast<size_t>(x + width) > source->getWidth(sourceTarget, sourceLevel) ||
+ static_cast<size_t>(y + height) > source->getHeight(sourceTarget, sourceLevel) ||
+ static_cast<size_t>(z + depth) > source->getDepth(sourceTarget, sourceLevel))
+ {
+ context->validationError(GL_INVALID_VALUE, kSourceTextureTooSmall);
+ return false;
+ }
+
+ if (TextureTargetToType(destTarget) != dest->getType())
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTextureType);
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0 || zoffset < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (static_cast<size_t>(xoffset + width) > dest->getWidth(destTarget, destLevel) ||
+ static_cast<size_t>(yoffset + height) > dest->getHeight(destTarget, destLevel) ||
+ static_cast<size_t>(zoffset + depth) > dest->getDepth(destTarget, destLevel))
+ {
+ context->validationError(GL_INVALID_VALUE, kDestinationTextureTooSmall);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexImage3D(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void *pixels)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().texture3DOES)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, internalformat, false, false, 0,
+ 0, 0, width, height, depth, border, format, type, -1,
+ pixels);
+}
+
+bool ValidateTexImage3DRobustANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const void *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, internalformat, false, false, 0,
+ 0, 0, width, height, depth, border, format, type,
+ bufSize, pixels);
+}
+
+bool ValidateTexSubImage3D(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLenum type,
+ const void *pixels)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().texture3DOES)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, GL_NONE, false, true, xoffset,
+ yoffset, zoffset, width, height, depth, 0, format, type,
+ -1, pixels);
+}
+
+bool ValidateTexSubImage3DRobustANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const void *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, GL_NONE, false, true, xoffset,
+ yoffset, zoffset, width, height, depth, 0, format, type,
+ bufSize, pixels);
+}
+
+bool ValidateCompressedTexSubImage3D(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLsizei imageSize,
+ const void *data)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().texture3DOES)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(format);
+ if (!formatInfo.compressed)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidCompressedFormat);
+ return false;
+ }
+
+ GLuint blockSize = 0;
+ if (!formatInfo.computeCompressedImageSize(gl::Extents(width, height, depth), &blockSize))
+ {
+ context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidCompressedImageSize);
+ return false;
+ }
+
+ if (!ValidateES3TexImage3DParameters(context, target, level, GL_NONE, true, true, xoffset,
+ yoffset, zoffset, width, height, depth, 0, format, GL_NONE,
+ -1, data))
+ {
+ return false;
+ }
+
+ if (!data)
+ {
+ context->validationError(GL_INVALID_VALUE, kPixelDataNull);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexSubImage3DRobustANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
+{
+ if (!ValidateRobustCompressedTexImageBase(context, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexSubImage3D(context, target, level, xoffset, yoffset, zoffset, width,
+ height, depth, format, imageSize, data);
+}
+
+bool ValidateGenQueries(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateDeleteQueries(Context *context, GLint n, const GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateGenSamplers(Context *context, GLint count, GLuint *)
+{
+ return ValidateGenOrDeleteCountES3(context, count);
+}
+
+bool ValidateDeleteSamplers(Context *context, GLint count, const GLuint *)
+{
+ return ValidateGenOrDeleteCountES3(context, count);
+}
+
+bool ValidateGenTransformFeedbacks(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateDeleteTransformFeedbacks(Context *context, GLint n, const GLuint *ids)
+{
+ if (!ValidateGenOrDeleteES3(context, n))
+ {
+ return false;
+ }
+ for (GLint i = 0; i < n; ++i)
+ {
+ auto *transformFeedback = context->getTransformFeedback(ids[i]);
+ if (transformFeedback != nullptr && transformFeedback->isActive())
+ {
+ // ES 3.0.4 section 2.15.1 page 86
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackActiveDelete);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateGenVertexArrays(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateDeleteVertexArrays(Context *context, GLint n, const GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateBeginTransformFeedback(Context *context, PrimitiveMode primitiveMode)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ switch (primitiveMode)
+ {
+ case PrimitiveMode::Triangles:
+ case PrimitiveMode::Lines:
+ case PrimitiveMode::Points:
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPrimitiveMode);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ if (transformFeedback->isActive())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransfomFeedbackAlreadyActive);
+ return false;
+ }
+
+ for (size_t i = 0; i < transformFeedback->getIndexedBufferCount(); i++)
+ {
+ const auto &buffer = transformFeedback->getIndexedBuffer(i);
+ if (buffer.get())
+ {
+ if (buffer->isMapped())
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferMapped);
+ return false;
+ }
+ if ((context->getLimitations().noDoubleBoundTransformFeedbackBuffers ||
+ context->getExtensions().webglCompatibility) &&
+ buffer->isDoubleBoundForTransformFeedback())
+ {
+ context->validationError(GL_INVALID_OPERATION,
+ kTransformFeedbackBufferMultipleOutputs);
+ return false;
+ }
+ }
+ }
+
+ Program *program = context->getState().getLinkedProgram(context);
+
+ if (!program)
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotBound);
+ return false;
+ }
+
+ if (program->getTransformFeedbackVaryingCount() == 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoTransformFeedbackOutputVariables);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferPointerv(Context *context, BufferBinding target, GLenum pname, void **params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetBufferPointervBase(context, target, pname, nullptr, params);
+}
+
+bool ValidateGetBufferPointervRobustANGLE(Context *context,
+ BufferBinding target,
+ GLenum pname,
+ GLsizei bufSize,
+ GLsizei *length,
+ void **params)
+{
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ GLsizei numParams = 0;
+
+ if (context->getClientMajorVersion() < 3 && !context->getExtensions().mapBuffer)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidateGetBufferPointervBase(context, target, pname, &numParams, params))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, bufSize, numParams))
+ {
+ return false;
+ }
+
+ SetRobustLengthParam(length, numParams);
+
+ return true;
+}
+
+bool ValidateUnmapBuffer(Context *context, BufferBinding target)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateUnmapBufferBase(context, target);
+}
+
+bool ValidateMapBufferRange(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length,
+ GLbitfield access)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateMapBufferRangeBase(context, target, offset, length, access);
+}
+
+bool ValidateFlushMappedBufferRange(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateFlushMappedBufferRangeBase(context, target, offset, length);
+}
+
+bool ValidateIndexedStateQuery(Context *context, GLenum pname, GLuint index, GLsizei *length)
+{
+ if (length)
+ {
+ *length = 0;
+ }
+
+ GLenum nativeType;
+ unsigned int numParams;
+ if (!context->getIndexedQueryParameterInfo(pname, &nativeType, &numParams))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ switch (pname)
+ {
+ case GL_TRANSFORM_FEEDBACK_BUFFER_START:
+ case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
+ case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
+ if (index >= caps.maxTransformFeedbackSeparateAttributes)
+ {
+ context->validationError(GL_INVALID_VALUE,
+ kIndexExceedsMaxTransformFeedbackAttribs);
+ return false;
+ }
+ break;
+
+ case GL_UNIFORM_BUFFER_START:
+ case GL_UNIFORM_BUFFER_SIZE:
+ case GL_UNIFORM_BUFFER_BINDING:
+ if (index >= caps.maxUniformBufferBindings)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxUniformBufferBindings);
+ return false;
+ }
+ break;
+
+ case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
+ case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
+ if (index >= 3u)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxWorkgroupDimensions);
+ return false;
+ }
+ break;
+
+ case GL_ATOMIC_COUNTER_BUFFER_START:
+ case GL_ATOMIC_COUNTER_BUFFER_SIZE:
+ case GL_ATOMIC_COUNTER_BUFFER_BINDING:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= caps.maxAtomicCounterBufferBindings)
+ {
+ context->validationError(GL_INVALID_VALUE,
+ kIndexExceedsMaxAtomicCounterBufferBindings);
+ return false;
+ }
+ break;
+
+ case GL_SHADER_STORAGE_BUFFER_START:
+ case GL_SHADER_STORAGE_BUFFER_SIZE:
+ case GL_SHADER_STORAGE_BUFFER_BINDING:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= caps.maxShaderStorageBufferBindings)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxShaderStorageBufferBindings);
+ return false;
+ }
+ break;
+
+ case GL_VERTEX_BINDING_BUFFER:
+ case GL_VERTEX_BINDING_DIVISOR:
+ case GL_VERTEX_BINDING_OFFSET:
+ case GL_VERTEX_BINDING_STRIDE:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= caps.maxVertexAttribBindings)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxVertexAttribBindings);
+ return false;
+ }
+ break;
+ case GL_SAMPLE_MASK_VALUE:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= caps.maxSampleMaskWords)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSampleMaskNumber);
+ return false;
+ }
+ break;
+ case GL_IMAGE_BINDING_NAME:
+ case GL_IMAGE_BINDING_LEVEL:
+ case GL_IMAGE_BINDING_LAYERED:
+ case GL_IMAGE_BINDING_LAYER:
+ case GL_IMAGE_BINDING_ACCESS:
+ case GL_IMAGE_BINDING_FORMAT:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= caps.maxImageUnits)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxImageUnits);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ if (length)
+ {
+ *length = 1;
+ }
+
+ return true;
+}
+
+bool ValidateGetIntegeri_v(Context *context, GLenum target, GLuint index, GLint *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateIndexedStateQuery(context, target, index, nullptr);
+}
+
+bool ValidateGetIntegeri_vRobustANGLE(Context *context,
+ GLenum target,
+ GLuint index,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLint *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ GLsizei numParams = 0;
+
+ if (!ValidateIndexedStateQuery(context, target, index, &numParams))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, bufSize, numParams))
+ {
+ return false;
+ }
+
+ SetRobustLengthParam(length, numParams);
+
+ return true;
+}
+
+bool ValidateGetInteger64i_v(Context *context, GLenum target, GLuint index, GLint64 *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateIndexedStateQuery(context, target, index, nullptr);
+}
+
+bool ValidateGetInteger64i_vRobustANGLE(Context *context,
+ GLenum target,
+ GLuint index,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLint64 *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ GLsizei numParams = 0;
+
+ if (!ValidateIndexedStateQuery(context, target, index, &numParams))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, bufSize, numParams))
+ {
+ return false;
+ }
+
+ SetRobustLengthParam(length, numParams);
+
+ return true;
+}
+
+bool ValidateCopyBufferSubData(Context *context,
+ BufferBinding readTarget,
+ BufferBinding writeTarget,
+ GLintptr readOffset,
+ GLintptr writeOffset,
+ GLsizeiptr size)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!context->isValidBufferBinding(readTarget) || !context->isValidBufferBinding(writeTarget))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *readBuffer = context->getState().getTargetBuffer(readTarget);
+ Buffer *writeBuffer = context->getState().getTargetBuffer(writeTarget);
+
+ if (!readBuffer || !writeBuffer)
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferNotBound);
+ return false;
+ }
+
+ // Verify that readBuffer and writeBuffer are not currently mapped
+ if (readBuffer->isMapped() || writeBuffer->isMapped())
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferMapped);
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibility &&
+ (readBuffer->isBoundForTransformFeedbackAndOtherUse() ||
+ writeBuffer->isBoundForTransformFeedbackAndOtherUse()))
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferBoundForTransformFeedback);
+ return false;
+ }
+
+ CheckedNumeric<GLintptr> checkedReadOffset(readOffset);
+ CheckedNumeric<GLintptr> checkedWriteOffset(writeOffset);
+ CheckedNumeric<GLintptr> checkedSize(size);
+
+ auto checkedReadSum = checkedReadOffset + checkedSize;
+ auto checkedWriteSum = checkedWriteOffset + checkedSize;
+
+ if (!checkedReadSum.IsValid() || !checkedWriteSum.IsValid() ||
+ !IsValueInRangeForNumericType<GLintptr>(readBuffer->getSize()) ||
+ !IsValueInRangeForNumericType<GLintptr>(writeBuffer->getSize()))
+ {
+ context->validationError(GL_INVALID_VALUE, kIntegerOverflow);
+ return false;
+ }
+
+ if (readOffset < 0 || writeOffset < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (size < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ if (checkedReadSum.ValueOrDie() > readBuffer->getSize() ||
+ checkedWriteSum.ValueOrDie() > writeBuffer->getSize())
+ {
+ context->validationError(GL_INVALID_VALUE, kBufferOffsetOverflow);
+ return false;
+ }
+
+ if (readBuffer == writeBuffer)
+ {
+ auto checkedOffsetDiff = (checkedReadOffset - checkedWriteOffset).Abs();
+ if (!checkedOffsetDiff.IsValid())
+ {
+ // This shold not be possible.
+ UNREACHABLE();
+ context->validationError(GL_INVALID_VALUE, kIntegerOverflow);
+ return false;
+ }
+
+ if (checkedOffsetDiff.ValueOrDie() < size)
+ {
+ context->validationError(GL_INVALID_VALUE, kCopyAlias);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetStringi(Context *context, GLenum name, GLuint index)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ switch (name)
+ {
+ case GL_EXTENSIONS:
+ if (index >= context->getExtensionStringCount())
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsNumExtensions);
+ return false;
+ }
+ break;
+
+ case GL_REQUESTABLE_EXTENSIONS_ANGLE:
+ if (!context->getExtensions().requestExtension)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+ if (index >= context->getRequestableExtensionStringCount())
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsNumRequestableExtensions);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRenderbufferStorageMultisample(Context *context,
+ GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRenderbufferStorageParametersBase(context, target, samples, internalformat, width,
+ height))
+ {
+ return false;
+ }
+
+ // The ES3 spec(section 4.4.2) states that the internal format must be sized and not an integer
+ // format if samples is greater than zero. In ES3.1(section 9.2.5), it can support integer
+ // multisample renderbuffer, but the samples should not be greater than MAX_INTEGER_SAMPLES.
+ const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalformat);
+ if (formatInfo.isInt())
+ {
+ if ((samples > 0 && context->getClientVersion() == ES_3_0) ||
+ static_cast<GLuint>(samples) > context->getCaps().maxIntegerSamples)
+ {
+ context->validationError(GL_INVALID_OPERATION, kSamplesOutOfRange);
+ return false;
+ }
+ }
+
+ // The behavior is different than the ANGLE version, which would generate a GL_OUT_OF_MEMORY.
+ const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
+ if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
+ {
+ context->validationError(GL_INVALID_OPERATION, kSamplesOutOfRange);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribIPointer(Context *context,
+ GLuint index,
+ GLint size,
+ VertexAttribType type,
+ GLsizei stride,
+ const void *pointer)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateIntegerVertexFormat(context, index, size, type))
+ {
+ return false;
+ }
+
+ if (stride < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeStride);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (context->getClientVersion() >= ES_3_1)
+ {
+ if (stride > caps.maxVertexAttribStride)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxVertexAttribStride);
+ return false;
+ }
+
+ // [OpenGL ES 3.1] Section 10.3.1 page 245:
+ // glVertexAttribBinding is part of the equivalent code of VertexAttribIPointer, so its
+ // validation should be inherited.
+ if (index >= caps.maxVertexAttribBindings)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxVertexAttribBindings);
+ return false;
+ }
+ }
+
+ // [OpenGL ES 3.0.2] Section 2.8 page 24:
+ // An INVALID_OPERATION error is generated when a non-zero vertex array object
+ // is bound, zero is bound to the ARRAY_BUFFER buffer object binding point,
+ // and the pointer argument is not NULL.
+ if (context->getState().getVertexArrayId() != 0 &&
+ context->getState().getTargetBuffer(BufferBinding::Array) == 0 && pointer != nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kClientDataInVertexArray);
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibility)
+ {
+ if (!ValidateWebGLVertexAttribPointer(context, type, false, stride, pointer, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetSynciv(Context *context,
+ GLsync sync,
+ GLenum pname,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLint *values)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ if (context->isContextLost())
+ {
+ context->validationError(GL_CONTEXT_LOST, kContextLost);
+
+ if (pname == GL_SYNC_STATUS)
+ {
+ // Generate an error but still return true, the context still needs to return a
+ // value in this case.
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ Sync *syncObject = context->getSync(sync);
+ if (!syncObject)
+ {
+ context->validationError(GL_INVALID_VALUE, kSyncMissing);
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_OBJECT_TYPE:
+ case GL_SYNC_CONDITION:
+ case GL_SYNC_FLAGS:
+ case GL_SYNC_STATUS:
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawElementsInstanced(Context *context,
+ PrimitiveMode mode,
+ GLsizei count,
+ DrawElementsType type,
+ const void *indices,
+ GLsizei instanceCount)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateDrawElementsInstancedBase(context, mode, count, type, indices, instanceCount);
+}
+
+bool ValidateMultiDrawArraysInstancedANGLE(Context *context,
+ PrimitiveMode mode,
+ const GLint *firsts,
+ const GLsizei *counts,
+ const GLsizei *instanceCounts,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDraw)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->getClientMajorVersion() < 3)
+ {
+ if (!context->getExtensions().instancedArraysAny())
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (!ValidateDrawInstancedANGLE(context))
+ {
+ return false;
+ }
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawArraysInstancedBase(context, mode, firsts[drawID], counts[drawID],
+ instanceCounts[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateMultiDrawElementsInstancedANGLE(Context *context,
+ PrimitiveMode mode,
+ const GLsizei *counts,
+ DrawElementsType type,
+ const GLvoid *const *indices,
+ const GLsizei *instanceCounts,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDraw)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->getClientMajorVersion() < 3)
+ {
+ if (!context->getExtensions().instancedArraysAny())
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (!ValidateDrawInstancedANGLE(context))
+ {
+ return false;
+ }
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawElementsInstancedBase(context, mode, counts[drawID], type, indices[drawID],
+ instanceCounts[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateFramebufferTextureMultiviewOVR(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLint baseViewIndex,
+ GLsizei numViews)
+{
+ if (!ValidateFramebufferTextureMultiviewBaseANGLE(context, target, attachment, texture, level,
+ numViews))
+ {
+ return false;
+ }
+
+ if (texture != 0)
+ {
+ if (baseViewIndex < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBaseViewIndex);
+ return false;
+ }
+
+ Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ switch (tex->getType())
+ {
+ case TextureType::_2DArray:
+ case TextureType::_2DMultisampleArray:
+ {
+ if (tex->getType() == TextureType::_2DMultisampleArray)
+ {
+ if (!context->getExtensions().multiviewMultisample)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureType);
+ return false;
+ }
+ }
+
+ const Caps &caps = context->getCaps();
+ if (static_cast<GLuint>(baseViewIndex + numViews) > caps.maxArrayTextureLayers)
+ {
+ context->validationError(GL_INVALID_VALUE, kViewsExceedMaxArrayLayers);
+ return false;
+ }
+
+ break;
+ }
+ default:
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureType);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureMultiviewLevelAndFormat(context, tex, level))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateUniform1ui(Context *context, GLint location, GLuint v0)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT, location, 1);
+}
+
+bool ValidateUniform2ui(Context *context, GLint location, GLuint v0, GLuint v1)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC2, location, 1);
+}
+
+bool ValidateUniform3ui(Context *context, GLint location, GLuint v0, GLuint v1, GLuint v2)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC3, location, 1);
+}
+
+bool ValidateUniform4ui(Context *context,
+ GLint location,
+ GLuint v0,
+ GLuint v1,
+ GLuint v2,
+ GLuint v3)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC4, location, 1);
+}
+
+bool ValidateUniform1uiv(Context *context, GLint location, GLsizei count, const GLuint *value)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT, location, count);
+}
+
+bool ValidateUniform2uiv(Context *context, GLint location, GLsizei count, const GLuint *value)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC2, location, count);
+}
+
+bool ValidateUniform3uiv(Context *context, GLint location, GLsizei count, const GLuint *value)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC3, location, count);
+}
+
+bool ValidateUniform4uiv(Context *context, GLint location, GLsizei count, const GLuint *value)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC4, location, count);
+}
+
+bool ValidateIsQuery(Context *context, GLuint id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateUniformMatrix2x3fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT2x3, location, count, transpose);
+}
+
+bool ValidateUniformMatrix3x2fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT3x2, location, count, transpose);
+}
+
+bool ValidateUniformMatrix2x4fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT2x4, location, count, transpose);
+}
+
+bool ValidateUniformMatrix4x2fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT4x2, location, count, transpose);
+}
+
+bool ValidateUniformMatrix3x4fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT3x4, location, count, transpose);
+}
+
+bool ValidateUniformMatrix4x3fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT4x3, location, count, transpose);
+}
+
+bool ValidateEndTransformFeedback(Context *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ if (!transformFeedback->isActive())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackNotActive);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTransformFeedbackVaryings(Context *context,
+ GLuint program,
+ GLsizei count,
+ const GLchar *const *varyings,
+ GLenum bufferMode)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (count < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ switch (bufferMode)
+ {
+ case GL_INTERLEAVED_ATTRIBS:
+ break;
+ case GL_SEPARATE_ATTRIBS:
+ {
+ const Caps &caps = context->getCaps();
+ if (static_cast<GLuint>(count) > caps.maxTransformFeedbackSeparateAttributes)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidTransformFeedbackAttribsCount);
+ return false;
+ }
+ break;
+ }
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetTransformFeedbackVarying(Context *context,
+ GLuint program,
+ GLuint index,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLsizei *size,
+ GLenum *type,
+ GLchar *name)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(programObject->getTransformFeedbackVaryingCount()))
+ {
+ context->validationError(GL_INVALID_VALUE, kTransformFeedbackVaryingIndexOutOfRange);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindTransformFeedback(Context *context, GLenum target, GLuint id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ switch (target)
+ {
+ case GL_TRANSFORM_FEEDBACK:
+ {
+ // Cannot bind a transform feedback object if the current one is started and not
+ // paused (3.0.2 pg 85 section 2.14.1)
+ if (context->getState().isTransformFeedbackActiveUnpaused())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackNotPaused);
+ return false;
+ }
+
+ // Cannot bind a transform feedback object that does not exist (3.0.2 pg 85 section
+ // 2.14.1)
+ if (!context->isTransformFeedbackGenerated(id))
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackDoesNotExist);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsTransformFeedback(Context *context, GLuint id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePauseTransformFeedback(Context *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ // Current transform feedback must be active and not paused in order to pause (3.0.2 pg 86)
+ if (!transformFeedback->isActive())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackNotActive);
+ return false;
+ }
+
+ if (transformFeedback->isPaused())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackPaused);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateResumeTransformFeedback(Context *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ // Current transform feedback must be active and paused in order to resume (3.0.2 pg 86)
+ if (!transformFeedback->isActive())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackNotActive);
+ return false;
+ }
+
+ if (!transformFeedback->isPaused())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackNotPaused);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribI4i(Context *context, GLuint index, GLint x, GLint y, GLint z, GLint w)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttribI4ui(Context *context,
+ GLuint index,
+ GLuint x,
+ GLuint y,
+ GLuint z,
+ GLuint w)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttribI4iv(Context *context, GLuint index, const GLint *v)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttribI4uiv(Context *context, GLuint index, const GLuint *v)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateGetFragDataLocation(Context *context, GLuint program, const GLchar *name)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformIndices(Context *context,
+ GLuint program,
+ GLsizei uniformCount,
+ const GLchar *const *uniformNames,
+ GLuint *uniformIndices)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (uniformCount < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniformsiv(Context *context,
+ GLuint program,
+ GLsizei uniformCount,
+ const GLuint *uniformIndices,
+ GLenum pname,
+ GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (uniformCount < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_UNIFORM_TYPE:
+ case GL_UNIFORM_SIZE:
+ break;
+ case GL_UNIFORM_NAME_LENGTH:
+ if (context->getExtensions().webglCompatibility)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_UNIFORM_BLOCK_INDEX:
+ case GL_UNIFORM_OFFSET:
+ case GL_UNIFORM_ARRAY_STRIDE:
+ case GL_UNIFORM_MATRIX_STRIDE:
+ case GL_UNIFORM_IS_ROW_MAJOR:
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ if (uniformCount > programObject->getActiveUniformCount())
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxActiveUniform);
+ return false;
+ }
+
+ for (int uniformId = 0; uniformId < uniformCount; uniformId++)
+ {
+ const GLuint index = uniformIndices[uniformId];
+
+ if (index >= static_cast<GLuint>(programObject->getActiveUniformCount()))
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxActiveUniform);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformBlockIndex(Context *context, GLuint program, const GLchar *uniformBlockName)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniformBlockiv(Context *context,
+ GLuint program,
+ GLuint uniformBlockIndex,
+ GLenum pname,
+ GLint *params)
+{
+ return ValidateGetActiveUniformBlockivBase(context, program, uniformBlockIndex, pname, nullptr);
+}
+
+bool ValidateGetActiveUniformBlockName(Context *context,
+ GLuint program,
+ GLuint uniformBlockIndex,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLchar *uniformBlockName)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (uniformBlockIndex >= programObject->getActiveUniformBlockCount())
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxActiveUniformBlock);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateUniformBlockBinding(Context *context,
+ GLuint program,
+ GLuint uniformBlockIndex,
+ GLuint uniformBlockBinding)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (uniformBlockBinding >= context->getCaps().maxUniformBufferBindings)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxUniformBufferBindings);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ // if never linked, there won't be any uniform blocks
+ if (uniformBlockIndex >= programObject->getActiveUniformBlockCount())
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxUniformBufferBindings);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawArraysInstanced(Context *context,
+ PrimitiveMode mode,
+ GLint first,
+ GLsizei count,
+ GLsizei primcount)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateDrawArraysInstancedBase(context, mode, first, count, primcount);
+}
+
+bool ValidateFenceSync(Context *context, GLenum condition, GLbitfield flags)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (condition != GL_SYNC_GPU_COMMANDS_COMPLETE)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFenceCondition);
+ return false;
+ }
+
+ if (flags != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidFlags);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsSync(Context *context, GLsync sync)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteSync(Context *context, GLsync sync)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (sync != static_cast<GLsync>(0) && !context->getSync(sync))
+ {
+ context->validationError(GL_INVALID_VALUE, kSyncMissing);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateClientWaitSync(Context *context, GLsync sync, GLbitfield flags, GLuint64 timeout)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if ((flags & ~(GL_SYNC_FLUSH_COMMANDS_BIT)) != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidFlags);
+ return false;
+ }
+
+ Sync *clientWaitSync = context->getSync(sync);
+ if (!clientWaitSync)
+ {
+ context->validationError(GL_INVALID_VALUE, kSyncMissing);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateWaitSync(Context *context, GLsync sync, GLbitfield flags, GLuint64 timeout)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (flags != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidFlags);
+ return false;
+ }
+
+ if (timeout != GL_TIMEOUT_IGNORED)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidTimeout);
+ return false;
+ }
+
+ Sync *waitSync = context->getSync(sync);
+ if (!waitSync)
+ {
+ context->validationError(GL_INVALID_VALUE, kSyncMissing);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetInteger64v(Context *context, GLenum pname, GLint64 *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ GLenum nativeType = GL_NONE;
+ unsigned int numParams = 0;
+ if (!ValidateStateQuery(context, pname, &nativeType, &numParams))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsSampler(Context *context, GLuint sampler)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindSampler(Context *context, GLuint unit, GLuint sampler)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (sampler != 0 && !context->isSampler(sampler))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidSampler);
+ return false;
+ }
+
+ if (unit >= context->getCaps().maxCombinedTextureImageUnits)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidCombinedImageUnit);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribDivisor(Context *context, GLuint index, GLuint divisor)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateTexStorage2D(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateES3TexStorage2DParameters(context, target, levels, internalformat, width, height,
+ 1))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexStorage3D(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateES3TexStorage3DParameters(context, target, levels, internalformat, width, height,
+ depth))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferParameteri64v(Context *context,
+ BufferBinding target,
+ GLenum pname,
+ GLint64 *params)
+{
+ return ValidateGetBufferParameterBase(context, target, pname, false, nullptr);
+}
+
+bool ValidateGetSamplerParameterfv(Context *context, GLuint sampler, GLenum pname, GLfloat *params)
+{
+ return ValidateGetSamplerParameterBase(context, sampler, pname, nullptr);
+}
+
+bool ValidateGetSamplerParameteriv(Context *context, GLuint sampler, GLenum pname, GLint *params)
+{
+ return ValidateGetSamplerParameterBase(context, sampler, pname, nullptr);
+}
+
+bool ValidateGetSamplerParameterIivOES(Context *context,
+ GLuint sampler,
+ GLenum pname,
+ GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGetSamplerParameterBase(context, sampler, pname, nullptr);
+}
+
+bool ValidateGetSamplerParameterIuivOES(Context *context,
+ GLuint sampler,
+ GLenum pname,
+ GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGetSamplerParameterBase(context, sampler, pname, nullptr);
+}
+
+bool ValidateSamplerParameterf(Context *context, GLuint sampler, GLenum pname, GLfloat param)
+{
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, false, &param);
+}
+
+bool ValidateSamplerParameterfv(Context *context,
+ GLuint sampler,
+ GLenum pname,
+ const GLfloat *params)
+{
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, true, params);
+}
+
+bool ValidateSamplerParameteri(Context *context, GLuint sampler, GLenum pname, GLint param)
+{
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, false, &param);
+}
+
+bool ValidateSamplerParameteriv(Context *context, GLuint sampler, GLenum pname, const GLint *params)
+{
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, true, params);
+}
+
+bool ValidateSamplerParameterIivOES(Context *context,
+ GLuint sampler,
+ GLenum pname,
+ const GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, true, params);
+}
+
+bool ValidateSamplerParameterIuivOES(Context *context,
+ GLuint sampler,
+ GLenum pname,
+ const GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, true, params);
+}
+
+bool ValidateGetVertexAttribIiv(Context *context, GLuint index, GLenum pname, GLint *params)
+{
+ return ValidateGetVertexAttribBase(context, index, pname, nullptr, false, true);
+}
+
+bool ValidateGetVertexAttribIuiv(Context *context, GLuint index, GLenum pname, GLuint *params)
+{
+ return ValidateGetVertexAttribBase(context, index, pname, nullptr, false, true);
+}
+
+bool ValidateGetInternalformativ(Context *context,
+ GLenum target,
+ GLenum internalformat,
+ GLenum pname,
+ GLsizei bufSize,
+ GLint *params)
+{
+ return ValidateGetInternalFormativBase(context, target, internalformat, pname, bufSize,
+ nullptr);
+}
+
+bool ValidateBindFragDataLocationIndexedEXT(Context *context,
+ GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char *name)
+{
+ if (!context->getExtensions().blendFuncExtended)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ if (index < 0 || index > 1)
+ {
+ // This error is not explicitly specified but the spec does say that "<index> may be zero or
+ // one to specify that the color be used as either the first or second color input to the
+ // blend equation, respectively"
+ context->validationError(GL_INVALID_VALUE, kFragDataBindingIndexOutOfRange);
+ return false;
+ }
+ if (index == 1)
+ {
+ if (colorNumber >= context->getExtensions().maxDualSourceDrawBuffers)
+ {
+ context->validationError(GL_INVALID_VALUE,
+ kColorNumberGreaterThanMaxDualSourceDrawBuffers);
+ return false;
+ }
+ }
+ else
+ {
+ if (colorNumber >= context->getCaps().maxDrawBuffers)
+ {
+ context->validationError(GL_INVALID_VALUE, kColorNumberGreaterThanMaxDrawBuffers);
+ return false;
+ }
+ }
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+ return true;
+}
+
+bool ValidateBindFragDataLocationEXT(Context *context,
+ GLuint program,
+ GLuint colorNumber,
+ const char *name)
+{
+ return ValidateBindFragDataLocationIndexedEXT(context, program, colorNumber, 0u, name);
+}
+
+bool ValidateGetFragDataIndexEXT(Context *context, GLuint program, const char *name)
+{
+ if (!context->getExtensions().blendFuncExtended)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+ if (!programObject->isLinked())
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateTexStorage2DMultisampleANGLE(Context *context,
+ TextureType target,
+ GLsizei samples,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLboolean fixedSampleLocations)
+{
+ if (!context->getExtensions().textureMultisample)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateTexStorage2DMultisampleBase(context, target, samples, internalFormat, width,
+ height);
+}
+
+bool ValidateGetTexLevelParameterfvANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum pname,
+ GLfloat *params)
+{
+ if (!context->getExtensions().textureMultisample)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateGetTexLevelParameterBase(context, target, level, pname, nullptr);
+}
+
+bool ValidateGetTexLevelParameterivANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum pname,
+ GLint *params)
+{
+ if (!context->getExtensions().textureMultisample)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateGetTexLevelParameterBase(context, target, level, pname, nullptr);
+}
+
+bool ValidateGetMultisamplefvANGLE(Context *context, GLenum pname, GLuint index, GLfloat *val)
+{
+ if (!context->getExtensions().textureMultisample)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateGetMultisamplefvBase(context, pname, index, val);
+}
+
+bool ValidateSampleMaskiANGLE(Context *context, GLuint maskNumber, GLbitfield mask)
+{
+ if (!context->getExtensions().textureMultisample)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateSampleMaskiBase(context, maskNumber, mask);
+}
+} // namespace gl