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.cpp5311
1 files changed, 5311 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..633cc02da9
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/validationES3.cpp
@@ -0,0 +1,5311 @@
+//
+// Copyright 2013 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/PixelLocalStorage.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(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum attachment,
+ TextureID texture,
+ GLint level,
+ GLsizei numViews)
+{
+ if (!(context->getExtensions().multiviewOVR || context->getExtensions().multiview2OVR))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kMultiviewNotAvailable);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ if (texture.value != 0 && numViews < 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kMultiviewViewsTooSmall);
+ return false;
+ }
+
+ if (static_cast<GLuint>(numViews) > context->getCaps().maxViews)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kMultiviewViewsTooLarge);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferTextureMultiviewLevelAndFormat(const Context *context,
+ angle::EntryPoint entryPoint,
+ const Texture *texture,
+ GLint level)
+{
+ TextureType type = texture->getType();
+ if (!ValidMipLevel(context, type, level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ const auto &format = texture->getFormat(NonCubeTextureTypeToTarget(type), level);
+ if (format.info->compressed)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kCompressedTexturesNotAttachable);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateUniformES3(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum uniformType,
+ UniformLocation location,
+ GLint count)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateUniform(context, entryPoint, uniformType, location, count);
+}
+
+bool ValidateUniformMatrixES3(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum valueType,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose)
+{
+ // Check for ES3 uniform entry points
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateUniformMatrix(context, entryPoint, valueType, location, count, transpose);
+}
+
+bool ValidateGenOrDeleteES3(const Context *context, angle::EntryPoint entryPoint, GLint n)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateGenOrDeleteCountES3(const Context *context, angle::EntryPoint entryPoint, GLint count)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ if (count < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateCopyTexture3DCommon(const Context *context,
+ angle::EntryPoint entryPoint,
+ const Texture *source,
+ GLint sourceLevel,
+ GLint srcInternalFormat,
+ const Texture *dest,
+ GLint destLevel,
+ GLint internalFormat,
+ TextureTarget destTarget)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!context->getExtensions().copyTexture3dANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kANGLECopyTexture3DUnavailable);
+ return false;
+ }
+
+ if (!ValidTexture3DTarget(context, source->getType()))
+ {
+ context->validationError(entryPoint, 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->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ srcInternalFormat);
+ return false;
+ }
+
+ if (!ValidTexture3DTarget(context, TextureTargetToType(destTarget)))
+ {
+ context->validationError(entryPoint, 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_RGBX8_ANGLE:
+ 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->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalFormat);
+ return false;
+ }
+
+ return true;
+}
+} // anonymous namespace
+
+static bool ValidateTexImageFormatCombination(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum internalFormat,
+ GLenum format,
+ GLenum type)
+{
+ // Different validation if on desktop api
+ if (context->getClientType() == EGL_OPENGL_API)
+ {
+ // The type and format are valid if any supported internal format has that type and format
+ if (!ValidDesktopFormat(format))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (!ValidDesktopType(type))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidType);
+ return false;
+ }
+ }
+ else
+ {
+ // The type and format are valid if any supported internal format has that type and format.
+ // ANGLE_texture_external_yuv_sampling extension adds support for YUV formats
+ if (gl::IsYuvFormat(format))
+ {
+ if (!context->getExtensions().yuvInternalFormatANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidES3Format(format))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ }
+
+ if (!ValidES3Type(type) || (type == GL_HALF_FLOAT_OES && context->isWebGL()))
+ {
+ context->validationError(entryPoint, 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->validationErrorF(entryPoint, GL_INVALID_VALUE, kInvalidInternalFormat,
+ internalFormat);
+ 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(entryPoint, GL_INVALID_OPERATION, k3DDepthStencil);
+ return false;
+ }
+
+ if (context->getClientType() == EGL_OPENGL_API)
+ {
+ // Check if this is a valid format combination to load texture data
+ if (!ValidDesktopFormatCombination(format, type, internalFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormatCombination);
+ return false;
+ }
+ }
+ else
+ {
+ // Check if this is a valid format combination to load texture data
+ // ANGLE_texture_external_yuv_sampling extension adds support for YUV formats
+ if (gl::IsYuvFormat(format))
+ {
+ if (type != GL_UNSIGNED_BYTE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidFormatCombination);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidES3FormatCombination(format, type, internalFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidFormatCombination);
+ return false;
+ }
+ }
+ }
+
+ const InternalFormat &formatInfo = GetInternalFormatInfo(internalFormat, type);
+ if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalFormat);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateES3CompressedFormatForTexture2DArray(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum format)
+{
+ if (IsETC1Format(format) || IsPVRTC1Format(format))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInternalFormatRequiresTexture2D);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateES3CompressedFormatForTexture3D(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum format)
+{
+ if (IsETC1Format(format) || IsPVRTC1Format(format))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInternalFormatRequiresTexture2D);
+ return false;
+ }
+
+ if (IsETC2EACFormat(format))
+ {
+ // ES 3.1, Section 8.7, page 169.
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInternalFormatRequiresTexture2DArray);
+ return false;
+ }
+
+ if (IsASTC2DFormat(format) && !(context->getExtensions().textureCompressionAstcHdrKHR ||
+ context->getExtensions().textureCompressionAstcSliced3dKHR))
+ {
+ // GL_KHR_texture_compression_astc_hdr, TEXTURE_3D is not supported without HDR profile
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInternalFormatRequiresTexture2DArrayASTC);
+ return false;
+ }
+
+ if (IsS3TCFormat(format))
+ {
+ // GL_EXT_texture_compression_s3tc
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInternalFormatRequiresTexture2DArrayS3TC);
+ return false;
+ }
+
+ if (IsRGTCFormat(format))
+ {
+ // GL_EXT_texture_compression_rgtc
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInternalFormatRequiresTexture2DArrayRGTC);
+ return false;
+ }
+
+ if (IsBPTCFormat(format) && (context->getLimitations().noCompressedTexture3D))
+ {
+ // GL_EXT_texture_compression_bptc
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInternalFormatRequiresTexture2DArrayBPTC);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexImageParametersBase(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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);
+
+ if (gl::IsYuvFormat(format))
+ {
+ // According to ANGLE_yuv_internal_format, the texture needs to be an immutable
+ // texture, texture target can only be TEXTURE_2D and there is no mipmap support
+ if (!context->getExtensions().yuvInternalFormatANGLE || !isSubImage)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (target != TextureTarget::_2D)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ if (level != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ }
+
+ // Validate image size
+ if (!ValidImageSizeParameters(context, entryPoint, texType, level, width, height, depth,
+ isSubImage))
+ {
+ // Error already processed.
+ return false;
+ }
+
+ // Verify zero border
+ if (border != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidBorder);
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0 || zoffset < 0)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+
+ switch (texType)
+ {
+ case TextureType::_2D:
+ case TextureType::External:
+ case TextureType::VideoImage:
+ if (width > (caps.max2DTextureSize >> level) ||
+ height > (caps.max2DTextureSize >> level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ case TextureType::Rectangle:
+ ASSERT(level == 0);
+ if (width > caps.maxRectangleTextureSize || height > caps.maxRectangleTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ if (isCompressed)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+ break;
+
+ case TextureType::CubeMap:
+ if (!isSubImage && width != height)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (width > (caps.maxCubeMapTextureSize >> level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ case TextureType::_3D:
+ if (width > (caps.max3DTextureSize >> level) ||
+ height > (caps.max3DTextureSize >> level) ||
+ depth > (caps.max3DTextureSize >> level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ case TextureType::_2DArray:
+ if (width > (caps.max2DTextureSize >> level) ||
+ height > (caps.max2DTextureSize >> level) || depth > caps.maxArrayTextureLayers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ case TextureType::CubeMapArray:
+ if (!isSubImage && width != height)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (width > (caps.maxCubeMapTextureSize >> level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+
+ if (width > (caps.max3DTextureSize >> level) ||
+ height > (caps.max3DTextureSize >> level) ||
+ depth > (caps.max3DTextureSize >> level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+
+ if (!isSubImage && depth % 6 != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kCubemapInvalidDepth);
+ return false;
+ }
+ break;
+
+ case TextureType::InvalidEnum:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kEnumInvalid);
+ return false;
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ ToGLenum(texType));
+ return false;
+ }
+
+ Texture *texture = context->getTextureByType(texType);
+ if (!texture)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kMissingTexture);
+ return false;
+ }
+
+ if (texture->getImmutableFormat() && !isSubImage)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_OPERATION, kInvalidMipLevel);
+ return false;
+ }
+
+ const InternalFormat &actualFormatInfo = isSubImage
+ ? *texture->getFormat(target, level).info
+ : GetInternalFormatInfo(internalformat, type);
+ if (isCompressed)
+ {
+ // compressedTexSubImage does not generate GL_INVALID_ENUM when format is unknown or invalid
+ if (!isSubImage)
+ {
+ if (!actualFormatInfo.compressed && !actualFormatInfo.paletted)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kCompressedMismatch);
+ return false;
+ }
+
+ if (!actualFormatInfo.textureSupport(context->getClientVersion(),
+ context->getExtensions()))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ }
+
+ if (texType == TextureType::_2DArray)
+ {
+ GLenum compressedDataFormat = isSubImage ? format : internalformat;
+ if (!ValidateES3CompressedFormatForTexture2DArray(context, entryPoint,
+ compressedDataFormat))
+ {
+ // Error already generated.
+ return false;
+ }
+ }
+
+ if (texType == TextureType::_3D)
+ {
+ GLenum compressedDataFormat = isSubImage ? format : internalformat;
+ if (!ValidateES3CompressedFormatForTexture3D(context, entryPoint, compressedDataFormat))
+ {
+ // Error already generated.
+ 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(entryPoint, GL_INVALID_OPERATION,
+ kInvalidCompressedImageSize);
+ return false;
+ }
+
+ if (format != actualInternalFormat)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kMismatchedFormat);
+ return false;
+ }
+
+ // GL_EXT_compressed_ETC1_RGB8_sub_texture allows this format
+ if (IsETC1Format(actualInternalFormat) &&
+ !context->getExtensions().compressedETC1RGB8SubTextureEXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalformat);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height,
+ depth))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidCompressedImageSize);
+ return false;
+ }
+ }
+
+ // Disallow 3D-only compressed formats from being set on 2D textures
+ if (actualFormatInfo.compressedBlockDepth > 1 && texType != TextureType::_2DArray)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidTextureTarget);
+ return false;
+ }
+ }
+ else
+ {
+ // Compressed formats are not valid internal formats for glTexImage*D
+ if (!isSubImage)
+ {
+ const InternalFormat &internalFormatInfo = GetSizedInternalFormatInfo(internalformat);
+ if (internalFormatInfo.compressed)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_VALUE, kInvalidInternalFormat,
+ internalformat);
+ return false;
+ }
+ }
+
+ if (!ValidateTexImageFormatCombination(context, entryPoint, texType, actualInternalFormat,
+ format, type))
+ {
+ return false;
+ }
+ }
+
+ // Validate sub image parameters
+ if (isSubImage)
+ {
+ if (isCompressed != actualFormatInfo.compressed)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kCompressedMismatch);
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0 || zoffset < 0)
+ {
+ context->validationError(entryPoint, 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(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ if (width > 0 && height > 0 && depth > 0 && pixels == nullptr &&
+ context->getState().getTargetBuffer(BufferBinding::PixelUnpack) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kPixelDataNull);
+ return false;
+ }
+ }
+
+ GLenum sizeCheckFormat = isSubImage ? format : internalformat;
+ if (!ValidImageDataSize(context, entryPoint, texType, width, height, depth, sizeCheckFormat,
+ type, pixels, imageSize))
+ {
+ return false;
+ }
+
+ // Check for pixel unpack buffer related API errors
+ 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>(GetTypeInfo(type).bytes);
+
+ if ((offset % dataBytesPerPixel) != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kDataTypeNotAligned);
+ return false;
+ }
+ }
+
+ // ...the buffer object's data store is currently mapped but not persistently.
+ if (pixelUnpackBuffer->isMapped() && !pixelUnpackBuffer->isPersistentlyMapped())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferMapped);
+ return false;
+ }
+ }
+
+ if (context->getExtensions().webglCompatibilityANGLE)
+ {
+ // Define:
+ // DataStoreWidth = (GL_UNPACK_ROW_LENGTH ? GL_UNPACK_ROW_LENGTH : width)
+ // DataStoreHeight = (GL_UNPACK_IMAGE_HEIGHT ? GL_UNPACK_IMAGE_HEIGHT : height)
+ //
+ // WebGL 2.0 imposes the following additional constraints:
+ //
+ // 1) texImage2D and texSubImage2D generate INVALID_OPERATION if:
+ // GL_UNPACK_SKIP_PIXELS + width > DataStoreWidth
+ // except for texImage2D if no GL_PIXEL_UNPACK_BUFFER is
+ // bound and _pixels_ is null.
+ //
+ // 2) texImage3D and texSubImage3D generate INVALID_OPERATION if:
+ // GL_UNPACK_SKIP_PIXELS + width > DataStoreWidth
+ // GL_UNPACK_SKIP_ROWS + height > DataStoreHeight
+ // except for texImage3D if no GL_PIXEL_UNPACK_BUFFER is
+ // bound and _pixels_ is null.
+ if (!pixelUnpackBuffer && !pixels && !isSubImage)
+ {
+ // Exception case for texImage2D or texImage3D, above.
+ }
+ else
+ {
+ const auto &unpack = context->getState().getUnpackState();
+ GLint dataStoreWidth = unpack.rowLength ? unpack.rowLength : width;
+ if (unpack.skipPixels + width > dataStoreWidth)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidUnpackParametersForWebGL);
+ return false;
+ }
+ if (target == TextureTarget::_3D || target == TextureTarget::_2DArray)
+ {
+ GLint dataStoreHeight = unpack.imageHeight ? unpack.imageHeight : height;
+ if (unpack.skipRows + height > dataStoreHeight)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidUnpackParametersForWebGL);
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ValidateES3TexImage2DParameters(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3TexImageParametersBase(
+ context, entryPoint, target, level, internalformat, isCompressed, isSubImage, xoffset,
+ yoffset, zoffset, width, height, depth, border, format, type, imageSize, pixels);
+}
+
+bool ValidateES3TexImage3DParameters(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3TexImageParametersBase(
+ context, entryPoint, 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,
+ FramebufferID 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.value != 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(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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, entryPoint, target, level, internalformat,
+ isSubImage, xoffset, yoffset, zoffset, x, y, width,
+ height, border, &textureFormat))
+ {
+ return false;
+ }
+ ASSERT(textureFormat.valid() || !isSubImage);
+
+ const auto &state = context->getState();
+ Framebuffer *framebuffer = state.getReadFramebuffer();
+ FramebufferID readFramebufferID = framebuffer->id();
+
+ if (!ValidateFramebufferComplete(context, entryPoint, framebuffer))
+ {
+ return false;
+ }
+
+ // needIntrinsic = true. Treat renderToTexture textures as single sample since they will be
+ // resolved before copying
+ if (!framebuffer->isDefault() &&
+ !ValidateFramebufferNotMultisampled(context, entryPoint, framebuffer, true))
+ {
+ 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(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+
+ if (isSubImage)
+ {
+ if (!IsValidES3CopyTexImageCombination(*textureFormat.info, *source->getFormat().info,
+ readFramebufferID))
+ {
+ context->validationError(entryPoint, 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(entryPoint, 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(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3CopyTexImageParametersBase(context, entryPoint, target, level, internalformat,
+ isSubImage, xoffset, yoffset, zoffset, x, y, width,
+ height, border);
+}
+
+bool ValidateES3CopyTexImage3DParameters(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3CopyTexImageParametersBase(context, entryPoint, target, level, internalformat,
+ isSubImage, xoffset, yoffset, zoffset, x, y, width,
+ height, border);
+}
+
+bool ValidateES3TexStorageParametersLevel(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ GLsizei maxDim = std::max(width, height);
+ if (target != TextureType::_2DArray)
+ {
+ maxDim = std::max(maxDim, depth);
+ }
+
+ if (levels > log2(maxDim) + 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidMipLevels);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexStorageParametersExtent(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ const Caps &caps = context->getCaps();
+
+ switch (target)
+ {
+ case TextureType::_2D:
+ {
+ if (width > caps.max2DTextureSize || height > caps.max2DTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::Rectangle:
+ {
+ if (levels != 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevels);
+ return false;
+ }
+
+ if (width > caps.maxRectangleTextureSize || height > caps.maxRectangleTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::CubeMap:
+ {
+ if (width != height)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (width > caps.maxCubeMapTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::_3D:
+ {
+ if (width > caps.max3DTextureSize || height > caps.max3DTextureSize ||
+ depth > caps.max3DTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::_2DArray:
+ {
+ if (width > caps.max2DTextureSize || height > caps.max2DTextureSize ||
+ depth > caps.maxArrayTextureLayers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::CubeMapArray:
+ {
+ if (width != height)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (width > caps.maxCubeMapTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+
+ if (width > caps.max3DTextureSize || height > caps.max3DTextureSize ||
+ depth > caps.max3DTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+
+ if (depth % 6 != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kCubemapInvalidDepth);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexStorageParametersTexObject(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target)
+{
+ Texture *texture = context->getTextureByType(target);
+ if (!texture || texture->id().value == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kMissingTexture);
+ return false;
+ }
+
+ if (texture->getImmutableFormat())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTextureIsImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexStorageParametersFormat(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ // From ANGLE_texture_external_yuv_sampling:
+ // Texture target can only be TEXTURE_2D, there is no mipmap support
+ if (gl::IsYuvFormat(internalformat))
+ {
+ if (!context->getExtensions().yuvInternalFormatANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (target != TextureType::_2D)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ if (levels != 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
+ if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (!formatInfo.sized)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ if (formatInfo.compressed)
+ {
+ if (target == TextureType::Rectangle)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+
+ if (target == TextureType::_2DArray)
+ {
+ if (!ValidateES3CompressedFormatForTexture2DArray(context, entryPoint,
+ formatInfo.internalFormat))
+ {
+ // Error already generated.
+ return false;
+ }
+ }
+
+ if (target == TextureType::_3D)
+ {
+ if (!ValidateES3CompressedFormatForTexture3D(context, entryPoint,
+ formatInfo.internalFormat))
+ {
+ // Error already generated.
+ return false;
+ }
+ }
+
+ if (!ValidCompressedImageSize(context, formatInfo.internalFormat, 0, width, height, depth))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidCompressedImageSize);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateES3TexStorageParametersBase(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (width < 1 || height < 1 || depth < 1 || levels < 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kTextureSizeTooSmall);
+ return false;
+ }
+
+ if (!ValidateES3TexStorageParametersLevel(context, entryPoint, target, levels, width, height,
+ depth))
+ {
+ // Error already generated.
+ return false;
+ }
+
+ if (!ValidateES3TexStorageParametersExtent(context, entryPoint, target, levels, width, height,
+ depth))
+ {
+ // Error already generated.
+ return false;
+ }
+
+ if (!ValidateES3TexStorageParametersTexObject(context, entryPoint, target))
+ {
+ // Error already generated.
+ return false;
+ }
+
+ if (!ValidateES3TexStorageParametersFormat(context, entryPoint, target, levels, internalformat,
+ width, height, depth))
+ {
+ // Error already generated.
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexStorage2DParameters(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (!ValidTexture2DTarget(context, target))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3TexStorageParametersBase(context, entryPoint, target, levels, internalformat,
+ width, height, depth);
+}
+
+bool ValidateES3TexStorage3DParameters(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (!ValidTexture3DTarget(context, target))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES3TexStorageParametersBase(context, entryPoint, target, levels, internalformat,
+ width, height, depth);
+}
+
+bool ValidateBeginQuery(const Context *context,
+ angle::EntryPoint entryPoint,
+ QueryType target,
+ QueryID id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateBeginQueryBase(context, entryPoint, target, id);
+}
+
+bool ValidateEndQuery(const Context *context, angle::EntryPoint entryPoint, QueryType target)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateEndQueryBase(context, entryPoint, target);
+}
+
+bool ValidateGetQueryiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ QueryType target,
+ GLenum pname,
+ const GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetQueryivBase(context, entryPoint, target, pname, nullptr);
+}
+
+bool ValidateGetQueryObjectuiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ QueryID id,
+ GLenum pname,
+ const GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetQueryObjectValueBase(context, entryPoint, id, pname, nullptr);
+}
+
+bool ValidateFramebufferTextureLayer(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum attachment,
+ TextureID texture,
+ GLint level,
+ GLint layer)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (texture.value != 0)
+ {
+ if (layer < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeLayer);
+ return false;
+ }
+
+ Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ switch (tex->getType())
+ {
+ case TextureType::_2DArray:
+ {
+ if (level > log2(caps.max2DTextureSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidMipLevel);
+ return false;
+ }
+
+ if (layer >= caps.maxArrayTextureLayers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidLayer);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::_3D:
+ {
+ if (level > log2(caps.max3DTextureSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidMipLevel);
+ return false;
+ }
+
+ if (layer >= caps.max3DTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidLayer);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::_2DMultisampleArray:
+ {
+ if (level != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidMipLevel);
+ return false;
+ }
+
+ if (layer >= caps.maxArrayTextureLayers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidLayer);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::CubeMap:
+ {
+ if (level > log2(caps.maxCubeMapTextureSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidMipLevel);
+ return false;
+ }
+
+ if (layer >= static_cast<GLint>(kCubeFaceCount))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidLayer);
+ return false;
+ }
+ }
+ break;
+
+ case TextureType::CubeMapArray:
+ {
+ if (level > log2(caps.maxCubeMapTextureSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidMipLevel);
+ return false;
+ }
+
+ if (layer >= caps.maxArrayTextureLayers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kFramebufferTextureInvalidLayer);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kFramebufferTextureLayerIncorrectTextureType);
+ return false;
+ }
+
+ const auto &format = tex->getFormat(TextureTypeToTarget(tex->getType(), layer), level);
+ if (format.info->compressed)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kCompressedTexturesNotAttachable);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateInvalidateFramebuffer(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLsizei numAttachments,
+ const GLenum *attachments)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ bool defaultFramebuffer = false;
+
+ switch (target)
+ {
+ case GL_DRAW_FRAMEBUFFER:
+ case GL_FRAMEBUFFER:
+ defaultFramebuffer = context->getState().getDrawFramebuffer()->isDefault();
+ break;
+ case GL_READ_FRAMEBUFFER:
+ defaultFramebuffer = context->getState().getReadFramebuffer()->isDefault();
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFramebufferTarget);
+ return false;
+ }
+
+ return ValidateDiscardFramebufferBase(context, entryPoint, target, numAttachments, attachments,
+ defaultFramebuffer);
+}
+
+bool ValidateInvalidateSubFramebuffer(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLsizei numAttachments,
+ const GLenum *attachments,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ if (width < 0 || height < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ return ValidateInvalidateFramebuffer(context, entryPoint, target, numAttachments, attachments);
+}
+
+bool ValidateClearBuffer(const Context *context, angle::EntryPoint entryPoint)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateFramebufferComplete(context, entryPoint, context->getState().getDrawFramebuffer()))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawRangeElements(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ GLuint start,
+ GLuint end,
+ GLsizei count,
+ DrawElementsType type,
+ const void *indices)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (end < start)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidElementRange);
+ return false;
+ }
+
+ if (!ValidateDrawElementsCommon(context, entryPoint, mode, count, type, indices, 0))
+ {
+ return false;
+ }
+
+ // Skip range checks for no-op calls.
+ if (count <= 0)
+ {
+ return true;
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformuiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ UniformLocation location,
+ const GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetUniformBase(context, entryPoint, program, location);
+}
+
+bool ValidateReadBuffer(const Context *context, angle::EntryPoint entryPoint, GLenum src)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ const Framebuffer *readFBO = context->getState().getReadFramebuffer();
+
+ if (readFBO == nullptr)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_ENUM, kInvalidReadBuffer);
+ return false;
+ }
+
+ if (readFBO->isDefault())
+ {
+ if (src != GL_BACK)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidDefaultReadBuffer);
+ return false;
+ }
+ }
+ else
+ {
+ GLuint drawBuffer = static_cast<GLuint>(src - GL_COLOR_ATTACHMENT0);
+
+ if (drawBuffer >= static_cast<GLuint>(context->getCaps().maxColorAttachments))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExceedsMaxColorAttachments);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexImage3D(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidTextureTarget(context, TextureTargetToType(target)))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ // Validate image size
+ if (!ValidImageSizeParameters(context, entryPoint, TextureTargetToType(target), level, width,
+ height, depth, false))
+ {
+ // Error already generated.
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
+ if (!formatInfo.compressed)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidCompressedFormat);
+ return false;
+ }
+
+ GLuint blockSize = 0;
+ if (!formatInfo.computeCompressedImageSize(Extents(width, height, depth), &blockSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIntegerOverflow);
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidCompressedImageSize);
+ return false;
+ }
+
+ // 3D texture target validation
+ if (target != TextureTarget::_3D && target != TextureTarget::_2DArray)
+ {
+ if (context->getClientVersion() < ES_3_2 || target != TextureTarget::CubeMapArray)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+ }
+
+ // validateES3TexImageFormat sets the error code if there is an error
+ if (!ValidateES3TexImage3DParameters(context, entryPoint, target, level, internalformat, true,
+ false, 0, 0, 0, width, height, depth, border, GL_NONE,
+ GL_NONE, -1, data))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexImage3DRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
+{
+ if (!ValidateRobustCompressedTexImageBase(context, entryPoint, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexImage3D(context, entryPoint, target, level, internalformat, width,
+ height, depth, border, imageSize, data);
+}
+
+bool ValidateBindVertexArray(const Context *context,
+ angle::EntryPoint entryPoint,
+ VertexArrayID array)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateBindVertexArrayBase(context, entryPoint, array);
+}
+
+bool ValidateIsVertexArray(const Context *context,
+ angle::EntryPoint entryPoint,
+ VertexArrayID array)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateBindBufferCommon(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLuint index,
+ BufferID buffer,
+ GLintptr offset,
+ GLsizeiptr size)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (buffer.value != 0 && offset < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (!context->getState().isBindGeneratesResourceEnabled() &&
+ !context->isBufferGenerated(buffer))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kObjectNotGenerated);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ switch (target)
+ {
+ case BufferBinding::TransformFeedback:
+ {
+ if (index >= static_cast<GLuint>(caps.maxTransformFeedbackSeparateAttributes))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kIndexExceedsTransformFeedbackBufferBindings);
+ return false;
+ }
+ if (buffer.value != 0 && ((offset % 4) != 0 || (size % 4) != 0))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kOffsetAndSizeAlignment);
+ return false;
+ }
+
+ if (context->getState().isTransformFeedbackActive())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kTransformFeedbackTargetActive);
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::Uniform:
+ {
+ if (index >= static_cast<GLuint>(caps.maxUniformBufferBindings))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kIndexExceedsMaxUniformBufferBindings);
+ return false;
+ }
+
+ ASSERT(caps.uniformBufferOffsetAlignment);
+ if (buffer.value != 0 && (offset % caps.uniformBufferOffsetAlignment) != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kUniformBufferOffsetAlignment);
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::AtomicCounter:
+ {
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= static_cast<GLuint>(caps.maxAtomicCounterBufferBindings))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kIndexExceedsMaxAtomicCounterBufferBindings);
+ return false;
+ }
+ if (buffer.value != 0 && (offset % 4) != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kOffsetAlignment);
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::ShaderStorage:
+ {
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= static_cast<GLuint>(caps.maxShaderStorageBufferBindings))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kExceedsMaxShaderStorageBufferBindings);
+ return false;
+ }
+ ASSERT(caps.shaderStorageBufferOffsetAlignment);
+ if (buffer.value != 0 && (offset % caps.shaderStorageBufferOffsetAlignment) != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kShaderStorageBufferOffsetAlignment);
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::Texture:
+ {
+ if (!context->getExtensions().textureBufferAny())
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM,
+ kTextureBufferExtensionNotAvailable);
+ return false;
+ }
+ if (index != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kIndexExceedsMaxUniformBufferBindings);
+ return false;
+ }
+ if (buffer.value != 0 && (offset % caps.textureBufferOffsetAlignment) != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kTextureBufferOffsetAlignment);
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::InvalidEnum:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kEnumInvalid);
+ return false;
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ ToGLenum(target));
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindBufferBase(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLuint index,
+ BufferID buffer)
+{
+ return ValidateBindBufferCommon(context, entryPoint, target, index, buffer, 0, 0);
+}
+
+bool ValidateBindBufferRange(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLuint index,
+ BufferID buffer,
+ GLintptr offset,
+ GLsizeiptr size)
+{
+ if (buffer.value != 0 && size <= 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidBindBufferSize);
+ return false;
+ }
+ return ValidateBindBufferCommon(context, entryPoint, target, index, buffer, offset, size);
+}
+
+bool ValidateProgramBinary(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLenum binaryFormat,
+ const void *binary,
+ GLsizei length)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateProgramBinaryBase(context, entryPoint, program, binaryFormat, binary, length);
+}
+
+bool ValidateGetProgramBinary(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLenum *binaryFormat,
+ const void *binary)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetProgramBinaryBase(context, entryPoint, program, bufSize, length, binaryFormat,
+ binary);
+}
+
+bool ValidateProgramParameteriBase(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLenum pname,
+ GLint value)
+{
+ if (GetValidProgram(context, entryPoint, program) == nullptr)
+ {
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
+ if (value != GL_FALSE && value != GL_TRUE)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidBooleanValue);
+ return false;
+ }
+ break;
+
+ case GL_PROGRAM_SEPARABLE:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kES31Required);
+ return false;
+ }
+
+ if (value != GL_FALSE && value != GL_TRUE)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidBooleanValue);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateProgramParameteri(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLenum pname,
+ GLint value)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateProgramParameteriBase(context, entryPoint, program, pname, value);
+}
+
+bool ValidateBlitFramebuffer(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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->getExtensions().framebufferBlitNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateBlitFramebufferParameters(context, entryPoint, srcX0, srcY0, srcX1, srcY1, dstX0,
+ dstY0, dstX1, dstY1, mask, filter);
+}
+
+bool ValidateClearBufferiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLint *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 || drawbuffer >= context->getCaps().maxDrawBuffers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxDrawBuffer);
+ return false;
+ }
+ if (context->getExtensions().webglCompatibilityANGLE)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_INT};
+ if (!ValidateWebGLFramebufferAttachmentClearType(context, entryPoint, drawbuffer,
+ validComponentTypes,
+ ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
+ break;
+
+ case GL_STENCIL:
+ if (drawbuffer != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kInvalidDepthStencilDrawBuffer);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, buffer);
+ return false;
+ }
+
+ return ValidateClearBuffer(context, entryPoint);
+}
+
+bool ValidateClearBufferuiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLuint *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 || drawbuffer >= context->getCaps().maxDrawBuffers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxDrawBuffer);
+ return false;
+ }
+ if (context->getExtensions().webglCompatibilityANGLE)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_UNSIGNED_INT};
+ if (!ValidateWebGLFramebufferAttachmentClearType(context, entryPoint, drawbuffer,
+ validComponentTypes,
+ ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, buffer);
+ return false;
+ }
+
+ return ValidateClearBuffer(context, entryPoint);
+}
+
+bool ValidateClearBufferfv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLfloat *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 || drawbuffer >= context->getCaps().maxDrawBuffers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxDrawBuffer);
+ return false;
+ }
+ if (context->getExtensions().webglCompatibilityANGLE)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_FLOAT, GL_UNSIGNED_NORMALIZED,
+ GL_SIGNED_NORMALIZED};
+ if (!ValidateWebGLFramebufferAttachmentClearType(context, entryPoint, drawbuffer,
+ validComponentTypes,
+ ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
+ break;
+
+ case GL_DEPTH:
+ if (drawbuffer != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kInvalidDepthStencilDrawBuffer);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, buffer);
+ return false;
+ }
+
+ return ValidateClearBuffer(context, entryPoint);
+}
+
+bool ValidateClearBufferfi(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum buffer,
+ GLint drawbuffer,
+ GLfloat depth,
+ GLint stencil)
+{
+ switch (buffer)
+ {
+ case GL_DEPTH_STENCIL:
+ if (drawbuffer != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kInvalidDepthStencilDrawBuffer);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, buffer);
+ return false;
+ }
+
+ return ValidateClearBuffer(context, entryPoint);
+}
+
+bool ValidateDrawBuffers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const GLenum *bufs)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateDrawBuffersBase(context, entryPoint, n, bufs);
+}
+
+bool ValidateCopyTexSubImage3D(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateES3CopyTexImage3DParameters(context, entryPoint, target, level, GL_NONE, true,
+ xoffset, yoffset, zoffset, x, y, width, height, 0);
+}
+
+bool ValidateCopyTexture3DANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureID sourceId,
+ GLint sourceLevel,
+ TextureTarget destTarget,
+ TextureID destId,
+ GLint destLevel,
+ GLint internalFormat,
+ GLenum destType,
+ GLboolean unpackFlipY,
+ GLboolean unpackPremultiplyAlpha,
+ GLboolean unpackUnmultiplyAlpha)
+{
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ if (!ValidateCopyTexture3DCommon(context, entryPoint, source, sourceLevel,
+ sourceFormat.info->internalFormat, dest, destLevel,
+ internalFormat, destTarget))
+ {
+ return false;
+ }
+
+ if (!ValidMipLevel(context, source->getType(), sourceLevel))
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_OPERATION, kInvalidSourceTextureSize);
+ return false;
+ }
+
+ if (dest->getImmutableFormat())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kDestinationImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCopySubTexture3DANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureID sourceId,
+ GLint sourceLevel,
+ TextureTarget destTarget,
+ TextureID 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(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ const InternalFormat &destFormat = *dest->getFormat(destTarget, destLevel).info;
+
+ if (!ValidateCopyTexture3DCommon(context, entryPoint, source, sourceLevel,
+ sourceFormat.info->internalFormat, dest, destLevel,
+ destFormat.internalFormat, destTarget))
+ {
+ return false;
+ }
+
+ if (x < 0 || y < 0 || z < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeXYZ);
+ return false;
+ }
+
+ if (width < 0 || height < 0 || depth < 0)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kSourceTextureTooSmall);
+ return false;
+ }
+
+ if (TextureTargetToType(destTarget) != dest->getType())
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTextureType);
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0 || zoffset < 0)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kDestinationTextureTooSmall);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexImage3D(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, entryPoint, target, level, internalformat,
+ false, false, 0, 0, 0, width, height, depth, border,
+ format, type, -1, pixels);
+}
+
+bool ValidateTexImage3DRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, entryPoint, bufSize))
+ {
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, entryPoint, target, level, internalformat,
+ false, false, 0, 0, 0, width, height, depth, border,
+ format, type, bufSize, pixels);
+}
+
+bool ValidateTexSubImage3D(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, entryPoint, target, level, GL_NONE, false, true,
+ xoffset, yoffset, zoffset, width, height, depth, 0,
+ format, type, -1, pixels);
+}
+
+bool ValidateTexSubImage3DRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, entryPoint, bufSize))
+ {
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, entryPoint, target, level, GL_NONE, false, true,
+ xoffset, yoffset, zoffset, width, height, depth, 0,
+ format, type, bufSize, pixels);
+}
+
+bool ValidateCompressedTexSubImage3D(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateES3TexImage3DParameters(context, entryPoint, target, level, GL_NONE, true, true,
+ xoffset, yoffset, zoffset, width, height, depth, 0, format,
+ GL_NONE, -1, data))
+ {
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(format);
+
+ if (!formatInfo.compressed)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidCompressedFormat);
+ return false;
+ }
+
+ GLuint blockSize = 0;
+ if (!formatInfo.computeCompressedImageSize(Extents(width, height, depth), &blockSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidCompressedImageSize);
+ return false;
+ }
+
+ if (data == nullptr)
+ {
+ if (context->getState().getTargetBuffer(BufferBinding::PixelUnpack) == nullptr)
+ {
+ // If data is null, we need an unpack buffer to read from
+ context->validationError(entryPoint, GL_INVALID_VALUE, kPixelDataNull);
+ return false;
+ }
+
+ if (context->getTextureByTarget(target)->isCompressedFormatEmulated(context, target, level))
+ {
+ // TODO (anglebug.com/7464): Can't populate from a buffer using emulated format
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidEmulatedFormat);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexSubImage3DRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ 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, entryPoint, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexSubImage3D(context, entryPoint, target, level, xoffset, yoffset,
+ zoffset, width, height, depth, format, imageSize, data);
+}
+
+bool ValidateGenQueries(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const QueryID *queries)
+{
+ return ValidateGenOrDeleteES3(context, entryPoint, n);
+}
+
+bool ValidateDeleteQueries(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const QueryID *queries)
+{
+ return ValidateGenOrDeleteES3(context, entryPoint, n);
+}
+
+bool ValidateGenSamplers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei count,
+ const SamplerID *samplers)
+{
+ return ValidateGenOrDeleteCountES3(context, entryPoint, count);
+}
+
+bool ValidateDeleteSamplers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei count,
+ const SamplerID *samplers)
+{
+ return ValidateGenOrDeleteCountES3(context, entryPoint, count);
+}
+
+bool ValidateGenTransformFeedbacks(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const TransformFeedbackID *ids)
+{
+ return ValidateGenOrDeleteES3(context, entryPoint, n);
+}
+
+bool ValidateDeleteTransformFeedbacks(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const TransformFeedbackID *ids)
+{
+ if (!ValidateGenOrDeleteES3(context, entryPoint, 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(entryPoint, GL_INVALID_OPERATION,
+ kTransformFeedbackActiveDelete);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateGenVertexArrays(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const VertexArrayID *arrays)
+{
+ return ValidateGenOrDeleteES3(context, entryPoint, n);
+}
+
+bool ValidateDeleteVertexArrays(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const VertexArrayID *arrays)
+{
+ return ValidateGenOrDeleteES3(context, entryPoint, n);
+}
+
+bool ValidateBeginTransformFeedback(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode primitiveMode)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ switch (primitiveMode)
+ {
+ case PrimitiveMode::Triangles:
+ case PrimitiveMode::Lines:
+ case PrimitiveMode::Points:
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPrimitiveMode);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ if (transformFeedback->isActive())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTransfomFeedbackAlreadyActive);
+ return false;
+ }
+
+ for (size_t i = 0; i < transformFeedback->getIndexedBufferCount(); i++)
+ {
+ const OffsetBindingPointer<Buffer> &buffer = transformFeedback->getIndexedBuffer(i);
+ if (buffer.get())
+ {
+ if (buffer->isMapped())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferMapped);
+ return false;
+ }
+ if ((context->getLimitations().noDoubleBoundTransformFeedbackBuffers ||
+ context->getExtensions().webglCompatibilityANGLE) &&
+ buffer->isDoubleBoundForTransformFeedback())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kTransformFeedbackBufferMultipleOutputs);
+ return false;
+ }
+ }
+ }
+
+ const ProgramExecutable *programExecutable =
+ context->getState().getLinkedProgramExecutable(context);
+ if (!programExecutable)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotBound);
+ return false;
+ }
+
+ if (programExecutable->getLinkedTransformFeedbackVaryings().empty())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kNoTransformFeedbackOutputVariables);
+ return false;
+ }
+
+ if (!ValidateProgramExecutableXFBBuffersPresent(context, programExecutable))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTransformFeedbackBufferMissing);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferPointerv(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLenum pname,
+ void *const *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateGetBufferPointervBase(context, entryPoint, target, pname, nullptr, params);
+}
+
+bool ValidateGetBufferPointervRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLenum pname,
+ GLsizei bufSize,
+ const GLsizei *length,
+ void *const *params)
+{
+ if (!ValidateRobustEntryPoint(context, entryPoint, bufSize))
+ {
+ return false;
+ }
+
+ GLsizei numParams = 0;
+
+ if (context->getClientMajorVersion() < 3 && !context->getExtensions().mapbufferOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidateGetBufferPointervBase(context, entryPoint, target, pname, &numParams, params))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, entryPoint, bufSize, numParams))
+ {
+ return false;
+ }
+
+ SetRobustLengthParam(length, numParams);
+
+ return true;
+}
+
+bool ValidateUnmapBuffer(const Context *context, angle::EntryPoint entryPoint, BufferBinding target)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateUnmapBufferBase(context, entryPoint, target);
+}
+
+bool ValidateMapBufferRange(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length,
+ GLbitfield access)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateMapBufferRangeBase(context, entryPoint, target, offset, length, access);
+}
+
+bool ValidateFlushMappedBufferRange(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateFlushMappedBufferRangeBase(context, entryPoint, target, offset, length);
+}
+
+bool ValidateIndexedStateQuery(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ GLuint index,
+ GLsizei *length)
+{
+ if (length)
+ {
+ *length = 0;
+ }
+
+ GLenum nativeType;
+ unsigned int numParams;
+ if (!context->getIndexedQueryParameterInfo(pname, &nativeType, &numParams))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ switch (pname)
+ {
+ case GL_BLEND_SRC_RGB:
+ case GL_BLEND_SRC_ALPHA:
+ case GL_BLEND_DST_RGB:
+ case GL_BLEND_DST_ALPHA:
+ case GL_BLEND_EQUATION_RGB:
+ case GL_BLEND_EQUATION_ALPHA:
+ case GL_COLOR_WRITEMASK:
+ if (!context->getExtensions().drawBuffersIndexedAny())
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM,
+ kDrawBuffersIndexedExtensionNotAvailable);
+ return false;
+ }
+ if (index >= static_cast<GLuint>(caps.maxDrawBuffers))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxDrawBuffer);
+ return false;
+ }
+ break;
+ case GL_TRANSFORM_FEEDBACK_BUFFER_START:
+ case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
+ case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
+ if (index >= static_cast<GLuint>(caps.maxTransformFeedbackSeparateAttributes))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kIndexExceedsMaxTransformFeedbackAttribs);
+ return false;
+ }
+ break;
+
+ case GL_UNIFORM_BUFFER_START:
+ case GL_UNIFORM_BUFFER_SIZE:
+ case GL_UNIFORM_BUFFER_BINDING:
+ if (index >= static_cast<GLuint>(caps.maxUniformBufferBindings))
+ {
+ context->validationError(entryPoint, 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(entryPoint, 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(entryPoint, GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= static_cast<GLuint>(caps.maxAtomicCounterBufferBindings))
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= static_cast<GLuint>(caps.maxShaderStorageBufferBindings))
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= static_cast<GLuint>(caps.maxVertexAttribBindings))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kExceedsMaxVertexAttribBindings);
+ return false;
+ }
+ break;
+ case GL_SAMPLE_MASK_VALUE:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= static_cast<GLuint>(caps.maxSampleMaskWords))
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_ENUM, kEnumRequiresGLES31);
+ return false;
+ }
+ if (index >= static_cast<GLuint>(caps.maxImageUnits))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxImageUnits);
+ return false;
+ }
+ break;
+ // GL_ANGLE_shader_pixel_local_storage
+ case GL_PIXEL_LOCAL_FORMAT_ANGLE:
+ case GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE:
+ case GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE:
+ case GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE:
+ {
+ // Check that the pixel local storage extension is enabled at all.
+ if (!context->getExtensions().shaderPixelLocalStorageANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kPLSExtensionNotEnabled);
+ return false;
+ }
+ // INVALID_FRAMEBUFFER_OPERATION is generated if the default framebuffer object name 0
+ // is bound to DRAW_FRAMEBUFFER.
+ Framebuffer *framebuffer = context->getState().getDrawFramebuffer();
+ if (framebuffer->id().value == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_FRAMEBUFFER_OPERATION,
+ kPLSDefaultFramebufferBound);
+ return false;
+ }
+ // INVALID_VALUE is generated if <index> >= MAX_PIXEL_LOCAL_STORAGE_PLANES_ANGLE.
+ if (index >= context->getCaps().maxPixelLocalStoragePlanes)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kPLSPlaneOutOfRange);
+ return false;
+ }
+ break;
+ }
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, pname);
+ return false;
+ }
+
+ if (length)
+ {
+ if (pname == GL_COLOR_WRITEMASK)
+ {
+ *length = 4;
+ }
+ else
+ {
+ *length = 1;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetIntegeri_v(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLuint index,
+ const GLint *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateIndexedStateQuery(context, entryPoint, target, index, nullptr);
+}
+
+bool ValidateGetIntegeri_vRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLuint index,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLint *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, entryPoint, bufSize))
+ {
+ return false;
+ }
+
+ GLsizei numParams = 0;
+
+ if (!ValidateIndexedStateQuery(context, entryPoint, target, index, &numParams))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, entryPoint, bufSize, numParams))
+ {
+ return false;
+ }
+
+ SetRobustLengthParam(length, numParams);
+
+ return true;
+}
+
+bool ValidateGetInteger64i_v(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLuint index,
+ const GLint64 *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateIndexedStateQuery(context, entryPoint, target, index, nullptr);
+}
+
+bool ValidateGetInteger64i_vRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLuint index,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLint64 *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, entryPoint, bufSize))
+ {
+ return false;
+ }
+
+ GLsizei numParams = 0;
+
+ if (!ValidateIndexedStateQuery(context, entryPoint, target, index, &numParams))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, entryPoint, bufSize, numParams))
+ {
+ return false;
+ }
+
+ SetRobustLengthParam(length, numParams);
+
+ return true;
+}
+
+bool ValidateCopyBufferSubData(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding readTarget,
+ BufferBinding writeTarget,
+ GLintptr readOffset,
+ GLintptr writeOffset,
+ GLsizeiptr size)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!context->isValidBufferBinding(readTarget) || !context->isValidBufferBinding(writeTarget))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *readBuffer = context->getState().getTargetBuffer(readTarget);
+ Buffer *writeBuffer = context->getState().getTargetBuffer(writeTarget);
+
+ if (!readBuffer || !writeBuffer)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotBound);
+ return false;
+ }
+
+ // EXT_buffer_storage allows persistently mapped buffers to be updated via glCopyBufferSubData
+ bool isReadPersistent = (readBuffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
+ bool isWritePersistent = (writeBuffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
+
+ // Verify that readBuffer and writeBuffer are not currently mapped unless persistent
+ if ((readBuffer->isMapped() && !isReadPersistent) ||
+ (writeBuffer->isMapped() && !isWritePersistent))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferMapped);
+ return false;
+ }
+
+ if (readBuffer->hasWebGLXFBBindingConflict(context->isWebGL()) ||
+ writeBuffer->hasWebGLXFBBindingConflict(context->isWebGL()))
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kIntegerOverflow);
+ return false;
+ }
+
+ if (readOffset < 0 || writeOffset < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (size < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ if (checkedReadSum.ValueOrDie() > readBuffer->getSize() ||
+ checkedWriteSum.ValueOrDie() > writeBuffer->getSize())
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kIntegerOverflow);
+ return false;
+ }
+
+ if (checkedOffsetDiff.ValueOrDie() < size)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kCopyAlias);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetStringi(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum name,
+ GLuint index)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ switch (name)
+ {
+ case GL_EXTENSIONS:
+ if (index >= context->getExtensionStringCount())
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsNumExtensions);
+ return false;
+ }
+ break;
+
+ case GL_REQUESTABLE_EXTENSIONS_ANGLE:
+ if (!context->getExtensions().requestExtensionANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+ if (index >= context->getRequestableExtensionStringCount())
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kExceedsNumRequestableExtensions);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRenderbufferStorageMultisample(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateRenderbufferStorageParametersBase(context, entryPoint, 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 InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
+ if (formatInfo.isInt())
+ {
+ if ((samples > 0 && context->getClientVersion() == ES_3_0) ||
+ samples > context->getCaps().maxIntegerSamples)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_OPERATION, kSamplesOutOfRange);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribIPointer(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLint size,
+ VertexAttribType type,
+ GLsizei stride,
+ const void *pointer)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateIntegerVertexFormat(context, entryPoint, index, size, type))
+ {
+ return false;
+ }
+
+ if (stride < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeStride);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (context->getClientVersion() >= ES_3_1)
+ {
+ if (stride > caps.maxVertexAttribStride)
+ {
+ context->validationError(entryPoint, 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 >= static_cast<GLuint>(caps.maxVertexAttribBindings))
+ {
+ context->validationError(entryPoint, 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().value != 0 &&
+ context->getState().getTargetBuffer(BufferBinding::Array) == 0 && pointer != nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kClientDataInVertexArray);
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibilityANGLE)
+ {
+ if (!ValidateWebGLVertexAttribPointer(context, entryPoint, type, false, stride, pointer,
+ true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetSynciv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsync sync,
+ GLenum pname,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLint *values)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().syncARB)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ if (context->isContextLost())
+ {
+ context->validationError(entryPoint, 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(entryPoint, 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(entryPoint, GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawElementsInstanced(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ GLsizei count,
+ DrawElementsType type,
+ const void *indices,
+ GLsizei instanceCount)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateDrawElementsInstancedBase(context, entryPoint, mode, count, type, indices,
+ instanceCount);
+}
+
+bool ValidateMultiDrawArraysInstancedANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ const GLint *firsts,
+ const GLsizei *counts,
+ const GLsizei *instanceCounts,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDrawANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->getClientMajorVersion() < 3)
+ {
+ if (!context->getExtensions().instancedArraysAny())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (!ValidateDrawInstancedANGLE(context, entryPoint))
+ {
+ return false;
+ }
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawArraysInstancedBase(context, entryPoint, mode, firsts[drawID],
+ counts[drawID], instanceCounts[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateMultiDrawElementsInstancedANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ const GLsizei *counts,
+ DrawElementsType type,
+ const GLvoid *const *indices,
+ const GLsizei *instanceCounts,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDrawANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->getClientMajorVersion() < 3)
+ {
+ if (!context->getExtensions().instancedArraysAny())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (!ValidateDrawInstancedANGLE(context, entryPoint))
+ {
+ return false;
+ }
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawElementsInstancedBase(context, entryPoint, mode, counts[drawID], type,
+ indices[drawID], instanceCounts[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateDrawArraysInstancedBaseInstanceANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ GLint first,
+ GLsizei count,
+ GLsizei instanceCount,
+ GLuint baseInstance)
+{
+ if (!context->getExtensions().baseVertexBaseInstanceANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateDrawArraysInstancedBase(context, entryPoint, mode, first, count, instanceCount);
+}
+
+bool ValidateDrawElementsInstancedBaseVertexBaseInstanceANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ GLsizei count,
+ DrawElementsType type,
+ const GLvoid *indices,
+ GLsizei instanceCount,
+ GLint baseVertex,
+ GLuint baseInstance)
+{
+ if (!context->getExtensions().baseVertexBaseInstanceANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateDrawElementsInstancedBase(context, entryPoint, mode, count, type, indices,
+ instanceCount);
+}
+
+bool ValidateMultiDrawArraysInstancedBaseInstanceANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode modePacked,
+ const GLint *firsts,
+ const GLsizei *counts,
+ const GLsizei *instanceCounts,
+ const GLuint *baseInstances,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDrawANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (drawcount < 0)
+ {
+ return false;
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawArraysInstancedBase(context, entryPoint, modePacked, firsts[drawID],
+ counts[drawID], instanceCounts[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode modePacked,
+ const GLsizei *counts,
+ DrawElementsType typePacked,
+ const GLvoid *const *indices,
+ const GLsizei *instanceCounts,
+ const GLint *baseVertices,
+ const GLuint *baseInstances,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDrawANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (drawcount < 0)
+ {
+ return false;
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawElementsInstancedBase(context, entryPoint, modePacked, counts[drawID],
+ typePacked, indices[drawID], instanceCounts[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateFramebufferTextureMultiviewOVR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum attachment,
+ TextureID texture,
+ GLint level,
+ GLint baseViewIndex,
+ GLsizei numViews)
+{
+ if (!ValidateFramebufferTextureMultiviewBaseANGLE(context, entryPoint, target, attachment,
+ texture, level, numViews))
+ {
+ return false;
+ }
+
+ if (texture.value != 0)
+ {
+ if (baseViewIndex < 0)
+ {
+ context->validationError(entryPoint, 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().multiviewMultisampleANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidTextureType);
+ return false;
+ }
+ }
+
+ const Caps &caps = context->getCaps();
+ if (baseViewIndex + numViews > caps.maxArrayTextureLayers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kViewsExceedMaxArrayLayers);
+ return false;
+ }
+
+ break;
+ }
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidTextureType);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureMultiviewLevelAndFormat(context, entryPoint, tex, level))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateUniform1ui(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLuint v0)
+{
+ return ValidateUniformES3(context, entryPoint, GL_UNSIGNED_INT, location, 1);
+}
+
+bool ValidateUniform2ui(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLuint v0,
+ GLuint v1)
+{
+ return ValidateUniformES3(context, entryPoint, GL_UNSIGNED_INT_VEC2, location, 1);
+}
+
+bool ValidateUniform3ui(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLuint v0,
+ GLuint v1,
+ GLuint v2)
+{
+ return ValidateUniformES3(context, entryPoint, GL_UNSIGNED_INT_VEC3, location, 1);
+}
+
+bool ValidateUniform4ui(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLuint v0,
+ GLuint v1,
+ GLuint v2,
+ GLuint v3)
+{
+ return ValidateUniformES3(context, entryPoint, GL_UNSIGNED_INT_VEC4, location, 1);
+}
+
+bool ValidateUniform1uiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLuint *value)
+{
+ return ValidateUniformES3(context, entryPoint, GL_UNSIGNED_INT, location, count);
+}
+
+bool ValidateUniform2uiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLuint *value)
+{
+ return ValidateUniformES3(context, entryPoint, GL_UNSIGNED_INT_VEC2, location, count);
+}
+
+bool ValidateUniform3uiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLuint *value)
+{
+ return ValidateUniformES3(context, entryPoint, GL_UNSIGNED_INT_VEC3, location, count);
+}
+
+bool ValidateUniform4uiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLuint *value)
+{
+ return ValidateUniformES3(context, entryPoint, GL_UNSIGNED_INT_VEC4, location, count);
+}
+
+bool ValidateIsQuery(const Context *context, angle::EntryPoint entryPoint, QueryID id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateUniformMatrix2x3fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, entryPoint, GL_FLOAT_MAT2x3, location, count,
+ transpose);
+}
+
+bool ValidateUniformMatrix3x2fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, entryPoint, GL_FLOAT_MAT3x2, location, count,
+ transpose);
+}
+
+bool ValidateUniformMatrix2x4fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, entryPoint, GL_FLOAT_MAT2x4, location, count,
+ transpose);
+}
+
+bool ValidateUniformMatrix4x2fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, entryPoint, GL_FLOAT_MAT4x2, location, count,
+ transpose);
+}
+
+bool ValidateUniformMatrix3x4fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, entryPoint, GL_FLOAT_MAT3x4, location, count,
+ transpose);
+}
+
+bool ValidateUniformMatrix4x3fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, entryPoint, GL_FLOAT_MAT4x3, location, count,
+ transpose);
+}
+
+bool ValidateEndTransformFeedback(const Context *context, angle::EntryPoint entryPoint)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ if (!transformFeedback->isActive())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTransformFeedbackNotActive);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTransformFeedbackVaryings(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLsizei count,
+ const GLchar *const *varyings,
+ GLenum bufferMode)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (count < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ switch (bufferMode)
+ {
+ case GL_INTERLEAVED_ATTRIBS:
+ break;
+ case GL_SEPARATE_ATTRIBS:
+ {
+ const Caps &caps = context->getCaps();
+ if (count > caps.maxTransformFeedbackSeparateAttributes)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kInvalidTransformFeedbackAttribsCount);
+ return false;
+ }
+ break;
+ }
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, bufferMode);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetTransformFeedbackVarying(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLuint index,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLsizei *size,
+ const GLenum *type,
+ const GLchar *name)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(programObject->getTransformFeedbackVaryingCount()))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kTransformFeedbackVaryingIndexOutOfRange);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindTransformFeedback(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ TransformFeedbackID id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, 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(entryPoint, 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(entryPoint, GL_INVALID_OPERATION,
+ kTransformFeedbackDoesNotExist);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, target);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsTransformFeedback(const Context *context,
+ angle::EntryPoint entryPoint,
+ TransformFeedbackID id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePauseTransformFeedback(const Context *context, angle::EntryPoint entryPoint)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_OPERATION, kTransformFeedbackNotActive);
+ return false;
+ }
+
+ if (transformFeedback->isPaused())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTransformFeedbackPaused);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateResumeTransformFeedback(const Context *context, angle::EntryPoint entryPoint)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_OPERATION, kTransformFeedbackNotActive);
+ return false;
+ }
+
+ if (!transformFeedback->isPaused())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTransformFeedbackNotPaused);
+ return false;
+ }
+
+ if (!ValidateProgramExecutableXFBBuffersPresent(
+ context, context->getState().getLinkedProgramExecutable(context)))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTransformFeedbackBufferMissing);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribI4i(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLint x,
+ GLint y,
+ GLint z,
+ GLint w)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttribI4ui(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLuint x,
+ GLuint y,
+ GLuint z,
+ GLuint w)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttribI4iv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ const GLint *v)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttribI4uiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ const GLuint *v)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateGetFragDataLocation(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ const GLchar *name)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformIndices(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLsizei uniformCount,
+ const GLchar *const *uniformNames,
+ const GLuint *uniformIndices)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (uniformCount < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniformsiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLsizei uniformCount,
+ const GLuint *uniformIndices,
+ GLenum pname,
+ const GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (uniformCount < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_UNIFORM_TYPE:
+ case GL_UNIFORM_SIZE:
+ break;
+ case GL_UNIFORM_NAME_LENGTH:
+ if (context->getExtensions().webglCompatibilityANGLE)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, pname);
+ 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->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, pname);
+ return false;
+ }
+
+ if (uniformCount > programObject->getActiveUniformCount())
+ {
+ context->validationError(entryPoint, 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(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxActiveUniform);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformBlockIndex(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ const GLchar *uniformBlockName)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniformBlockiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ UniformBlockIndex uniformBlockIndex,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetActiveUniformBlockivBase(context, entryPoint, program, uniformBlockIndex,
+ pname, nullptr);
+}
+
+bool ValidateGetActiveUniformBlockName(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ UniformBlockIndex uniformBlockIndex,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLchar *uniformBlockName)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (uniformBlockIndex.value >= programObject->getActiveUniformBlockCount())
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxActiveUniformBlock);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateUniformBlockBinding(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ UniformBlockIndex uniformBlockIndex,
+ GLuint uniformBlockBinding)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (uniformBlockBinding >= static_cast<GLuint>(context->getCaps().maxUniformBufferBindings))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kIndexExceedsMaxUniformBufferBindings);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ // if never linked, there won't be any uniform blocks
+ if (uniformBlockIndex.value >= programObject->getActiveUniformBlockCount())
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kIndexExceedsMaxUniformBufferBindings);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawArraysInstanced(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ GLint first,
+ GLsizei count,
+ GLsizei primcount)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateDrawArraysInstancedBase(context, entryPoint, mode, first, count, primcount);
+}
+
+bool ValidateFenceSync(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum condition,
+ GLbitfield flags)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().syncARB)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (condition != GL_SYNC_GPU_COMMANDS_COMPLETE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFenceCondition);
+ return false;
+ }
+
+ if (flags != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidFlags);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsSync(const Context *context, angle::EntryPoint entryPoint, GLsync sync)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().syncARB)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteSync(const Context *context, angle::EntryPoint entryPoint, GLsync sync)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().syncARB)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (sync != static_cast<GLsync>(0) && !context->getSync(sync))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kSyncMissing);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateClientWaitSync(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsync sync,
+ GLbitfield flags,
+ GLuint64 timeout)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().syncARB)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if ((flags & ~(GL_SYNC_FLUSH_COMMANDS_BIT)) != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidFlags);
+ return false;
+ }
+
+ Sync *clientWaitSync = context->getSync(sync);
+ if (!clientWaitSync)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kSyncMissing);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateWaitSync(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsync sync,
+ GLbitfield flags,
+ GLuint64 timeout)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().syncARB)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (flags != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidFlags);
+ return false;
+ }
+
+ if (timeout != GL_TIMEOUT_IGNORED)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidTimeout);
+ return false;
+ }
+
+ Sync *waitSync = context->getSync(sync);
+ if (!waitSync)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kSyncMissing);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetInteger64v(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ const GLint64 *params)
+{
+ if ((context->getClientMajorVersion() < 3) && !context->getExtensions().syncARB)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ GLenum nativeType = GL_NONE;
+ unsigned int numParams = 0;
+ if (!ValidateStateQuery(context, entryPoint, pname, &nativeType, &numParams))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsSampler(const Context *context, angle::EntryPoint entryPoint, SamplerID sampler)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindSampler(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint unit,
+ SamplerID sampler)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (GetIDValue(sampler) != 0 && !context->isSampler(sampler))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidSampler);
+ return false;
+ }
+
+ if (unit >= static_cast<GLuint>(context->getCaps().maxCombinedTextureImageUnits))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidCombinedImageUnit);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribDivisor(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLuint divisor)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateTexStorage2D(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateES3TexStorage2DParameters(context, entryPoint, target, levels, internalformat,
+ width, height, 1))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexStorage3D(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+
+ if (!ValidateES3TexStorage3DParameters(context, entryPoint, target, levels, internalformat,
+ width, height, depth))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferParameteri64v(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLenum pname,
+ const GLint64 *params)
+{
+ return ValidateGetBufferParameterBase(context, entryPoint, target, pname, false, nullptr);
+}
+
+bool ValidateGetSamplerParameterfv(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ const GLfloat *params)
+{
+ return ValidateGetSamplerParameterBase(context, entryPoint, sampler, pname, nullptr);
+}
+
+bool ValidateGetSamplerParameteriv(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetSamplerParameterBase(context, entryPoint, sampler, pname, nullptr);
+}
+
+bool ValidateGetSamplerParameterIivOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ const GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGetSamplerParameterBase(context, entryPoint, sampler, pname, nullptr);
+}
+
+bool ValidateGetSamplerParameterIuivOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ const GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGetSamplerParameterBase(context, entryPoint, sampler, pname, nullptr);
+}
+
+bool ValidateSamplerParameterf(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ GLfloat param)
+{
+ return ValidateSamplerParameterBase(context, entryPoint, sampler, pname, -1, false, &param);
+}
+
+bool ValidateSamplerParameterfv(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ const GLfloat *params)
+{
+ return ValidateSamplerParameterBase(context, entryPoint, sampler, pname, -1, true, params);
+}
+
+bool ValidateSamplerParameteri(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ GLint param)
+{
+ return ValidateSamplerParameterBase(context, entryPoint, sampler, pname, -1, false, &param);
+}
+
+bool ValidateSamplerParameteriv(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateSamplerParameterBase(context, entryPoint, sampler, pname, -1, true, params);
+}
+
+bool ValidateSamplerParameterIivOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ const GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateSamplerParameterBase(context, entryPoint, sampler, pname, -1, true, params);
+}
+
+bool ValidateSamplerParameterIuivOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ SamplerID sampler,
+ GLenum pname,
+ const GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateSamplerParameterBase(context, entryPoint, sampler, pname, -1, true, params);
+}
+
+bool ValidateGetVertexAttribIiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetVertexAttribBase(context, entryPoint, index, pname, nullptr, false, true);
+}
+
+bool ValidateGetVertexAttribIuiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLenum pname,
+ const GLuint *params)
+{
+ return ValidateGetVertexAttribBase(context, entryPoint, index, pname, nullptr, false, true);
+}
+
+bool ValidateGetInternalformativ(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum internalformat,
+ GLenum pname,
+ GLsizei bufSize,
+ const GLint *params)
+{
+ return ValidateGetInternalFormativBase(context, entryPoint, target, internalformat, pname,
+ bufSize, nullptr);
+}
+
+bool ValidateBindFragDataLocationIndexedEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLuint colorNumber,
+ GLuint index,
+ const char *name)
+{
+ if (!context->getExtensions().blendFuncExtendedEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ if (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(entryPoint, GL_INVALID_VALUE, kFragDataBindingIndexOutOfRange);
+ return false;
+ }
+ if (index == 1)
+ {
+ if (colorNumber >= context->getCaps().maxDualSourceDrawBuffers)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kColorNumberGreaterThanMaxDualSourceDrawBuffers);
+ return false;
+ }
+ }
+ else
+ {
+ if (colorNumber >= static_cast<GLuint>(context->getCaps().maxDrawBuffers))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kColorNumberGreaterThanMaxDrawBuffers);
+ return false;
+ }
+ }
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+ return true;
+}
+
+bool ValidateBindFragDataLocationEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLuint colorNumber,
+ const char *name)
+{
+ return ValidateBindFragDataLocationIndexedEXT(context, entryPoint, program, colorNumber, 0u,
+ name);
+}
+
+bool ValidateGetFragDataIndexEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ const char *name)
+{
+ if (!context->getExtensions().blendFuncExtendedEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+ if (!programObject->isLinked())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateTexStorage2DMultisampleANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei samples,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLboolean fixedSampleLocations)
+{
+ if (!context->getExtensions().textureMultisampleANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateTexStorage2DMultisampleBase(context, entryPoint, target, samples, internalFormat,
+ width, height);
+}
+
+bool ValidateGetTexLevelParameterfvANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum pname,
+ const GLfloat *params)
+{
+ if (!context->getExtensions().textureMultisampleANGLE &&
+ !context->getExtensions().getTexLevelParameterANGLE)
+ {
+ context->validationError(
+ entryPoint, GL_INVALID_OPERATION,
+ kMultisampleTextureExtensionOrGetTexLevelParameterExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateGetTexLevelParameterBase(context, entryPoint, target, level, pname, nullptr);
+}
+
+bool ValidateGetTexLevelParameterivANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum pname,
+ const GLint *params)
+{
+ if (!context->getExtensions().textureMultisampleANGLE &&
+ !context->getExtensions().getTexLevelParameterANGLE)
+ {
+ context->validationError(
+ entryPoint, GL_INVALID_OPERATION,
+ kMultisampleTextureExtensionOrGetTexLevelParameterExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateGetTexLevelParameterBase(context, entryPoint, target, level, pname, nullptr);
+}
+
+bool ValidateGetMultisamplefvANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ GLuint index,
+ const GLfloat *val)
+{
+ if (!context->getExtensions().textureMultisampleANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateGetMultisamplefvBase(context, entryPoint, pname, index, val);
+}
+
+bool ValidateSampleMaskiANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint maskNumber,
+ GLbitfield mask)
+{
+ if (!context->getExtensions().textureMultisampleANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ return ValidateSampleMaskiBase(context, entryPoint, maskNumber, mask);
+}
+} // namespace gl