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.cpp7187
1 files changed, 7187 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..df0aab4b09
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/validationES2.cpp
@@ -0,0 +1,7187 @@
+//
+// Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// 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/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(gl::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;
+}
+
+template <typename T>
+bool ValidatePathInstances(gl::Context *context,
+ GLsizei numPaths,
+ const void *paths,
+ GLuint pathBase)
+{
+ const auto *array = static_cast<const T *>(paths);
+
+ for (GLsizei i = 0; i < numPaths; ++i)
+ {
+ const GLuint pathName = array[i] + pathBase;
+ if (context->isPathGenerated(pathName) && !context->isPath(pathName))
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoSuchPath);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateInstancedPathParameters(gl::Context *context,
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const void *paths,
+ GLuint pathBase,
+ GLenum transformType,
+ const GLfloat *transformValues)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (paths == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathNameArray);
+ return false;
+ }
+
+ if (numPaths < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathNumPaths);
+ return false;
+ }
+
+ if (!angle::IsValueInRangeForNumericType<std::uint32_t>(numPaths))
+ {
+ context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ std::uint32_t pathNameTypeSize = 0;
+ std::uint32_t componentCount = 0;
+
+ switch (pathNameType)
+ {
+ case GL_UNSIGNED_BYTE:
+ pathNameTypeSize = sizeof(GLubyte);
+ if (!ValidatePathInstances<GLubyte>(context, numPaths, paths, pathBase))
+ return false;
+ break;
+
+ case GL_BYTE:
+ pathNameTypeSize = sizeof(GLbyte);
+ if (!ValidatePathInstances<GLbyte>(context, numPaths, paths, pathBase))
+ return false;
+ break;
+
+ case GL_UNSIGNED_SHORT:
+ pathNameTypeSize = sizeof(GLushort);
+ if (!ValidatePathInstances<GLushort>(context, numPaths, paths, pathBase))
+ return false;
+ break;
+
+ case GL_SHORT:
+ pathNameTypeSize = sizeof(GLshort);
+ if (!ValidatePathInstances<GLshort>(context, numPaths, paths, pathBase))
+ return false;
+ break;
+
+ case GL_UNSIGNED_INT:
+ pathNameTypeSize = sizeof(GLuint);
+ if (!ValidatePathInstances<GLuint>(context, numPaths, paths, pathBase))
+ return false;
+ break;
+
+ case GL_INT:
+ pathNameTypeSize = sizeof(GLint);
+ if (!ValidatePathInstances<GLint>(context, numPaths, paths, pathBase))
+ return false;
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPathNameType);
+ return false;
+ }
+
+ switch (transformType)
+ {
+ case GL_NONE:
+ componentCount = 0;
+ break;
+ case GL_TRANSLATE_X_CHROMIUM:
+ case GL_TRANSLATE_Y_CHROMIUM:
+ componentCount = 1;
+ break;
+ case GL_TRANSLATE_2D_CHROMIUM:
+ componentCount = 2;
+ break;
+ case GL_TRANSLATE_3D_CHROMIUM:
+ componentCount = 3;
+ break;
+ case GL_AFFINE_2D_CHROMIUM:
+ case GL_TRANSPOSE_AFFINE_2D_CHROMIUM:
+ componentCount = 6;
+ break;
+ case GL_AFFINE_3D_CHROMIUM:
+ case GL_TRANSPOSE_AFFINE_3D_CHROMIUM:
+ componentCount = 12;
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidTransformation);
+ return false;
+ }
+ if (componentCount != 0 && transformValues == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kNoTransformArray);
+ return false;
+ }
+
+ angle::CheckedNumeric<std::uint32_t> checkedSize(0);
+ checkedSize += (numPaths * pathNameTypeSize);
+ checkedSize += (numPaths * sizeof(GLfloat) * componentCount);
+ if (!checkedSize.IsValid())
+ {
+ context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ return true;
+}
+
+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_RGB:
+ case GL_RGBA:
+ case GL_RGB8:
+ case GL_RGBA8:
+ case GL_BGRA_EXT:
+ case GL_BGRA8_EXT:
+ case GL_SRGB_EXT:
+ case GL_SRGB_ALPHA_EXT:
+ case GL_R8:
+ case GL_R8UI:
+ case GL_RG8:
+ case GL_RG8UI:
+ case GL_SRGB8:
+ case GL_RGB565:
+ case GL_RGB8UI:
+ case GL_RGB10_A2:
+ case GL_SRGB8_ALPHA8:
+ case GL_RGB5_A1:
+ case GL_RGBA4:
+ case GL_RGBA8UI:
+ case GL_RGB9_E5:
+ case GL_R16F:
+ case GL_R32F:
+ case GL_RG16F:
+ case GL_RG32F:
+ case GL_RGB16F:
+ case GL_RGB32F:
+ case GL_RGBA16F:
+ case GL_RGBA32F:
+ case GL_R11F_G11F_B10F:
+ case GL_LUMINANCE:
+ case GL_LUMINANCE_ALPHA:
+ case GL_ALPHA:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopySubTextureDestionationInternalFormat(GLenum internalFormat)
+{
+ return IsValidCopyTextureDestinationInternalFormatEnum(internalFormat);
+}
+
+bool IsValidCopyTextureDestinationFormatType(Context *context, GLint internalFormat, GLenum type)
+{
+ if (!IsValidCopyTextureDestinationInternalFormatEnum(internalFormat))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+
+ if (!ValidES3FormatCombination(GetUnsizedFormat(internalFormat), type, internalFormat))
+ {
+ context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+
+ const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat, type);
+ if (!internalFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+
+ return true;
+}
+
+bool IsValidCopyTextureDestinationTargetEnum(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().textureRectangle;
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopyTextureDestinationTarget(Context *context,
+ TextureType textureType,
+ TextureTarget target)
+{
+ return TextureTargetToType(target) == textureType;
+}
+
+bool IsValidCopyTextureSourceTarget(Context *context, TextureType type)
+{
+ switch (type)
+ {
+ case TextureType::_2D:
+ return true;
+ case TextureType::Rectangle:
+ return context->getExtensions().textureRectangle;
+ case TextureType::External:
+ return context->getExtensions().eglImageExternal;
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopyTextureSourceLevel(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(Context *context,
+ TextureType type,
+ GLint level,
+ GLsizei width,
+ GLsizei height,
+ bool isSubImage)
+{
+ if (!ValidMipLevel(context, type, level))
+ {
+ return false;
+ }
+
+ if (!ValidImageSizeParameters(context, type, level, width, height, 1, isSubImage))
+ {
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ switch (type)
+ {
+ case TextureType::_2D:
+ return static_cast<GLuint>(width) <= (caps.max2DTextureSize >> level) &&
+ static_cast<GLuint>(height) <= (caps.max2DTextureSize >> level);
+ case TextureType::Rectangle:
+ ASSERT(level == 0);
+ return static_cast<GLuint>(width) <= (caps.max2DTextureSize >> level) &&
+ static_cast<GLuint>(height) <= (caps.max2DTextureSize >> level);
+
+ case TextureType::CubeMap:
+ return static_cast<GLuint>(width) <= (caps.maxCubeMapTextureSize >> level) &&
+ static_cast<GLuint>(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;
+ }
+}
+
+bool ValidateES2CopyTexImageParameters(Context *context,
+ 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(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ TextureType texType = TextureTargetToType(target);
+ if (!ValidImageSizeParameters(context, texType, level, width, height, 1, isSubImage))
+ {
+ // Error is already handled.
+ return false;
+ }
+
+ Format textureFormat = Format::Invalid();
+ if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
+ xoffset, yoffset, 0, x, y, width, height, border,
+ &textureFormat))
+ {
+ return false;
+ }
+
+ const gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer();
+ GLenum colorbufferFormat =
+ framebuffer->getReadColorAttachment()->getFormat().info->sizedInternalFormat;
+ const auto &formatInfo = *textureFormat.info;
+
+ // [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)
+ {
+ context->validationError(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)
+ {
+ context->validationError(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)
+ {
+ context->validationError(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)
+ {
+ context->validationError(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)
+ {
+ context->validationError(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)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ case GL_ETC1_RGB8_OES:
+ case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
+ case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_RGBA_BPTC_UNORM_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:
+ case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT:
+ case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT:
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_STENCIL_OES:
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ default:
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+
+ if (formatInfo.type == GL_FLOAT && !context->getExtensions().textureFloat)
+ {
+ context->validationError(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)
+ {
+ context->validationError(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_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX)
+ {
+ context->validationError(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_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES &&
+ colorbufferFormat != GL_BGR5_A1_ANGLEX)
+ {
+ context->validationError(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)
+ {
+ context->validationError(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)
+ {
+ context->validationError(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)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ if (context->getExtensions().textureCompressionDXT1)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ if (context->getExtensions().textureCompressionDXT3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ if (context->getExtensions().textureCompressionDXT5)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_OES:
+ if (context->getExtensions().compressedETC1RGB8Texture)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
+ case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
+ if (context->getExtensions().lossyETCDecode)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT32_OES:
+ if (context->getExtensions().depthTextureAny())
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_DEPTH_STENCIL_OES:
+ case GL_DEPTH24_STENCIL8_OES:
+ if (context->getExtensions().depthTextureAny() ||
+ context->getExtensions().packedDepthStencil)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ 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().multisampleCompatibility;
+
+ 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().debug;
+
+ case GL_BIND_GENERATES_RESOURCE_CHROMIUM:
+ return queryOnly && context->getExtensions().bindGeneratesResource;
+
+ case GL_CLIENT_ARRAYS_ANGLE:
+ return queryOnly && context->getExtensions().clientArrays;
+
+ case GL_FRAMEBUFFER_SRGB_EXT:
+ return context->getExtensions().sRGBWriteControl;
+
+ case GL_SAMPLE_MASK:
+ return context->getClientVersion() >= Version(3, 1);
+
+ case GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
+ return queryOnly && context->getExtensions().robustResourceInitialization;
+
+ // GLES1 emulation: GLES1-specific caps
+ 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:
+ case GL_COLOR_LOGIC_OP:
+ return context->getClientVersion() < Version(2, 0);
+ case GL_POINT_SIZE_ARRAY_OES:
+ return context->getClientVersion() < Version(2, 0) &&
+ context->getExtensions().pointSizeArray;
+ case GL_TEXTURE_CUBE_MAP:
+ return context->getClientVersion() < Version(2, 0) &&
+ context->getExtensions().textureCubeMap;
+ case GL_POINT_SPRITE_OES:
+ return context->getClientVersion() < Version(2, 0) &&
+ context->getExtensions().pointSprite;
+ 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 IsValidESSLShaderSourceString(const char *str, size_t len, bool lineContinuationAllowed)
+{
+ enum class ParseState
+ {
+ // Have not seen an ASCII non-whitespace character yet on
+ // this line. Possible that we might see a preprocessor
+ // directive.
+ BEGINING_OF_LINE,
+
+ // Have seen at least one ASCII non-whitespace character
+ // on this line.
+ MIDDLE_OF_LINE,
+
+ // Handling a preprocessor directive. Passes through all
+ // characters up to the end of the line. Disables comment
+ // processing.
+ IN_PREPROCESSOR_DIRECTIVE,
+
+ // Handling a single-line comment. The comment text is
+ // replaced with a single space.
+ IN_SINGLE_LINE_COMMENT,
+
+ // Handling a multi-line comment. Newlines are passed
+ // through to preserve line numbers.
+ IN_MULTI_LINE_COMMENT
+ };
+
+ ParseState state = ParseState::BEGINING_OF_LINE;
+ size_t pos = 0;
+
+ while (pos < len)
+ {
+ char c = str[pos];
+ char next = pos + 1 < len ? str[pos + 1] : 0;
+
+ // Check for newlines
+ if (c == '\n' || c == '\r')
+ {
+ if (state != ParseState::IN_MULTI_LINE_COMMENT)
+ {
+ state = ParseState::BEGINING_OF_LINE;
+ }
+
+ pos++;
+ continue;
+ }
+
+ switch (state)
+ {
+ case ParseState::BEGINING_OF_LINE:
+ if (c == ' ')
+ {
+ // Maintain the BEGINING_OF_LINE state until a non-space is seen
+ pos++;
+ }
+ else if (c == '#')
+ {
+ state = ParseState::IN_PREPROCESSOR_DIRECTIVE;
+ pos++;
+ }
+ else
+ {
+ // Don't advance, re-process this character with the MIDDLE_OF_LINE state
+ state = ParseState::MIDDLE_OF_LINE;
+ }
+ break;
+
+ case ParseState::MIDDLE_OF_LINE:
+ if (c == '/' && next == '/')
+ {
+ state = ParseState::IN_SINGLE_LINE_COMMENT;
+ pos++;
+ }
+ else if (c == '/' && next == '*')
+ {
+ state = ParseState::IN_MULTI_LINE_COMMENT;
+ pos++;
+ }
+ else if (lineContinuationAllowed && c == '\\' && (next == '\n' || next == '\r'))
+ {
+ // Skip line continuation characters
+ }
+ else if (!IsValidESSLCharacter(c))
+ {
+ return false;
+ }
+ pos++;
+ break;
+
+ case ParseState::IN_PREPROCESSOR_DIRECTIVE:
+ // Line-continuation characters may not be permitted.
+ // Otherwise, just pass it through. Do not parse comments in this state.
+ if (!lineContinuationAllowed && c == '\\')
+ {
+ return false;
+ }
+ pos++;
+ break;
+
+ case ParseState::IN_SINGLE_LINE_COMMENT:
+ // Line-continuation characters are processed before comment processing.
+ // Advance string if a new line character is immediately behind
+ // line-continuation character.
+ if (c == '\\' && (next == '\n' || next == '\r'))
+ {
+ pos++;
+ }
+ pos++;
+ break;
+
+ case ParseState::IN_MULTI_LINE_COMMENT:
+ if (c == '*' && next == '/')
+ {
+ state = ParseState::MIDDLE_OF_LINE;
+ pos++;
+ }
+ pos++;
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateWebGLNamePrefix(Context *context, 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(GL_INVALID_OPERATION, kWebglBindAttribLocationReservedPrefix);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateWebGLNameLength(Context *context, 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(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(GL_INVALID_VALUE, kWebgl2NameLengthLimitExceeded);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateMatrixMode(Context *context, GLenum matrixMode)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidMatrixMode);
+ return false;
+ }
+ return true;
+}
+
+bool ValidBlendFunc(const Context *context, GLenum val)
+{
+ const gl::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.blendFuncExtended;
+
+ 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 IsValidImageLayout(ImageLayout layout)
+{
+ switch (layout)
+ {
+ case ImageLayout::Undefined:
+ case ImageLayout::General:
+ case ImageLayout::ColorAttachment:
+ case ImageLayout::DepthStencilAttachment:
+ case ImageLayout::DepthStencilReadOnlyAttachment:
+ case ImageLayout::ShaderReadOnly:
+ case ImageLayout::TransferSrc:
+ case ImageLayout::TransferDst:
+ case ImageLayout::DepthReadOnlyStencilAttachment:
+ case ImageLayout::DepthAttachmentStencilReadOnly:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool ValidateES2TexImageParameters(Context *context,
+ 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(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ return ValidateES2TexImageParametersBase(context, target, level, internalformat, isCompressed,
+ isSubImage, xoffset, yoffset, width, height, border,
+ format, type, imageSize, pixels);
+}
+
+} // anonymous namespace
+
+bool ValidateES2TexImageParametersBase(Context *context,
+ 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, texType, level, width, height, 1, isSubImage))
+ {
+ // Error already handled.
+ return false;
+ }
+
+ if (!ValidMipLevel(context, texType, level))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if (xoffset < 0 || std::numeric_limits<GLsizei>::max() - xoffset < width ||
+ std::numeric_limits<GLsizei>::max() - yoffset < height)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+
+ const gl::Caps &caps = context->getCaps();
+
+ switch (texType)
+ {
+ case TextureType::_2D:
+ case TextureType::External:
+ if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ case TextureType::Rectangle:
+ ASSERT(level == 0);
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ if (isCompressed)
+ {
+ context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+ break;
+
+ case TextureType::CubeMap:
+ if (!isSubImage && width != height)
+ {
+ context->validationError(GL_INVALID_VALUE, kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.maxCubeMapTextureSize >> level))
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ gl::Texture *texture = context->getTextureByType(texType);
+ if (!texture)
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferNotBound);
+ return false;
+ }
+
+ // Verify zero border
+ if (border != 0)
+ {
+ context->validationError(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)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidInternalFormat);
+ return false;
+ }
+
+ if (!internalFormatInfo.textureSupport(context->getClientVersion(),
+ context->getExtensions()))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidInternalFormat);
+ return false;
+ }
+
+ if (isSubImage)
+ {
+ // 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.
+ if (actualInternalFormat == GL_ETC1_RGB8_OES)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ 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(GL_INVALID_OPERATION, kInvalidCompressedImageSize);
+ return false;
+ }
+
+ if (format != actualInternalFormat)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height, 1))
+ {
+ context->validationError(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;
+ default:
+ context->validationError(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(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_RED:
+ case GL_RG:
+ if (!context->getExtensions().textureRG)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ case GL_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ if (!context->getExtensions().textureFloat)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(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_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ break;
+ default:
+ context->validationError(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:
+ break;
+ default:
+ context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_BGRA_EXT:
+ if (!context->getExtensions().textureFormatBGRA8888)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ default:
+ context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_SRGB_EXT:
+ case GL_SRGB_ALPHA_EXT:
+ if (!context->getExtensions().sRGB)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ default:
+ context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: // error cases for compressed textures are
+ // handled below
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ break;
+ case GL_DEPTH_COMPONENT:
+ switch (type)
+ {
+ case GL_UNSIGNED_SHORT:
+ case GL_UNSIGNED_INT:
+ break;
+ default:
+ context->validationError(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(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ switch (format)
+ {
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ if (context->getExtensions().textureCompressionDXT1)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ if (context->getExtensions().textureCompressionDXT3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ if (context->getExtensions().textureCompressionDXT5)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_OES:
+ if (context->getExtensions().compressedETC1RGB8Texture)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
+ case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
+ if (context->getExtensions().lossyETCDecode)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_STENCIL_OES:
+ if (!context->getExtensions().depthTextureANGLE &&
+ !(context->getExtensions().packedDepthStencil &&
+ context->getExtensions().depthTextureOES))
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ if (target != TextureTarget::_2D)
+ {
+ context->validationError(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(GL_INVALID_OPERATION, kPixelDataNotNull);
+ return false;
+ }
+ if (level != 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kLevelNotZero);
+ 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().colorBufferFloatRGBA)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ nonEqualFormatsAllowed = true;
+
+ if (type != GL_FLOAT)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ if (format != GL_RGBA)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+
+ case GL_RGB32F:
+ if (!context->getExtensions().colorBufferFloatRGB)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ nonEqualFormatsAllowed = true;
+
+ if (type != GL_FLOAT)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ if (format != GL_RGB)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+
+ case GL_BGRA_EXT:
+ if (!context->getExtensions().textureFormatBGRA8888)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_DEPTH_COMPONENT:
+ if (!(context->getExtensions().depthTextureAny()))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_DEPTH_STENCIL:
+ if (!(context->getExtensions().depthTextureANGLE ||
+ context->getExtensions().packedDepthStencil))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_RED:
+ case GL_RG:
+ if (!context->getExtensions().textureRG)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+ break;
+
+ case GL_SRGB_EXT:
+ case GL_SRGB_ALPHA_EXT:
+ if (!context->getExtensions().sRGB)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_VALUE, kInvalidInternalFormat);
+ return false;
+ }
+ }
+
+ if (type == GL_FLOAT)
+ {
+ if (!context->getExtensions().textureFloat)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ }
+ else if (type == GL_HALF_FLOAT_OES)
+ {
+ if (!context->getExtensions().textureHalfFloat)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ }
+ }
+
+ if (isSubImage)
+ {
+ const InternalFormat &textureInternalFormat = *texture->getFormat(target, level).info;
+ if (textureInternalFormat.internalFormat == GL_NONE)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureLevel);
+ return false;
+ }
+
+ if (format != textureInternalFormat.format)
+ {
+ context->validationError(GL_INVALID_OPERATION, err::kTextureFormatMismatch);
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibility)
+ {
+ if (GetInternalFormatInfo(format, type).sizedInternalFormat !=
+ textureInternalFormat.sizedInternalFormat)
+ {
+ context->validationError(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(GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ if (width > 0 && height > 0 && pixels == nullptr &&
+ context->getState().getTargetBuffer(BufferBinding::PixelUnpack) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kPixelDataNull);
+ return false;
+ }
+ }
+ else
+ {
+ if (texture->getImmutableFormat())
+ {
+ context->validationError(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(GL_INVALID_OPERATION, kInvalidFormatCombination);
+ return false;
+ }
+
+ GLenum sizeCheckFormat = isSubImage ? format : internalformat;
+ return ValidImageDataSize(context, texType, width, height, 1, sizeCheckFormat, type, pixels,
+ imageSize);
+}
+
+bool ValidateES2TexStorageParameters(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (target != TextureType::_2D && target != TextureType::CubeMap &&
+ target != TextureType::Rectangle)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ if (width < 1 || height < 1 || levels < 1)
+ {
+ context->validationError(GL_INVALID_VALUE, kTextureSizeTooSmall);
+ return false;
+ }
+
+ if (target == TextureType::CubeMap && width != height)
+ {
+ context->validationError(GL_INVALID_VALUE, kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (levels != 1 && levels != gl::log2(std::max(width, height)) + 1)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels);
+ return false;
+ }
+
+ const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalformat);
+ if (formatInfo.format == GL_NONE || formatInfo.type == GL_NONE)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+ return false;
+ }
+
+ const gl::Caps &caps = context->getCaps();
+
+ switch (target)
+ {
+ case TextureType::_2D:
+ if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
+ static_cast<GLuint>(height) > caps.max2DTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+ case TextureType::Rectangle:
+ if (levels != 1)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ if (formatInfo.compressed)
+ {
+ context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+ break;
+ case TextureType::CubeMap:
+ if (static_cast<GLuint>(width) > caps.maxCubeMapTextureSize ||
+ static_cast<GLuint>(height) > caps.maxCubeMapTextureSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ if (levels != 1 && !context->getExtensions().textureNPOT)
+ {
+ if (!gl::isPow2(width) || !gl::isPow2(height))
+ {
+ context->validationError(GL_INVALID_OPERATION, kDimensionsMustBePow2);
+ return false;
+ }
+ }
+
+ switch (internalformat)
+ {
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ if (!context->getExtensions().textureCompressionDXT1)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ if (!context->getExtensions().textureCompressionDXT3)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ if (!context->getExtensions().textureCompressionDXT5)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_OES:
+ if (!context->getExtensions().compressedETC1RGB8Texture)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
+ case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
+ case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
+ if (!context->getExtensions().lossyETCDecode)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_RGBA32F_EXT:
+ case GL_RGB32F_EXT:
+ case GL_ALPHA32F_EXT:
+ case GL_LUMINANCE32F_EXT:
+ case GL_LUMINANCE_ALPHA32F_EXT:
+ if (!context->getExtensions().textureFloat)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_RGBA16F_EXT:
+ case GL_RGB16F_EXT:
+ case GL_ALPHA16F_EXT:
+ case GL_LUMINANCE16F_EXT:
+ case GL_LUMINANCE_ALPHA16F_EXT:
+ if (!context->getExtensions().textureHalfFloat)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_R8_EXT:
+ case GL_RG8_EXT:
+ if (!context->getExtensions().textureRG)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_R16F_EXT:
+ case GL_RG16F_EXT:
+ if (!context->getExtensions().textureRG || !context->getExtensions().textureHalfFloat)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_R32F_EXT:
+ case GL_RG32F_EXT:
+ if (!context->getExtensions().textureRG || !context->getExtensions().textureFloat)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT32_OES:
+ if (!(context->getExtensions().depthTextureAny()))
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ if (target != TextureType::_2D)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget);
+ return false;
+ }
+ // ANGLE_depth_texture only supports 1-level textures
+ if (!context->getExtensions().depthTextureOES)
+ {
+ if (levels != 1)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels);
+ return false;
+ }
+ }
+ break;
+ case GL_DEPTH24_STENCIL8_OES:
+ if (!(context->getExtensions().depthTextureANGLE ||
+ (context->getExtensions().packedDepthStencil &&
+ context->getExtensions().textureStorage)))
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ if (target != TextureType::_2D)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget);
+ return false;
+ }
+ if (!context->getExtensions().packedDepthStencil)
+ {
+ // ANGLE_depth_texture only supports 1-level textures
+ if (levels != 1)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ gl::Texture *texture = context->getTextureByType(target);
+ if (!texture || texture->id() == 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kMissingTexture);
+ return false;
+ }
+
+ if (texture->getImmutableFormat())
+ {
+ context->validationError(GL_INVALID_OPERATION, kTextureIsImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDiscardFramebufferEXT(Context *context,
+ GLenum target,
+ GLsizei numAttachments,
+ const GLenum *attachments)
+{
+ if (!context->getExtensions().discardFramebuffer)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ bool defaultFramebuffer = false;
+
+ switch (target)
+ {
+ case GL_FRAMEBUFFER:
+ defaultFramebuffer =
+ (context->getState().getTargetFramebuffer(GL_FRAMEBUFFER)->id() == 0);
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidFramebufferTarget);
+ return false;
+ }
+
+ return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments,
+ defaultFramebuffer);
+}
+
+bool ValidateBindVertexArrayOES(Context *context, GLuint array)
+{
+ if (!context->getExtensions().vertexArrayObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateBindVertexArrayBase(context, array);
+}
+
+bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n, const GLuint *arrays)
+{
+ if (!context->getExtensions().vertexArrayObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenVertexArraysOES(Context *context, GLsizei n, GLuint *arrays)
+{
+ if (!context->getExtensions().vertexArrayObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateIsVertexArrayOES(Context *context, GLuint array)
+{
+ if (!context->getExtensions().vertexArrayObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateProgramBinaryOES(Context *context,
+ GLuint program,
+ GLenum binaryFormat,
+ const void *binary,
+ GLint length)
+{
+ if (!context->getExtensions().getProgramBinary)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateProgramBinaryBase(context, program, binaryFormat, binary, length);
+}
+
+bool ValidateGetProgramBinaryOES(Context *context,
+ GLuint program,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLenum *binaryFormat,
+ void *binary)
+{
+ if (!context->getExtensions().getProgramBinary)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGetProgramBinaryBase(context, 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(Context *context,
+ GLenum source,
+ GLenum type,
+ GLenum severity,
+ GLsizei count,
+ const GLuint *ids,
+ GLboolean enabled)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidDebugSource(source, false) && source != GL_DONT_CARE)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidDebugSource);
+ return false;
+ }
+
+ if (!ValidDebugType(type) && type != GL_DONT_CARE)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidDebugType);
+ return false;
+ }
+
+ if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidDebugSeverity);
+ return false;
+ }
+
+ if (count > 0)
+ {
+ if (source == GL_DONT_CARE || type == GL_DONT_CARE)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidDebugSourceType);
+ return false;
+ }
+
+ if (severity != GL_DONT_CARE)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidDebugSeverity);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDebugMessageInsertKHR(Context *context,
+ GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar *buf)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(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(GL_INVALID_ENUM, kInvalidDebugSource);
+ return false;
+ }
+
+ if (!ValidDebugType(type))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidDebugType);
+ return false;
+ }
+
+ if (!ValidDebugSource(source, true))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidDebugSource);
+ return false;
+ }
+
+ size_t messageLength = (length < 0) ? strlen(buf) : length;
+ if (messageLength > context->getExtensions().maxDebugMessageLength)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxDebugMessageLength);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDebugMessageCallbackKHR(Context *context,
+ GLDEBUGPROCKHR callback,
+ const void *userParam)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetDebugMessageLogKHR(Context *context,
+ GLuint count,
+ GLsizei bufSize,
+ GLenum *sources,
+ GLenum *types,
+ GLuint *ids,
+ GLenum *severities,
+ GLsizei *lengths,
+ GLchar *messageLog)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (bufSize < 0 && messageLog != nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePushDebugGroupKHR(Context *context,
+ GLenum source,
+ GLuint id,
+ GLsizei length,
+ const GLchar *message)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidDebugSource(source, true))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidDebugSource);
+ return false;
+ }
+
+ size_t messageLength = (length < 0) ? strlen(message) : length;
+ if (messageLength > context->getExtensions().maxDebugMessageLength)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxDebugMessageLength);
+ return false;
+ }
+
+ size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+ if (currentStackSize >= context->getExtensions().maxDebugGroupStackDepth)
+ {
+ context->validationError(GL_STACK_OVERFLOW, kExceedsMaxDebugGroupStackDepth);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePopDebugGroupKHR(Context *context)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+ if (currentStackSize <= 1)
+ {
+ context->validationError(GL_STACK_UNDERFLOW, kCannotPopDefaultDebugGroup);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier, GLuint name)
+{
+ switch (identifier)
+ {
+ case GL_BUFFER:
+ if (context->getBuffer(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidBufferName);
+ return false;
+ }
+ return true;
+
+ case GL_SHADER:
+ if (context->getShader(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidShaderName);
+ return false;
+ }
+ return true;
+
+ case GL_PROGRAM:
+ if (context->getProgramNoResolveLink(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidProgramName);
+ return false;
+ }
+ return true;
+
+ case GL_VERTEX_ARRAY:
+ if (context->getVertexArray(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidVertexArrayName);
+ return false;
+ }
+ return true;
+
+ case GL_QUERY:
+ if (context->getQuery(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidQueryName);
+ return false;
+ }
+ return true;
+
+ case GL_TRANSFORM_FEEDBACK:
+ if (context->getTransformFeedback(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidTransformFeedbackName);
+ return false;
+ }
+ return true;
+
+ case GL_SAMPLER:
+ if (context->getSampler(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSamplerName);
+ return false;
+ }
+ return true;
+
+ case GL_TEXTURE:
+ if (context->getTexture(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidTextureName);
+ return false;
+ }
+ return true;
+
+ case GL_RENDERBUFFER:
+ if (context->getRenderbuffer(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidRenderbufferName);
+ return false;
+ }
+ return true;
+
+ case GL_FRAMEBUFFER:
+ if (context->getFramebuffer(name) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidFramebufferName);
+ return false;
+ }
+ return true;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidIndentifier);
+ return false;
+ }
+}
+
+static bool ValidateLabelLength(Context *context, 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->getExtensions().maxLabelLength)
+ {
+ context->validationError(GL_INVALID_VALUE, kExceedsMaxLabelLength);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateObjectLabelKHR(Context *context,
+ GLenum identifier,
+ GLuint name,
+ GLsizei length,
+ const GLchar *label)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidateObjectIdentifierAndName(context, identifier, name))
+ {
+ return false;
+ }
+
+ if (!ValidateLabelLength(context, length, label))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetObjectLabelKHR(Context *context,
+ GLenum identifier,
+ GLuint name,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLchar *label)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ if (!ValidateObjectIdentifierAndName(context, identifier, name))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateObjectPtrName(Context *context, const void *ptr)
+{
+ if (context->getSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSyncPointer);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateObjectPtrLabelKHR(Context *context,
+ const void *ptr,
+ GLsizei length,
+ const GLchar *label)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!ValidateObjectPtrName(context, ptr))
+ {
+ return false;
+ }
+
+ if (!ValidateLabelLength(context, length, label))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetObjectPtrLabelKHR(Context *context,
+ const void *ptr,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLchar *label)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ if (!ValidateObjectPtrName(context, ptr))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params)
+{
+ if (!context->getExtensions().debug)
+ {
+ context->validationError(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->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetPointervRobustANGLERobustANGLE(Context *context,
+ GLenum pname,
+ GLsizei bufSize,
+ GLsizei *length,
+ void **params)
+{
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateBlitFramebufferANGLE(Context *context,
+ GLint srcX0,
+ GLint srcY0,
+ GLint srcX1,
+ GLint srcY1,
+ GLint dstX0,
+ GLint dstY0,
+ GLint dstX1,
+ GLint dstY1,
+ GLbitfield mask,
+ GLenum filter)
+{
+ if (!context->getExtensions().framebufferBlit)
+ {
+ context->validationError(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(GL_INVALID_OPERATION, kBlitExtensionScaleOrFlip);
+ return false;
+ }
+
+ if (filter == GL_LINEAR)
+ {
+ context->validationError(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(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(GL_INVALID_OPERATION,
+ kBlitExtensionToInvalidAttachmentType);
+ return false;
+ }
+
+ // Return an error if the destination formats do not match
+ if (!Format::EquivalentForBlit(attachment->getFormat(),
+ readColorAttachment->getFormat()))
+ {
+ context->validationError(GL_INVALID_OPERATION,
+ kBlitExtensionFormatMismatch);
+ return false;
+ }
+ }
+ }
+
+ GLint samples = readFramebuffer->getSamples(context);
+ if (samples != 0 &&
+ IsPartialBlit(context, readColorAttachment, drawColorAttachment, srcX0, srcY0,
+ srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
+ {
+ context->validationError(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(GL_INVALID_OPERATION,
+ kBlitExtensionDepthStencilWholeBufferBlit);
+ return false;
+ }
+
+ if (readBuffer->getSamples() != 0 || drawBuffer->getSamples() != 0)
+ {
+ context->validationError(GL_INVALID_OPERATION,
+ kBlitExtensionMultisampledDepthOrStencil);
+ return false;
+ }
+ }
+ }
+ }
+
+ return ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0,
+ dstX1, dstY1, mask, filter);
+}
+
+bool ValidateClear(Context *context, GLbitfield mask)
+{
+ Framebuffer *fbo = context->getState().getDrawFramebuffer();
+ const Extensions &extensions = context->getExtensions();
+
+ if (!ValidateFramebufferComplete(context, fbo))
+ {
+ return false;
+ }
+
+ if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidClearMask);
+ return false;
+ }
+
+ if (extensions.webglCompatibility && (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, drawBufferIdx, validComponentTypes, ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
+ }
+
+ if ((extensions.multiview || extensions.multiview2) && extensions.disjointTimerQuery)
+ {
+ const State &state = context->getState();
+ Framebuffer *framebuffer = state.getDrawFramebuffer();
+ if (framebuffer->getNumViews() > 1 && state.isQueryActive(QueryType::TimeElapsed))
+ {
+ context->validationError(GL_INVALID_OPERATION, kMultiviewTimerQuery);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDrawBuffersEXT(Context *context, GLsizei n, const GLenum *bufs)
+{
+ if (!context->getExtensions().drawBuffers)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateDrawBuffersBase(context, n, bufs);
+}
+
+bool ValidateTexImage2D(Context *context,
+ 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, target, level, internalformat, false, false,
+ 0, 0, width, height, border, format, type, -1, pixels);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexImage2DParameters(context, target, level, internalformat, false, false, 0,
+ 0, 0, width, height, 1, border, format, type, -1,
+ pixels);
+}
+
+bool ValidateTexImage2DRobustANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const void *pixels)
+{
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexImageParameters(context, target, level, internalformat, false, false,
+ 0, 0, width, height, border, format, type, bufSize,
+ pixels);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexImage2DParameters(context, target, level, internalformat, false, false, 0,
+ 0, 0, width, height, 1, border, format, type, bufSize,
+ pixels);
+}
+
+bool ValidateTexSubImage2D(Context *context,
+ 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, target, level, GL_NONE, false, true, xoffset,
+ yoffset, width, height, 0, format, type, -1, pixels);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexImage2DParameters(context, target, level, GL_NONE, false, true, xoffset,
+ yoffset, 0, width, height, 1, 0, format, type, -1,
+ pixels);
+}
+
+bool ValidateTexSubImage2DRobustANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const void *pixels)
+{
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexImageParameters(context, target, level, GL_NONE, false, true, xoffset,
+ yoffset, width, height, 0, format, type, bufSize,
+ pixels);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexImage2DParameters(context, target, level, GL_NONE, false, true, xoffset,
+ yoffset, 0, width, height, 1, 0, format, type, bufSize,
+ pixels);
+}
+
+bool ValidateTexSubImage3DOES(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLenum type,
+ const void *pixels)
+{
+ return ValidateTexSubImage3D(context, target, level, xoffset, yoffset, zoffset, width, height,
+ depth, format, type, pixels);
+}
+
+bool ValidateCompressedTexImage2D(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ const void *data)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ if (!ValidateES2TexImageParameters(context, 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, 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 blockSize = 0;
+ if (!formatInfo.computeCompressedImageSize(gl::Extents(width, height, 1), &blockSize))
+ {
+ context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kCompressedTextureDimensionsMustMatchData);
+ return false;
+ }
+
+ if (target == TextureTarget::Rectangle)
+ {
+ context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexImage2DRobustANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
+{
+ if (!ValidateRobustCompressedTexImageBase(context, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexImage2D(context, target, level, internalformat, width, height,
+ border, imageSize, data);
+}
+
+bool ValidateCompressedTexImage3DOES(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLsizei imageSize,
+ const void *data)
+{
+ return ValidateCompressedTexImage3D(context, target, level, internalformat, width, height,
+ depth, border, imageSize, data);
+}
+
+bool ValidateCompressedTexSubImage2DRobustANGLE(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
+{
+ if (!ValidateRobustCompressedTexImageBase(context, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexSubImage2D(context, target, level, xoffset, yoffset, width, height,
+ format, imageSize, data);
+}
+
+bool ValidateCompressedTexSubImage2D(Context *context,
+ 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, 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, 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(gl::Extents(width, height, 1), &blockSize))
+ {
+ context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSize)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidCompressedImageSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexSubImage3DOES(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLsizei imageSize,
+ const void *data)
+{
+ return ValidateCompressedTexSubImage3D(context, target, level, xoffset, yoffset, zoffset, width,
+ height, depth, format, imageSize, data);
+}
+
+bool ValidateGetBufferPointervOES(Context *context,
+ BufferBinding target,
+ GLenum pname,
+ void **params)
+{
+ if (!context->getExtensions().mapBuffer)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGetBufferPointervBase(context, target, pname, nullptr, params);
+}
+
+bool ValidateMapBufferOES(Context *context, BufferBinding target, GLenum access)
+{
+ if (!context->getExtensions().mapBuffer)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!context->isValidBufferBinding(target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getState().getTargetBuffer(target);
+
+ if (buffer == nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferNotMappable);
+ return false;
+ }
+
+ if (access != GL_WRITE_ONLY_OES)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidAccessBits);
+ return false;
+ }
+
+ if (buffer->isMapped())
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferAlreadyMapped);
+ return false;
+ }
+
+ return ValidateMapBufferBase(context, target);
+}
+
+bool ValidateUnmapBufferOES(Context *context, BufferBinding target)
+{
+ if (!context->getExtensions().mapBuffer)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateUnmapBufferBase(context, target);
+}
+
+bool ValidateMapBufferRangeEXT(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length,
+ GLbitfield access)
+{
+ if (!context->getExtensions().mapBufferRange)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateMapBufferRangeBase(context, target, offset, length, access);
+}
+
+bool ValidateBufferStorageMemEXT(Context *context,
+ TextureType target,
+ GLsizeiptr size,
+ GLuint memory,
+ GLuint64 offset)
+{
+ if (!context->getExtensions().memoryObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateCreateMemoryObjectsEXT(Context *context, GLsizei n, GLuint *memoryObjects)
+{
+ if (!context->getExtensions().memoryObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateDeleteMemoryObjectsEXT(Context *context, GLsizei n, const GLuint *memoryObjects)
+{
+ if (!context->getExtensions().memoryObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGetMemoryObjectParameterivEXT(Context *context,
+ GLuint memoryObject,
+ GLenum pname,
+ GLint *params)
+{
+ if (!context->getExtensions().memoryObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateGetUnsignedBytevEXT(Context *context, GLenum pname, GLubyte *data)
+{
+ if (!context->getExtensions().memoryObject && !context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateGetUnsignedBytei_vEXT(Context *context, GLenum target, GLuint index, GLubyte *data)
+{
+ if (!context->getExtensions().memoryObject && !context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateIsMemoryObjectEXT(Context *context, GLuint memoryObject)
+{
+ if (!context->getExtensions().memoryObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateMemoryObjectParameterivEXT(Context *context,
+ GLuint memoryObject,
+ GLenum pname,
+ const GLint *params)
+{
+ if (!context->getExtensions().memoryObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateTexStorageMem2DEXT(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset)
+{
+ if (!context->getExtensions().memoryObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexStorageParameters(context, target, levels, internalFormat, width,
+ height);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexStorage2DParameters(context, target, levels, internalFormat, width, height,
+ 1);
+}
+
+bool ValidateTexStorageMem3DEXT(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLuint memory,
+ GLuint64 offset)
+{
+ if (!context->getExtensions().memoryObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateImportMemoryFdEXT(Context *context,
+ GLuint memory,
+ GLuint64 size,
+ HandleType handleType,
+ GLint fd)
+{
+ if (!context->getExtensions().memoryObjectFd)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ switch (handleType)
+ {
+ case HandleType::OpaqueFd:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidHandleType);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteSemaphoresEXT(Context *context, GLsizei n, const GLuint *semaphores)
+{
+ if (!context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenSemaphoresEXT(Context *context, GLsizei n, GLuint *semaphores)
+{
+ if (!context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGetSemaphoreParameterui64vEXT(Context *context,
+ GLuint semaphore,
+ GLenum pname,
+ GLuint64 *params)
+{
+ if (!context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateIsSemaphoreEXT(Context *context, GLuint semaphore)
+{
+ if (!context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateSemaphoreParameterui64vEXT(Context *context,
+ GLuint semaphore,
+ GLenum pname,
+ const GLuint64 *params)
+{
+ if (!context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateSignalSemaphoreEXT(Context *context,
+ GLuint semaphore,
+ GLuint numBufferBarriers,
+ const GLuint *buffers,
+ GLuint numTextureBarriers,
+ const GLuint *textures,
+ const GLenum *dstLayouts)
+{
+ if (!context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ for (GLuint i = 0; i < numTextureBarriers; ++i)
+ {
+ if (!IsValidImageLayout(FromGLenum<ImageLayout>(dstLayouts[i])))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidImageLayout);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateWaitSemaphoreEXT(Context *context,
+ GLuint semaphore,
+ GLuint numBufferBarriers,
+ const GLuint *buffers,
+ GLuint numTextureBarriers,
+ const GLuint *textures,
+ const GLenum *srcLayouts)
+{
+ if (!context->getExtensions().semaphore)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ for (GLuint i = 0; i < numTextureBarriers; ++i)
+ {
+ if (!IsValidImageLayout(FromGLenum<ImageLayout>(srcLayouts[i])))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidImageLayout);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateImportSemaphoreFdEXT(Context *context,
+ GLuint semaphore,
+ HandleType handleType,
+ GLint fd)
+{
+ if (!context->getExtensions().semaphoreFd)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ switch (handleType)
+ {
+ case HandleType::OpaqueFd:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidHandleType);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateMapBufferBase(Context *context, 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(GL_INVALID_OPERATION, kBufferBoundForTransformFeedback);
+ return false;
+ }
+ }
+ }
+
+ if (context->getExtensions().webglCompatibility &&
+ buffer->isBoundForTransformFeedbackAndOtherUse())
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferBoundForTransformFeedback);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFlushMappedBufferRangeEXT(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length)
+{
+ if (!context->getExtensions().mapBufferRange)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateFlushMappedBufferRangeBase(context, target, offset, length);
+}
+
+bool ValidateBindUniformLocationCHROMIUM(Context *context,
+ GLuint program,
+ GLint location,
+ const GLchar *name)
+{
+ if (!context->getExtensions().bindUniformLocation)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (location < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeLocation);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (static_cast<size_t>(location) >=
+ (caps.maxVertexUniformVectors + caps.maxFragmentUniformVectors) * 4)
+ {
+ context->validationError(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->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidNameCharacters);
+ return false;
+ }
+
+ if (strncmp(name, "gl_", 3) == 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNameBeginsWithGL);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCoverageModulationCHROMIUM(Context *context, GLenum components)
+{
+ if (!context->getExtensions().framebufferMixedSamples)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ switch (components)
+ {
+ case GL_RGB:
+ case GL_RGBA:
+ case GL_ALPHA:
+ case GL_NONE:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidCoverageComponents);
+ return false;
+ }
+
+ return true;
+}
+
+// CHROMIUM_path_rendering
+
+bool ValidateMatrixLoadfCHROMIUM(Context *context, GLenum matrixMode, const GLfloat *matrix)
+{
+ if (!ValidateMatrixMode(context, matrixMode))
+ {
+ return false;
+ }
+
+ if (matrix == nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidPathMatrix);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateMatrixLoadIdentityCHROMIUM(Context *context, GLenum matrixMode)
+{
+ return ValidateMatrixMode(context, matrixMode);
+}
+
+bool ValidateGenPathsCHROMIUM(Context *context, GLsizei range)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ // range = 0 is undefined in NV_path_rendering.
+ // we add stricter semantic check here and require a non zero positive range.
+ if (range <= 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidRange);
+ return false;
+ }
+
+ if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range))
+ {
+ context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeletePathsCHROMIUM(Context *context, GLuint path, GLsizei range)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ // range = 0 is undefined in NV_path_rendering.
+ // we add stricter semantic check here and require a non zero positive range.
+ if (range <= 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidRange);
+ return false;
+ }
+
+ angle::CheckedNumeric<std::uint32_t> checkedRange(path);
+ checkedRange += range;
+
+ if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range) || !checkedRange.IsValid())
+ {
+ context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+ return true;
+}
+
+bool ValidatePathCommandsCHROMIUM(Context *context,
+ GLuint path,
+ GLsizei numCommands,
+ const GLubyte *commands,
+ GLsizei numCoords,
+ GLenum coordType,
+ const void *coords)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (!context->isPathGenerated(path))
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoSuchPath);
+ return false;
+ }
+
+ if (numCommands < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathNumCommands);
+ return false;
+ }
+ else if (numCommands > 0)
+ {
+ if (!commands)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathCommandsArray);
+ return false;
+ }
+ }
+
+ if (numCoords < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathNumCoords);
+ return false;
+ }
+ else if (numCoords > 0)
+ {
+ if (!coords)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathNumCoordsArray);
+ return false;
+ }
+ }
+
+ std::uint32_t coordTypeSize = 0;
+ switch (coordType)
+ {
+ case GL_BYTE:
+ coordTypeSize = sizeof(GLbyte);
+ break;
+
+ case GL_UNSIGNED_BYTE:
+ coordTypeSize = sizeof(GLubyte);
+ break;
+
+ case GL_SHORT:
+ coordTypeSize = sizeof(GLshort);
+ break;
+
+ case GL_UNSIGNED_SHORT:
+ coordTypeSize = sizeof(GLushort);
+ break;
+
+ case GL_FLOAT:
+ coordTypeSize = sizeof(GLfloat);
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPathCoordinateType);
+ return false;
+ }
+
+ angle::CheckedNumeric<std::uint32_t> checkedSize(numCommands);
+ checkedSize += (coordTypeSize * numCoords);
+ if (!checkedSize.IsValid())
+ {
+ context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
+ return false;
+ }
+
+ // early return skips command data validation when it doesn't exist.
+ if (!commands)
+ return true;
+
+ GLsizei expectedNumCoords = 0;
+ for (GLsizei i = 0; i < numCommands; ++i)
+ {
+ switch (commands[i])
+ {
+ case GL_CLOSE_PATH_CHROMIUM: // no coordinates.
+ break;
+ case GL_MOVE_TO_CHROMIUM:
+ case GL_LINE_TO_CHROMIUM:
+ expectedNumCoords += 2;
+ break;
+ case GL_QUADRATIC_CURVE_TO_CHROMIUM:
+ expectedNumCoords += 4;
+ break;
+ case GL_CUBIC_CURVE_TO_CHROMIUM:
+ expectedNumCoords += 6;
+ break;
+ case GL_CONIC_CURVE_TO_CHROMIUM:
+ expectedNumCoords += 5;
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPathCommand);
+ return false;
+ }
+ }
+
+ if (expectedNumCoords != numCoords)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathNumCoords);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePathParameterfCHROMIUM(Context *context, GLuint path, GLenum pname, GLfloat value)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (!context->isPathGenerated(path))
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoSuchPath);
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_PATH_STROKE_WIDTH_CHROMIUM:
+ if (value < 0.0f)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathStrokeWidth);
+ return false;
+ }
+ break;
+ case GL_PATH_END_CAPS_CHROMIUM:
+ switch (static_cast<GLenum>(value))
+ {
+ case GL_FLAT_CHROMIUM:
+ case GL_SQUARE_CHROMIUM:
+ case GL_ROUND_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPathEndCaps);
+ return false;
+ }
+ break;
+ case GL_PATH_JOIN_STYLE_CHROMIUM:
+ switch (static_cast<GLenum>(value))
+ {
+ case GL_MITER_REVERT_CHROMIUM:
+ case GL_BEVEL_CHROMIUM:
+ case GL_ROUND_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPathJoinStyle);
+ return false;
+ }
+ break;
+ case GL_PATH_MITER_LIMIT_CHROMIUM:
+ if (value < 0.0f)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathMiterLimit);
+ return false;
+ }
+ break;
+
+ case GL_PATH_STROKE_BOUND_CHROMIUM:
+ // no errors, only clamping.
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPathParameter);
+ return false;
+ }
+ return true;
+}
+
+bool ValidatePathParameteriCHROMIUM(Context *context, GLuint path, GLenum pname, GLint value)
+{
+ // TODO(jmadill): Use proper clamping cast.
+ return ValidatePathParameterfCHROMIUM(context, path, pname, static_cast<GLfloat>(value));
+}
+
+bool ValidateGetPathParameterfvCHROMIUM(Context *context, GLuint path, GLenum pname, GLfloat *value)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!context->isPathGenerated(path))
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoSuchPath);
+ return false;
+ }
+
+ if (!value)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathValueArray);
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_PATH_STROKE_WIDTH_CHROMIUM:
+ case GL_PATH_END_CAPS_CHROMIUM:
+ case GL_PATH_JOIN_STYLE_CHROMIUM:
+ case GL_PATH_MITER_LIMIT_CHROMIUM:
+ case GL_PATH_STROKE_BOUND_CHROMIUM:
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPathParameter);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetPathParameterivCHROMIUM(Context *context, GLuint path, GLenum pname, GLint *value)
+{
+ return ValidateGetPathParameterfvCHROMIUM(context, path, pname,
+ reinterpret_cast<GLfloat *>(value));
+}
+
+bool ValidatePathStencilFuncCHROMIUM(Context *context, GLenum func, GLint ref, GLuint mask)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ 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:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+// Note that the spec specifies that for the path drawing commands
+// if the path object is not an existing path object the command
+// does nothing and no error is generated.
+// However if the path object exists but has not been specified any
+// commands then an error is generated.
+
+bool ValidateStencilFillPathCHROMIUM(Context *context, GLuint path, GLenum fillMode, GLuint mask)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->isPathGenerated(path) && !context->isPath(path))
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoSuchPath);
+ return false;
+ }
+
+ switch (fillMode)
+ {
+ case GL_INVERT:
+ case GL_COUNT_UP_CHROMIUM:
+ case GL_COUNT_DOWN_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidFillMode);
+ return false;
+ }
+
+ if (!isPow2(mask + 1))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidStencilBitMask);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilStrokePathCHROMIUM(Context *context, GLuint path, GLint reference, GLuint mask)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (context->isPathGenerated(path) && !context->isPath(path))
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoPathOrNoPathData);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCoverPathCHROMIUM(Context *context, GLuint path, GLenum coverMode)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ if (context->isPathGenerated(path) && !context->isPath(path))
+ {
+ context->validationError(GL_INVALID_OPERATION, kNoSuchPath);
+ return false;
+ }
+
+ switch (coverMode)
+ {
+ case GL_CONVEX_HULL_CHROMIUM:
+ case GL_BOUNDING_BOX_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidCoverMode);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateCoverFillPathCHROMIUM(Context *context, GLuint path, GLenum coverMode)
+{
+ return ValidateCoverPathCHROMIUM(context, path, coverMode);
+}
+
+bool ValidateCoverStrokePathCHROMIUM(Context *context, GLuint path, GLenum coverMode)
+{
+ return ValidateCoverPathCHROMIUM(context, path, coverMode);
+}
+
+bool ValidateStencilThenCoverFillPathCHROMIUM(Context *context,
+ GLuint path,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum coverMode)
+{
+ return ValidateStencilFillPathCHROMIUM(context, path, fillMode, mask) &&
+ ValidateCoverPathCHROMIUM(context, path, coverMode);
+}
+
+bool ValidateStencilThenCoverStrokePathCHROMIUM(Context *context,
+ GLuint path,
+ GLint reference,
+ GLuint mask,
+ GLenum coverMode)
+{
+ return ValidateStencilStrokePathCHROMIUM(context, path, reference, mask) &&
+ ValidateCoverPathCHROMIUM(context, path, coverMode);
+}
+
+bool ValidateIsPathCHROMIUM(Context *context, GLuint path)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateCoverFillPathInstancedCHROMIUM(Context *context,
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const void *paths,
+ GLuint pathBase,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat *transformValues)
+{
+ if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
+ transformType, transformValues))
+ return false;
+
+ switch (coverMode)
+ {
+ case GL_CONVEX_HULL_CHROMIUM:
+ case GL_BOUNDING_BOX_CHROMIUM:
+ case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidCoverMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCoverStrokePathInstancedCHROMIUM(Context *context,
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const void *paths,
+ GLuint pathBase,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat *transformValues)
+{
+ if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
+ transformType, transformValues))
+ return false;
+
+ switch (coverMode)
+ {
+ case GL_CONVEX_HULL_CHROMIUM:
+ case GL_BOUNDING_BOX_CHROMIUM:
+ case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidCoverMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilFillPathInstancedCHROMIUM(Context *context,
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const void *paths,
+ GLuint pathBase,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum transformType,
+ const GLfloat *transformValues)
+{
+
+ if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
+ transformType, transformValues))
+ return false;
+
+ switch (fillMode)
+ {
+ case GL_INVERT:
+ case GL_COUNT_UP_CHROMIUM:
+ case GL_COUNT_DOWN_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidFillMode);
+ return false;
+ }
+ if (!isPow2(mask + 1))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidStencilBitMask);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateStencilStrokePathInstancedCHROMIUM(Context *context,
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const void *paths,
+ GLuint pathBase,
+ GLint reference,
+ GLuint mask,
+ GLenum transformType,
+ const GLfloat *transformValues)
+{
+ if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
+ transformType, transformValues))
+ return false;
+
+ // no more validation here.
+
+ return true;
+}
+
+bool ValidateStencilThenCoverFillPathInstancedCHROMIUM(Context *context,
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const void *paths,
+ GLuint pathBase,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat *transformValues)
+{
+ if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
+ transformType, transformValues))
+ return false;
+
+ switch (coverMode)
+ {
+ case GL_CONVEX_HULL_CHROMIUM:
+ case GL_BOUNDING_BOX_CHROMIUM:
+ case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidCoverMode);
+ return false;
+ }
+
+ switch (fillMode)
+ {
+ case GL_INVERT:
+ case GL_COUNT_UP_CHROMIUM:
+ case GL_COUNT_DOWN_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidFillMode);
+ return false;
+ }
+ if (!isPow2(mask + 1))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidStencilBitMask);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilThenCoverStrokePathInstancedCHROMIUM(Context *context,
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const void *paths,
+ GLuint pathBase,
+ GLint reference,
+ GLuint mask,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat *transformValues)
+{
+ if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
+ transformType, transformValues))
+ return false;
+
+ switch (coverMode)
+ {
+ case GL_CONVEX_HULL_CHROMIUM:
+ case GL_BOUNDING_BOX_CHROMIUM:
+ case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidCoverMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindFragmentInputLocationCHROMIUM(Context *context,
+ GLuint program,
+ GLint location,
+ const GLchar *name)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ const GLint MaxLocation = context->getCaps().maxVaryingVectors * 4;
+ if (location >= MaxLocation)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidVaryingLocation);
+ return false;
+ }
+
+ const auto *programObject = context->getProgramNoResolveLink(program);
+ if (!programObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotBound);
+ return false;
+ }
+
+ if (!name)
+ {
+ context->validationError(GL_INVALID_VALUE, kMissingName);
+ return false;
+ }
+
+ if (angle::BeginsWith(name, "gl_"))
+ {
+ context->validationError(GL_INVALID_OPERATION, kNameBeginsWithGL);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateProgramPathFragmentInputGenCHROMIUM(Context *context,
+ GLuint program,
+ GLint location,
+ GLenum genMode,
+ GLint components,
+ const GLfloat *coeffs)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ const auto *programObject = context->getProgramResolveLink(program);
+ if (!programObject || programObject->isFlaggedForDeletion())
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramDoesNotExist);
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+
+ switch (genMode)
+ {
+ case GL_NONE:
+ if (components != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidComponents);
+ return false;
+ }
+ break;
+
+ case GL_OBJECT_LINEAR_CHROMIUM:
+ case GL_EYE_LINEAR_CHROMIUM:
+ case GL_CONSTANT_CHROMIUM:
+ if (components < 1 || components > 4)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidComponents);
+ return false;
+ }
+ if (!coeffs)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidPathCoefficientsArray);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPathGenMode);
+ return false;
+ }
+
+ // If the location is -1 then the command is silently ignored
+ // and no further validation is needed.
+ if (location == -1)
+ return true;
+
+ const auto &binding = programObject->getFragmentInputBindingInfo(location);
+
+ if (!binding.valid)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFragmentInputBinding);
+ return false;
+ }
+
+ if (binding.type != GL_NONE)
+ {
+ GLint expectedComponents = 0;
+ switch (binding.type)
+ {
+ case GL_FLOAT:
+ expectedComponents = 1;
+ break;
+ case GL_FLOAT_VEC2:
+ expectedComponents = 2;
+ break;
+ case GL_FLOAT_VEC3:
+ expectedComponents = 3;
+ break;
+ case GL_FLOAT_VEC4:
+ expectedComponents = 4;
+ break;
+ default:
+ context->validationError(GL_INVALID_OPERATION, kFragmentInputTypeNotFloatingPoint);
+ return false;
+ }
+ if (expectedComponents != components && genMode != GL_NONE)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidPathComponents);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateCopyTextureCHROMIUM(Context *context,
+ GLuint sourceId,
+ GLint sourceLevel,
+ TextureTarget destTarget,
+ GLuint destId,
+ GLint destLevel,
+ GLint internalFormat,
+ GLenum destType,
+ GLboolean unpackFlipY,
+ GLboolean unpackPremultiplyAlpha,
+ GLboolean unpackUnmultiplyAlpha)
+{
+ if (!context->getExtensions().copyTexture)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTexture);
+ return false;
+ }
+
+ if (!IsValidCopyTextureSourceTarget(context, source->getType()))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+
+ TextureType sourceType = source->getType();
+ ASSERT(sourceType != TextureType::CubeMap);
+ TextureTarget sourceTarget = NonCubeTextureTypeToTarget(sourceType);
+
+ if (!IsValidCopyTextureSourceLevel(context, sourceType, sourceLevel))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTextureLevel);
+ return false;
+ }
+
+ GLsizei sourceWidth = static_cast<GLsizei>(source->getWidth(sourceTarget, sourceLevel));
+ GLsizei sourceHeight = static_cast<GLsizei>(source->getHeight(sourceTarget, sourceLevel));
+ if (sourceWidth == 0 || sourceHeight == 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+
+ const InternalFormat &sourceFormat = *source->getFormat(sourceTarget, sourceLevel).info;
+ if (!IsValidCopyTextureSourceInternalFormatEnum(sourceFormat.internalFormat))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidSourceTextureInternalFormat);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTargetEnum(context, destTarget))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTarget(context, dest->getType(), destTarget))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTextureType);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationLevel(context, dest->getType(), destLevel, sourceWidth,
+ sourceHeight, false))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationFormatType(context, internalFormat, destType))
+ {
+ return false;
+ }
+
+ if (dest->getType() == TextureType::CubeMap && sourceWidth != sourceHeight)
+ {
+ context->validationError(GL_INVALID_VALUE, kCubemapFacesEqualDimensions);
+ return false;
+ }
+
+ if (dest->getImmutableFormat())
+ {
+ context->validationError(GL_INVALID_OPERATION, kDestinationImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCopySubTextureCHROMIUM(Context *context,
+ GLuint sourceId,
+ GLint sourceLevel,
+ TextureTarget destTarget,
+ GLuint destId,
+ GLint destLevel,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLboolean unpackFlipY,
+ GLboolean unpackPremultiplyAlpha,
+ GLboolean unpackUnmultiplyAlpha)
+{
+ if (!context->getExtensions().copyTexture)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTexture);
+ return false;
+ }
+
+ if (!IsValidCopyTextureSourceTarget(context, source->getType()))
+ {
+ context->validationError(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(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if (source->getWidth(sourceTarget, sourceLevel) == 0 ||
+ source->getHeight(sourceTarget, sourceLevel) == 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTextureLevel);
+ return false;
+ }
+
+ if (x < 0 || y < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (width < 0 || height < 0)
+ {
+ context->validationError(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(GL_INVALID_VALUE, kSourceTextureTooSmall);
+ return false;
+ }
+
+ const Format &sourceFormat = source->getFormat(sourceTarget, sourceLevel);
+ if (!IsValidCopySubTextureSourceInternalFormat(sourceFormat.info->internalFormat))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTargetEnum(context, destTarget))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTarget(context, dest->getType(), destTarget))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTextureType);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationLevel(context, dest->getType(), destLevel, width, height,
+ true))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+
+ if (dest->getWidth(destTarget, destLevel) == 0 || dest->getHeight(destTarget, destLevel) == 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kDestinationLevelNotDefined);
+ return false;
+ }
+
+ const InternalFormat &destFormat = *dest->getFormat(destTarget, destLevel).info;
+ if (!IsValidCopySubTextureDestionationInternalFormat(destFormat.internalFormat))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFormatCombination);
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (static_cast<size_t>(xoffset + width) > dest->getWidth(destTarget, destLevel) ||
+ static_cast<size_t>(yoffset + height) > dest->getHeight(destTarget, destLevel))
+ {
+ context->validationError(GL_INVALID_VALUE, kOffsetOverflow);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedCopyTextureCHROMIUM(Context *context, GLuint sourceId, GLuint destId)
+{
+ if (!context->getExtensions().copyCompressedTexture)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ const gl::Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTexture);
+ return false;
+ }
+
+ if (source->getType() != TextureType::_2D)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidSourceTextureType);
+ return false;
+ }
+
+ if (source->getWidth(TextureTarget::_2D, 0) == 0 ||
+ source->getHeight(TextureTarget::_2D, 0) == 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kSourceTextureLevelZeroDefined);
+ return false;
+ }
+
+ const gl::Format &sourceFormat = source->getFormat(TextureTarget::_2D, 0);
+ if (!sourceFormat.info->compressed)
+ {
+ context->validationError(GL_INVALID_OPERATION, kSourceTextureMustBeCompressed);
+ return false;
+ }
+
+ const gl::Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTexture);
+ return false;
+ }
+
+ if (dest->getType() != TextureType::_2D)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidDestinationTextureType);
+ return false;
+ }
+
+ if (dest->getImmutableFormat())
+ {
+ context->validationError(GL_INVALID_OPERATION, kDestinationImmutable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCreateShader(Context *context, ShaderType type)
+{
+ switch (type)
+ {
+ case ShaderType::Vertex:
+ case ShaderType::Fragment:
+ break;
+
+ case ShaderType::Compute:
+ if (context->getClientVersion() < Version(3, 1))
+ {
+ context->validationError(GL_INVALID_ENUM, kES31Required);
+ return false;
+ }
+ break;
+
+ case ShaderType::Geometry:
+ if (!context->getExtensions().geometryShader)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidShaderType);
+ return false;
+ }
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidShaderType);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBufferData(Context *context,
+ BufferBinding target,
+ GLsizeiptr size,
+ const void *data,
+ BufferUsage usage)
+{
+ if (size < 0)
+ {
+ context->validationError(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(GL_INVALID_ENUM, kInvalidBufferUsage);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidBufferUsage);
+ return false;
+ }
+
+ if (!context->isValidBufferBinding(target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getState().getTargetBuffer(target);
+
+ if (!buffer)
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferNotBound);
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibility &&
+ buffer->isBoundForTransformFeedbackAndOtherUse())
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferBoundForTransformFeedback);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBufferSubData(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr size,
+ const void *data)
+{
+ if (size < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ if (offset < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeOffset);
+ return false;
+ }
+
+ if (!context->isValidBufferBinding(target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getState().getTargetBuffer(target);
+
+ if (!buffer)
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferNotBound);
+ return false;
+ }
+
+ if (buffer->isMapped())
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferMapped);
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibility &&
+ buffer->isBoundForTransformFeedbackAndOtherUse())
+ {
+ context->validationError(GL_INVALID_OPERATION, kBufferBoundForTransformFeedback);
+ return false;
+ }
+
+ // Check for possible overflow of size + offset
+ angle::CheckedNumeric<size_t> checkedSize(size);
+ checkedSize += offset;
+ if (!checkedSize.IsValid())
+ {
+ context->validationError(GL_INVALID_VALUE, kParamOverflow);
+ return false;
+ }
+
+ if (size + offset > buffer->getSize())
+ {
+ context->validationError(GL_INVALID_VALUE, kInsufficientBufferSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRequestExtensionANGLE(Context *context, const GLchar *name)
+{
+ if (!context->getExtensions().requestExtension)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (!context->isExtensionRequestable(name))
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotRequestable);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateActiveTexture(Context *context, GLenum texture)
+{
+ if (context->getClientMajorVersion() < 2)
+ {
+ return ValidateMultitextureUnit(context, texture);
+ }
+
+ if (texture < GL_TEXTURE0 ||
+ texture > GL_TEXTURE0 + context->getCaps().maxCombinedTextureImageUnits - 1)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidCombinedImageUnit);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateAttachShader(Context *context, GLuint program, GLuint shader)
+{
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ if (programObject->getAttachedShader(shaderObject->getType()))
+ {
+ context->validationError(GL_INVALID_OPERATION, kShaderAttachmentHasShader);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindAttribLocation(Context *context, GLuint program, GLuint index, const GLchar *name)
+{
+ if (index >= MAX_VERTEX_ATTRIBS)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ if (strncmp(name, "gl_", 3) == 0)
+ {
+ context->validationError(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(GL_INVALID_VALUE, kInvalidNameCharacters);
+ return false;
+ }
+
+ if (!ValidateWebGLNameLength(context, length) || !ValidateWebGLNamePrefix(context, name))
+ {
+ return false;
+ }
+ }
+
+ return GetValidProgram(context, program) != nullptr;
+}
+
+bool ValidateBindFramebuffer(Context *context, GLenum target, GLuint framebuffer)
+{
+ if (!ValidFramebufferTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFramebufferTarget);
+ return false;
+ }
+
+ if (!context->getState().isBindGeneratesResourceEnabled() &&
+ !context->isFramebufferGenerated(framebuffer))
+ {
+ context->validationError(GL_INVALID_OPERATION, kObjectNotGenerated);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindRenderbuffer(Context *context, GLenum target, GLuint renderbuffer)
+{
+ if (target != GL_RENDERBUFFER)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidRenderbufferTarget);
+ return false;
+ }
+
+ if (!context->getState().isBindGeneratesResourceEnabled() &&
+ !context->isRenderbufferGenerated(renderbuffer))
+ {
+ context->validationError(GL_INVALID_OPERATION, kObjectNotGenerated);
+ return false;
+ }
+
+ return true;
+}
+
+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().blendMinMax;
+
+ default:
+ return false;
+ }
+}
+
+bool ValidateBlendColor(Context *context, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ return true;
+}
+
+bool ValidateBlendEquation(Context *context, GLenum mode)
+{
+ if (!ValidBlendEquationMode(context, mode))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBlendEquation);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBlendEquationSeparate(Context *context, GLenum modeRGB, GLenum modeAlpha)
+{
+ if (!ValidBlendEquationMode(context, modeRGB))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBlendEquation);
+ return false;
+ }
+
+ if (!ValidBlendEquationMode(context, modeAlpha))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBlendEquation);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBlendFunc(Context *context, GLenum sfactor, GLenum dfactor)
+{
+ return ValidateBlendFuncSeparate(context, sfactor, dfactor, sfactor, dfactor);
+}
+
+bool ValidateBlendFuncSeparate(Context *context,
+ GLenum srcRGB,
+ GLenum dstRGB,
+ GLenum srcAlpha,
+ GLenum dstAlpha)
+{
+ if (!ValidSrcBlendFunc(context, srcRGB))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidDstBlendFunc(context, dstRGB))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidSrcBlendFunc(context, srcAlpha))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidDstBlendFunc(context, dstAlpha))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidBlendFunction);
+ return false;
+ }
+
+ if (context->getLimitations().noSimultaneousConstantColorAndAlphaBlendFunc ||
+ context->getExtensions().webglCompatibility)
+ {
+ 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->getExtensions().webglCompatibility)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidConstantColor);
+ return false;
+ }
+
+ WARN() << kConstantColorAlphaLimitation;
+ context->validationError(GL_INVALID_OPERATION, kConstantColorAlphaLimitation);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetString(Context *context, 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().requestExtension)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidName);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateLineWidth(Context *context, GLfloat width)
+{
+ if (width <= 0.0f || isNaN(width))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidWidth);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDepthRangef(Context *context, GLfloat zNear, GLfloat zFar)
+{
+ if (context->getExtensions().webglCompatibility && zNear > zFar)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidDepthRange);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRenderbufferStorage(Context *context,
+ GLenum target,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ return ValidateRenderbufferStorageParametersBase(context, target, 0, internalformat, width,
+ height);
+}
+
+bool ValidateRenderbufferStorageMultisampleANGLE(Context *context,
+ GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (!context->getExtensions().framebufferMultisample)
+ {
+ context->validationError(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 (static_cast<GLuint>(samples) > context->getCaps().maxSamples)
+ {
+ context->validationError(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(GL_OUT_OF_MEMORY, kSamplesOutOfRange);
+ return false;
+ }
+ }
+
+ return ValidateRenderbufferStorageParametersBase(context, target, samples, internalformat,
+ width, height);
+}
+
+bool ValidateCheckFramebufferStatus(Context *context, GLenum target)
+{
+ if (!ValidFramebufferTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFramebufferTarget);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateClearColor(Context *context, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ return true;
+}
+
+bool ValidateClearDepthf(Context *context, GLfloat depth)
+{
+ return true;
+}
+
+bool ValidateClearStencil(Context *context, GLint s)
+{
+ return true;
+}
+
+bool ValidateColorMask(Context *context,
+ GLboolean red,
+ GLboolean green,
+ GLboolean blue,
+ GLboolean alpha)
+{
+ return true;
+}
+
+bool ValidateCompileShader(Context *context, GLuint shader)
+{
+ return true;
+}
+
+bool ValidateCreateProgram(Context *context)
+{
+ return true;
+}
+
+bool ValidateCullFace(Context *context, CullFaceMode mode)
+{
+ switch (mode)
+ {
+ case CullFaceMode::Front:
+ case CullFaceMode::Back:
+ case CullFaceMode::FrontAndBack:
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidCullMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteProgram(Context *context, GLuint program)
+{
+ if (program == 0)
+ {
+ return false;
+ }
+
+ if (!context->getProgramResolveLink(program))
+ {
+ if (context->getShader(program))
+ {
+ context->validationError(GL_INVALID_OPERATION, kExpectedProgramName);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidProgramName);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDeleteShader(Context *context, GLuint shader)
+{
+ if (shader == 0)
+ {
+ return false;
+ }
+
+ if (!context->getShader(shader))
+ {
+ if (context->getProgramResolveLink(shader))
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidShaderName);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_VALUE, kExpectedShaderName);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDepthFunc(Context *context, 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->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDepthMask(Context *context, GLboolean flag)
+{
+ return true;
+}
+
+bool ValidateDetachShader(Context *context, GLuint program, GLuint shader)
+{
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ const Shader *attachedShader = programObject->getAttachedShader(shaderObject->getType());
+ if (attachedShader != shaderObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kShaderToDetachMustBeAttached);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDisableVertexAttribArray(Context *context, GLuint index)
+{
+ if (index >= MAX_VERTEX_ATTRIBS)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateEnableVertexAttribArray(Context *context, GLuint index)
+{
+ if (index >= MAX_VERTEX_ATTRIBS)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFinish(Context *context)
+{
+ return true;
+}
+
+bool ValidateFlush(Context *context)
+{
+ return true;
+}
+
+bool ValidateFrontFace(Context *context, GLenum mode)
+{
+ switch (mode)
+ {
+ case GL_CW:
+ case GL_CCW:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveAttrib(Context *context,
+ GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLint *size,
+ GLenum *type,
+ GLchar *name)
+{
+ if (bufsize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(programObject->getActiveAttributeCount()))
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxActiveUniform);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniform(Context *context,
+ GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLint *size,
+ GLenum *type,
+ GLchar *name)
+{
+ if (bufsize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(programObject->getActiveUniformCount()))
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxActiveUniform);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetAttachedShaders(Context *context,
+ GLuint program,
+ GLsizei maxcount,
+ GLsizei *count,
+ GLuint *shaders)
+{
+ if (maxcount < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeMaxCount);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetAttribLocation(Context *context, GLuint program, const GLchar *name)
+{
+ // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+ // shader-related entry points
+ if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidNameCharacters);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotBound);
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBooleanv(Context *context, GLenum pname, GLboolean *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetError(Context *context)
+{
+ return true;
+}
+
+bool ValidateGetFloatv(Context *context, GLenum pname, GLfloat *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetIntegerv(Context *context, GLenum pname, GLint *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetProgramInfoLog(Context *context,
+ GLuint program,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLchar *infolog)
+{
+ if (bufsize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderInfoLog(Context *context,
+ GLuint shader,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLchar *infolog)
+{
+ if (bufsize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderPrecisionFormat(Context *context,
+ GLenum shadertype,
+ GLenum precisiontype,
+ GLint *range,
+ GLint *precision)
+{
+ switch (shadertype)
+ {
+ case GL_VERTEX_SHADER:
+ case GL_FRAGMENT_SHADER:
+ break;
+ case GL_COMPUTE_SHADER:
+ context->validationError(GL_INVALID_OPERATION, kUnimplementedComputeShaderPrecision);
+ return false;
+ default:
+ context->validationError(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(GL_INVALID_ENUM, kInvalidPrecision);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderSource(Context *context,
+ GLuint shader,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLchar *source)
+{
+ if (bufsize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformLocation(Context *context, GLuint 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->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidNameCharacters);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateHint(Context *context, GLenum target, GLenum mode)
+{
+ switch (mode)
+ {
+ case GL_FASTEST:
+ case GL_NICEST:
+ case GL_DONT_CARE:
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ switch (target)
+ {
+ case GL_GENERATE_MIPMAP_HINT:
+ break;
+
+ case GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
+ if (context->getClientVersion() < ES_3_0 &&
+ !context->getExtensions().standardDerivatives)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ 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->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsBuffer(Context *context, GLuint buffer)
+{
+ return true;
+}
+
+bool ValidateIsFramebuffer(Context *context, GLuint framebuffer)
+{
+ return true;
+}
+
+bool ValidateIsProgram(Context *context, GLuint program)
+{
+ return true;
+}
+
+bool ValidateIsRenderbuffer(Context *context, GLuint renderbuffer)
+{
+ return true;
+}
+
+bool ValidateIsShader(Context *context, GLuint shader)
+{
+ return true;
+}
+
+bool ValidateIsTexture(Context *context, GLuint texture)
+{
+ return true;
+}
+
+bool ValidatePixelStorei(Context *context, GLenum pname, GLint param)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ switch (pname)
+ {
+ case GL_UNPACK_IMAGE_HEIGHT:
+ case GL_UNPACK_SKIP_IMAGES:
+ context->validationError(GL_INVALID_ENUM, kInvalidPname);
+ return false;
+
+ case GL_UNPACK_ROW_LENGTH:
+ case GL_UNPACK_SKIP_ROWS:
+ case GL_UNPACK_SKIP_PIXELS:
+ if (!context->getExtensions().unpackSubimage)
+ {
+ context->validationError(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().packSubimage)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+ break;
+ }
+ }
+
+ if (param < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeParam);
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_UNPACK_ALIGNMENT:
+ if (param != 1 && param != 2 && param != 4 && param != 8)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidUnpackAlignment);
+ return false;
+ }
+ break;
+
+ case GL_PACK_ALIGNMENT:
+ if (param != 1 && param != 2 && param != 4 && param != 8)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidUnpackAlignment);
+ return false;
+ }
+ break;
+
+ case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
+ if (!context->getExtensions().packReverseRowOrder)
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ }
+ 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->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePolygonOffset(Context *context, GLfloat factor, GLfloat units)
+{
+ return true;
+}
+
+bool ValidateReleaseShaderCompiler(Context *context)
+{
+ return true;
+}
+
+bool ValidateSampleCoverage(Context *context, GLfloat value, GLboolean invert)
+{
+ return true;
+}
+
+bool ValidateScissor(Context *context, GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ if (width < 0 || height < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateShaderBinary(Context *context,
+ GLsizei n,
+ const GLuint *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(GL_INVALID_ENUM, kInvalidShaderBinaryFormat);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateShaderSource(Context *context,
+ GLuint shader,
+ GLsizei count,
+ const GLchar *const *string,
+ const GLint *length)
+{
+ if (count < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+ // shader-related entry points
+ if (context->getExtensions().webglCompatibility)
+ {
+ for (GLsizei i = 0; i < count; i++)
+ {
+ size_t len =
+ (length && length[i] >= 0) ? static_cast<size_t>(length[i]) : strlen(string[i]);
+
+ // Backslash as line-continuation is allowed in WebGL 2.0.
+ if (!IsValidESSLShaderSourceString(string[i], len,
+ context->getClientVersion() >= ES_3_0))
+ {
+ context->validationError(GL_INVALID_VALUE, kShaderSourceInvalidCharacters);
+ return false;
+ }
+ }
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilFunc(Context *context, GLenum func, GLint ref, GLuint mask)
+{
+ if (!IsValidStencilFunc(func))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilFuncSeparate(Context *context, GLenum face, GLenum func, GLint ref, GLuint mask)
+{
+ if (!IsValidStencilFace(face))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilFunc(func))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilMask(Context *context, GLuint mask)
+{
+ return true;
+}
+
+bool ValidateStencilMaskSeparate(Context *context, GLenum face, GLuint mask)
+{
+ if (!IsValidStencilFace(face))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilOp(Context *context, GLenum fail, GLenum zfail, GLenum zpass)
+{
+ if (!IsValidStencilOp(fail))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilOp(zfail))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilOp(zpass))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilOpSeparate(Context *context,
+ GLenum face,
+ GLenum fail,
+ GLenum zfail,
+ GLenum zpass)
+{
+ if (!IsValidStencilFace(face))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidStencil);
+ return false;
+ }
+
+ return ValidateStencilOp(context, fail, zfail, zpass);
+}
+
+bool ValidateUniform1f(Context *context, GLint location, GLfloat x)
+{
+ return ValidateUniform(context, GL_FLOAT, location, 1);
+}
+
+bool ValidateUniform1fv(Context *context, GLint location, GLsizei count, const GLfloat *v)
+{
+ return ValidateUniform(context, GL_FLOAT, location, count);
+}
+
+bool ValidateUniform1i(Context *context, GLint location, GLint x)
+{
+ return ValidateUniform1iv(context, location, 1, &x);
+}
+
+bool ValidateUniform2fv(Context *context, GLint location, GLsizei count, const GLfloat *v)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC2, location, count);
+}
+
+bool ValidateUniform2i(Context *context, GLint location, GLint x, GLint y)
+{
+ return ValidateUniform(context, GL_INT_VEC2, location, 1);
+}
+
+bool ValidateUniform2iv(Context *context, GLint location, GLsizei count, const GLint *v)
+{
+ return ValidateUniform(context, GL_INT_VEC2, location, count);
+}
+
+bool ValidateUniform3f(Context *context, GLint location, GLfloat x, GLfloat y, GLfloat z)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC3, location, 1);
+}
+
+bool ValidateUniform3fv(Context *context, GLint location, GLsizei count, const GLfloat *v)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC3, location, count);
+}
+
+bool ValidateUniform3i(Context *context, GLint location, GLint x, GLint y, GLint z)
+{
+ return ValidateUniform(context, GL_INT_VEC3, location, 1);
+}
+
+bool ValidateUniform3iv(Context *context, GLint location, GLsizei count, const GLint *v)
+{
+ return ValidateUniform(context, GL_INT_VEC3, location, count);
+}
+
+bool ValidateUniform4f(Context *context, GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC4, location, 1);
+}
+
+bool ValidateUniform4fv(Context *context, GLint location, GLsizei count, const GLfloat *v)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC4, location, count);
+}
+
+bool ValidateUniform4i(Context *context, GLint location, GLint x, GLint y, GLint z, GLint w)
+{
+ return ValidateUniform(context, GL_INT_VEC4, location, 1);
+}
+
+bool ValidateUniform4iv(Context *context, GLint location, GLsizei count, const GLint *v)
+{
+ return ValidateUniform(context, GL_INT_VEC4, location, count);
+}
+
+bool ValidateUniformMatrix2fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, GL_FLOAT_MAT2, location, count, transpose);
+}
+
+bool ValidateUniformMatrix3fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, GL_FLOAT_MAT3, location, count, transpose);
+}
+
+bool ValidateUniformMatrix4fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, GL_FLOAT_MAT4, location, count, transpose);
+}
+
+bool ValidateValidateProgram(Context *context, GLuint program)
+{
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttrib1f(Context *context, GLuint index, GLfloat x)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib1fv(Context *context, GLuint index, const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib2f(Context *context, GLuint index, GLfloat x, GLfloat y)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib2fv(Context *context, GLuint index, const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib3f(Context *context, GLuint index, GLfloat x, GLfloat y, GLfloat z)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib3fv(Context *context, GLuint index, const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib4f(Context *context,
+ GLuint index,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib4fv(Context *context, GLuint index, const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateViewport(Context *context, GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ if (width < 0 || height < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kViewportNegativeSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetFramebufferAttachmentParameteriv(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLenum pname,
+ GLint *params)
+{
+ return ValidateGetFramebufferAttachmentParameterivBase(context, target, attachment, pname,
+ nullptr);
+}
+
+bool ValidateGetProgramiv(Context *context, GLuint program, GLenum pname, GLint *params)
+{
+ return ValidateGetProgramivBase(context, program, pname, nullptr);
+}
+
+bool ValidateCopyTexImage2D(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2CopyTexImageParameters(context, target, level, internalformat, false, 0,
+ 0, x, y, width, height, border);
+ }
+
+ ASSERT(context->getClientMajorVersion() == 3);
+ return ValidateES3CopyTexImage2DParameters(context, target, level, internalformat, false, 0, 0,
+ 0, x, y, width, height, border);
+}
+
+bool ValidateCopyTexSubImage2D(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2CopyTexImageParameters(context, target, level, GL_NONE, true, xoffset,
+ yoffset, x, y, width, height, 0);
+ }
+
+ return ValidateES3CopyTexImage2DParameters(context, target, level, GL_NONE, true, xoffset,
+ yoffset, 0, x, y, width, height, 0);
+}
+
+bool ValidateCopyTexSubImage3DOES(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ return ValidateCopyTexSubImage3D(context, target, level, xoffset, yoffset, zoffset, x, y, width,
+ height);
+}
+
+bool ValidateDeleteBuffers(Context *context, GLint n, const GLuint *)
+{
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateDeleteFramebuffers(Context *context, GLint n, const GLuint *)
+{
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateDeleteRenderbuffers(Context *context, GLint n, const GLuint *)
+{
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateDeleteTextures(Context *context, GLint n, const GLuint *)
+{
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateDisable(Context *context, GLenum cap)
+{
+ if (!ValidCap(context, cap, false))
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateEnable(Context *context, GLenum cap)
+{
+ if (!ValidCap(context, cap, false))
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ if (context->getLimitations().noSampleAlphaToCoverageSupport &&
+ cap == GL_SAMPLE_ALPHA_TO_COVERAGE)
+ {
+ context->validationError(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(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer)
+{
+ if (!ValidFramebufferTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFramebufferTarget);
+ return false;
+ }
+
+ if (renderbuffertarget != GL_RENDERBUFFER && renderbuffer != 0)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidRenderbufferTarget);
+ return false;
+ }
+
+ return ValidateFramebufferRenderbufferParameters(context, target, attachment,
+ renderbuffertarget, renderbuffer);
+}
+
+bool ValidateFramebufferTexture2D(Context *context,
+ GLenum target,
+ GLenum attachment,
+ TextureTarget textarget,
+ GLuint 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().fboRenderMipmap &&
+ level != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidFramebufferTextureLevel);
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ if (texture != 0)
+ {
+ gl::Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ const gl::Caps &caps = context->getCaps();
+
+ switch (textarget)
+ {
+ case TextureTarget::_2D:
+ {
+ if (level > gl::log2(caps.max2DTextureSize))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ if (tex->getType() != TextureType::_2D)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget);
+ return false;
+ }
+ }
+ break;
+
+ case TextureTarget::Rectangle:
+ {
+ if (level != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ if (tex->getType() != TextureType::Rectangle)
+ {
+ context->validationError(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 > gl::log2(caps.maxCubeMapTextureSize))
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
+ return false;
+ }
+ if (tex->getType() != TextureType::CubeMap)
+ {
+ context->validationError(GL_INVALID_OPERATION, kTextureTargetMismatch);
+ return false;
+ }
+ }
+ break;
+
+ case TextureTarget::_2DMultisample:
+ {
+ if (context->getClientVersion() < ES_3_1 &&
+ !context->getExtensions().textureMultisample)
+ {
+ context->validationError(GL_INVALID_OPERATION,
+ kMultisampleTextureExtensionOrES31Required);
+ return false;
+ }
+
+ if (level != 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kLevelNotZero);
+ return false;
+ }
+ if (tex->getType() != TextureType::_2DMultisample)
+ {
+ context->validationError(GL_INVALID_OPERATION, kTextureTargetMismatch);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferTexture3DOES(Context *context,
+ GLenum target,
+ GLenum attachment,
+ TextureTarget textargetPacked,
+ GLuint texture,
+ GLint level,
+ GLint zoffset)
+{
+ UNIMPLEMENTED();
+ return false;
+}
+
+bool ValidateGenBuffers(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenFramebuffers(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenRenderbuffers(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenTextures(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenerateMipmap(Context *context, TextureType target)
+{
+ if (!ValidTextureTarget(context, target))
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ return false;
+ }
+
+ Texture *texture = context->getTextureByType(target);
+
+ if (texture == nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kTextureNotBound);
+ return false;
+ }
+
+ const GLuint effectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel();
+
+ // This error isn't spelled out in the spec in a very explicit way, but we interpret the spec so
+ // that out-of-range base level has a non-color-renderable / non-texture-filterable format.
+ if (effectiveBaseLevel >= gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
+ {
+ context->validationError(GL_INVALID_OPERATION, kBaseLevelOutOfRange);
+ return false;
+ }
+
+ TextureTarget baseTarget = (target == TextureType::CubeMap)
+ ? TextureTarget::CubeMapPositiveX
+ : NonCubeTextureTypeToTarget(target);
+ const auto &format = *(texture->getFormat(baseTarget, effectiveBaseLevel).info);
+ if (format.sizedInternalFormat == GL_NONE || format.compressed || format.depthBits > 0 ||
+ format.stencilBits > 0)
+ {
+ context->validationError(GL_INVALID_OPERATION, kGenerateMipmapNotAllowed);
+ return false;
+ }
+
+ // GenerateMipmap accepts formats that are unsized or both color renderable and filterable.
+ bool formatUnsized = !format.sized;
+ bool formatColorRenderableAndFilterable =
+ format.filterSupport(context->getClientVersion(), context->getExtensions()) &&
+ format.textureAttachmentSupport(context->getClientVersion(), context->getExtensions());
+ if (!formatUnsized && !formatColorRenderableAndFilterable)
+ {
+ context->validationError(GL_INVALID_OPERATION, kGenerateMipmapNotAllowed);
+ return false;
+ }
+
+ // GL_EXT_sRGB adds an unsized SRGB (no alpha) format which has explicitly disabled mipmap
+ // generation
+ if (format.colorEncoding == GL_SRGB && format.format == GL_RGB)
+ {
+ context->validationError(GL_INVALID_OPERATION, kGenerateMipmapNotAllowed);
+ return false;
+ }
+
+ // According to the OpenGL extension spec EXT_sRGB.txt, EXT_SRGB is based on ES 2.0 and
+ // generateMipmap is not allowed if texture format is SRGB_EXT or SRGB_ALPHA_EXT.
+ if (context->getClientVersion() < Version(3, 0) && format.colorEncoding == GL_SRGB)
+ {
+ context->validationError(GL_INVALID_OPERATION, kGenerateMipmapNotAllowed);
+ return false;
+ }
+
+ // Non-power of 2 ES2 check
+ if (context->getClientVersion() < Version(3, 0) && !context->getExtensions().textureNPOT &&
+ (!isPow2(static_cast<int>(texture->getWidth(baseTarget, 0))) ||
+ !isPow2(static_cast<int>(texture->getHeight(baseTarget, 0)))))
+ {
+ ASSERT(target == TextureType::_2D || target == TextureType::Rectangle ||
+ target == TextureType::CubeMap);
+ context->validationError(GL_INVALID_OPERATION, kTextureNotPow2);
+ return false;
+ }
+
+ // Cube completeness check
+ if (target == TextureType::CubeMap && !texture->getTextureState().isCubeComplete())
+ {
+ context->validationError(GL_INVALID_OPERATION, kCubemapIncomplete);
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibility &&
+ (texture->getWidth(baseTarget, effectiveBaseLevel) == 0 ||
+ texture->getHeight(baseTarget, effectiveBaseLevel) == 0))
+ {
+ context->validationError(GL_INVALID_OPERATION, kGenerateMipmapZeroSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferParameteriv(Context *context,
+ BufferBinding target,
+ GLenum pname,
+ GLint *params)
+{
+ return ValidateGetBufferParameterBase(context, target, pname, false, nullptr);
+}
+
+bool ValidateGetRenderbufferParameteriv(Context *context,
+ GLenum target,
+ GLenum pname,
+ GLint *params)
+{
+ return ValidateGetRenderbufferParameterivBase(context, target, pname, nullptr);
+}
+
+bool ValidateGetShaderiv(Context *context, GLuint shader, GLenum pname, GLint *params)
+{
+ return ValidateGetShaderivBase(context, shader, pname, nullptr);
+}
+
+bool ValidateGetTexParameterfv(Context *context, TextureType target, GLenum pname, GLfloat *params)
+{
+ return ValidateGetTexParameterBase(context, target, pname, nullptr);
+}
+
+bool ValidateGetTexParameteriv(Context *context, TextureType target, GLenum pname, GLint *params)
+{
+ return ValidateGetTexParameterBase(context, target, pname, nullptr);
+}
+
+bool ValidateGetTexParameterIivOES(Context *context,
+ TextureType target,
+ GLenum pname,
+ GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGetTexParameterBase(context, target, pname, nullptr);
+}
+
+bool ValidateGetTexParameterIuivOES(Context *context,
+ TextureType target,
+ GLenum pname,
+ GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateGetTexParameterBase(context, target, pname, nullptr);
+}
+
+bool ValidateGetUniformfv(Context *context, GLuint program, GLint location, GLfloat *params)
+{
+ return ValidateGetUniformBase(context, program, location);
+}
+
+bool ValidateGetUniformiv(Context *context, GLuint program, GLint location, GLint *params)
+{
+ return ValidateGetUniformBase(context, program, location);
+}
+
+bool ValidateGetVertexAttribfv(Context *context, GLuint index, GLenum pname, GLfloat *params)
+{
+ return ValidateGetVertexAttribBase(context, index, pname, nullptr, false, false);
+}
+
+bool ValidateGetVertexAttribiv(Context *context, GLuint index, GLenum pname, GLint *params)
+{
+ return ValidateGetVertexAttribBase(context, index, pname, nullptr, false, false);
+}
+
+bool ValidateGetVertexAttribPointerv(Context *context, GLuint index, GLenum pname, void **pointer)
+{
+ return ValidateGetVertexAttribBase(context, index, pname, nullptr, true, false);
+}
+
+bool ValidateIsEnabled(Context *context, GLenum cap)
+{
+ if (!ValidCap(context, cap, true))
+ {
+ context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateLinkProgram(Context *context, GLuint program)
+{
+ if (context->hasActiveTransformFeedback(program))
+ {
+ // ES 3.0.4 section 2.15 page 91
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackActiveDuringLink);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateReadPixels(Context *context,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ void *pixels)
+{
+ return ValidateReadPixelsBase(context, x, y, width, height, format, type, -1, nullptr, nullptr,
+ nullptr, pixels);
+}
+
+bool ValidateTexParameterf(Context *context, TextureType target, GLenum pname, GLfloat param)
+{
+ return ValidateTexParameterBase(context, target, pname, -1, false, &param);
+}
+
+bool ValidateTexParameterfv(Context *context,
+ TextureType target,
+ GLenum pname,
+ const GLfloat *params)
+{
+ return ValidateTexParameterBase(context, target, pname, -1, true, params);
+}
+
+bool ValidateTexParameteri(Context *context, TextureType target, GLenum pname, GLint param)
+{
+ return ValidateTexParameterBase(context, target, pname, -1, false, &param);
+}
+
+bool ValidateTexParameteriv(Context *context, TextureType target, GLenum pname, const GLint *params)
+{
+ return ValidateTexParameterBase(context, target, pname, -1, true, params);
+}
+
+bool ValidateTexParameterIivOES(Context *context,
+ TextureType target,
+ GLenum pname,
+ const GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateTexParameterBase(context, target, pname, -1, true, params);
+}
+
+bool ValidateTexParameterIuivOES(Context *context,
+ TextureType target,
+ GLenum pname,
+ const GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kES3Required);
+ return false;
+ }
+ return ValidateTexParameterBase(context, target, pname, -1, true, params);
+}
+
+bool ValidateUseProgram(Context *context, GLuint program)
+{
+ if (program != 0)
+ {
+ Program *programObject = context->getProgramResolveLink(program);
+ if (!programObject)
+ {
+ // ES 3.1.0 section 7.3 page 72
+ if (context->getShader(program))
+ {
+ context->validationError(GL_INVALID_OPERATION, kExpectedProgramName);
+ return false;
+ }
+ else
+ {
+ context->validationError(GL_INVALID_VALUE, kInvalidProgramName);
+ return false;
+ }
+ }
+ if (!programObject->isLinked())
+ {
+ context->validationError(GL_INVALID_OPERATION, kProgramNotLinked);
+ return false;
+ }
+ }
+ if (context->getState().isTransformFeedbackActiveUnpaused())
+ {
+ // ES 3.0.4 section 2.15 page 91
+ context->validationError(GL_INVALID_OPERATION, kTransformFeedbackUseProgram);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteFencesNV(Context *context, GLsizei n, const GLuint *fences)
+{
+ if (!context->getExtensions().fence)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ if (n < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFinishFenceNV(Context *context, GLuint fence)
+{
+ if (!context->getExtensions().fence)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ FenceNV *fenceObject = context->getFenceNV(fence);
+
+ if (fenceObject == nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFence);
+ return false;
+ }
+
+ if (!fenceObject->isSet())
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFenceState);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGenFencesNV(Context *context, GLsizei n, GLuint *fences)
+{
+ if (!context->getExtensions().fence)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ if (n < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeCount);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetFenceivNV(Context *context, GLuint fence, GLenum pname, GLint *params)
+{
+ if (!context->getExtensions().fence)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ FenceNV *fenceObject = context->getFenceNV(fence);
+
+ if (fenceObject == nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFence);
+ return false;
+ }
+
+ if (!fenceObject->isSet())
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFenceState);
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_FENCE_STATUS_NV:
+ case GL_FENCE_CONDITION_NV:
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidPname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetGraphicsResetStatusEXT(Context *context)
+{
+ if (!context->getExtensions().robustness)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetTranslatedShaderSourceANGLE(Context *context,
+ GLuint shader,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLchar *source)
+{
+ if (!context->getExtensions().translatedShaderSource)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (bufsize < 0)
+ {
+ context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
+ return false;
+ }
+
+ Shader *shaderObject = context->getShader(shader);
+
+ if (!shaderObject)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidShaderName);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsFenceNV(Context *context, GLuint fence)
+{
+ if (!context->getExtensions().fence)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateSetFenceNV(Context *context, GLuint fence, GLenum condition)
+{
+ if (!context->getExtensions().fence)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ if (condition != GL_ALL_COMPLETED_NV)
+ {
+ context->validationError(GL_INVALID_ENUM, kInvalidFenceCondition);
+ return false;
+ }
+
+ FenceNV *fenceObject = context->getFenceNV(fence);
+
+ if (fenceObject == nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFence);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTestFenceNV(Context *context, GLuint fence)
+{
+ if (!context->getExtensions().fence)
+ {
+ context->validationError(GL_INVALID_OPERATION, kNVFenceNotSupported);
+ return false;
+ }
+
+ FenceNV *fenceObject = context->getFenceNV(fence);
+
+ if (fenceObject == nullptr)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFence);
+ return false;
+ }
+
+ if (fenceObject->isSet() != GL_TRUE)
+ {
+ context->validationError(GL_INVALID_OPERATION, kInvalidFenceState);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexStorage2DEXT(Context *context,
+ TextureType type,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (!context->getExtensions().textureStorage)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ return ValidateES2TexStorageParameters(context, type, levels, internalformat, width,
+ height);
+ }
+
+ ASSERT(context->getClientMajorVersion() >= 3);
+ return ValidateES3TexStorage2DParameters(context, type, levels, internalformat, width, height,
+ 1);
+}
+
+bool ValidateVertexAttribDivisorANGLE(Context *context, GLuint index, GLuint divisor)
+{
+ if (!context->getExtensions().instancedArraysANGLE)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (index >= MAX_VERTEX_ATTRIBS)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ if (context->getLimitations().attributeZeroRequiresZeroDivisorInEXT)
+ {
+ if (index == 0 && divisor != 0)
+ {
+ context->validationError(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(Context *context, GLuint index, GLuint divisor)
+{
+ if (!context->getExtensions().instancedArraysEXT)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (index >= MAX_VERTEX_ATTRIBS)
+ {
+ context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexImage3DOES(Context *context,
+ TextureTarget target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void *pixels)
+{
+ return ValidateTexImage3D(context, target, level, internalformat, width, height, depth, border,
+ format, type, pixels);
+}
+
+bool ValidatePopGroupMarkerEXT(Context *context)
+{
+ if (!context->getExtensions().debugMarker)
+ {
+ // 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(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexStorage1DEXT(Context *context,
+ GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width)
+{
+ UNIMPLEMENTED();
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+}
+
+bool ValidateTexStorage3DEXT(Context *context,
+ TextureType target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (!context->getExtensions().textureStorage)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ return ValidateES3TexStorage3DParameters(context, target, levels, internalformat, width, height,
+ depth);
+}
+
+bool ValidateMaxShaderCompilerThreadsKHR(Context *context, GLuint count)
+{
+ if (!context->getExtensions().parallelShaderCompile)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateMultiDrawArraysANGLE(Context *context,
+ PrimitiveMode mode,
+ const GLint *firsts,
+ const GLsizei *counts,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDraw)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawArrays(context, mode, firsts[drawID], counts[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateMultiDrawElementsANGLE(Context *context,
+ PrimitiveMode mode,
+ const GLsizei *counts,
+ DrawElementsType type,
+ const GLvoid *const *indices,
+ GLsizei drawcount)
+{
+ if (!context->getExtensions().multiDraw)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+ for (GLsizei drawID = 0; drawID < drawcount; ++drawID)
+ {
+ if (!ValidateDrawElements(context, mode, counts[drawID], type, indices[drawID]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateProvokingVertexANGLE(Context *context, ProvokingVertexConvention modePacked)
+{
+ if (!context->getExtensions().provokingVertex)
+ {
+ context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+ return false;
+ }
+
+ switch (modePacked)
+ {
+ case ProvokingVertexConvention::FirstVertexConvention:
+ case ProvokingVertexConvention::LastVertexConvention:
+ break;
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidProvokingVertex);
+ return false;
+ }
+
+ return true;
+}
+
+void RecordBindTextureTypeError(Context *context, TextureType target)
+{
+ ASSERT(!context->getStateCache().isValidBindTextureType(target));
+
+ switch (target)
+ {
+ case TextureType::Rectangle:
+ ASSERT(!context->getExtensions().textureRectangle);
+ context->validationError(GL_INVALID_ENUM, kTextureRectangleNotSupported);
+ break;
+
+ case TextureType::_3D:
+ case TextureType::_2DArray:
+ ASSERT(context->getClientMajorVersion() < 3);
+ context->validationError(GL_INVALID_ENUM, kES3Required);
+ break;
+
+ case TextureType::_2DMultisample:
+ ASSERT(context->getClientVersion() < Version(3, 1) &&
+ !context->getExtensions().textureMultisample);
+ context->validationError(GL_INVALID_ENUM, kMultisampleTextureExtensionOrES31Required);
+ break;
+
+ case TextureType::_2DMultisampleArray:
+ ASSERT(!context->getExtensions().textureStorageMultisample2DArray);
+ context->validationError(GL_INVALID_ENUM, kMultisampleArrayExtensionRequired);
+ break;
+
+ case TextureType::External:
+ ASSERT(!context->getExtensions().eglImageExternal &&
+ !context->getExtensions().eglStreamConsumerExternal);
+ context->validationError(GL_INVALID_ENUM, kExternalTextureNotSupported);
+ break;
+
+ default:
+ context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
+ }
+}
+
+} // namespace gl