summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/validationES2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/validationES2.cpp')
-rw-r--r--gfx/angle/checkout/src/libANGLE/validationES2.cpp6544
1 files changed, 6544 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/validationES2.cpp b/gfx/angle/checkout/src/libANGLE/validationES2.cpp
new file mode 100644
index 0000000000..00f5fc5502
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/validationES2.cpp
@@ -0,0 +1,6544 @@
+//
+// 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.
+//
+
+// validationES2.cpp: Validation functions for OpenGL ES 2.0 entry point parameters
+
+#include "libANGLE/validationES2_autogen.h"
+
+#include <cstdint>
+
+#include "common/mathutil.h"
+#include "common/string_utils.h"
+#include "common/utilities.h"
+#include "libANGLE/Context.h"
+#include "libANGLE/ErrorStrings.h"
+#include "libANGLE/Fence.h"
+#include "libANGLE/Framebuffer.h"
+#include "libANGLE/FramebufferAttachment.h"
+#include "libANGLE/MemoryObject.h"
+#include "libANGLE/Renderbuffer.h"
+#include "libANGLE/Shader.h"
+#include "libANGLE/Texture.h"
+#include "libANGLE/Uniform.h"
+#include "libANGLE/VertexArray.h"
+#include "libANGLE/formatutils.h"
+#include "libANGLE/validationES.h"
+#include "libANGLE/validationES2.h"
+#include "libANGLE/validationES3_autogen.h"
+
+namespace gl
+{
+using namespace err;
+
+namespace
+{
+
+bool IsPartialBlit(const Context *context,
+ const FramebufferAttachment *readBuffer,
+ const FramebufferAttachment *writeBuffer,
+ GLint srcX0,
+ GLint srcY0,
+ GLint srcX1,
+ GLint srcY1,
+ GLint dstX0,
+ GLint dstY0,
+ GLint dstX1,
+ GLint dstY1)
+{
+ const Extents &writeSize = writeBuffer->getSize();
+ const Extents &readSize = readBuffer->getSize();
+
+ if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 || dstX1 != writeSize.width ||
+ dstY1 != writeSize.height || srcX1 != readSize.width || srcY1 != readSize.height)
+ {
+ return true;
+ }
+
+ if (context->getState().isScissorTestEnabled())
+ {
+ const Rectangle &scissor = context->getState().getScissor();
+ return scissor.x > 0 || scissor.y > 0 || scissor.width < writeSize.width ||
+ scissor.height < writeSize.height;
+ }
+
+ return false;
+}
+
+bool IsValidCopyTextureSourceInternalFormatEnum(GLenum internalFormat)
+{
+ // Table 1.1 from the CHROMIUM_copy_texture spec
+ switch (GetUnsizedFormat(internalFormat))
+ {
+ case GL_RED:
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ case GL_LUMINANCE_ALPHA:
+ case GL_RGB:
+ case GL_RGBA:
+ case GL_RGB8:
+ case GL_RGBA8:
+ case GL_BGRA_EXT:
+ case GL_BGRA8_EXT:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopySubTextureSourceInternalFormat(GLenum internalFormat)
+{
+ return IsValidCopyTextureSourceInternalFormatEnum(internalFormat);
+}
+
+bool IsValidCopyTextureDestinationInternalFormatEnum(GLint internalFormat)
+{
+ // Table 1.0 from the CHROMIUM_copy_texture spec
+ switch (internalFormat)
+ {
+ case GL_ALPHA:
+ case GL_BGRA8_EXT:
+ case GL_BGRA_EXT:
+ case GL_LUMINANCE:
+ case GL_LUMINANCE_ALPHA:
+ case GL_R11F_G11F_B10F:
+ case GL_R16F:
+ case GL_R32F:
+ case GL_R8:
+ case GL_R8UI:
+ case GL_RG16F:
+ case GL_RG32F:
+ case GL_RG8:
+ case GL_RG8UI:
+ case GL_RGB:
+ case GL_RGB10_A2:
+ case GL_RGB16F:
+ case GL_RGB32F:
+ case GL_RGB565:
+ case GL_RGB5_A1:
+ case GL_RGB8:
+ case GL_RGB8UI:
+ case GL_RGB9_E5:
+ case GL_RGBA:
+ case GL_RGBA16F:
+ case GL_RGBA32F:
+ case GL_RGBA4:
+ case GL_RGBA8:
+ case GL_RGBA8UI:
+ case GL_RGBX8_ANGLE:
+ case GL_SRGB8:
+ case GL_SRGB8_ALPHA8:
+ case GL_SRGB_ALPHA_EXT:
+ case GL_SRGB_EXT:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopySubTextureDestionationInternalFormat(GLenum internalFormat)
+{
+ return IsValidCopyTextureDestinationInternalFormatEnum(internalFormat);
+}
+
+bool IsValidCopyTextureDestinationFormatType(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint internalFormat,
+ GLenum type)
+{
+ if (!IsValidCopyTextureDestinationInternalFormatEnum(internalFormat))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalFormat);
+ return false;
+ }
+
+ if (!ValidES3FormatCombination(GetUnsizedFormat(internalFormat), type, internalFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+
+ const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat, type);
+ if (!internalFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalFormat);
+ return false;
+ }
+
+ return true;
+}
+
+bool IsValidCopyTextureDestinationTargetEnum(const Context *context, TextureTarget target)
+{
+ switch (target)
+ {
+ case TextureTarget::_2D:
+ case TextureTarget::CubeMapNegativeX:
+ case TextureTarget::CubeMapNegativeY:
+ case TextureTarget::CubeMapNegativeZ:
+ case TextureTarget::CubeMapPositiveX:
+ case TextureTarget::CubeMapPositiveY:
+ case TextureTarget::CubeMapPositiveZ:
+ return true;
+
+ case TextureTarget::Rectangle:
+ return context->getExtensions().textureRectangleANGLE;
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopyTextureDestinationTarget(const Context *context,
+ TextureType textureType,
+ TextureTarget target)
+{
+ return TextureTargetToType(target) == textureType;
+}
+
+bool IsValidCopyTextureSourceTarget(const Context *context, TextureType type)
+{
+ switch (type)
+ {
+ case TextureType::_2D:
+ return true;
+ case TextureType::Rectangle:
+ return context->getExtensions().textureRectangleANGLE;
+ case TextureType::External:
+ return context->getExtensions().EGLImageExternalOES;
+ case TextureType::VideoImage:
+ return context->getExtensions().videoTextureWEBGL;
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopyTextureSourceLevel(const Context *context, TextureType type, GLint level)
+{
+ if (!ValidMipLevel(context, type, level))
+ {
+ return false;
+ }
+
+ if (level > 0 && context->getClientVersion() < ES_3_0)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool IsValidCopyTextureDestinationLevel(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType type,
+ GLint level,
+ GLsizei width,
+ GLsizei height,
+ bool isSubImage)
+{
+ if (!ValidMipLevel(context, type, level))
+ {
+ return false;
+ }
+
+ if (!ValidImageSizeParameters(context, entryPoint, type, level, width, height, 1, isSubImage))
+ {
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ switch (type)
+ {
+ case TextureType::_2D:
+ return width <= (caps.max2DTextureSize >> level) &&
+ height <= (caps.max2DTextureSize >> level);
+ case TextureType::Rectangle:
+ ASSERT(level == 0);
+ return width <= (caps.max2DTextureSize >> level) &&
+ height <= (caps.max2DTextureSize >> level);
+
+ case TextureType::CubeMap:
+ return width <= (caps.maxCubeMapTextureSize >> level) &&
+ height <= (caps.maxCubeMapTextureSize >> level);
+ default:
+ return true;
+ }
+}
+
+bool IsValidStencilFunc(GLenum func)
+{
+ switch (func)
+ {
+ case GL_NEVER:
+ case GL_ALWAYS:
+ case GL_LESS:
+ case GL_LEQUAL:
+ case GL_EQUAL:
+ case GL_GEQUAL:
+ case GL_GREATER:
+ case GL_NOTEQUAL:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidStencilFace(GLenum face)
+{
+ switch (face)
+ {
+ case GL_FRONT:
+ case GL_BACK:
+ case GL_FRONT_AND_BACK:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidStencilOp(GLenum op)
+{
+ switch (op)
+ {
+ case GL_ZERO:
+ case GL_KEEP:
+ case GL_REPLACE:
+ case GL_INCR:
+ case GL_DECR:
+ case GL_INVERT:
+ case GL_INCR_WRAP:
+ case GL_DECR_WRAP:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static inline bool Valid1to4ComponentFloatColorBufferFormat(const Context *context, GLenum format)
+{
+ return (context->getExtensions().textureFloatOES &&
+ (format == GL_RGBA32F || format == GL_RGB32F || format == GL_RG32F ||
+ format == GL_R32F)) ||
+ (context->getExtensions().textureHalfFloatOES &&
+ (format == GL_RGBA16F || format == GL_RGB16F || format == GL_RG16F ||
+ format == GL_R16F));
+}
+
+static inline bool Valid2to4ComponentFloatColorBufferFormat(const Context *context, GLenum format)
+{
+ return (context->getExtensions().textureFloatOES &&
+ (format == GL_RGBA32F || format == GL_RGB32F || format == GL_RG32F)) ||
+ (context->getExtensions().textureHalfFloatOES &&
+ (format == GL_RGBA16F || format == GL_RGB16F || format == GL_RG16F));
+}
+
+static inline bool Valid3to4ComponentFloatColorBufferFormat(const Context *context, GLenum format)
+{
+ return (context->getExtensions().textureFloatOES &&
+ (format == GL_RGBA32F || format == GL_RGB32F)) ||
+ (context->getExtensions().textureHalfFloatOES &&
+ (format == GL_RGBA16F || format == GL_RGB16F));
+}
+
+static inline bool Valid4ComponentFloatColorBufferFormat(const Context *context, GLenum format)
+{
+ return (context->getExtensions().textureFloatOES && format == GL_RGBA32F) ||
+ (context->getExtensions().textureHalfFloatOES && format == GL_RGBA16F);
+}
+
+bool ValidateES2CopyTexImageParameters(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border)
+{
+ if (!ValidTexture2DDestinationTarget(context, target))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ TextureType texType = TextureTargetToType(target);
+ if (!ValidImageSizeParameters(context, entryPoint, texType, level, width, height, 1,
+ isSubImage))
+ {
+ // Error is already handled.
+ return false;
+ }
+
+ Format textureFormat = Format::Invalid();
+ if (!ValidateCopyTexImageParametersBase(context, entryPoint, target, level, internalformat,
+ isSubImage, xoffset, yoffset, 0, x, y, width, height,
+ border, &textureFormat))
+ {
+ return false;
+ }
+
+ ASSERT(textureFormat.valid() || !isSubImage);
+
+ const Framebuffer *framebuffer = context->getState().getReadFramebuffer();
+ GLenum colorbufferFormat =
+ framebuffer->getReadColorAttachment()->getFormat().info->sizedInternalFormat;
+ const auto &formatInfo = *textureFormat.info;
+
+ // ValidateCopyTexImageParametersBase rejects compressed formats with GL_INVALID_OPERATION.
+ ASSERT(!formatInfo.compressed);
+ ASSERT(!GetInternalFormatInfo(internalformat, GL_UNSIGNED_BYTE).compressed);
+
+ // ValidateCopyTexImageParametersBase rejects depth formats with GL_INVALID_OPERATION.
+ ASSERT(!formatInfo.depthBits);
+ ASSERT(!GetInternalFormatInfo(internalformat, GL_UNSIGNED_BYTE).depthBits);
+
+ // [OpenGL ES 2.0.24] table 3.9
+ if (isSubImage)
+ {
+ switch (formatInfo.format)
+ {
+ case GL_ALPHA:
+ if (colorbufferFormat != GL_ALPHA8_EXT && colorbufferFormat != GL_RGBA4 &&
+ colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_RGBA8_OES &&
+ colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ !Valid4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_LUMINANCE:
+ if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT &&
+ colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
+ colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
+ colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_BGRA8_EXT &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ colorbufferFormat != GL_BGRX8_ANGLEX && colorbufferFormat != GL_RGBX8_ANGLE &&
+ !Valid1to4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_RED_EXT:
+ if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT &&
+ colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
+ colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
+ colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_R32F &&
+ colorbufferFormat != GL_RG32F && colorbufferFormat != GL_RGB32F &&
+ colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ colorbufferFormat != GL_BGRX8_ANGLEX && colorbufferFormat != GL_RGBX8_ANGLE &&
+ !Valid1to4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_RG_EXT:
+ if (colorbufferFormat != GL_RG8_EXT && colorbufferFormat != GL_RGB565 &&
+ colorbufferFormat != GL_RGB8_OES && colorbufferFormat != GL_RGBA4 &&
+ colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_RGBA8_OES &&
+ colorbufferFormat != GL_RG32F && colorbufferFormat != GL_RGB32F &&
+ colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ colorbufferFormat != GL_BGRX8_ANGLEX && colorbufferFormat != GL_RGBX8_ANGLE &&
+ !Valid2to4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_RGB:
+ if (colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
+ colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
+ colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_RGB32F &&
+ colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ colorbufferFormat != GL_BGRX8_ANGLEX && colorbufferFormat != GL_RGBX8_ANGLE &&
+ !Valid3to4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_LUMINANCE_ALPHA:
+ case GL_RGBA:
+ if (colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
+ colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_RGBA32F &&
+ colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ !Valid4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+
+ if (formatInfo.type == GL_FLOAT && !context->getExtensions().textureFloatOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ }
+ else
+ {
+ switch (internalformat)
+ {
+ case GL_ALPHA:
+ if (colorbufferFormat != GL_ALPHA8_EXT && colorbufferFormat != GL_RGBA4 &&
+ colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_BGRA8_EXT &&
+ colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ !Valid4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_LUMINANCE:
+ case GL_RED_EXT:
+ if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT &&
+ colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
+ colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
+ colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ colorbufferFormat != GL_BGRX8_ANGLEX && colorbufferFormat != GL_RGBX8_ANGLE &&
+ !Valid1to4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_RG_EXT:
+ if (colorbufferFormat != GL_RG8_EXT && colorbufferFormat != GL_RGB565 &&
+ colorbufferFormat != GL_RGB8_OES && colorbufferFormat != GL_RGBA4 &&
+ colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_BGRA8_EXT &&
+ colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ colorbufferFormat != GL_BGRX8_ANGLEX && colorbufferFormat != GL_RGBX8_ANGLE &&
+ !Valid2to4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_RGB:
+ if (colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
+ colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
+ colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX &&
+ colorbufferFormat != GL_BGRX8_ANGLEX && colorbufferFormat != GL_RGBX8_ANGLE &&
+ !Valid3to4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_LUMINANCE_ALPHA:
+ case GL_RGBA:
+ if (colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
+ colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX && colorbufferFormat != GL_RGBA16F &&
+ !Valid4ComponentFloatColorBufferFormat(context, colorbufferFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ internalformat);
+ 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 ValidCap(const Context *context, GLenum cap, bool queryOnly)
+{
+ switch (cap)
+ {
+ // EXT_multisample_compatibility
+ case GL_MULTISAMPLE_EXT:
+ case GL_SAMPLE_ALPHA_TO_ONE_EXT:
+ return context->getExtensions().multisampleCompatibilityEXT;
+
+ case GL_CULL_FACE:
+ case GL_POLYGON_OFFSET_FILL:
+ case GL_SAMPLE_ALPHA_TO_COVERAGE:
+ case GL_SAMPLE_COVERAGE:
+ case GL_SCISSOR_TEST:
+ case GL_STENCIL_TEST:
+ case GL_DEPTH_TEST:
+ case GL_BLEND:
+ case GL_DITHER:
+ return true;
+
+ case GL_PRIMITIVE_RESTART_FIXED_INDEX:
+ case GL_RASTERIZER_DISCARD:
+ return (context->getClientMajorVersion() >= 3);
+
+ case GL_DEBUG_OUTPUT_SYNCHRONOUS:
+ case GL_DEBUG_OUTPUT:
+ return context->getExtensions().debugKHR;
+
+ case GL_BIND_GENERATES_RESOURCE_CHROMIUM:
+ return queryOnly && context->getExtensions().bindGeneratesResourceCHROMIUM;
+
+ case GL_CLIENT_ARRAYS_ANGLE:
+ return queryOnly && context->getExtensions().clientArraysANGLE;
+
+ case GL_FRAMEBUFFER_SRGB_EXT:
+ return context->getExtensions().sRGBWriteControlEXT;
+
+ case GL_SAMPLE_MASK:
+ return context->getClientVersion() >= Version(3, 1);
+
+ case GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
+ return queryOnly && context->getExtensions().robustResourceInitializationANGLE;
+
+ case GL_TEXTURE_RECTANGLE_ANGLE:
+ return context->isWebGL();
+
+ // GL_APPLE_clip_distance/GL_EXT_clip_cull_distance
+ case GL_CLIP_DISTANCE0_EXT:
+ case GL_CLIP_DISTANCE1_EXT:
+ case GL_CLIP_DISTANCE2_EXT:
+ case GL_CLIP_DISTANCE3_EXT:
+ case GL_CLIP_DISTANCE4_EXT:
+ case GL_CLIP_DISTANCE5_EXT:
+ case GL_CLIP_DISTANCE6_EXT:
+ case GL_CLIP_DISTANCE7_EXT:
+ if (context->getExtensions().clipDistanceAPPLE ||
+ context->getExtensions().clipCullDistanceEXT)
+ {
+ return true;
+ }
+ break;
+ case GL_SAMPLE_SHADING:
+ return context->getExtensions().sampleShadingOES;
+ case GL_SHADING_RATE_PRESERVE_ASPECT_RATIO_QCOM:
+ return context->getExtensions().shadingRateQCOM;
+
+ // COLOR_LOGIC_OP is in GLES1, but exposed through an ANGLE extension.
+ case GL_COLOR_LOGIC_OP:
+ return context->getClientVersion() < Version(2, 0) ||
+ context->getExtensions().logicOpANGLE;
+
+ default:
+ break;
+ }
+
+ // GLES1 emulation: GLES1-specific caps after this point
+ if (context->getClientVersion().major != 1)
+ {
+ return false;
+ }
+
+ switch (cap)
+ {
+ case GL_ALPHA_TEST:
+ case GL_VERTEX_ARRAY:
+ case GL_NORMAL_ARRAY:
+ case GL_COLOR_ARRAY:
+ case GL_TEXTURE_COORD_ARRAY:
+ case GL_TEXTURE_2D:
+ case GL_LIGHTING:
+ case GL_LIGHT0:
+ case GL_LIGHT1:
+ case GL_LIGHT2:
+ case GL_LIGHT3:
+ case GL_LIGHT4:
+ case GL_LIGHT5:
+ case GL_LIGHT6:
+ case GL_LIGHT7:
+ case GL_NORMALIZE:
+ case GL_RESCALE_NORMAL:
+ case GL_COLOR_MATERIAL:
+ case GL_CLIP_PLANE0:
+ case GL_CLIP_PLANE1:
+ case GL_CLIP_PLANE2:
+ case GL_CLIP_PLANE3:
+ case GL_CLIP_PLANE4:
+ case GL_CLIP_PLANE5:
+ case GL_FOG:
+ case GL_POINT_SMOOTH:
+ case GL_LINE_SMOOTH:
+ return context->getClientVersion() < Version(2, 0);
+ case GL_POINT_SIZE_ARRAY_OES:
+ return context->getClientVersion() < Version(2, 0) &&
+ context->getExtensions().pointSizeArrayOES;
+ case GL_TEXTURE_CUBE_MAP:
+ return context->getClientVersion() < Version(2, 0) &&
+ context->getExtensions().textureCubeMapOES;
+ case GL_POINT_SPRITE_OES:
+ return context->getClientVersion() < Version(2, 0) &&
+ context->getExtensions().pointSpriteOES;
+ default:
+ return false;
+ }
+}
+
+// Return true if a character belongs to the ASCII subset as defined in GLSL ES 1.0 spec section
+// 3.1.
+bool IsValidESSLCharacter(unsigned char c)
+{
+ // Printing characters are valid except " $ ` @ \ ' DEL.
+ if (c >= 32 && c <= 126 && c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' &&
+ c != '\'')
+ {
+ return true;
+ }
+
+ // Horizontal tab, line feed, vertical tab, form feed, carriage return are also valid.
+ if (c >= 9 && c <= 13)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool IsValidESSLString(const char *str, size_t len)
+{
+ for (size_t i = 0; i < len; i++)
+ {
+ if (!IsValidESSLCharacter(str[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateWebGLNamePrefix(const Context *context,
+ angle::EntryPoint entryPoint,
+ const GLchar *name)
+{
+ ASSERT(context->isWebGL());
+
+ // WebGL 1.0 [Section 6.16] GLSL Constructs
+ // Identifiers starting with "webgl_" and "_webgl_" are reserved for use by WebGL.
+ if (strncmp(name, "webgl_", 6) == 0 || strncmp(name, "_webgl_", 7) == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kWebglBindAttribLocationReservedPrefix);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateWebGLNameLength(const Context *context, angle::EntryPoint entryPoint, size_t length)
+{
+ ASSERT(context->isWebGL());
+
+ if (context->isWebGL1() && length > 256)
+ {
+ // WebGL 1.0 [Section 6.21] Maxmimum Uniform and Attribute Location Lengths
+ // WebGL imposes a limit of 256 characters on the lengths of uniform and attribute
+ // locations.
+ context->validationError(entryPoint, GL_INVALID_VALUE, kWebglNameLengthLimitExceeded);
+
+ return false;
+ }
+ else if (length > 1024)
+ {
+ // WebGL 2.0 [Section 4.3.2] WebGL 2.0 imposes a limit of 1024 characters on the lengths of
+ // uniform and attribute locations.
+ context->validationError(entryPoint, GL_INVALID_VALUE, kWebgl2NameLengthLimitExceeded);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidBlendFunc(const Context *context, GLenum val)
+{
+ const Extensions &ext = context->getExtensions();
+
+ // these are always valid for src and dst.
+ switch (val)
+ {
+ case GL_ZERO:
+ case GL_ONE:
+ case GL_SRC_COLOR:
+ case GL_ONE_MINUS_SRC_COLOR:
+ case GL_DST_COLOR:
+ case GL_ONE_MINUS_DST_COLOR:
+ case GL_SRC_ALPHA:
+ case GL_ONE_MINUS_SRC_ALPHA:
+ case GL_DST_ALPHA:
+ case GL_ONE_MINUS_DST_ALPHA:
+ case GL_CONSTANT_COLOR:
+ case GL_ONE_MINUS_CONSTANT_COLOR:
+ case GL_CONSTANT_ALPHA:
+ case GL_ONE_MINUS_CONSTANT_ALPHA:
+ return true;
+
+ // EXT_blend_func_extended.
+ case GL_SRC1_COLOR_EXT:
+ case GL_SRC1_ALPHA_EXT:
+ case GL_ONE_MINUS_SRC1_COLOR_EXT:
+ case GL_ONE_MINUS_SRC1_ALPHA_EXT:
+ case GL_SRC_ALPHA_SATURATE_EXT:
+ return ext.blendFuncExtendedEXT;
+
+ default:
+ return false;
+ }
+}
+
+bool ValidSrcBlendFunc(const Context *context, GLenum val)
+{
+ if (ValidBlendFunc(context, val))
+ return true;
+
+ if (val == GL_SRC_ALPHA_SATURATE)
+ return true;
+
+ return false;
+}
+
+bool ValidDstBlendFunc(const Context *context, GLenum val)
+{
+ if (ValidBlendFunc(context, val))
+ return true;
+
+ if (val == GL_SRC_ALPHA_SATURATE)
+ {
+ if (context->getClientMajorVersion() >= 3)
+ return true;
+ }
+
+ return false;
+}
+
+bool ValidateES2TexImageParameters(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isCompressed,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ 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 ValidateES2TexImageParametersBase(context, entryPoint, target, level, internalformat,
+ isCompressed, isSubImage, xoffset, yoffset, width,
+ height, border, format, type, imageSize, pixels);
+}
+
+} // anonymous namespace
+
+bool ValidateES2TexImageParametersBase(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ bool isCompressed,
+ bool isSubImage,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ GLsizei imageSize,
+ const void *pixels)
+{
+
+ TextureType texType = TextureTargetToType(target);
+ if (!ValidImageSizeParameters(context, entryPoint, texType, level, width, height, 1,
+ isSubImage))
+ {
+ // Error already handled.
+ return false;
+ }
+
+ if (!ValidMipLevel(context, texType, level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if ((xoffset < 0 || std::numeric_limits<GLsizei>::max() - xoffset < width) ||
+ (yoffset < 0 || std::numeric_limits<GLsizei>::max() - yoffset < height))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ 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) ||
+ height > (caps.maxCubeMapTextureSize >> level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ Texture *texture = context->getTextureByType(texType);
+ if (!texture)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotBound);
+ return false;
+ }
+
+ // Verify zero border
+ if (border != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidBorder);
+ return false;
+ }
+
+ bool nonEqualFormatsAllowed = false;
+
+ if (isCompressed)
+ {
+ GLenum actualInternalFormat =
+ isSubImage ? texture->getFormat(target, level).info->sizedInternalFormat
+ : internalformat;
+
+ const InternalFormat &internalFormatInfo = GetSizedInternalFormatInfo(actualInternalFormat);
+
+ if (!internalFormatInfo.compressed && !internalFormatInfo.paletted)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kInvalidInternalFormat,
+ internalformat);
+ return false;
+ }
+
+ if (!internalFormatInfo.textureSupport(context->getClientVersion(),
+ context->getExtensions()))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kInvalidInternalFormat,
+ internalformat);
+ return false;
+ }
+
+ if (isSubImage)
+ {
+ // From OpenGL ES Version 1.1.12, section 3.7.4 Compressed Paletted
+ // Textures:
+ //
+ // Subimages may not be specified for compressed paletted textures.
+ // Calling CompressedTexSubImage2D with any of the PALETTE*
+ // arguments in table 3.11 will generate an INVALID OPERATION error.
+ if (internalFormatInfo.paletted)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalformat);
+ return false;
+ }
+
+ // From the OES_compressed_ETC1_RGB8_texture spec:
+ //
+ // INVALID_OPERATION is generated by CompressedTexSubImage2D, TexSubImage2D, or
+ // CopyTexSubImage2D if the texture image <level> bound to <target> has internal format
+ // ETC1_RGB8_OES.
+ //
+ // This is relaxed if GL_EXT_compressed_ETC1_RGB8_sub_texture is supported.
+ if (IsETC1Format(actualInternalFormat) &&
+ !context->getExtensions().compressedETC1RGB8SubTextureEXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalformat);
+ return false;
+ }
+
+ if (!ValidCompressedSubImageSize(context, actualInternalFormat, xoffset, yoffset, 0,
+ width, height, 1, 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, kInvalidFormat);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height, 1))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidCompressedImageSize);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // validate <type> by itself (used as secondary key below)
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ case GL_UNSIGNED_SHORT:
+ case GL_UNSIGNED_INT:
+ case GL_UNSIGNED_INT_24_8_OES:
+ case GL_HALF_FLOAT_OES:
+ case GL_FLOAT:
+ break;
+ case GL_UNSIGNED_INT_2_10_10_10_REV_EXT:
+ if (!context->getExtensions().textureType2101010REVEXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, type);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidType);
+ return false;
+ }
+
+ // validate <format> + <type> combinations
+ // - invalid <format> -> sets INVALID_ENUM
+ // - invalid <format>+<type> combination -> sets INVALID_OPERATION
+ switch (format)
+ {
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ case GL_LUMINANCE_ALPHA:
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ case GL_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_RED:
+ case GL_RG:
+ if (!context->getExtensions().textureRgEXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ format);
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ case GL_FLOAT:
+ if (!context->getExtensions().textureFloatOES)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM,
+ kEnumNotSupported, type);
+ return false;
+ }
+ break;
+ case GL_HALF_FLOAT_OES:
+ if (!context->getExtensions().textureFloatOES &&
+ !context->getExtensions().textureHalfFloatOES)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM,
+ kEnumNotSupported, type);
+ return false;
+ }
+ break;
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ if (!context->getExtensions().textureNorm16EXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM,
+ kEnumNotSupported, type);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_RGB:
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_UNSIGNED_INT_2_10_10_10_REV_EXT:
+ case GL_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ break;
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ if (!context->getExtensions().textureNorm16EXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_RGBA:
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ case GL_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ case GL_UNSIGNED_INT_2_10_10_10_REV_EXT:
+ break;
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ if (!context->getExtensions().textureNorm16EXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_BGRA_EXT:
+ if (!context->getExtensions().textureFormatBGRA8888EXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ format);
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_SRGB_EXT:
+ case GL_SRGB_ALPHA_EXT:
+ if (!context->getExtensions().sRGBEXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ format);
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_DEPTH_COMPONENT:
+ switch (type)
+ {
+ case GL_UNSIGNED_SHORT:
+ case GL_UNSIGNED_INT:
+ break;
+ case GL_FLOAT:
+ if (!context->getExtensions().depthBufferFloat2NV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_DEPTH_STENCIL_OES:
+ switch (type)
+ {
+ case GL_UNSIGNED_INT_24_8_OES:
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_STENCIL_INDEX:
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, format);
+ return false;
+ }
+
+ switch (format)
+ {
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_STENCIL_OES:
+ if (!context->getExtensions().depthTextureANGLE &&
+ !((context->getExtensions().packedDepthStencilOES ||
+ context->getExtensions().depthTextureCubeMapOES) &&
+ context->getExtensions().depthTextureOES))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ format);
+ return false;
+ }
+
+ switch (target)
+ {
+ case TextureTarget::_2D:
+ break;
+ case TextureTarget::CubeMapNegativeX:
+ case TextureTarget::CubeMapNegativeY:
+ case TextureTarget::CubeMapNegativeZ:
+ case TextureTarget::CubeMapPositiveX:
+ case TextureTarget::CubeMapPositiveY:
+ case TextureTarget::CubeMapPositiveZ:
+ if (!context->getExtensions().depthTextureCubeMapOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTargetAndFormat);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTargetAndFormat);
+ return false;
+ }
+
+ // OES_depth_texture supports loading depth data and multiple levels,
+ // but ANGLE_depth_texture does not
+ if (!context->getExtensions().depthTextureOES)
+ {
+ if (pixels != nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kPixelDataNotNull);
+ return false;
+ }
+ if (level != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kLevelNotZero);
+ return false;
+ }
+ }
+ break;
+ case GL_STENCIL_INDEX:
+ if (!context->getExtensions().textureStencil8OES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+
+ switch (target)
+ {
+ case TextureTarget::_2D:
+ case TextureTarget::_2DArray:
+ case TextureTarget::CubeMapNegativeX:
+ case TextureTarget::CubeMapNegativeY:
+ case TextureTarget::CubeMapNegativeZ:
+ case TextureTarget::CubeMapPositiveX:
+ case TextureTarget::CubeMapPositiveY:
+ case TextureTarget::CubeMapPositiveZ:
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTargetAndFormat);
+ return false;
+ }
+
+ if (internalformat != GL_STENCIL_INDEX8)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTargetAndFormat);
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!isSubImage)
+ {
+ switch (internalformat)
+ {
+ // Core ES 2.0 formats
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ case GL_LUMINANCE_ALPHA:
+ case GL_RGB:
+ case GL_RGBA:
+ break;
+
+ case GL_RGBA32F:
+ if (!context->getExtensions().colorBufferFloatRgbaCHROMIUM)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ nonEqualFormatsAllowed = true;
+
+ if (type != GL_FLOAT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ if (format != GL_RGBA)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+
+ case GL_RGB32F:
+ if (!context->getExtensions().colorBufferFloatRgbCHROMIUM)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ nonEqualFormatsAllowed = true;
+
+ if (type != GL_FLOAT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ if (format != GL_RGB)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+
+ case GL_BGRA_EXT:
+ if (!context->getExtensions().textureFormatBGRA8888EXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_DEPTH_COMPONENT:
+ if (!(context->getExtensions().depthTextureAny()))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_DEPTH_STENCIL:
+ if (!(context->getExtensions().depthTextureANGLE ||
+ context->getExtensions().packedDepthStencilOES ||
+ context->getExtensions().depthTextureCubeMapOES))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_STENCIL_INDEX8:
+ if (!context->getExtensions().textureStencil8OES)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_RED:
+ case GL_RG:
+ if (!context->getExtensions().textureRgEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_SRGB_EXT:
+ case GL_SRGB_ALPHA_EXT:
+ if (!context->getExtensions().sRGBEXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ internalformat);
+ return false;
+ }
+ break;
+
+ case GL_RGB10_A2_EXT:
+ if (!context->getExtensions().textureType2101010REVEXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ internalformat);
+ return false;
+ }
+
+ if (type != GL_UNSIGNED_INT_2_10_10_10_REV_EXT || format != GL_RGBA)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMismatchedTypeAndFormat);
+ return false;
+ }
+
+ nonEqualFormatsAllowed = true;
+
+ break;
+
+ case GL_RGB5_A1:
+ if (context->getExtensions().textureType2101010REVEXT &&
+ type == GL_UNSIGNED_INT_2_10_10_10_REV_EXT && format == GL_RGBA)
+ {
+ nonEqualFormatsAllowed = true;
+ }
+
+ break;
+
+ case GL_RGBX8_ANGLE:
+ if (context->getExtensions().rgbxInternalFormatANGLE &&
+ type == GL_UNSIGNED_BYTE && format == GL_RGB)
+ {
+ nonEqualFormatsAllowed = true;
+ }
+
+ break;
+
+ case GL_R16_EXT:
+ case GL_RG16_EXT:
+ case GL_RGB16_EXT:
+ case GL_RGBA16_EXT:
+ case GL_R16_SNORM_EXT:
+ case GL_RG16_SNORM_EXT:
+ case GL_RGB16_SNORM_EXT:
+ case GL_RGBA16_SNORM_EXT:
+ if (!context->getExtensions().textureNorm16EXT)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ internalformat);
+ return false;
+ }
+ break;
+ default:
+ // Compressed formats are not valid internal formats for glTexImage*D
+ context->validationErrorF(entryPoint, GL_INVALID_VALUE, kInvalidInternalFormat,
+ internalformat);
+ return false;
+ }
+ }
+
+ if (type == GL_FLOAT)
+ {
+ if (!context->getExtensions().textureFloatOES)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, type);
+ return false;
+ }
+ }
+ else if (type == GL_HALF_FLOAT_OES)
+ {
+ if (!context->getExtensions().textureHalfFloatOES)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, type);
+ return false;
+ }
+ }
+ }
+
+ if (isSubImage)
+ {
+ const InternalFormat &textureInternalFormat = *texture->getFormat(target, level).info;
+ if (textureInternalFormat.internalFormat == GL_NONE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidTextureLevel);
+ return false;
+ }
+
+ if (format != textureInternalFormat.format)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, err::kTextureFormatMismatch);
+ return false;
+ }
+
+ if (context->isWebGL())
+ {
+ if (GetInternalFormatInfo(format, type).sizedInternalFormat !=
+ textureInternalFormat.sizedInternalFormat)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTextureTypeMismatch);
+ return false;
+ }
+ }
+
+ if (static_cast<size_t>(xoffset + width) > texture->getWidth(target, level) ||
+ static_cast<size_t>(yoffset + height) > texture->getHeight(target, level))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ if (width > 0 && height > 0 && pixels == nullptr &&
+ context->getState().getTargetBuffer(BufferBinding::PixelUnpack) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kPixelDataNull);
+ return false;
+ }
+ }
+ else
+ {
+ if (texture->getImmutableFormat())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTextureIsImmutable);
+ return false;
+ }
+ }
+
+ // From GL_CHROMIUM_color_buffer_float_rgb[a]:
+ // GL_RGB[A] / GL_RGB[A]32F becomes an allowable format / internalformat parameter pair for
+ // TexImage2D. The restriction in section 3.7.1 of the OpenGL ES 2.0 spec that the
+ // internalformat parameter and format parameter of TexImage2D must match is lifted for this
+ // case.
+ if (!isSubImage && !isCompressed && internalformat != format && !nonEqualFormatsAllowed)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormatCombination);
+ return false;
+ }
+
+ GLenum sizeCheckFormat = isSubImage ? format : internalformat;
+ return ValidImageDataSize(context, entryPoint, texType, width, height, 1, sizeCheckFormat, type,
+ pixels, imageSize);
+}
+
+bool ValidateES2TexStorageParametersBase(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (target != TextureType::_2D && target != TextureType::CubeMap &&
+ target != TextureType::Rectangle)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ if (width < 1 || height < 1 || levels < 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kTextureSizeTooSmall);
+ return false;
+ }
+
+ if (target == TextureType::CubeMap && width != height)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (levels != 1 && levels != log2(std::max(width, height)) + 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidMipLevels);
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
+ if (formatInfo.format == GL_NONE || formatInfo.type == GL_NONE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ 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, kInvalidMipLevel);
+ return false;
+ }
+
+ if (width > caps.maxRectangleTextureSize || height > caps.maxRectangleTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ if (formatInfo.compressed)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+ break;
+ case TextureType::CubeMap:
+ if (width > caps.maxCubeMapTextureSize || height > caps.maxCubeMapTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+ case TextureType::InvalidEnum:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kEnumInvalid);
+ return false;
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
+ ToGLenum(target));
+ return false;
+ }
+
+ if (levels != 1 && !context->getExtensions().textureNpotOES)
+ {
+ if (!isPow2(width) || !isPow2(height))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kDimensionsMustBePow2);
+ return false;
+ }
+ }
+
+ if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ // Even with OES_texture_npot, some compressed formats may impose extra restrictions.
+ if (formatInfo.compressed)
+ {
+ if (!ValidCompressedImageSize(context, formatInfo.internalFormat, 0, width, height, 1))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidCompressedImageSize);
+ return false;
+ }
+ }
+
+ switch (internalformat)
+ {
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT32_OES:
+ switch (target)
+ {
+ case TextureType::_2D:
+ break;
+ case TextureType::CubeMap:
+ if (!context->getExtensions().depthTextureCubeMapOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidTextureTarget);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidTextureTarget);
+ return false;
+ }
+
+ // ANGLE_depth_texture only supports 1-level textures
+ if (!context->getExtensions().depthTextureOES)
+ {
+ if (levels != 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidMipLevels);
+ return false;
+ }
+ }
+ break;
+ case GL_DEPTH24_STENCIL8_OES:
+ switch (target)
+ {
+ case TextureType::_2D:
+ break;
+ case TextureType::CubeMap:
+ if (!context->getExtensions().depthTextureCubeMapOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidTextureTarget);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidTextureTarget);
+ return false;
+ }
+
+ if (!context->getExtensions().packedDepthStencilOES &&
+ !context->getExtensions().depthTextureCubeMapOES)
+ {
+ // ANGLE_depth_texture only supports 1-level textures
+ if (levels != 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidMipLevels);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ 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 ValidateDiscardFramebufferEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLsizei numAttachments,
+ const GLenum *attachments)
+{
+ if (!context->getExtensions().discardFramebufferEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ bool defaultFramebuffer = false;
+
+ switch (target)
+ {
+ case GL_FRAMEBUFFER:
+ defaultFramebuffer =
+ (context->getState().getTargetFramebuffer(GL_FRAMEBUFFER)->isDefault());
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFramebufferTarget);
+ return false;
+ }
+
+ return ValidateDiscardFramebufferBase(context, entryPoint, target, numAttachments, attachments,
+ defaultFramebuffer);
+}
+
+bool ValidateBindVertexArrayOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ VertexArrayID array)
+{
+ if (!context->getExtensions().vertexArrayObjectOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateBindVertexArrayBase(context, entryPoint, array);
+}
+
+bool ValidateDeleteVertexArraysOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const VertexArrayID *arrays)
+{
+ if (!context->getExtensions().vertexArrayObjectOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateGenVertexArraysOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const VertexArrayID *arrays)
+{
+ if (!context->getExtensions().vertexArrayObjectOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateIsVertexArrayOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ VertexArrayID array)
+{
+ if (!context->getExtensions().vertexArrayObjectOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateProgramBinaryOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLenum binaryFormat,
+ const void *binary,
+ GLint length)
+{
+ if (!context->getExtensions().getProgramBinaryOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateProgramBinaryBase(context, entryPoint, program, binaryFormat, binary, length);
+}
+
+bool ValidateGetProgramBinaryOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLenum *binaryFormat,
+ const void *binary)
+{
+ if (!context->getExtensions().getProgramBinaryOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGetProgramBinaryBase(context, entryPoint, program, bufSize, length, binaryFormat,
+ binary);
+}
+
+static bool ValidDebugSource(GLenum source, bool mustBeThirdPartyOrApplication)
+{
+ switch (source)
+ {
+ case GL_DEBUG_SOURCE_API:
+ case GL_DEBUG_SOURCE_SHADER_COMPILER:
+ case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+ case GL_DEBUG_SOURCE_OTHER:
+ // Only THIRD_PARTY and APPLICATION sources are allowed to be manually inserted
+ return !mustBeThirdPartyOrApplication;
+
+ case GL_DEBUG_SOURCE_THIRD_PARTY:
+ case GL_DEBUG_SOURCE_APPLICATION:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool ValidDebugType(GLenum type)
+{
+ switch (type)
+ {
+ case GL_DEBUG_TYPE_ERROR:
+ case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+ case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+ case GL_DEBUG_TYPE_PERFORMANCE:
+ case GL_DEBUG_TYPE_PORTABILITY:
+ case GL_DEBUG_TYPE_OTHER:
+ case GL_DEBUG_TYPE_MARKER:
+ case GL_DEBUG_TYPE_PUSH_GROUP:
+ case GL_DEBUG_TYPE_POP_GROUP:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool ValidDebugSeverity(GLenum severity)
+{
+ switch (severity)
+ {
+ case GL_DEBUG_SEVERITY_HIGH:
+ case GL_DEBUG_SEVERITY_MEDIUM:
+ case GL_DEBUG_SEVERITY_LOW:
+ case GL_DEBUG_SEVERITY_NOTIFICATION:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool ValidateDebugMessageControlKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum source,
+ GLenum type,
+ GLenum severity,
+ GLsizei count,
+ const GLuint *ids,
+ GLboolean enabled)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidDebugSource(source, false) && source != GL_DONT_CARE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugSource);
+ return false;
+ }
+
+ if (!ValidDebugType(type) && type != GL_DONT_CARE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugType);
+ return false;
+ }
+
+ if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugSeverity);
+ return false;
+ }
+
+ if (count > 0)
+ {
+ if (source == GL_DONT_CARE || type == GL_DONT_CARE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidDebugSourceType);
+ return false;
+ }
+
+ if (severity != GL_DONT_CARE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidDebugSeverity);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDebugMessageInsertKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar *buf)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!context->getState().getDebug().isOutputEnabled())
+ {
+ // If the DEBUG_OUTPUT state is disabled calls to DebugMessageInsert are discarded and do
+ // not generate an error.
+ return false;
+ }
+
+ if (!ValidDebugSeverity(severity))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugSource);
+ return false;
+ }
+
+ if (!ValidDebugType(type))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugType);
+ return false;
+ }
+
+ if (!ValidDebugSource(source, true))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugSource);
+ return false;
+ }
+
+ size_t messageLength = (length < 0) ? strlen(buf) : length;
+ if (messageLength > context->getCaps().maxDebugMessageLength)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxDebugMessageLength);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDebugMessageCallbackKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLDEBUGPROCKHR callback,
+ const void *userParam)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetDebugMessageLogKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint count,
+ GLsizei bufSize,
+ const GLenum *sources,
+ const GLenum *types,
+ const GLuint *ids,
+ const GLenum *severities,
+ const GLsizei *lengths,
+ const GLchar *messageLog)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (bufSize < 0 && messageLog != nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePushDebugGroupKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum source,
+ GLuint id,
+ GLsizei length,
+ const GLchar *message)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidDebugSource(source, true))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugSource);
+ return false;
+ }
+
+ size_t messageLength = (length < 0) ? strlen(message) : length;
+ if (messageLength > context->getCaps().maxDebugMessageLength)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxDebugMessageLength);
+ return false;
+ }
+
+ size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+ if (currentStackSize >= context->getCaps().maxDebugGroupStackDepth)
+ {
+ context->validationError(entryPoint, GL_STACK_OVERFLOW, kExceedsMaxDebugGroupStackDepth);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePopDebugGroupKHR(const Context *context, angle::EntryPoint entryPoint)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+ if (currentStackSize <= 1)
+ {
+ context->validationError(entryPoint, GL_STACK_UNDERFLOW, kCannotPopDefaultDebugGroup);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateObjectIdentifierAndName(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum identifier,
+ GLuint name)
+{
+ switch (identifier)
+ {
+ case GL_BUFFER:
+ if (context->getBuffer({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidBufferName);
+ return false;
+ }
+ return true;
+
+ case GL_SHADER:
+ if (context->getShader({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidShaderName);
+ return false;
+ }
+ return true;
+
+ case GL_PROGRAM:
+ if (context->getProgramNoResolveLink({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidProgramName);
+ return false;
+ }
+ return true;
+
+ case GL_VERTEX_ARRAY:
+ if (context->getVertexArray({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidVertexArrayName);
+ return false;
+ }
+ return true;
+
+ case GL_QUERY:
+ if (context->getQuery({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidQueryName);
+ return false;
+ }
+ return true;
+
+ case GL_TRANSFORM_FEEDBACK:
+ if (context->getTransformFeedback({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kInvalidTransformFeedbackName);
+ return false;
+ }
+ return true;
+
+ case GL_SAMPLER:
+ if (context->getSampler({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidSamplerName);
+ return false;
+ }
+ return true;
+
+ case GL_TEXTURE:
+ if (context->getTexture({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidTextureName);
+ return false;
+ }
+ return true;
+
+ case GL_RENDERBUFFER:
+ if (!context->isRenderbuffer({name}))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidRenderbufferName);
+ return false;
+ }
+ return true;
+
+ case GL_FRAMEBUFFER:
+ if (context->getFramebuffer({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidFramebufferName);
+ return false;
+ }
+ return true;
+
+ case GL_PROGRAM_PIPELINE:
+ if (context->getProgramPipeline({name}) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidProgramPipelineName);
+ return false;
+ }
+ return true;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidIndentifier);
+ return false;
+ }
+}
+
+static bool ValidateLabelLength(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei length,
+ const GLchar *label)
+{
+ size_t labelLength = 0;
+
+ if (length < 0)
+ {
+ if (label != nullptr)
+ {
+ labelLength = strlen(label);
+ }
+ }
+ else
+ {
+ labelLength = static_cast<size_t>(length);
+ }
+
+ if (labelLength > context->getCaps().maxLabelLength)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxLabelLength);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateObjectLabelKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum identifier,
+ GLuint name,
+ GLsizei length,
+ const GLchar *label)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidateObjectIdentifierAndName(context, entryPoint, identifier, name))
+ {
+ return false;
+ }
+
+ if (!ValidateLabelLength(context, entryPoint, length, label))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetObjectLabelKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum identifier,
+ GLuint name,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLchar *label)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ if (!ValidateObjectIdentifierAndName(context, entryPoint, identifier, name))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateObjectPtrName(const Context *context,
+ angle::EntryPoint entryPoint,
+ const void *ptr)
+{
+ if (context->getSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidSyncPointer);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateObjectPtrLabelKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ const void *ptr,
+ GLsizei length,
+ const GLchar *label)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidateObjectPtrName(context, entryPoint, ptr))
+ {
+ return false;
+ }
+
+ if (!ValidateLabelLength(context, entryPoint, length, label))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetObjectPtrLabelKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ const void *ptr,
+ GLsizei bufSize,
+ const GLsizei *length,
+ const GLchar *label)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ if (!ValidateObjectPtrName(context, entryPoint, ptr))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetPointervKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ void *const *params)
+{
+ if (!context->getExtensions().debugKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ // TODO: represent this in Context::getQueryParameterInfo.
+ switch (pname)
+ {
+ case GL_DEBUG_CALLBACK_FUNCTION:
+ case GL_DEBUG_CALLBACK_USER_PARAM:
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, pname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetPointervRobustANGLERobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ GLsizei bufSize,
+ const GLsizei *length,
+ void *const *params)
+{
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateBlitFramebufferANGLE(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->getExtensions().framebufferBlitANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBlitExtensionNotAvailable);
+ return false;
+ }
+
+ if (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0)
+ {
+ // TODO(jmadill): Determine if this should be available on other implementations.
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBlitExtensionScaleOrFlip);
+ return false;
+ }
+
+ if (filter == GL_LINEAR)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kBlitExtensionLinear);
+ return false;
+ }
+
+ Framebuffer *readFramebuffer = context->getState().getReadFramebuffer();
+ Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer();
+
+ if (mask & GL_COLOR_BUFFER_BIT)
+ {
+ const FramebufferAttachment *readColorAttachment =
+ readFramebuffer->getReadColorAttachment();
+ const FramebufferAttachment *drawColorAttachment =
+ drawFramebuffer->getFirstColorAttachment();
+
+ if (readColorAttachment && drawColorAttachment)
+ {
+ if (!(readColorAttachment->type() == GL_TEXTURE &&
+ (readColorAttachment->getTextureImageIndex().getType() == TextureType::_2D ||
+ readColorAttachment->getTextureImageIndex().getType() ==
+ TextureType::Rectangle)) &&
+ readColorAttachment->type() != GL_RENDERBUFFER &&
+ readColorAttachment->type() != GL_FRAMEBUFFER_DEFAULT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBlitExtensionFromInvalidAttachmentType);
+ return false;
+ }
+
+ for (size_t drawbufferIdx = 0;
+ drawbufferIdx < drawFramebuffer->getDrawbufferStateCount(); ++drawbufferIdx)
+ {
+ const FramebufferAttachment *attachment =
+ drawFramebuffer->getDrawBuffer(drawbufferIdx);
+ if (attachment)
+ {
+ if (!(attachment->type() == GL_TEXTURE &&
+ (attachment->getTextureImageIndex().getType() == TextureType::_2D ||
+ attachment->getTextureImageIndex().getType() ==
+ TextureType::Rectangle)) &&
+ attachment->type() != GL_RENDERBUFFER &&
+ attachment->type() != GL_FRAMEBUFFER_DEFAULT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBlitExtensionToInvalidAttachmentType);
+ return false;
+ }
+
+ // Return an error if the destination formats do not match
+ if (!Format::EquivalentForBlit(attachment->getFormat(),
+ readColorAttachment->getFormat()))
+ {
+ context->validationErrorF(
+ entryPoint, GL_INVALID_OPERATION, kBlitExtensionFormatMismatch,
+ readColorAttachment->getFormat().info->sizedInternalFormat,
+ attachment->getFormat().info->sizedInternalFormat);
+ return false;
+ }
+ }
+ }
+
+ GLint samples = readFramebuffer->getSamples(context);
+ if (samples != 0 &&
+ IsPartialBlit(context, readColorAttachment, drawColorAttachment, srcX0, srcY0,
+ srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBlitExtensionMultisampledWholeBufferBlit);
+ return false;
+ }
+ }
+ }
+
+ GLenum masks[] = {GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT};
+ GLenum attachments[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
+ for (size_t i = 0; i < 2; i++)
+ {
+ if (mask & masks[i])
+ {
+ const FramebufferAttachment *readBuffer =
+ readFramebuffer->getAttachment(context, attachments[i]);
+ const FramebufferAttachment *drawBuffer =
+ drawFramebuffer->getAttachment(context, attachments[i]);
+
+ if (readBuffer && drawBuffer)
+ {
+ if (IsPartialBlit(context, readBuffer, drawBuffer, srcX0, srcY0, srcX1, srcY1,
+ dstX0, dstY0, dstX1, dstY1))
+ {
+ // only whole-buffer copies are permitted
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBlitExtensionDepthStencilWholeBufferBlit);
+ return false;
+ }
+
+ if (readBuffer->getResourceSamples() != 0 || drawBuffer->getResourceSamples() != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBlitExtensionMultisampledDepthOrStencil);
+ return false;
+ }
+ }
+ }
+ }
+
+ return ValidateBlitFramebufferParameters(context, entryPoint, srcX0, srcY0, srcX1, srcY1, dstX0,
+ dstY0, dstX1, dstY1, mask, filter);
+}
+
+bool ValidateBlitFramebufferNV(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->getExtensions().framebufferBlitANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBlitExtensionNotAvailable);
+ return false;
+ }
+
+ return ValidateBlitFramebufferParameters(context, entryPoint, srcX0, srcY0, srcX1, srcY1, dstX0,
+ dstY0, dstX1, dstY1, mask, filter);
+}
+
+bool ValidateClear(const Context *context, angle::EntryPoint entryPoint, GLbitfield mask)
+{
+ Framebuffer *fbo = context->getState().getDrawFramebuffer();
+ const Extensions &extensions = context->getExtensions();
+
+ if (!ValidateFramebufferComplete(context, entryPoint, fbo))
+ {
+ return false;
+ }
+
+ if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidClearMask);
+ return false;
+ }
+
+ if (extensions.webglCompatibilityANGLE && (mask & GL_COLOR_BUFFER_BIT) != 0)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_FLOAT, GL_UNSIGNED_NORMALIZED,
+ GL_SIGNED_NORMALIZED};
+
+ for (GLuint drawBufferIdx = 0; drawBufferIdx < fbo->getDrawbufferStateCount();
+ drawBufferIdx++)
+ {
+ if (!ValidateWebGLFramebufferAttachmentClearType(context, entryPoint, drawBufferIdx,
+ validComponentTypes,
+ ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
+ }
+
+ if ((extensions.multiviewOVR || extensions.multiview2OVR) && extensions.disjointTimerQueryEXT)
+ {
+ const State &state = context->getState();
+ Framebuffer *framebuffer = state.getDrawFramebuffer();
+ if (framebuffer->getNumViews() > 1 && state.isQueryActive(QueryType::TimeElapsed))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kMultiviewTimerQuery);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDrawBuffersEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const GLenum *bufs)
+{
+ if (!context->getExtensions().drawBuffersEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateDrawBuffersBase(context, entryPoint, n, bufs);
+}
+
+bool ValidateTexImage2D(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexImageParameters(context, entryPoint, target, level, internalformat,
+ false, false, 0, 0, width, height, border, format,
+ type, -1, pixels);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexImage2DParameters(context, entryPoint, target, level, internalformat,
+ false, false, 0, 0, 0, width, height, 1, border, format,
+ type, -1, pixels);
+}
+
+bool ValidateTexImage2DRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const void *pixels)
+{
+ if (!ValidateRobustEntryPoint(context, entryPoint, bufSize))
+ {
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexImageParameters(context, entryPoint, target, level, internalformat,
+ false, false, 0, 0, width, height, border, format,
+ type, bufSize, pixels);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexImage2DParameters(context, entryPoint, target, level, internalformat,
+ false, false, 0, 0, 0, width, height, 1, border, format,
+ type, bufSize, pixels);
+}
+
+bool ValidateTexSubImage2D(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const void *pixels)
+{
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexImageParameters(context, entryPoint, target, level, GL_NONE, false,
+ true, xoffset, yoffset, width, height, 0, format, type,
+ -1, pixels);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexImage2DParameters(context, entryPoint, target, level, GL_NONE, false, true,
+ xoffset, yoffset, 0, width, height, 1, 0, format, type,
+ -1, pixels);
+}
+
+bool ValidateTexSubImage2DRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const void *pixels)
+{
+ if (!ValidateRobustEntryPoint(context, entryPoint, bufSize))
+ {
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexImageParameters(context, entryPoint, target, level, GL_NONE, false,
+ true, xoffset, yoffset, width, height, 0, format, type,
+ bufSize, pixels);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexImage2DParameters(context, entryPoint, target, level, GL_NONE, false, true,
+ xoffset, yoffset, 0, width, height, 1, 0, format, type,
+ bufSize, pixels);
+}
+
+bool ValidateTexSubImage3DOES(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)
+{
+ return ValidateTexSubImage3D(context, entryPoint, target, level, xoffset, yoffset, zoffset,
+ width, height, depth, format, type, pixels);
+}
+
+bool ValidateCompressedTexImage2D(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ const void *data)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ if (!ValidateES2TexImageParameters(context, entryPoint, target, level, internalformat, true,
+ false, 0, 0, width, height, border, GL_NONE, GL_NONE, -1,
+ data))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ ASSERT(context->getClientMajorVersion() >= 3);
+ if (!ValidateES3TexImage2DParameters(context, entryPoint, target, level, internalformat,
+ true, false, 0, 0, 0, width, height, 1, border,
+ GL_NONE, GL_NONE, -1, data))
+ {
+ return false;
+ }
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
+
+ GLuint expectedImageSize = 0;
+ if (!formatInfo.computeCompressedImageSize(Extents(width, height, 1), &expectedImageSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != expectedImageSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE,
+ kCompressedTextureDimensionsMustMatchData);
+ return false;
+ }
+
+ if (target == TextureTarget::Rectangle)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexImage2DRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
+{
+ if (!ValidateRobustCompressedTexImageBase(context, entryPoint, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexImage2D(context, entryPoint, target, level, internalformat, width,
+ height, border, imageSize, data);
+}
+
+bool ValidateCompressedTexImage3DOES(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)
+{
+ return ValidateCompressedTexImage3D(context, entryPoint, target, level, internalformat, width,
+ height, depth, border, imageSize, data);
+}
+
+bool ValidateCompressedTexSubImage2DRobustANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
+{
+ if (!ValidateRobustCompressedTexImageBase(context, entryPoint, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexSubImage2D(context, entryPoint, target, level, xoffset, yoffset,
+ width, height, format, imageSize, data);
+}
+
+bool ValidateCompressedTexSubImage2D(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLsizei imageSize,
+ const void *data)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ if (!ValidateES2TexImageParameters(context, entryPoint, target, level, GL_NONE, true, true,
+ xoffset, yoffset, width, height, 0, format, GL_NONE, -1,
+ data))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ ASSERT(context->getClientMajorVersion() >= 3);
+ if (!ValidateES3TexImage2DParameters(context, entryPoint, target, level, GL_NONE, true,
+ true, xoffset, yoffset, 0, width, height, 1, 0, format,
+ GL_NONE, -1, data))
+ {
+ return false;
+ }
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(format);
+ GLuint blockSize = 0;
+ if (!formatInfo.computeCompressedImageSize(Extents(width, height, 1), &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;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexSubImage3DOES(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)
+{
+ return ValidateCompressedTexSubImage3D(context, entryPoint, target, level, xoffset, yoffset,
+ zoffset, width, height, depth, format, imageSize, data);
+}
+
+bool ValidateGetBufferPointervOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLenum pname,
+ void *const *params)
+{
+ if (!context->getExtensions().mapbufferOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGetBufferPointervBase(context, entryPoint, target, pname, nullptr, params);
+}
+
+bool ValidateMapBufferOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLenum access)
+{
+ if (!context->getExtensions().mapbufferOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!context->isValidBufferBinding(target))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getState().getTargetBuffer(target);
+
+ if (buffer == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotMappable);
+ return false;
+ }
+
+ if (access != GL_WRITE_ONLY_OES)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidAccessBits);
+ return false;
+ }
+
+ // Though there is no explicit mention of an interaction between GL_EXT_buffer_storage
+ // and GL_OES_mapbuffer extension, allow it as long as the access type of glmapbufferOES
+ // is compatible with the buffer's usage flags specified during glBufferStorageEXT
+ if (buffer->isImmutable() && (buffer->getStorageExtUsageFlags() & GL_MAP_WRITE_BIT) == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotMappable);
+ return false;
+ }
+
+ if (buffer->isMapped())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferAlreadyMapped);
+ return false;
+ }
+
+ return ValidateMapBufferBase(context, entryPoint, target);
+}
+
+bool ValidateUnmapBufferOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target)
+{
+ if (!context->getExtensions().mapbufferOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateUnmapBufferBase(context, entryPoint, target);
+}
+
+bool ValidateMapBufferRangeEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length,
+ GLbitfield access)
+{
+ if (!context->getExtensions().mapBufferRangeEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateMapBufferRangeBase(context, entryPoint, target, offset, length, access);
+}
+
+bool ValidateMapBufferBase(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target)
+{
+ Buffer *buffer = context->getState().getTargetBuffer(target);
+ ASSERT(buffer != nullptr);
+
+ // Check if this buffer is currently being used as a transform feedback output buffer
+ if (context->getState().isTransformFeedbackActive())
+ {
+ TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
+ for (size_t i = 0; i < transformFeedback->getIndexedBufferCount(); i++)
+ {
+ const auto &transformFeedbackBuffer = transformFeedback->getIndexedBuffer(i);
+ if (transformFeedbackBuffer.get() == buffer)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBufferBoundForTransformFeedback);
+ return false;
+ }
+ }
+ }
+
+ if (buffer->hasWebGLXFBBindingConflict(context->isWebGL()))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBufferBoundForTransformFeedback);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFlushMappedBufferRangeEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length)
+{
+ if (!context->getExtensions().mapBufferRangeEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateFlushMappedBufferRangeBase(context, entryPoint, target, offset, length);
+}
+
+bool ValidateBindUniformLocationCHROMIUM(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ UniformLocation location,
+ const GLchar *name)
+{
+ if (!context->getExtensions().bindUniformLocationCHROMIUM)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (location.value < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeLocation);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (static_cast<long>(location.value) >=
+ (caps.maxVertexUniformVectors + caps.maxFragmentUniformVectors) * 4)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidBindUniformLocation);
+ return false;
+ }
+
+ // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+ // shader-related entry points
+ if (context->isWebGL() && !IsValidESSLString(name, strlen(name)))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidNameCharacters);
+ return false;
+ }
+
+ if (strncmp(name, "gl_", 3) == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNameBeginsWithGL);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCoverageModulationCHROMIUM(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum components)
+{
+ if (!context->getExtensions().framebufferMixedSamplesCHROMIUM)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ switch (components)
+ {
+ case GL_RGB:
+ case GL_RGBA:
+ case GL_ALPHA:
+ case GL_NONE:
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidCoverageComponents);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCopyTextureCHROMIUM(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)
+{
+ if (!context->getExtensions().copyTextureCHROMIUM)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidSourceTexture);
+ return false;
+ }
+
+ if (!IsValidCopyTextureSourceTarget(context, source->getType()))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalFormat);
+ return false;
+ }
+
+ TextureType sourceType = source->getType();
+ ASSERT(sourceType != TextureType::CubeMap);
+ TextureTarget sourceTarget = NonCubeTextureTypeToTarget(sourceType);
+
+ if (!IsValidCopyTextureSourceLevel(context, sourceType, 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->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ internalFormat);
+ return false;
+ }
+
+ const InternalFormat &sourceFormat = *source->getFormat(sourceTarget, sourceLevel).info;
+ if (!IsValidCopyTextureSourceInternalFormatEnum(sourceFormat.internalFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidSourceTextureInternalFormat);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTargetEnum(context, destTarget))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ const InternalFormat &destInternalFormatInfo = GetInternalFormatInfo(internalFormat, destType);
+ if (sourceType == TextureType::External && destInternalFormatInfo.isInt() &&
+ !context->getExtensions().EGLImageExternalEssl3OES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kANGLECopyTextureMissingRequiredExtension);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTarget(context, dest->getType(), destTarget))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTextureType);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationLevel(context, entryPoint, dest->getType(), destLevel,
+ sourceWidth, sourceHeight, false))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationFormatType(context, entryPoint, internalFormat, destType))
+ {
+ return false;
+ }
+
+ if (dest->getType() == TextureType::CubeMap && sourceWidth != sourceHeight)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (dest->getImmutableFormat())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kDestinationImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCopySubTextureCHROMIUM(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureID sourceId,
+ GLint sourceLevel,
+ TextureTarget destTarget,
+ TextureID destId,
+ GLint destLevel,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLboolean unpackFlipY,
+ GLboolean unpackPremultiplyAlpha,
+ GLboolean unpackUnmultiplyAlpha)
+{
+ if (!context->getExtensions().copyTextureCHROMIUM)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidSourceTexture);
+ return false;
+ }
+
+ if (!IsValidCopyTextureSourceTarget(context, source->getType()))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidSourceTextureType);
+ return false;
+ }
+
+ TextureType sourceType = source->getType();
+ ASSERT(sourceType != TextureType::CubeMap);
+ TextureTarget sourceTarget = NonCubeTextureTypeToTarget(sourceType);
+
+ if (!IsValidCopyTextureSourceLevel(context, sourceType, sourceLevel))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if (source->getWidth(sourceTarget, sourceLevel) == 0 ||
+ source->getHeight(sourceTarget, sourceLevel) == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidSourceTextureLevel);
+ return false;
+ }
+
+ if (x < 0 || y < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (width < 0 || height < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ if (static_cast<size_t>(x + width) > source->getWidth(sourceTarget, sourceLevel) ||
+ static_cast<size_t>(y + height) > source->getHeight(sourceTarget, sourceLevel))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kSourceTextureTooSmall);
+ return false;
+ }
+
+ const Format &sourceFormat = source->getFormat(sourceTarget, sourceLevel);
+ if (!IsValidCopySubTextureSourceInternalFormat(sourceFormat.info->internalFormat))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
+ sourceFormat.info->internalFormat);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTargetEnum(context, destTarget))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTarget(context, dest->getType(), destTarget))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTextureType);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationLevel(context, entryPoint, dest->getType(), destLevel, width,
+ height, true))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if (dest->getWidth(destTarget, destLevel) == 0 || dest->getHeight(destTarget, destLevel) == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kDestinationLevelNotDefined);
+ return false;
+ }
+
+ const InternalFormat &destFormat = *dest->getFormat(destTarget, destLevel).info;
+ if (!IsValidCopySubTextureDestionationInternalFormat(destFormat.internalFormat))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormatCombination);
+ return false;
+ }
+
+ if (sourceType == TextureType::External && destFormat.isInt() &&
+ !context->getExtensions().EGLImageExternalEssl3OES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kANGLECopyTextureMissingRequiredExtension);
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 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))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedCopyTextureCHROMIUM(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureID sourceId,
+ TextureID destId)
+{
+ if (!context->getExtensions().copyCompressedTextureCHROMIUM)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidSourceTexture);
+ return false;
+ }
+
+ if (source->getType() != TextureType::_2D)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidSourceTextureType);
+ return false;
+ }
+
+ if (source->getWidth(TextureTarget::_2D, 0) == 0 ||
+ source->getHeight(TextureTarget::_2D, 0) == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kSourceTextureLevelZeroDefined);
+ return false;
+ }
+
+ const Format &sourceFormat = source->getFormat(TextureTarget::_2D, 0);
+ if (!sourceFormat.info->compressed)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kSourceTextureMustBeCompressed);
+ return false;
+ }
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ if (dest->getType() != TextureType::_2D)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidDestinationTextureType);
+ return false;
+ }
+
+ if (dest->getImmutableFormat())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kDestinationImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCreateShader(const Context *context, angle::EntryPoint entryPoint, ShaderType type)
+{
+ switch (type)
+ {
+ case ShaderType::Vertex:
+ case ShaderType::Fragment:
+ break;
+
+ case ShaderType::Compute:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kES31Required);
+ return false;
+ }
+ break;
+
+ case ShaderType::Geometry:
+ if (!context->getExtensions().geometryShaderAny() &&
+ context->getClientVersion() < ES_3_2)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderType);
+ return false;
+ }
+ break;
+
+ case ShaderType::TessControl:
+ if (!context->getExtensions().tessellationShaderEXT &&
+ context->getClientVersion() < ES_3_2)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderType);
+ return false;
+ }
+ break;
+
+ case ShaderType::TessEvaluation:
+ if (!context->getExtensions().tessellationShaderEXT &&
+ context->getClientVersion() < ES_3_2)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderType);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderType);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBufferData(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLsizeiptr size,
+ const void *data,
+ BufferUsage usage)
+{
+ if (size < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ switch (usage)
+ {
+ case BufferUsage::StreamDraw:
+ case BufferUsage::StaticDraw:
+ case BufferUsage::DynamicDraw:
+ break;
+
+ case BufferUsage::StreamRead:
+ case BufferUsage::StaticRead:
+ case BufferUsage::DynamicRead:
+ case BufferUsage::StreamCopy:
+ case BufferUsage::StaticCopy:
+ case BufferUsage::DynamicCopy:
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBufferUsage);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBufferUsage);
+ return false;
+ }
+
+ if (!context->isValidBufferBinding(target))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getState().getTargetBuffer(target);
+
+ if (!buffer)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotBound);
+ return false;
+ }
+
+ if (buffer->hasWebGLXFBBindingConflict(context->isWebGL()))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBufferBoundForTransformFeedback);
+ return false;
+ }
+
+ if (buffer->isImmutable())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBufferSubData(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr size,
+ const void *data)
+{
+ if (size < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ if (offset < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (!context->isValidBufferBinding(target))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getState().getTargetBuffer(target);
+
+ if (!buffer)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotBound);
+ return false;
+ }
+
+ // EXT_buffer_storage allows persistently mapped buffers to be updated via glBufferSubData
+ bool isPersistent = (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
+
+ // Verify that buffer is not currently mapped unless persistent
+ if (buffer->isMapped() && !isPersistent)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferMapped);
+ return false;
+ }
+
+ if (buffer->hasWebGLXFBBindingConflict(context->isWebGL()))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kBufferBoundForTransformFeedback);
+ return false;
+ }
+
+ if (buffer->isImmutable() &&
+ (buffer->getStorageExtUsageFlags() & GL_DYNAMIC_STORAGE_BIT_EXT) == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotUpdatable);
+ return false;
+ }
+
+ // Check for possible overflow of size + offset
+ angle::CheckedNumeric<decltype(size + offset)> checkedSize(size);
+ checkedSize += offset;
+ if (!checkedSize.IsValid())
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kParamOverflow);
+ return false;
+ }
+
+ if (size + offset > buffer->getSize())
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInsufficientBufferSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRequestExtensionANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ const GLchar *name)
+{
+ if (!context->getExtensions().requestExtensionANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!context->isExtensionRequestable(name))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotRequestable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDisableExtensionANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ const GLchar *name)
+{
+ if (!context->getExtensions().requestExtensionANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!context->isExtensionDisablable(name))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotDisablable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateActiveTexture(const Context *context, angle::EntryPoint entryPoint, GLenum texture)
+{
+ if (context->getClientMajorVersion() < 2)
+ {
+ return ValidateMultitextureUnit(context, entryPoint, texture);
+ }
+
+ if (texture < GL_TEXTURE0 ||
+ texture >
+ GL_TEXTURE0 + static_cast<GLuint>(context->getCaps().maxCombinedTextureImageUnits) - 1)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidCombinedImageUnit);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateAttachShader(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ ShaderProgramID shader)
+{
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, entryPoint, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ if (programObject->getAttachedShader(shaderObject->getType()))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kShaderAttachmentHasShader);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindAttribLocation(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLuint index,
+ const GLchar *name)
+{
+ if (index >= static_cast<GLuint>(context->getCaps().maxVertexAttributes))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ if (strncmp(name, "gl_", 3) == 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kNameBeginsWithGL);
+ return false;
+ }
+
+ if (context->isWebGL())
+ {
+ const size_t length = strlen(name);
+
+ if (!IsValidESSLString(name, length))
+ {
+ // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters
+ // for shader-related entry points
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidNameCharacters);
+ return false;
+ }
+
+ if (!ValidateWebGLNameLength(context, entryPoint, length) ||
+ !ValidateWebGLNamePrefix(context, entryPoint, name))
+ {
+ return false;
+ }
+ }
+
+ return GetValidProgram(context, entryPoint, program) != nullptr;
+}
+
+bool ValidateBindFramebuffer(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ FramebufferID framebuffer)
+{
+ return ValidateBindFramebufferBase(context, entryPoint, target, framebuffer);
+}
+
+bool ValidateBindRenderbuffer(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ RenderbufferID renderbuffer)
+{
+ return ValidateBindRenderbufferBase(context, entryPoint, target, renderbuffer);
+}
+
+static bool ValidBlendEquationMode(const Context *context, GLenum mode)
+{
+ switch (mode)
+ {
+ case GL_FUNC_ADD:
+ case GL_FUNC_SUBTRACT:
+ case GL_FUNC_REVERSE_SUBTRACT:
+ return true;
+
+ case GL_MIN:
+ case GL_MAX:
+ return context->getClientVersion() >= ES_3_0 || context->getExtensions().blendMinmaxEXT;
+
+ default:
+ return false;
+ }
+}
+
+static bool ValidAdvancedBlendEquationMode(const Context *context, GLenum mode)
+{
+ switch (mode)
+ {
+ case GL_MULTIPLY_KHR:
+ case GL_SCREEN_KHR:
+ case GL_OVERLAY_KHR:
+ case GL_DARKEN_KHR:
+ case GL_LIGHTEN_KHR:
+ case GL_COLORDODGE_KHR:
+ case GL_COLORBURN_KHR:
+ case GL_HARDLIGHT_KHR:
+ case GL_SOFTLIGHT_KHR:
+ case GL_DIFFERENCE_KHR:
+ case GL_EXCLUSION_KHR:
+ case GL_HSL_HUE_KHR:
+ case GL_HSL_SATURATION_KHR:
+ case GL_HSL_COLOR_KHR:
+ case GL_HSL_LUMINOSITY_KHR:
+ return context->getClientVersion() >= ES_3_2 ||
+ context->getExtensions().blendEquationAdvancedKHR;
+
+ default:
+ return false;
+ }
+}
+
+bool ValidateBlendColor(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLfloat red,
+ GLfloat green,
+ GLfloat blue,
+ GLfloat alpha)
+{
+ return true;
+}
+
+bool ValidateBlendEquation(const Context *context, angle::EntryPoint entryPoint, GLenum mode)
+{
+ if (!ValidBlendEquationMode(context, mode) && !ValidAdvancedBlendEquationMode(context, mode))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBlendEquation);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBlendEquationSeparate(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum modeRGB,
+ GLenum modeAlpha)
+{
+ if (!ValidBlendEquationMode(context, modeRGB))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBlendEquation);
+ return false;
+ }
+
+ if (!ValidBlendEquationMode(context, modeAlpha))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBlendEquation);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBlendFunc(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum sfactor,
+ GLenum dfactor)
+{
+ return ValidateBlendFuncSeparate(context, entryPoint, sfactor, dfactor, sfactor, dfactor);
+}
+
+bool ValidateBlendFuncSeparate(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum srcRGB,
+ GLenum dstRGB,
+ GLenum srcAlpha,
+ GLenum dstAlpha)
+{
+ if (!ValidSrcBlendFunc(context, srcRGB))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidDstBlendFunc(context, dstRGB))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidSrcBlendFunc(context, srcAlpha))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidDstBlendFunc(context, dstAlpha))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidBlendFunction);
+ return false;
+ }
+
+ if (context->getLimitations().noSimultaneousConstantColorAndAlphaBlendFunc ||
+ context->isWebGL())
+ {
+ bool constantColorUsed =
+ (srcRGB == GL_CONSTANT_COLOR || srcRGB == GL_ONE_MINUS_CONSTANT_COLOR ||
+ dstRGB == GL_CONSTANT_COLOR || dstRGB == GL_ONE_MINUS_CONSTANT_COLOR);
+
+ bool constantAlphaUsed =
+ (srcRGB == GL_CONSTANT_ALPHA || srcRGB == GL_ONE_MINUS_CONSTANT_ALPHA ||
+ dstRGB == GL_CONSTANT_ALPHA || dstRGB == GL_ONE_MINUS_CONSTANT_ALPHA);
+
+ if (constantColorUsed && constantAlphaUsed)
+ {
+ if (context->isWebGL())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidConstantColor);
+ return false;
+ }
+
+ WARN() << kConstantColorAlphaLimitation;
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kConstantColorAlphaLimitation);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetString(const Context *context, angle::EntryPoint entryPoint, GLenum name)
+{
+ switch (name)
+ {
+ case GL_VENDOR:
+ case GL_RENDERER:
+ case GL_VERSION:
+ case GL_SHADING_LANGUAGE_VERSION:
+ case GL_EXTENSIONS:
+ break;
+
+ case GL_REQUESTABLE_EXTENSIONS_ANGLE:
+ if (!context->getExtensions().requestExtensionANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+ break;
+
+ case GL_SERIALIZED_CONTEXT_STRING_ANGLE:
+ if (!context->getExtensions().getSerializedContextStringANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateLineWidth(const Context *context, angle::EntryPoint entryPoint, GLfloat width)
+{
+ if (width <= 0.0f || isNaN(width))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidWidth);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDepthRangef(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLfloat zNear,
+ GLfloat zFar)
+{
+ if (context->isWebGL() && zNear > zFar)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidDepthRange);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRenderbufferStorage(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ return ValidateRenderbufferStorageParametersBase(context, entryPoint, target, 0, internalformat,
+ width, height);
+}
+
+bool ValidateRenderbufferStorageMultisampleANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (!context->getExtensions().framebufferMultisampleANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ // ANGLE_framebuffer_multisample states that the value of samples must be less than or equal
+ // to MAX_SAMPLES_ANGLE (Context::getCaps().maxSamples) otherwise GL_INVALID_VALUE is
+ // generated.
+ if (samples > context->getCaps().maxSamples)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kSamplesOutOfRange);
+ return false;
+ }
+
+ // ANGLE_framebuffer_multisample states GL_OUT_OF_MEMORY is generated on a failure to create
+ // the specified storage. This is different than ES 3.0 in which a sample number higher
+ // than the maximum sample number supported by this format generates a GL_INVALID_VALUE.
+ // The TextureCaps::getMaxSamples method is only guarenteed to be valid when the context is ES3.
+ if (context->getClientMajorVersion() >= 3)
+ {
+ const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
+ if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
+ {
+ context->validationError(entryPoint, GL_OUT_OF_MEMORY, kSamplesOutOfRange);
+ return false;
+ }
+ }
+
+ return ValidateRenderbufferStorageParametersBase(context, entryPoint, target, samples,
+ internalformat, width, height);
+}
+
+bool ValidateCheckFramebufferStatus(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target)
+{
+ if (!ValidFramebufferTarget(context, target))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFramebufferTarget);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateClearColor(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLfloat red,
+ GLfloat green,
+ GLfloat blue,
+ GLfloat alpha)
+{
+ return true;
+}
+
+bool ValidateClearDepthf(const Context *context, angle::EntryPoint entryPoint, GLfloat depth)
+{
+ return true;
+}
+
+bool ValidateClearStencil(const Context *context, angle::EntryPoint entryPoint, GLint s)
+{
+ return true;
+}
+
+bool ValidateColorMask(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLboolean red,
+ GLboolean green,
+ GLboolean blue,
+ GLboolean alpha)
+{
+ return true;
+}
+
+bool ValidateCompileShader(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID shader)
+{
+ return true;
+}
+
+bool ValidateCreateProgram(const Context *context, angle::EntryPoint entryPoint)
+{
+ return true;
+}
+
+bool ValidateCullFace(const Context *context, angle::EntryPoint entryPoint, CullFaceMode mode)
+{
+ switch (mode)
+ {
+ case CullFaceMode::Front:
+ case CullFaceMode::Back:
+ case CullFaceMode::FrontAndBack:
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidCullMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteProgram(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program)
+{
+ if (program.value == 0)
+ {
+ return false;
+ }
+
+ if (!context->getProgramResolveLink(program))
+ {
+ if (context->getShader(program))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExpectedProgramName);
+ return false;
+ }
+ else
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidProgramName);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDeleteShader(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID shader)
+{
+ if (shader.value == 0)
+ {
+ return false;
+ }
+
+ if (!context->getShader(shader))
+ {
+ if (context->getProgramResolveLink(shader))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidShaderName);
+ return false;
+ }
+ else
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kExpectedShaderName);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDepthFunc(const Context *context, angle::EntryPoint entryPoint, GLenum func)
+{
+ switch (func)
+ {
+ case GL_NEVER:
+ case GL_ALWAYS:
+ case GL_LESS:
+ case GL_LEQUAL:
+ case GL_EQUAL:
+ case GL_GREATER:
+ case GL_GEQUAL:
+ case GL_NOTEQUAL:
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, func);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDepthMask(const Context *context, angle::EntryPoint entryPoint, GLboolean flag)
+{
+ return true;
+}
+
+bool ValidateDetachShader(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ ShaderProgramID shader)
+{
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, entryPoint, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ const Shader *attachedShader = programObject->getAttachedShader(shaderObject->getType());
+ if (attachedShader != shaderObject)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kShaderToDetachMustBeAttached);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDisableVertexAttribArray(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index)
+{
+ if (index >= static_cast<GLuint>(context->getCaps().maxVertexAttributes))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateEnableVertexAttribArray(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index)
+{
+ if (index >= static_cast<GLuint>(context->getCaps().maxVertexAttributes))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFinish(const Context *context, angle::EntryPoint entryPoint)
+{
+ return true;
+}
+
+bool ValidateFlush(const Context *context, angle::EntryPoint entryPoint)
+{
+ return true;
+}
+
+bool ValidateFrontFace(const Context *context, angle::EntryPoint entryPoint, GLenum mode)
+{
+ switch (mode)
+ {
+ case GL_CW:
+ case GL_CCW:
+ break;
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, mode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveAttrib(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLuint index,
+ GLsizei bufsize,
+ const GLsizei *length,
+ const GLint *size,
+ const GLenum *type,
+ const GLchar *name)
+{
+ 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->getActiveAttributeCount()))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxActiveUniform);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniform(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLuint index,
+ GLsizei bufsize,
+ const GLsizei *length,
+ const GLint *size,
+ const GLenum *type,
+ const GLchar *name)
+{
+ 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->getActiveUniformCount()))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxActiveUniform);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetAttachedShaders(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLsizei maxcount,
+ const GLsizei *count,
+ const ShaderProgramID *shaders)
+{
+ if (maxcount < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeMaxCount);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetAttribLocation(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ const GLchar *name)
+{
+ if (strncmp(name, "gl_", 3) == 0)
+ {
+ return false;
+ }
+
+ if (context->isWebGL())
+ {
+ const size_t length = strlen(name);
+
+ if (!IsValidESSLString(name, length))
+ {
+ // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters
+ // for shader-related entry points
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidNameCharacters);
+ return false;
+ }
+
+ if (!ValidateWebGLNameLength(context, entryPoint, length) ||
+ strncmp(name, "webgl_", 6) == 0 || strncmp(name, "_webgl_", 7) == 0)
+ {
+ return false;
+ }
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+
+ if (!programObject)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotBound);
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBooleanv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ const GLboolean *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, entryPoint, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetError(const Context *context, angle::EntryPoint entryPoint)
+{
+ return true;
+}
+
+bool ValidateGetFloatv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ const GLfloat *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, entryPoint, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetIntegerv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ const GLint *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, entryPoint, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetProgramInfoLog(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLsizei bufsize,
+ const GLsizei *length,
+ const GLchar *infolog)
+{
+ if (bufsize < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderInfoLog(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID shader,
+ GLsizei bufsize,
+ const GLsizei *length,
+ const GLchar *infolog)
+{
+ if (bufsize < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, entryPoint, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderPrecisionFormat(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum shadertype,
+ GLenum precisiontype,
+ const GLint *range,
+ const GLint *precision)
+{
+ switch (shadertype)
+ {
+ case GL_VERTEX_SHADER:
+ case GL_FRAGMENT_SHADER:
+ break;
+ case GL_COMPUTE_SHADER:
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kUnimplementedComputeShaderPrecision);
+ return false;
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderType);
+ return false;
+ }
+
+ switch (precisiontype)
+ {
+ case GL_LOW_FLOAT:
+ case GL_MEDIUM_FLOAT:
+ case GL_HIGH_FLOAT:
+ case GL_LOW_INT:
+ case GL_MEDIUM_INT:
+ case GL_HIGH_INT:
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPrecision);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderSource(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID shader,
+ GLsizei bufsize,
+ const GLsizei *length,
+ const GLchar *source)
+{
+ if (bufsize < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, entryPoint, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformLocation(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ const GLchar *name)
+{
+ if (strstr(name, "gl_") == name)
+ {
+ return false;
+ }
+
+ // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+ // shader-related entry points
+ if (context->isWebGL() && !IsValidESSLString(name, strlen(name)))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidNameCharacters);
+ 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 ValidateHint(const Context *context, angle::EntryPoint entryPoint, GLenum target, GLenum mode)
+{
+ switch (mode)
+ {
+ case GL_FASTEST:
+ case GL_NICEST:
+ case GL_DONT_CARE:
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, mode);
+ return false;
+ }
+
+ switch (target)
+ {
+ case GL_GENERATE_MIPMAP_HINT:
+ break;
+
+ case GL_TEXTURE_FILTERING_HINT_CHROMIUM:
+ if (!context->getExtensions().textureFilteringHintCHROMIUM)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, target);
+ return false;
+ }
+ break;
+
+ case GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
+ if (context->getClientVersion() < ES_3_0 &&
+ !context->getExtensions().standardDerivativesOES)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, target);
+ return false;
+ }
+ break;
+
+ case GL_PERSPECTIVE_CORRECTION_HINT:
+ case GL_POINT_SMOOTH_HINT:
+ case GL_LINE_SMOOTH_HINT:
+ case GL_FOG_HINT:
+ if (context->getClientMajorVersion() >= 2)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, target);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, target);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsBuffer(const Context *context, angle::EntryPoint entryPoint, BufferID buffer)
+{
+ return true;
+}
+
+bool ValidateIsFramebuffer(const Context *context,
+ angle::EntryPoint entryPoint,
+ FramebufferID framebuffer)
+{
+ return true;
+}
+
+bool ValidateIsProgram(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program)
+{
+ return true;
+}
+
+bool ValidateIsRenderbuffer(const Context *context,
+ angle::EntryPoint entryPoint,
+ RenderbufferID renderbuffer)
+{
+ return true;
+}
+
+bool ValidateIsShader(const Context *context, angle::EntryPoint entryPoint, ShaderProgramID shader)
+{
+ return true;
+}
+
+bool ValidateIsTexture(const Context *context, angle::EntryPoint entryPoint, TextureID texture)
+{
+ return true;
+}
+
+bool ValidatePixelStorei(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum pname,
+ GLint param)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ switch (pname)
+ {
+ case GL_UNPACK_IMAGE_HEIGHT:
+ case GL_UNPACK_SKIP_IMAGES:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPname);
+ return false;
+
+ case GL_UNPACK_ROW_LENGTH:
+ case GL_UNPACK_SKIP_ROWS:
+ case GL_UNPACK_SKIP_PIXELS:
+ if (!context->getExtensions().unpackSubimageEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+ break;
+
+ case GL_PACK_ROW_LENGTH:
+ case GL_PACK_SKIP_ROWS:
+ case GL_PACK_SKIP_PIXELS:
+ if (!context->getExtensions().packSubimageNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+ break;
+ }
+ }
+
+ if (param < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeParam);
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_UNPACK_ALIGNMENT:
+ if (param != 1 && param != 2 && param != 4 && param != 8)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidUnpackAlignment);
+ return false;
+ }
+ break;
+
+ case GL_PACK_ALIGNMENT:
+ if (param != 1 && param != 2 && param != 4 && param != 8)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidUnpackAlignment);
+ return false;
+ }
+ break;
+
+ case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
+ if (!context->getExtensions().packReverseRowOrderANGLE)
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, pname);
+ }
+ break;
+
+ case GL_UNPACK_ROW_LENGTH:
+ case GL_UNPACK_IMAGE_HEIGHT:
+ case GL_UNPACK_SKIP_IMAGES:
+ case GL_UNPACK_SKIP_ROWS:
+ case GL_UNPACK_SKIP_PIXELS:
+ case GL_PACK_ROW_LENGTH:
+ case GL_PACK_SKIP_ROWS:
+ case GL_PACK_SKIP_PIXELS:
+ break;
+
+ default:
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, pname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePolygonOffset(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLfloat factor,
+ GLfloat units)
+{
+ return true;
+}
+
+bool ValidateReleaseShaderCompiler(const Context *context, angle::EntryPoint entryPoint)
+{
+ return true;
+}
+
+bool ValidateSampleCoverage(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLfloat value,
+ GLboolean invert)
+{
+ return true;
+}
+
+bool ValidateScissor(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ if (width < 0 || height < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateShaderBinary(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const ShaderProgramID *shaders,
+ GLenum binaryformat,
+ const void *binary,
+ GLsizei length)
+{
+ const std::vector<GLenum> &shaderBinaryFormats = context->getCaps().shaderBinaryFormats;
+ if (std::find(shaderBinaryFormats.begin(), shaderBinaryFormats.end(), binaryformat) ==
+ shaderBinaryFormats.end())
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderBinaryFormat);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateShaderSource(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID shader,
+ GLsizei count,
+ const GLchar *const *string,
+ const GLint *length)
+{
+ if (count < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, entryPoint, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilFunc(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum func,
+ GLint ref,
+ GLuint mask)
+{
+ if (!IsValidStencilFunc(func))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilFuncSeparate(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum face,
+ GLenum func,
+ GLint ref,
+ GLuint mask)
+{
+ if (!IsValidStencilFace(face))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilFunc(func))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilMask(const Context *context, angle::EntryPoint entryPoint, GLuint mask)
+{
+ return true;
+}
+
+bool ValidateStencilMaskSeparate(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum face,
+ GLuint mask)
+{
+ if (!IsValidStencilFace(face))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilOp(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum fail,
+ GLenum zfail,
+ GLenum zpass)
+{
+ if (!IsValidStencilOp(fail))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilOp(zfail))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilOp(zpass))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilOpSeparate(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum face,
+ GLenum fail,
+ GLenum zfail,
+ GLenum zpass)
+{
+ if (!IsValidStencilFace(face))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return ValidateStencilOp(context, entryPoint, fail, zfail, zpass);
+}
+
+bool ValidateUniform1f(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLfloat x)
+{
+ return ValidateUniform(context, entryPoint, GL_FLOAT, location, 1);
+}
+
+bool ValidateUniform1fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLfloat *v)
+{
+ return ValidateUniform(context, entryPoint, GL_FLOAT, location, count);
+}
+
+bool ValidateUniform1i(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLint x)
+{
+ return ValidateUniform1iv(context, entryPoint, location, 1, &x);
+}
+
+bool ValidateUniform2fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLfloat *v)
+{
+ return ValidateUniform(context, entryPoint, GL_FLOAT_VEC2, location, count);
+}
+
+bool ValidateUniform2i(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLint x,
+ GLint y)
+{
+ return ValidateUniform(context, entryPoint, GL_INT_VEC2, location, 1);
+}
+
+bool ValidateUniform2iv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLint *v)
+{
+ return ValidateUniform(context, entryPoint, GL_INT_VEC2, location, count);
+}
+
+bool ValidateUniform3f(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z)
+{
+ return ValidateUniform(context, entryPoint, GL_FLOAT_VEC3, location, 1);
+}
+
+bool ValidateUniform3fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLfloat *v)
+{
+ return ValidateUniform(context, entryPoint, GL_FLOAT_VEC3, location, count);
+}
+
+bool ValidateUniform3i(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLint x,
+ GLint y,
+ GLint z)
+{
+ return ValidateUniform(context, entryPoint, GL_INT_VEC3, location, 1);
+}
+
+bool ValidateUniform3iv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLint *v)
+{
+ return ValidateUniform(context, entryPoint, GL_INT_VEC3, location, count);
+}
+
+bool ValidateUniform4f(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w)
+{
+ return ValidateUniform(context, entryPoint, GL_FLOAT_VEC4, location, 1);
+}
+
+bool ValidateUniform4fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLfloat *v)
+{
+ return ValidateUniform(context, entryPoint, GL_FLOAT_VEC4, location, count);
+}
+
+bool ValidateUniform4i(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLint x,
+ GLint y,
+ GLint z,
+ GLint w)
+{
+ return ValidateUniform(context, entryPoint, GL_INT_VEC4, location, 1);
+}
+
+bool ValidateUniform4iv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ const GLint *v)
+{
+ return ValidateUniform(context, entryPoint, GL_INT_VEC4, location, count);
+}
+
+bool ValidateUniformMatrix2fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, entryPoint, GL_FLOAT_MAT2, location, count, transpose);
+}
+
+bool ValidateUniformMatrix3fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, entryPoint, GL_FLOAT_MAT3, location, count, transpose);
+}
+
+bool ValidateUniformMatrix4fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ UniformLocation location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, entryPoint, GL_FLOAT_MAT4, location, count, transpose);
+}
+
+bool ValidateValidateProgram(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program)
+{
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttrib1f(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLfloat x)
+{
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttrib1fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttrib2f(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLfloat x,
+ GLfloat y)
+{
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttrib2fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttrib3f(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z)
+{
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttrib3fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttrib4f(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w)
+{
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateVertexAttrib4fv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, entryPoint, index);
+}
+
+bool ValidateViewport(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ if (width < 0 || height < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kViewportNegativeSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetFramebufferAttachmentParameteriv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum attachment,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetFramebufferAttachmentParameterivBase(context, entryPoint, target, attachment,
+ pname, nullptr);
+}
+
+bool ValidateGetProgramiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetProgramivBase(context, entryPoint, program, pname, nullptr);
+}
+
+bool ValidateCopyTexImage2D(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2CopyTexImageParameters(context, entryPoint, target, level, internalformat,
+ false, 0, 0, x, y, width, height, border);
+ }
+
+ ASSERT(context->getClientMajorVersion() == 3);
+ return ValidateES3CopyTexImage2DParameters(context, entryPoint, target, level, internalformat,
+ false, 0, 0, 0, x, y, width, height, border);
+}
+
+bool ValidateCopyTexSubImage2D(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2CopyTexImageParameters(context, entryPoint, target, level, GL_NONE, true,
+ xoffset, yoffset, x, y, width, height, 0);
+ }
+
+ return ValidateES3CopyTexImage2DParameters(context, entryPoint, target, level, GL_NONE, true,
+ xoffset, yoffset, 0, x, y, width, height, 0);
+}
+
+bool ValidateCopyTexSubImage3DOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ return ValidateCopyTexSubImage3D(context, entryPoint, target, level, xoffset, yoffset, zoffset,
+ x, y, width, height);
+}
+
+bool ValidateDeleteBuffers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint n,
+ const BufferID *buffers)
+{
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateDeleteFramebuffers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint n,
+ const FramebufferID *framebuffers)
+{
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateDeleteRenderbuffers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint n,
+ const RenderbufferID *renderbuffers)
+{
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateDeleteTextures(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint n,
+ const TextureID *textures)
+{
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateDisable(const Context *context, angle::EntryPoint entryPoint, GLenum cap)
+{
+ if (!ValidCap(context, cap, false))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, cap);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateEnable(const Context *context, angle::EntryPoint entryPoint, GLenum cap)
+{
+ if (!ValidCap(context, cap, false))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, cap);
+ return false;
+ }
+
+ if (context->getLimitations().noSampleAlphaToCoverageSupport &&
+ cap == GL_SAMPLE_ALPHA_TO_COVERAGE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kNoSampleAlphaToCoveragesLimitation);
+
+ // We also output an error message to the debugger window if tracing is active, so that
+ // developers can see the error message.
+ ERR() << kNoSampleAlphaToCoveragesLimitation;
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferRenderbuffer(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ RenderbufferID renderbuffer)
+{
+ return ValidateFramebufferRenderbufferBase(context, entryPoint, target, attachment,
+ renderbuffertarget, renderbuffer);
+}
+
+bool ValidateFramebufferTexture2D(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum attachment,
+ TextureTarget textarget,
+ TextureID texture,
+ GLint level)
+{
+ // Attachments are required to be bound to level 0 without ES3 or the GL_OES_fbo_render_mipmap
+ // extension
+ if (context->getClientMajorVersion() < 3 && !context->getExtensions().fboRenderMipmapOES &&
+ level != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidFramebufferTextureLevel);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ if (texture.value != 0)
+ {
+ Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ const Caps &caps = context->getCaps();
+
+ switch (textarget)
+ {
+ case TextureTarget::_2D:
+ {
+ if (level > log2(caps.max2DTextureSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ if (tex->getType() != TextureType::_2D)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kInvalidTextureTarget);
+ return false;
+ }
+ }
+ break;
+
+ case TextureTarget::Rectangle:
+ {
+ if (level != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ if (tex->getType() != TextureType::Rectangle)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kTextureTargetMismatch);
+ return false;
+ }
+ }
+ break;
+
+ case TextureTarget::CubeMapNegativeX:
+ case TextureTarget::CubeMapNegativeY:
+ case TextureTarget::CubeMapNegativeZ:
+ case TextureTarget::CubeMapPositiveX:
+ case TextureTarget::CubeMapPositiveY:
+ case TextureTarget::CubeMapPositiveZ:
+ {
+ if (level > log2(caps.maxCubeMapTextureSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ if (tex->getType() != TextureType::CubeMap)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kTextureTargetMismatch);
+ return false;
+ }
+ }
+ break;
+
+ case TextureTarget::_2DMultisample:
+ {
+ if (context->getClientVersion() < ES_3_1 &&
+ !context->getExtensions().textureMultisampleANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ if (level != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kLevelNotZero);
+ return false;
+ }
+ if (tex->getType() != TextureType::_2DMultisample)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kTextureTargetMismatch);
+ return false;
+ }
+ }
+ break;
+
+ case TextureTarget::External:
+ {
+ if (!context->getExtensions().YUVTargetEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kYUVTargetExtensionRequired);
+ return false;
+ }
+
+ if (attachment != GL_COLOR_ATTACHMENT0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidAttachment);
+ return false;
+ }
+
+ if (tex->getType() != TextureType::External)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kTextureTargetMismatch);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferTexture3DOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum attachment,
+ TextureTarget textargetPacked,
+ TextureID texture,
+ GLint level,
+ GLint zoffset)
+{
+ // We don't call into a base ValidateFramebufferTexture3D here because
+ // it doesn't exist for OpenGL ES. This function is replaced by
+ // FramebufferTextureLayer in ES 3.x, which has broader support.
+ if (!context->getExtensions().texture3DOES)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ // Attachments are required to be bound to level 0 without ES3 or the
+ // GL_OES_fbo_render_mipmap extension
+ if (context->getClientMajorVersion() < 3 && !context->getExtensions().fboRenderMipmapOES &&
+ level != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidFramebufferTextureLevel);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ if (texture.value != 0)
+ {
+ Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ const Caps &caps = context->getCaps();
+
+ switch (textargetPacked)
+ {
+ case TextureTarget::_3D:
+ {
+ if (level > log2(caps.max3DTextureSize))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ if (zoffset >= caps.max3DTextureSize)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidZOffset);
+ return false;
+ }
+ if (tex->getType() != TextureType::_3D)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidTextureType);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidTextureTarget);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGenBuffers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint n,
+ const BufferID *buffers)
+{
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateGenFramebuffers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint n,
+ const FramebufferID *framebuffers)
+{
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateGenRenderbuffers(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint n,
+ const RenderbufferID *renderbuffers)
+{
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateGenTextures(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint n,
+ const TextureID *textures)
+{
+ return ValidateGenOrDelete(context, entryPoint, n);
+}
+
+bool ValidateGenerateMipmap(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target)
+{
+ return ValidateGenerateMipmapBase(context, entryPoint, target);
+}
+
+bool ValidateGetBufferParameteriv(const Context *context,
+ angle::EntryPoint entryPoint,
+ BufferBinding target,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetBufferParameterBase(context, entryPoint, target, pname, false, nullptr);
+}
+
+bool ValidateGetRenderbufferParameteriv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetRenderbufferParameterivBase(context, entryPoint, target, pname, nullptr);
+}
+
+bool ValidateGetShaderiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID shader,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetShaderivBase(context, entryPoint, shader, pname, nullptr);
+}
+
+bool ValidateGetTexParameterfv(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ const GLfloat *params)
+{
+ return ValidateGetTexParameterBase(context, entryPoint, target, pname, nullptr);
+}
+
+bool ValidateGetTexParameteriv(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetTexParameterBase(context, entryPoint, target, pname, nullptr);
+}
+
+bool ValidateGetTexParameterIivOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ const GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGetTexParameterBase(context, entryPoint, target, pname, nullptr);
+}
+
+bool ValidateGetTexParameterIuivOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ const GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGetTexParameterBase(context, entryPoint, target, pname, nullptr);
+}
+
+bool ValidateGetUniformfv(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ UniformLocation location,
+ const GLfloat *params)
+{
+ return ValidateGetUniformBase(context, entryPoint, program, location);
+}
+
+bool ValidateGetUniformiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program,
+ UniformLocation location,
+ const GLint *params)
+{
+ return ValidateGetUniformBase(context, entryPoint, program, location);
+}
+
+bool ValidateGetVertexAttribfv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLenum pname,
+ const GLfloat *params)
+{
+ return ValidateGetVertexAttribBase(context, entryPoint, index, pname, nullptr, false, false);
+}
+
+bool ValidateGetVertexAttribiv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateGetVertexAttribBase(context, entryPoint, index, pname, nullptr, false, false);
+}
+
+bool ValidateGetVertexAttribPointerv(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLenum pname,
+ void *const *pointer)
+{
+ return ValidateGetVertexAttribBase(context, entryPoint, index, pname, nullptr, true, false);
+}
+
+bool ValidateIsEnabled(const Context *context, angle::EntryPoint entryPoint, GLenum cap)
+{
+ if (!ValidCap(context, cap, true))
+ {
+ context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, cap);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateLinkProgram(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program)
+{
+ if (context->hasActiveTransformFeedback(program))
+ {
+ // ES 3.0.4 section 2.15 page 91
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kTransformFeedbackActiveDuringLink);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, entryPoint, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateReadPixels(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const void *pixels)
+{
+ return ValidateReadPixelsBase(context, entryPoint, x, y, width, height, format, type, -1,
+ nullptr, nullptr, nullptr, pixels);
+}
+
+bool ValidateTexParameterf(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ GLfloat param)
+{
+ return ValidateTexParameterBase(context, entryPoint, target, pname, -1, false, &param);
+}
+
+bool ValidateTexParameterfv(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ const GLfloat *params)
+{
+ return ValidateTexParameterBase(context, entryPoint, target, pname, -1, true, params);
+}
+
+bool ValidateTexParameteri(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ GLint param)
+{
+ return ValidateTexParameterBase(context, entryPoint, target, pname, -1, false, &param);
+}
+
+bool ValidateTexParameteriv(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ const GLint *params)
+{
+ return ValidateTexParameterBase(context, entryPoint, target, pname, -1, true, params);
+}
+
+bool ValidateTexParameterIivOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ const GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateTexParameterBase(context, entryPoint, target, pname, -1, true, params);
+}
+
+bool ValidateTexParameterIuivOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLenum pname,
+ const GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateTexParameterBase(context, entryPoint, target, pname, -1, true, params);
+}
+
+bool ValidateUseProgram(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID program)
+{
+ if (program.value != 0)
+ {
+ Program *programObject = context->getProgramResolveLink(program);
+ if (!programObject)
+ {
+ // ES 3.1.0 section 7.3 page 72
+ if (context->getShader(program))
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExpectedProgramName);
+ return false;
+ }
+ else
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidProgramName);
+ return false;
+ }
+ }
+ if (!programObject->isLinked())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+ }
+ if (context->getState().isTransformFeedbackActiveUnpaused())
+ {
+ // ES 3.0.4 section 2.15 page 91
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kTransformFeedbackUseProgram);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteFencesNV(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const FenceNVID *fences)
+{
+ if (!context->getExtensions().fenceNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ if (n < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFinishFenceNV(const Context *context, angle::EntryPoint entryPoint, FenceNVID fence)
+{
+ if (!context->getExtensions().fenceNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ FenceNV *fenceObject = context->getFenceNV(fence);
+
+ if (fenceObject == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFence);
+ return false;
+ }
+
+ if (!fenceObject->isSet())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFenceState);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGenFencesNV(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLsizei n,
+ const FenceNVID *fences)
+{
+ if (!context->getExtensions().fenceNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ if (n < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetFenceivNV(const Context *context,
+ angle::EntryPoint entryPoint,
+ FenceNVID fence,
+ GLenum pname,
+ const GLint *params)
+{
+ if (!context->getExtensions().fenceNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ FenceNV *fenceObject = context->getFenceNV(fence);
+
+ if (fenceObject == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFence);
+ return false;
+ }
+
+ if (!fenceObject->isSet())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFenceState);
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_FENCE_STATUS_NV:
+ case GL_FENCE_CONDITION_NV:
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetGraphicsResetStatusEXT(const Context *context, angle::EntryPoint entryPoint)
+{
+ if (!context->getExtensions().robustnessEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetTranslatedShaderSourceANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ ShaderProgramID shader,
+ GLsizei bufsize,
+ const GLsizei *length,
+ const GLchar *source)
+{
+ if (!context->getExtensions().translatedShaderSourceANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (bufsize < 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Shader *shaderObject = context->getShader(shader);
+
+ if (!shaderObject)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidShaderName);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsFenceNV(const Context *context, angle::EntryPoint entryPoint, FenceNVID fence)
+{
+ if (!context->getExtensions().fenceNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateSetFenceNV(const Context *context,
+ angle::EntryPoint entryPoint,
+ FenceNVID fence,
+ GLenum condition)
+{
+ if (!context->getExtensions().fenceNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ if (condition != GL_ALL_COMPLETED_NV)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFenceCondition);
+ return false;
+ }
+
+ FenceNV *fenceObject = context->getFenceNV(fence);
+
+ if (fenceObject == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFence);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTestFenceNV(const Context *context, angle::EntryPoint entryPoint, FenceNVID fence)
+{
+ if (!context->getExtensions().fenceNV)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ FenceNV *fenceObject = context->getFenceNV(fence);
+
+ if (fenceObject == nullptr)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFence);
+ return false;
+ }
+
+ if (fenceObject->isSet() != GL_TRUE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFenceState);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexStorage2DEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType type,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (!context->getExtensions().textureStorageEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexStorageParametersBase(context, entryPoint, type, levels,
+ internalformat, width, height);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexStorage2DParameters(context, entryPoint, type, levels, internalformat,
+ width, height, 1);
+}
+
+bool ValidateVertexAttribDivisorANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLuint divisor)
+{
+ if (!context->getExtensions().instancedArraysANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(context->getCaps().maxVertexAttributes))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ if (context->getLimitations().attributeZeroRequiresZeroDivisorInEXT)
+ {
+ if (index == 0 && divisor != 0)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION,
+ kAttributeZeroRequiresDivisorLimitation);
+
+ // We also output an error message to the debugger window if tracing is active, so
+ // that developers can see the error message.
+ ERR() << kAttributeZeroRequiresDivisorLimitation;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribDivisorEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint index,
+ GLuint divisor)
+{
+ if (!context->getExtensions().instancedArraysEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(context->getCaps().maxVertexAttributes))
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexImage3DOES(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void *pixels)
+{
+ return ValidateTexImage3D(context, entryPoint, target, level, internalformat, width, height,
+ depth, border, format, type, pixels);
+}
+
+bool ValidatePopGroupMarkerEXT(const Context *context, angle::EntryPoint entryPoint)
+{
+ if (!context->getExtensions().debugMarkerEXT)
+ {
+ // The debug marker calls should not set error state
+ // However, it seems reasonable to set an error state if the extension is not enabled
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexStorage1DEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width)
+{
+ UNIMPLEMENTED();
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+}
+
+bool ValidateTexStorage3DEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (!context->getExtensions().textureStorageEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateES3TexStorage3DParameters(context, entryPoint, target, levels, internalformat,
+ width, height, depth);
+}
+
+bool ValidateMaxShaderCompilerThreadsKHR(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLuint count)
+{
+ if (!context->getExtensions().parallelShaderCompileKHR)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateMultiDrawArraysANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ const GLint *firsts,
+ const GLsizei *counts,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDrawANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawArrays(context, entryPoint, mode, firsts[drawID], counts[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateMultiDrawElementsANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ PrimitiveMode mode,
+ const GLsizei *counts,
+ DrawElementsType type,
+ const GLvoid *const *indices,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDrawANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawElements(context, entryPoint, mode, counts[drawID], type, indices[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateProvokingVertexANGLE(const Context *context,
+ angle::EntryPoint entryPoint,
+ ProvokingVertexConvention modePacked)
+{
+ if (!context->getExtensions().provokingVertexANGLE)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ switch (modePacked)
+ {
+ case ProvokingVertexConvention::FirstVertexConvention:
+ case ProvokingVertexConvention::LastVertexConvention:
+ break;
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidProvokingVertex);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferTexture2DMultisampleEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLenum attachment,
+ TextureTarget textarget,
+ TextureID texture,
+ GLint level,
+ GLsizei samples)
+{
+ if (!context->getExtensions().multisampledRenderToTextureEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (samples < 0)
+ {
+ return false;
+ }
+
+ // EXT_multisampled_render_to_texture states that the value of samples
+ // must be less than or equal to MAX_SAMPLES_EXT (Context::getCaps().maxSamples)
+ // otherwise GL_INVALID_VALUE is generated.
+ if (samples > context->getCaps().maxSamples)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kSamplesOutOfRange);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ // EXT_multisampled_render_to_texture returns INVALID_OPERATION when a sample number higher than
+ // the maximum sample number supported by this format is passed.
+ // The TextureCaps::getMaxSamples method is only guarenteed to be valid when the context is ES3.
+ if (texture.value != 0 && context->getClientMajorVersion() >= 3)
+ {
+ Texture *tex = context->getTexture(texture);
+ GLenum sizedInternalFormat = tex->getFormat(textarget, level).info->sizedInternalFormat;
+ const TextureCaps &formatCaps = context->getTextureCaps().get(sizedInternalFormat);
+ if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kSamplesOutOfRange);
+ return false;
+ }
+ }
+
+ // Unless EXT_multisampled_render_to_texture2 is enabled, only color attachment 0 can be used.
+ if (!context->getExtensions().multisampledRenderToTexture2EXT &&
+ attachment != GL_COLOR_ATTACHMENT0)
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidAttachment);
+ return false;
+ }
+
+ if (!ValidTexture2DDestinationTarget(context, textarget))
+ {
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRenderbufferStorageMultisampleEXT(const Context *context,
+ angle::EntryPoint entryPoint,
+ GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (!context->getExtensions().multisampledRenderToTextureEXT)
+ {
+ context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (!ValidateRenderbufferStorageParametersBase(context, entryPoint, target, samples,
+ internalformat, width, height))
+ {
+ return false;
+ }
+
+ // EXT_multisampled_render_to_texture states that the value of samples
+ // must be less than or equal to MAX_SAMPLES_EXT (Context::getCaps().maxSamples)
+ // otherwise GL_INVALID_VALUE is generated.
+ if (samples > context->getCaps().maxSamples)
+ {
+ context->validationError(entryPoint, GL_INVALID_VALUE, kSamplesOutOfRange);
+ return false;
+ }
+
+ // EXT_multisampled_render_to_texture returns GL_OUT_OF_MEMORY on failure to create
+ // the specified storage. This is different than ES 3.0 in which a sample number higher
+ // than the maximum sample number supported by this format generates a GL_INVALID_VALUE.
+ // The TextureCaps::getMaxSamples method is only guarenteed to be valid when the context is ES3.
+ if (context->getClientMajorVersion() >= 3)
+ {
+ const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
+ if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
+ {
+ context->validationError(entryPoint, GL_OUT_OF_MEMORY, kSamplesOutOfRange);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void RecordBindTextureTypeError(const Context *context,
+ angle::EntryPoint entryPoint,
+ TextureType target)
+{
+ ASSERT(!context->getStateCache().isValidBindTextureType(target));
+
+ switch (target)
+ {
+ case TextureType::Rectangle:
+ ASSERT(!context->getExtensions().textureRectangleANGLE);
+ context->validationError(entryPoint, GL_INVALID_ENUM, kTextureRectangleNotSupported);
+ break;
+
+ case TextureType::_3D:
+ case TextureType::_2DArray:
+ ASSERT(context->getClientMajorVersion() < 3);
+ context->validationError(entryPoint, GL_INVALID_ENUM, kES3Required);
+ break;
+
+ case TextureType::_2DMultisample:
+ ASSERT(context->getClientVersion() < Version(3, 1) &&
+ !context->getExtensions().textureMultisampleANGLE);
+ context->validationError(entryPoint, GL_INVALID_ENUM,
+ kMultisampleTextureExtensionOrES31Required);
+ break;
+
+ case TextureType::_2DMultisampleArray:
+ ASSERT(!context->getExtensions().textureStorageMultisample2dArrayOES);
+ context->validationError(entryPoint, GL_INVALID_ENUM,
+ kMultisampleArrayExtensionRequired);
+ break;
+
+ case TextureType::External:
+ ASSERT(!context->getExtensions().EGLImageExternalOES &&
+ !context->getExtensions().EGLStreamConsumerExternalNV);
+ context->validationError(entryPoint, GL_INVALID_ENUM, kExternalTextureNotSupported);
+ break;
+
+ case TextureType::VideoImage:
+ ASSERT(!context->getExtensions().videoTextureWEBGL);
+ context->validationError(entryPoint, GL_INVALID_ENUM, kExtensionNotEnabled);
+ break;
+
+ case TextureType::Buffer:
+ ASSERT(!context->getExtensions().textureBufferOES &&
+ !context->getExtensions().textureBufferEXT);
+ context->validationError(entryPoint, GL_INVALID_ENUM, kExtensionNotEnabled);
+ break;
+
+ default:
+ context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
+ }
+}
+
+} // namespace gl