summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/libANGLE/validationES2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/angle/src/libANGLE/validationES2.cpp')
-rw-r--r--src/3rdparty/angle/src/libANGLE/validationES2.cpp6313
1 files changed, 5454 insertions, 859 deletions
diff --git a/src/3rdparty/angle/src/libANGLE/validationES2.cpp b/src/3rdparty/angle/src/libANGLE/validationES2.cpp
index 2e5b955e99..5e505aa607 100644
--- a/src/3rdparty/angle/src/libANGLE/validationES2.cpp
+++ b/src/3rdparty/angle/src/libANGLE/validationES2.cpp
@@ -7,16 +7,24 @@
// validationES2.cpp: Validation functions for OpenGL ES 2.0 entry point parameters
#include "libANGLE/validationES2.h"
-#include "libANGLE/validationES.h"
+
+#include <cstdint>
+
+#include "common/mathutil.h"
+#include "common/string_utils.h"
+#include "common/utilities.h"
#include "libANGLE/Context.h"
-#include "libANGLE/Texture.h"
+#include "libANGLE/ErrorStrings.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/FramebufferAttachment.h"
-
-#include "common/mathutil.h"
-#include "common/utilities.h"
+#include "libANGLE/validationES.h"
+#include "libANGLE/validationES3.h"
namespace gl
{
@@ -45,9 +53,9 @@ bool IsPartialBlit(gl::Context *context,
return true;
}
- if (context->getState().isScissorTestEnabled())
+ if (context->getGLState().isScissorTestEnabled())
{
- const Rectangle &scissor = context->getState().getScissor();
+ const Rectangle &scissor = context->getGLState().getScissor();
return scissor.x > 0 || scissor.y > 0 || scissor.width < writeSize.width ||
scissor.height < writeSize.height;
}
@@ -55,35 +63,999 @@ bool IsPartialBlit(gl::Context *context,
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->hasPath(pathName) && !context->hasPathData(pathName))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoSuchPath);
+ 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->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+
+ if (paths == nullptr)
+ {
+ context->handleError(InvalidValue() << "No path name array.");
+ return false;
+ }
+
+ if (numPaths < 0)
+ {
+ context->handleError(InvalidValue() << "Invalid (negative) numPaths.");
+ return false;
+ }
+
+ if (!angle::IsValueInRangeForNumericType<std::uint32_t>(numPaths))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
+ 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->handleError(InvalidEnum() << "Invalid path name type.");
+ 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->handleError(InvalidEnum() << "Invalid transformation.");
+ return false;
+ }
+ if (componentCount != 0 && transformValues == nullptr)
+ {
+ context->handleError(InvalidValue() << "No transform array given.");
+ return false;
+ }
+
+ angle::CheckedNumeric<std::uint32_t> checkedSize(0);
+ checkedSize += (numPaths * pathNameTypeSize);
+ checkedSize += (numPaths * sizeof(GLfloat) * componentCount);
+ if (!checkedSize.IsValid())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
+ 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))
+ {
+ return false;
+ }
+
+ if (!ValidES3FormatCombination(GetUnsizedFormat(internalFormat), type, internalFormat))
+ {
+ context->handleError(InvalidOperation()
+ << "Invalid combination of type and internalFormat.");
+ return false;
+ }
+
+ const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat, type);
+ if (!internalFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool IsValidCopyTextureDestinationTargetEnum(Context *context, GLenum target)
+{
+ switch (target)
+ {
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ return true;
+
+ case GL_TEXTURE_RECTANGLE_ANGLE:
+ return context->getExtensions().textureRectangle;
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopyTextureDestinationTarget(Context *context, GLenum textureType, GLenum target)
+{
+ if (IsCubeMapTextureTarget(target))
+ {
+ return textureType == GL_TEXTURE_CUBE_MAP;
+ }
+ else
+ {
+ return textureType == target;
+ }
+}
+
+bool IsValidCopyTextureSourceTarget(Context *context, GLenum target)
+{
+ switch (target)
+ {
+ case GL_TEXTURE_2D:
+ return true;
+ case GL_TEXTURE_RECTANGLE_ANGLE:
+ return context->getExtensions().textureRectangle;
+
+ // TODO(geofflang): accept GL_TEXTURE_EXTERNAL_OES if the texture_external extension is
+ // supported
+
+ default:
+ return false;
+ }
+}
+
+bool IsValidCopyTextureSourceLevel(Context *context, GLenum target, GLint level)
+{
+ if (!ValidMipLevel(context, target, level))
+ {
+ return false;
+ }
+
+ if (level > 0 && context->getClientVersion() < ES_3_0)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool IsValidCopyTextureDestinationLevel(Context *context,
+ GLenum target,
+ GLint level,
+ GLsizei width,
+ GLsizei height)
+{
+ if (!ValidMipLevel(context, target, level))
+ {
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (target == GL_TEXTURE_2D)
+ {
+ if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
+ {
+ return false;
+ }
+ }
+ else if (target == GL_TEXTURE_RECTANGLE_ANGLE)
+ {
+ ASSERT(level == 0);
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize)
+ {
+ return false;
+ }
+ }
+ else if (IsCubeMapTextureTarget(target))
+ {
+ if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.maxCubeMapTextureSize >> level))
+ {
+ return false;
+ }
+ }
+
+ 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(ValidationContext *context,
+ GLenum 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))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
+ return false;
+ }
+
+ if (!ValidImageSizeParameters(context, target, level, width, height, 1, isSubImage))
+ {
+ context->handleError(InvalidValue() << "Invalid texture dimensions.");
+ 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->getGLState().getReadFramebuffer();
+ GLenum colorbufferFormat =
+ framebuffer->getReadColorbuffer()->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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ return false;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_STENCIL_OES:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ return false;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ return false;
+ }
+
+ if (formatInfo.type == GL_FLOAT && !context->getExtensions().textureFloat)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ if (context->getExtensions().textureCompressionDXT1)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ return false;
+ }
+ else
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ if (context->getExtensions().textureCompressionDXT3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ return false;
+ }
+ else
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ if (context->getExtensions().textureCompressionDXT5)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
+ return false;
+ }
+ else
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_OES:
+ if (context->getExtensions().compressedETC1RGB8Texture)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ else
+ {
+ context->handleError(InvalidEnum());
+ 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->handleError(InvalidOperation()
+ << "ETC lossy decode formats can't be copied to.");
+ return false;
+ }
+ else
+ {
+ context->handleError(InvalidEnum()
+ << "ANGLE_lossy_etc_decode extension is not supported.");
+ return false;
+ }
+ break;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT32_OES:
+ case GL_DEPTH_STENCIL_OES:
+ case GL_DEPTH24_STENCIL8_OES:
+ if (context->getExtensions().depthTextures)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ else
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ default:
+ context->handleError(InvalidEnum());
+ 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;
+
+ 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(ValidationContext *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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), WebglBindAttribLocationReservedPrefix);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateWebGLNameLength(ValidationContext *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.
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), WebglNameLengthLimitExceeded);
+
+ 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.
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), Webgl2NameLengthLimitExceeded);
+ return false;
+ }
+
+ return true;
+}
+
} // anonymous namespace
-bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage,
- GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
- GLint border, GLenum format, GLenum type, const GLvoid *pixels)
+bool ValidateES2TexImageParameters(Context *context,
+ GLenum 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->recordError(Error(GL_INVALID_ENUM));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
return false;
}
if (!ValidImageSizeParameters(context, target, level, width, height, 1, isSubImage))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ if (!ValidMipLevel(context, target, level))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
return false;
}
- if (level < 0 || xoffset < 0 ||
- std::numeric_limits<GLsizei>::max() - xoffset < width ||
+ if (xoffset < 0 || std::numeric_limits<GLsizei>::max() - xoffset < width ||
std::numeric_limits<GLsizei>::max() - yoffset < height)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), ResourceMaxTextureSize);
return false;
}
- if (!isSubImage && !isCompressed && internalformat != format)
+ // 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.
+ bool nonEqualFormatsAllowed =
+ (internalformat == GL_RGB32F && context->getExtensions().colorBufferFloatRGB) ||
+ (internalformat == GL_RGBA32F && context->getExtensions().colorBufferFloatRGBA);
+
+ if (!isSubImage && !isCompressed && internalformat != format && !nonEqualFormatsAllowed)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
@@ -94,7 +1066,23 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
+ return false;
+ }
+ }
+ else if (target == GL_TEXTURE_RECTANGLE_ANGLE)
+ {
+ ASSERT(level == 0);
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ if (isCompressed)
+ {
+ context->handleError(InvalidEnum()
+ << "Rectangle texture cannot have a compressed format.");
return false;
}
}
@@ -102,37 +1090,46 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
{
if (!isSubImage && width != height)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), CubemapFacesEqualDimensions);
return false;
}
if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level) ||
static_cast<GLuint>(height) > (caps.maxCubeMapTextureSize >> level))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
}
else
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
- gl::Texture *texture = context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target);
+ gl::Texture *texture =
+ context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target);
if (!texture)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), BufferNotBound);
return false;
}
if (isSubImage)
{
+ const InternalFormat &textureInternalFormat = *texture->getFormat(target, level).info;
+ if (textureInternalFormat.internalFormat == GL_NONE)
+ {
+ context->handleError(InvalidOperation() << "Texture level does not exist.");
+ return false;
+ }
+
if (format != GL_NONE)
{
- if (gl::GetSizedInternalFormat(format, type) != texture->getInternalFormat(target, level))
+ if (GetInternalFormatInfo(format, type).sizedInternalFormat !=
+ textureInternalFormat.sizedInternalFormat)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), TypeMismatch);
return false;
}
}
@@ -140,7 +1137,14 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
if (static_cast<size_t>(xoffset + width) > texture->getWidth(target, level) ||
static_cast<size_t>(yoffset + height) > texture->getHeight(target, level))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ if (width > 0 && height > 0 && pixels == nullptr &&
+ context->getGLState().getTargetBuffer(gl::BufferBinding::PixelUnpack) == nullptr)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), PixelDataNull);
return false;
}
}
@@ -148,7 +1152,7 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
{
if (texture->getImmutableFormat())
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
}
@@ -156,62 +1160,100 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
// Verify zero border
if (border != 0)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidBorder);
return false;
}
if (isCompressed)
{
GLenum actualInternalFormat =
- isSubImage ? texture->getInternalFormat(target, level) : internalformat;
+ isSubImage ? texture->getFormat(target, level).info->sizedInternalFormat
+ : internalformat;
switch (actualInternalFormat)
{
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- if (!context->getExtensions().textureCompressionDXT1)
- {
- context->recordError(Error(GL_INVALID_ENUM));
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ if (!context->getExtensions().textureCompressionDXT1)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ if (!context->getExtensions().textureCompressionDXT3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ if (!context->getExtensions().textureCompressionDXT5)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
+ if (!context->getExtensions().textureCompressionS3TCsRGB)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_OES:
+ if (!context->getExtensions().compressedETC1RGB8Texture)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
+ return false;
+ }
+ if (isSubImage)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidInternalFormat);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
+ return false;
+ }
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
return false;
- }
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
- if (!context->getExtensions().textureCompressionDXT1)
+ }
+
+ if (isSubImage)
+ {
+ if (!ValidCompressedSubImageSize(context, actualInternalFormat, xoffset, yoffset, width,
+ height, texture->getWidth(target, level),
+ texture->getHeight(target, level)))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidOperation() << "Invalid compressed format dimension.");
return false;
}
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
- if (!context->getExtensions().textureCompressionDXT5)
+
+ if (format != actualInternalFormat)
{
- context->recordError(Error(GL_INVALID_ENUM));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
return false;
}
- break;
- case GL_ETC1_RGB8_OES:
- if (!context->getExtensions().compressedETC1RGB8Texture)
+ }
+ else
+ {
+ if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidOperation() << "Invalid compressed format dimension.");
return false;
}
- break;
- case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
- if (!context->getExtensions().lossyETCDecode)
- {
- context->recordError(
- Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported"));
- return false;
- }
- break;
- default:
- context->recordError(Error(
- GL_INVALID_ENUM, "internalformat is not a supported compressed internal format"));
- return false;
- }
- if (!ValidCompressedImageSize(context, actualInternalFormat, width, height))
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
}
}
else
@@ -219,19 +1261,19 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
// 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->recordError(Error(GL_INVALID_ENUM));
- return false;
+ 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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidType);
+ return false;
}
// validate <format> + <type> combinations
@@ -239,217 +1281,286 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
// - 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:
+ 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:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- break;
- case GL_RED:
- case GL_RG:
- if (!context->getExtensions().textureRG)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- switch (type)
- {
- case GL_UNSIGNED_BYTE:
- case GL_FLOAT:
- case GL_HALF_FLOAT_OES:
- break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- 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:
+ case GL_RED:
+ case GL_RG:
+ if (!context->getExtensions().textureRG)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ case GL_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ if (!context->getExtensions().textureFloat)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- break;
- case GL_RGBA:
- switch (type)
- {
- case GL_UNSIGNED_BYTE:
- case GL_UNSIGNED_SHORT_4_4_4_4:
- case GL_UNSIGNED_SHORT_5_5_5_1:
- case GL_FLOAT:
- case GL_HALF_FLOAT_OES:
+ case GL_RGB:
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- break;
- case GL_BGRA_EXT:
- switch (type)
- {
- case GL_UNSIGNED_BYTE:
+ 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:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- break;
- case GL_SRGB_EXT:
- case GL_SRGB_ALPHA_EXT:
- if (!context->getExtensions().sRGB)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- switch (type)
- {
- case GL_UNSIGNED_BYTE:
+ case GL_BGRA_EXT:
+ if (!context->getExtensions().textureFormatBGRA8888)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- 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:
+ case GL_SRGB_EXT:
+ case GL_SRGB_ALPHA_EXT:
+ if (!context->getExtensions().sRGB)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- break;
- case GL_DEPTH_STENCIL_OES:
- switch (type)
- {
- case GL_UNSIGNED_INT_24_8_OES:
+ 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:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+ case GL_DEPTH_STENCIL_OES:
+ switch (type)
+ {
+ case GL_UNSIGNED_INT_24_8_OES:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
+ default:
+ context->handleError(InvalidEnum());
return false;
- }
- break;
- default:
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
}
switch (format)
{
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- if (context->getExtensions().textureCompressionDXT1)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- else
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
- if (context->getExtensions().textureCompressionDXT3)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- else
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
- if (context->getExtensions().textureCompressionDXT5)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- else
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_ETC1_RGB8_OES:
- if (context->getExtensions().compressedETC1RGB8Texture)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- else
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
- if (context->getExtensions().lossyETCDecode)
- {
- context->recordError(
- Error(GL_INVALID_OPERATION,
- "ETC1_RGB8_LOSSY_DECODE_ANGLE can't work with this type."));
- return false;
- }
- else
- {
- context->recordError(
- Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
- return false;
- }
- break;
- case GL_DEPTH_COMPONENT:
- case GL_DEPTH_STENCIL_OES:
- if (!context->getExtensions().depthTextures)
- {
- context->recordError(Error(GL_INVALID_VALUE));
- return false;
- }
- if (target != GL_TEXTURE_2D)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- // OES_depth_texture supports loading depth data and multiple levels,
- // but ANGLE_depth_texture does not
- if (pixels != NULL || level != 0)
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ if (context->getExtensions().textureCompressionDXT1)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ else
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ if (context->getExtensions().textureCompressionDXT3)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ else
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ if (context->getExtensions().textureCompressionDXT5)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ else
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ break;
+ case GL_ETC1_RGB8_OES:
+ if (context->getExtensions().compressedETC1RGB8Texture)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ else
+ {
+ context->handleError(InvalidEnum());
+ 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->handleError(InvalidOperation()
+ << "ETC lossy decode formats can't work with this type.");
+ return false;
+ }
+ else
+ {
+ context->handleError(InvalidEnum()
+ << "ANGLE_lossy_etc_decode extension is not supported.");
+ return false;
+ }
+ break;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_STENCIL_OES:
+ if (!context->getExtensions().depthTextures)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ if (target != GL_TEXTURE_2D)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTargetAndFormat);
+ return false;
+ }
+ // OES_depth_texture supports loading depth data and multiple levels,
+ // but ANGLE_depth_texture does not
+ if (pixels != nullptr)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), PixelDataNotNull);
+ return false;
+ }
+ if (level != 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), LevelNotZero);
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!isSubImage)
+ {
+ switch (internalformat)
{
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
+ case GL_RGBA32F:
+ if (!context->getExtensions().colorBufferFloatRGBA)
+ {
+ context->handleError(InvalidValue()
+ << "Sized GL_RGBA32F internal format requires "
+ "GL_CHROMIUM_color_buffer_float_rgba");
+ return false;
+ }
+ if (type != GL_FLOAT)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
+ if (format != GL_RGBA)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+
+ case GL_RGB32F:
+ if (!context->getExtensions().colorBufferFloatRGB)
+ {
+ context->handleError(InvalidValue()
+ << "Sized GL_RGB32F internal format requires "
+ "GL_CHROMIUM_color_buffer_float_rgb");
+ return false;
+ }
+ if (type != GL_FLOAT)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
+ if (format != GL_RGB)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
+ break;
+
+ default:
+ break;
}
- break;
- default:
- break;
}
if (type == GL_FLOAT)
{
if (!context->getExtensions().textureFloat)
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
}
@@ -457,561 +1568,246 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
{
if (!context->getExtensions().textureHalfFloat)
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
}
}
+ GLenum sizeCheckFormat = isSubImage ? format : internalformat;
+ if (!ValidImageDataSize(context, target, width, height, 1, sizeCheckFormat, type, pixels,
+ imageSize))
+ {
+ return false;
+ }
+
return true;
}
-bool ValidateES2CopyTexImageParameters(ValidationContext *context,
- GLenum target,
- GLint level,
- GLenum internalformat,
- bool isSubImage,
- GLint xoffset,
- GLint yoffset,
- GLint x,
- GLint y,
- GLsizei width,
- GLsizei height,
- GLint border)
+bool ValidateES2TexStorageParameters(Context *context,
+ GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
{
- GLenum textureInternalFormat = GL_NONE;
+ if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP &&
+ target != GL_TEXTURE_RECTANGLE_ANGLE)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
- if (!ValidTexture2DDestinationTarget(context, target))
+ if (width < 1 || height < 1 || levels < 1)
{
- context->recordError(Error(GL_INVALID_ENUM, "Invalid texture target"));
+ context->handleError(InvalidValue());
return false;
}
- if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
- xoffset, yoffset, 0, x, y, width, height, border, &textureInternalFormat))
+ if (target == GL_TEXTURE_CUBE_MAP && width != height)
{
+ context->handleError(InvalidValue());
return false;
}
- const gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer();
- GLenum colorbufferFormat = framebuffer->getReadColorbuffer()->getInternalFormat();
- const auto &internalFormatInfo = gl::GetInternalFormatInfo(textureInternalFormat);
- GLenum textureFormat = internalFormatInfo.format;
+ if (levels != 1 && levels != gl::log2(std::max(width, height)) + 1)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
- // [OpenGL ES 2.0.24] table 3.9
- if (isSubImage)
+ const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalformat);
+ if (formatInfo.format == GL_NONE || formatInfo.type == GL_NONE)
{
- switch (textureFormat)
- {
- case GL_ALPHA:
- if (colorbufferFormat != GL_ALPHA8_EXT &&
- colorbufferFormat != GL_RGBA4 &&
- colorbufferFormat != GL_RGB5_A1 &&
- colorbufferFormat != GL_RGBA8_OES)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- 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)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- 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)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- 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)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- 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)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- 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)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- 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:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- case GL_DEPTH_COMPONENT:
- case GL_DEPTH_STENCIL_OES:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
-
- if (internalFormatInfo.type == GL_FLOAT &&
- !context->getExtensions().textureFloat)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidEnum());
+ return false;
+ }
+
+ const gl::Caps &caps = context->getCaps();
+
+ switch (target)
+ {
+ case GL_TEXTURE_2D:
+ if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
+ static_cast<GLuint>(height) > caps.max2DTextureSize)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ break;
+ case GL_TEXTURE_RECTANGLE_ANGLE:
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize || levels != 1)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ if (formatInfo.compressed)
+ {
+ context->handleError(InvalidEnum()
+ << "Rectangle texture cannot have a compressed format.");
+ return false;
+ }
+ break;
+ case GL_TEXTURE_CUBE_MAP:
+ if (static_cast<GLuint>(width) > caps.maxCubeMapTextureSize ||
+ static_cast<GLuint>(height) > caps.maxCubeMapTextureSize)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ break;
+ default:
+ context->handleError(InvalidEnum());
return false;
- }
}
- else
+
+ if (levels != 1 && !context->getExtensions().textureNPOT)
{
- switch (internalformat)
+ if (!gl::isPow2(width) || !gl::isPow2(height))
{
- 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->recordError(Error(GL_INVALID_OPERATION));
- 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->recordError(Error(GL_INVALID_OPERATION));
- 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->recordError(Error(GL_INVALID_OPERATION));
- 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->recordError(Error(GL_INVALID_OPERATION));
- 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->recordError(Error(GL_INVALID_OPERATION));
- 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->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- break;
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- if (context->getExtensions().textureCompressionDXT1)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ }
+
+ switch (internalformat)
+ {
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ if (!context->getExtensions().textureCompressionDXT1)
+ {
+ context->handleError(InvalidEnum());
return false;
}
- else
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+ if (!context->getExtensions().textureCompressionDXT3)
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
break;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
- if (context->getExtensions().textureCompressionDXT3)
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+ if (!context->getExtensions().textureCompressionDXT5)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidEnum());
return false;
}
- else
+ break;
+ case GL_ETC1_RGB8_OES:
+ if (!context->getExtensions().compressedETC1RGB8Texture)
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
- if (context->getExtensions().textureCompressionDXT5)
+ 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->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidEnum()
+ << "ANGLE_lossy_etc_decode extension is not supported.");
return false;
}
- else
+ 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->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
break;
- case GL_ETC1_RGB8_OES:
- if (context->getExtensions().compressedETC1RGB8Texture)
+ 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->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidEnum());
return false;
}
- else
+ break;
+ case GL_R8_EXT:
+ case GL_RG8_EXT:
+ if (!context->getExtensions().textureRG)
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
break;
- case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
- if (context->getExtensions().lossyETCDecode)
- {
- context->recordError(Error(GL_INVALID_OPERATION,
- "ETC1_RGB8_LOSSY_DECODE_ANGLE can't be copied to."));
- return false;
- }
- else
- {
- context->recordError(
- Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
- return false;
- }
- break;
- case GL_DEPTH_COMPONENT:
- case GL_DEPTH_COMPONENT16:
- case GL_DEPTH_COMPONENT32_OES:
- case GL_DEPTH_STENCIL_OES:
- case GL_DEPTH24_STENCIL8_OES:
- if (context->getExtensions().depthTextures)
+ case GL_R16F_EXT:
+ case GL_RG16F_EXT:
+ if (!context->getExtensions().textureRG || !context->getExtensions().textureHalfFloat)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidEnum());
return false;
}
- else
+ break;
+ case GL_R32F_EXT:
+ case GL_RG32F_EXT:
+ if (!context->getExtensions().textureRG || !context->getExtensions().textureFloat)
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
- default:
- context->recordError(Error(GL_INVALID_ENUM));
- 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 ValidateES2TexStorageParameters(Context *context, GLenum target, GLsizei levels, GLenum internalformat,
- GLsizei width, GLsizei height)
-{
- if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
-
- if (width < 1 || height < 1 || levels < 1)
- {
- context->recordError(Error(GL_INVALID_VALUE));
- return false;
- }
-
- if (target == GL_TEXTURE_CUBE_MAP && width != height)
- {
- context->recordError(Error(GL_INVALID_VALUE));
- return false;
- }
-
- if (levels != 1 && levels != gl::log2(std::max(width, height)) + 1)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
-
- const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat);
- if (formatInfo.format == GL_NONE || formatInfo.type == GL_NONE)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
-
- const gl::Caps &caps = context->getCaps();
-
- switch (target)
- {
- case GL_TEXTURE_2D:
- if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
- static_cast<GLuint>(height) > caps.max2DTextureSize)
- {
- context->recordError(Error(GL_INVALID_VALUE));
- return false;
- }
- break;
- case GL_TEXTURE_CUBE_MAP:
- if (static_cast<GLuint>(width) > caps.maxCubeMapTextureSize ||
- static_cast<GLuint>(height) > caps.maxCubeMapTextureSize)
- {
- context->recordError(Error(GL_INVALID_VALUE));
- return false;
- }
- break;
- default:
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
-
- if (levels != 1 && !context->getExtensions().textureNPOT)
- {
- if (!gl::isPow2(width) || !gl::isPow2(height))
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- }
-
- switch (internalformat)
- {
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- if (!context->getExtensions().textureCompressionDXT1)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
- if (!context->getExtensions().textureCompressionDXT3)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
- if (!context->getExtensions().textureCompressionDXT5)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_ETC1_RGB8_OES:
- if (!context->getExtensions().compressedETC1RGB8Texture)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
- if (!context->getExtensions().lossyETCDecode)
- {
- context->recordError(
- Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
- 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->recordError(Error(GL_INVALID_ENUM));
- 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->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_R8_EXT:
- case GL_RG8_EXT:
- case GL_R16F_EXT:
- case GL_RG16F_EXT:
- case GL_R32F_EXT:
- case GL_RG32F_EXT:
- if (!context->getExtensions().textureRG)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- break;
- case GL_DEPTH_COMPONENT16:
- case GL_DEPTH_COMPONENT32_OES:
- case GL_DEPTH24_STENCIL8_OES:
- if (!context->getExtensions().depthTextures)
- {
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
- }
- if (target != GL_TEXTURE_2D)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- // ANGLE_depth_texture only supports 1-level textures
- if (levels != 1)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
- break;
- default:
- break;
+ break;
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT32_OES:
+ case GL_DEPTH24_STENCIL8_OES:
+ if (!context->getExtensions().depthTextures)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ if (target != GL_TEXTURE_2D)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ // ANGLE_depth_texture only supports 1-level textures
+ if (levels != 1)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ break;
+ default:
+ break;
}
gl::Texture *texture = context->getTargetTexture(target);
if (!texture || texture->id() == 0)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
if (texture->getImmutableFormat())
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
return true;
}
-// check for combinations of format and type that are valid for ReadPixels
-bool ValidES2ReadFormatType(Context *context, GLenum format, GLenum type)
-{
- switch (format)
- {
- case GL_RGBA:
- switch (type)
- {
- case GL_UNSIGNED_BYTE:
- break;
- default:
- return false;
- }
- break;
- case GL_BGRA_EXT:
- switch (type)
- {
- case GL_UNSIGNED_BYTE:
- case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT:
- case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT:
- break;
- default:
- return false;
- }
- break;
- case GL_RG_EXT:
- case GL_RED_EXT:
- if (!context->getExtensions().textureRG)
- {
- return false;
- }
- switch (type)
- {
- case GL_UNSIGNED_BYTE:
- break;
- default:
- return false;
- }
- break;
-
- default:
- return false;
- }
- return true;
-}
-
-bool ValidateDiscardFramebufferEXT(Context *context, GLenum target, GLsizei numAttachments,
+bool ValidateDiscardFramebufferEXT(Context *context,
+ GLenum target,
+ GLsizei numAttachments,
const GLenum *attachments)
{
if (!context->getExtensions().discardFramebuffer)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
@@ -1019,55 +1815,57 @@ bool ValidateDiscardFramebufferEXT(Context *context, GLenum target, GLsizei numA
switch (target)
{
- case GL_FRAMEBUFFER:
- defaultFramebuffer = (context->getState().getTargetFramebuffer(GL_FRAMEBUFFER)->id() == 0);
- break;
- default:
- context->recordError(Error(GL_INVALID_ENUM, "Invalid framebuffer target"));
- return false;
+ case GL_FRAMEBUFFER:
+ defaultFramebuffer =
+ (context->getGLState().getTargetFramebuffer(GL_FRAMEBUFFER)->id() == 0);
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
+ return false;
}
- return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments, defaultFramebuffer);
+ return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments,
+ defaultFramebuffer);
}
bool ValidateBindVertexArrayOES(Context *context, GLuint array)
{
if (!context->getExtensions().vertexArrayObject)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
return ValidateBindVertexArrayBase(context, array);
}
-bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n)
+bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n, const GLuint *arrays)
{
if (!context->getExtensions().vertexArrayObject)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
- return ValidateDeleteVertexArraysBase(context, n);
+ return ValidateGenOrDelete(context, n);
}
-bool ValidateGenVertexArraysOES(Context *context, GLsizei n)
+bool ValidateGenVertexArraysOES(Context *context, GLsizei n, GLuint *arrays)
{
if (!context->getExtensions().vertexArrayObject)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
- return ValidateGenVertexArraysBase(context, n);
+ return ValidateGenOrDelete(context, n);
}
-bool ValidateIsVertexArrayOES(Context *context)
+bool ValidateIsVertexArrayOES(Context *context, GLuint array)
{
if (!context->getExtensions().vertexArrayObject)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
@@ -1082,7 +1880,7 @@ bool ValidateProgramBinaryOES(Context *context,
{
if (!context->getExtensions().getProgramBinary)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
@@ -1098,7 +1896,7 @@ bool ValidateGetProgramBinaryOES(Context *context,
{
if (!context->getExtensions().getProgramBinary)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
@@ -1170,25 +1968,25 @@ bool ValidateDebugMessageControlKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
if (!ValidDebugSource(source, false) && source != GL_DONT_CARE)
{
- context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDebugSource);
return false;
}
if (!ValidDebugType(type) && type != GL_DONT_CARE)
{
- context->recordError(Error(GL_INVALID_ENUM, "Invalid debug type."));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDebugType);
return false;
}
if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE)
{
- context->recordError(Error(GL_INVALID_ENUM, "Invalid debug severity."));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDebugSeverity);
return false;
}
@@ -1196,17 +1994,17 @@ bool ValidateDebugMessageControlKHR(Context *context,
{
if (source == GL_DONT_CARE || type == GL_DONT_CARE)
{
- context->recordError(Error(
- GL_INVALID_OPERATION,
- "If count is greater than zero, source and severity cannot be GL_DONT_CARE."));
+ context->handleError(
+ InvalidOperation()
+ << "If count is greater than zero, source and severity cannot be GL_DONT_CARE.");
return false;
}
if (severity != GL_DONT_CARE)
{
- context->recordError(
- Error(GL_INVALID_OPERATION,
- "If count is greater than zero, severity must be GL_DONT_CARE."));
+ context->handleError(
+ InvalidOperation()
+ << "If count is greater than zero, severity must be GL_DONT_CARE.");
return false;
}
}
@@ -1224,11 +2022,11 @@ bool ValidateDebugMessageInsertKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
- if (!context->getState().getDebug().isOutputEnabled())
+ if (!context->getGLState().getDebug().isOutputEnabled())
{
// If the DEBUG_OUTPUT state is disabled calls to DebugMessageInsert are discarded and do
// not generate an error.
@@ -1237,27 +2035,27 @@ bool ValidateDebugMessageInsertKHR(Context *context,
if (!ValidDebugSeverity(severity))
{
- context->recordError(Error(GL_INVALID_ENUM, "Invalid debug severity."));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDebugSource);
return false;
}
if (!ValidDebugType(type))
{
- context->recordError(Error(GL_INVALID_ENUM, "Invalid debug type."));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDebugType);
return false;
}
if (!ValidDebugSource(source, true))
{
- context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDebugSource);
return false;
}
size_t messageLength = (length < 0) ? strlen(buf) : length;
if (messageLength > context->getExtensions().maxDebugMessageLength)
{
- context->recordError(
- Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH."));
+ context->handleError(InvalidValue()
+ << "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH.");
return false;
}
@@ -1270,7 +2068,7 @@ bool ValidateDebugMessageCallbackKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
@@ -1289,14 +2087,13 @@ bool ValidateGetDebugMessageLogKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
if (bufSize < 0 && messageLog != nullptr)
{
- context->recordError(
- Error(GL_INVALID_VALUE, "bufSize must be positive if messageLog is not null."));
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
return false;
}
@@ -1311,30 +2108,30 @@ bool ValidatePushDebugGroupKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
if (!ValidDebugSource(source, true))
{
- context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDebugSource);
return false;
}
size_t messageLength = (length < 0) ? strlen(message) : length;
if (messageLength > context->getExtensions().maxDebugMessageLength)
{
- context->recordError(
- Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH."));
+ context->handleError(InvalidValue()
+ << "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH.");
return false;
}
- size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+ size_t currentStackSize = context->getGLState().getDebug().getGroupStackDepth();
if (currentStackSize >= context->getExtensions().maxDebugGroupStackDepth)
{
- context->recordError(
- Error(GL_STACK_OVERFLOW,
- "Cannot push more than GL_MAX_DEBUG_GROUP_STACK_DEPTH debug groups."));
+ context
+ ->handleError(StackOverflow()
+ << "Cannot push more than GL_MAX_DEBUG_GROUP_STACK_DEPTH debug groups.");
return false;
}
@@ -1345,14 +2142,14 @@ bool ValidatePopDebugGroupKHR(Context *context)
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
- size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+ size_t currentStackSize = context->getGLState().getDebug().getGroupStackDepth();
if (currentStackSize <= 1)
{
- context->recordError(Error(GL_STACK_UNDERFLOW, "Cannot pop the default debug group."));
+ context->handleError(StackUnderflow() << "Cannot pop the default debug group.");
return false;
}
@@ -1366,7 +2163,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_BUFFER:
if (context->getBuffer(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid buffer."));
+ context->handleError(InvalidValue() << "name is not a valid buffer.");
return false;
}
return true;
@@ -1374,7 +2171,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_SHADER:
if (context->getShader(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid shader."));
+ context->handleError(InvalidValue() << "name is not a valid shader.");
return false;
}
return true;
@@ -1382,7 +2179,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_PROGRAM:
if (context->getProgram(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid program."));
+ context->handleError(InvalidValue() << "name is not a valid program.");
return false;
}
return true;
@@ -1390,7 +2187,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_VERTEX_ARRAY:
if (context->getVertexArray(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid vertex array."));
+ context->handleError(InvalidValue() << "name is not a valid vertex array.");
return false;
}
return true;
@@ -1398,7 +2195,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_QUERY:
if (context->getQuery(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid query."));
+ context->handleError(InvalidValue() << "name is not a valid query.");
return false;
}
return true;
@@ -1406,8 +2203,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_TRANSFORM_FEEDBACK:
if (context->getTransformFeedback(name) == nullptr)
{
- context->recordError(
- Error(GL_INVALID_VALUE, "name is not a valid transform feedback."));
+ context->handleError(InvalidValue() << "name is not a valid transform feedback.");
return false;
}
return true;
@@ -1415,7 +2211,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_SAMPLER:
if (context->getSampler(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid sampler."));
+ context->handleError(InvalidValue() << "name is not a valid sampler.");
return false;
}
return true;
@@ -1423,7 +2219,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_TEXTURE:
if (context->getTexture(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid texture."));
+ context->handleError(InvalidValue() << "name is not a valid texture.");
return false;
}
return true;
@@ -1431,7 +2227,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_RENDERBUFFER:
if (context->getRenderbuffer(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid renderbuffer."));
+ context->handleError(InvalidValue() << "name is not a valid renderbuffer.");
return false;
}
return true;
@@ -1439,15 +2235,38 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
case GL_FRAMEBUFFER:
if (context->getFramebuffer(name) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid framebuffer."));
+ context->handleError(InvalidValue() << "name is not a valid framebuffer.");
return false;
}
return true;
default:
- context->recordError(Error(GL_INVALID_ENUM, "Invalid identifier."));
+ context->handleError(InvalidEnum() << "Invalid identifier.");
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->handleError(InvalidValue() << "Label length is larger than GL_MAX_LABEL_LENGTH.");
+ return false;
+ }
return true;
}
@@ -1460,7 +2279,7 @@ bool ValidateObjectLabelKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
@@ -1469,11 +2288,8 @@ bool ValidateObjectLabelKHR(Context *context,
return false;
}
- size_t labelLength = (length < 0) ? strlen(label) : length;
- if (labelLength > context->getExtensions().maxLabelLength)
+ if (!ValidateLabelLength(context, length, label))
{
- context->recordError(
- Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH."));
return false;
}
@@ -1489,13 +2305,13 @@ bool ValidateGetObjectLabelKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
if (bufSize < 0)
{
- context->recordError(Error(GL_INVALID_VALUE, "bufSize cannot be negative."));
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
return false;
}
@@ -1504,15 +2320,14 @@ bool ValidateGetObjectLabelKHR(Context *context,
return false;
}
- // Can no-op if bufSize is zero.
- return bufSize > 0;
+ return true;
}
static bool ValidateObjectPtrName(Context *context, const void *ptr)
{
- if (context->getFenceSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr)
+ if (context->getSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr)
{
- context->recordError(Error(GL_INVALID_VALUE, "name is not a valid sync."));
+ context->handleError(InvalidValue() << "name is not a valid sync.");
return false;
}
@@ -1526,7 +2341,7 @@ bool ValidateObjectPtrLabelKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
@@ -1535,11 +2350,8 @@ bool ValidateObjectPtrLabelKHR(Context *context,
return false;
}
- size_t labelLength = (length < 0) ? strlen(label) : length;
- if (labelLength > context->getExtensions().maxLabelLength)
+ if (!ValidateLabelLength(context, length, label))
{
- context->recordError(
- Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH."));
return false;
}
@@ -1554,13 +2366,13 @@ bool ValidateGetObjectPtrLabelKHR(Context *context,
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
if (bufSize < 0)
{
- context->recordError(Error(GL_INVALID_VALUE, "bufSize cannot be negative."));
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
return false;
}
@@ -1569,15 +2381,14 @@ bool ValidateGetObjectPtrLabelKHR(Context *context,
return false;
}
- // Can no-op if bufSize is zero.
- return bufSize > 0;
+ return true;
}
bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params)
{
if (!context->getExtensions().debug)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
@@ -1589,7 +2400,7 @@ bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params)
break;
default:
- context->recordError(Error(GL_INVALID_ENUM, "Invalid pname."));
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
return false;
}
@@ -1610,27 +2421,27 @@ bool ValidateBlitFramebufferANGLE(Context *context,
{
if (!context->getExtensions().framebufferBlit)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Blit extension not available."));
+ context->handleError(InvalidOperation() << "Blit extension not available.");
return false;
}
if (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0)
{
// TODO(jmadill): Determine if this should be available on other implementations.
- context->recordError(Error(
- GL_INVALID_OPERATION,
- "Scaling and flipping in BlitFramebufferANGLE not supported by this implementation."));
+ context->handleError(InvalidOperation() << "Scaling and flipping in "
+ "BlitFramebufferANGLE not supported by this "
+ "implementation.");
return false;
}
if (filter == GL_LINEAR)
{
- context->recordError(Error(GL_INVALID_ENUM, "Linear blit not supported in this extension"));
+ context->handleError(InvalidEnum() << "Linear blit not supported in this extension");
return false;
}
- const Framebuffer *readFramebuffer = context->getState().getReadFramebuffer();
- const Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer();
+ Framebuffer *readFramebuffer = context->getGLState().getReadFramebuffer();
+ Framebuffer *drawFramebuffer = context->getGLState().getDrawFramebuffer();
if (mask & GL_COLOR_BUFFER_BIT)
{
@@ -1644,7 +2455,7 @@ bool ValidateBlitFramebufferANGLE(Context *context,
readColorAttachment->type() != GL_RENDERBUFFER &&
readColorAttachment->type() != GL_FRAMEBUFFER_DEFAULT)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
@@ -1660,26 +2471,25 @@ bool ValidateBlitFramebufferANGLE(Context *context,
attachment->type() != GL_RENDERBUFFER &&
attachment->type() != GL_FRAMEBUFFER_DEFAULT)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
// Return an error if the destination formats do not match
- if (attachment->getInternalFormat() != readColorAttachment->getInternalFormat())
+ if (!Format::EquivalentForBlit(attachment->getFormat(),
+ readColorAttachment->getFormat()))
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
}
}
- int readSamples = readFramebuffer->getSamples(context->getData());
-
- if (readSamples != 0 &&
+ if (readFramebuffer->getSamples(context) != 0 &&
IsPartialBlit(context, readColorAttachment, drawColorAttachment, srcX0, srcY0,
srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
}
@@ -1702,16 +2512,15 @@ bool ValidateBlitFramebufferANGLE(Context *context,
dstX0, dstY0, dstX1, dstY1))
{
// only whole-buffer copies are permitted
- ERR(
- "Only whole-buffer depth and stencil blits are supported by this "
- "implementation.");
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation() << "Only whole-buffer depth and "
+ "stencil blits are supported by "
+ "this extension.");
return false;
}
if (readBuffer->getSamples() != 0 || drawBuffer->getSamples() != 0)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
}
@@ -1724,21 +2533,35 @@ bool ValidateBlitFramebufferANGLE(Context *context,
bool ValidateClear(ValidationContext *context, GLbitfield mask)
{
- const Framebuffer *framebufferObject = context->getState().getDrawFramebuffer();
- ASSERT(framebufferObject);
-
- if (framebufferObject->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE)
+ Framebuffer *fbo = context->getGLState().getDrawFramebuffer();
+ if (fbo->checkStatus(context) != GL_FRAMEBUFFER_COMPLETE)
{
- context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION));
+ context->handleError(InvalidFramebufferOperation());
return false;
}
if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidClearMask);
return false;
}
+ if (context->getExtensions().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;
+ }
+ }
+ }
+
return true;
}
@@ -1746,11 +2569,3783 @@ bool ValidateDrawBuffersEXT(ValidationContext *context, GLsizei n, const GLenum
{
if (!context->getExtensions().drawBuffers)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Extension not supported."));
+ context->handleError(InvalidOperation() << "Extension not supported.");
return false;
}
return ValidateDrawBuffersBase(context, n, bufs);
}
+bool ValidateTexImage2D(Context *context,
+ GLenum 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 ValidateTexImage2DRobust(Context *context,
+ GLenum 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,
+ GLenum 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,
+ GLenum 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 ValidateCompressedTexImage2D(Context *context,
+ GLenum 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);
+ auto blockSizeOrErr = formatInfo.computeCompressedImageSize(gl::Extents(width, height, 1));
+ if (blockSizeOrErr.isError())
+ {
+ context->handleError(blockSizeOrErr.getError());
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSizeOrErr.getResult())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), CompressedTextureDimensionsMustMatchData);
+ return false;
+ }
+
+ if (target == GL_TEXTURE_RECTANGLE_ANGLE)
+ {
+ context->handleError(InvalidEnum() << "Rectangle texture cannot have a compressed format.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexImage2DRobustANGLE(Context *context,
+ GLenum 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 ValidateCompressedTexSubImage2DRobustANGLE(Context *context,
+ GLenum 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,
+ GLenum 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);
+ auto blockSizeOrErr = formatInfo.computeCompressedImageSize(gl::Extents(width, height, 1));
+ if (blockSizeOrErr.isError())
+ {
+ context->handleError(blockSizeOrErr.getError());
+ return false;
+ }
+
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSizeOrErr.getResult())
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferPointervOES(Context *context,
+ BufferBinding target,
+ GLenum pname,
+ void **params)
+{
+ return ValidateGetBufferPointervBase(context, target, pname, nullptr, params);
+}
+
+bool ValidateMapBufferOES(Context *context, BufferBinding target, GLenum access)
+{
+ if (!context->getExtensions().mapBuffer)
+ {
+ context->handleError(InvalidOperation() << "Map buffer extension not available.");
+ return false;
+ }
+
+ if (!ValidBufferType(context, target))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getGLState().getTargetBuffer(target);
+
+ if (buffer == nullptr)
+ {
+ context->handleError(InvalidOperation() << "Attempted to map buffer object zero.");
+ return false;
+ }
+
+ if (access != GL_WRITE_ONLY_OES)
+ {
+ context->handleError(InvalidEnum() << "Non-write buffer mapping not supported.");
+ return false;
+ }
+
+ if (buffer->isMapped())
+ {
+ context->handleError(InvalidOperation() << "Buffer is already mapped.");
+ return false;
+ }
+
+ return ValidateMapBufferBase(context, target);
+}
+
+bool ValidateUnmapBufferOES(Context *context, BufferBinding target)
+{
+ if (!context->getExtensions().mapBuffer)
+ {
+ context->handleError(InvalidOperation() << "Map buffer extension not available.");
+ return false;
+ }
+
+ return ValidateUnmapBufferBase(context, target);
+}
+
+bool ValidateMapBufferRangeEXT(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length,
+ GLbitfield access)
+{
+ if (!context->getExtensions().mapBufferRange)
+ {
+ context->handleError(InvalidOperation() << "Map buffer range extension not available.");
+ return false;
+ }
+
+ return ValidateMapBufferRangeBase(context, target, offset, length, access);
+}
+
+bool ValidateMapBufferBase(Context *context, BufferBinding target)
+{
+ Buffer *buffer = context->getGLState().getTargetBuffer(target);
+ ASSERT(buffer != nullptr);
+
+ // Check if this buffer is currently being used as a transform feedback output buffer
+ TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+ if (transformFeedback != nullptr && transformFeedback->isActive())
+ {
+ for (size_t i = 0; i < transformFeedback->getIndexedBufferCount(); i++)
+ {
+ const auto &transformFeedbackBuffer = transformFeedback->getIndexedBuffer(i);
+ if (transformFeedbackBuffer.get() == buffer)
+ {
+ context->handleError(InvalidOperation()
+ << "Buffer is currently bound for transform feedback.");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ValidateFlushMappedBufferRangeEXT(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length)
+{
+ if (!context->getExtensions().mapBufferRange)
+ {
+ context->handleError(InvalidOperation() << "Map buffer range extension not available.");
+ return false;
+ }
+
+ return ValidateFlushMappedBufferRangeBase(context, target, offset, length);
+}
+
+bool ValidateBindTexture(Context *context, GLenum target, GLuint texture)
+{
+ Texture *textureObject = context->getTexture(texture);
+ if (textureObject && textureObject->getTarget() != target && texture != 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), TypeMismatch);
+ return false;
+ }
+
+ if (!context->getGLState().isBindGeneratesResourceEnabled() &&
+ !context->isTextureGenerated(texture))
+ {
+ context->handleError(InvalidOperation() << "Texture was not generated");
+ return false;
+ }
+
+ switch (target)
+ {
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP:
+ break;
+
+ case GL_TEXTURE_RECTANGLE_ANGLE:
+ if (!context->getExtensions().textureRectangle)
+ {
+ context->handleError(InvalidEnum()
+ << "Context does not support GL_ANGLE_texture_rectangle");
+ return false;
+ }
+ break;
+
+ case GL_TEXTURE_3D:
+ case GL_TEXTURE_2D_ARRAY:
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), ES3Required);
+ return false;
+ }
+ break;
+
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ if (context->getClientVersion() < Version(3, 1))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), ES31Required);
+ return false;
+ }
+ break;
+
+ case GL_TEXTURE_EXTERNAL_OES:
+ if (!context->getExtensions().eglImageExternal &&
+ !context->getExtensions().eglStreamConsumerExternal)
+ {
+ context->handleError(InvalidEnum() << "External texture extension not enabled");
+ return false;
+ }
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindUniformLocationCHROMIUM(Context *context,
+ GLuint program,
+ GLint location,
+ const GLchar *name)
+{
+ if (!context->getExtensions().bindUniformLocation)
+ {
+ context->handleError(InvalidOperation()
+ << "GL_CHROMIUM_bind_uniform_location is not available.");
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (location < 0)
+ {
+ context->handleError(InvalidValue() << "Location cannot be less than 0.");
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (static_cast<size_t>(location) >=
+ (caps.maxVertexUniformVectors + caps.maxFragmentUniformVectors) * 4)
+ {
+ context->handleError(InvalidValue() << "Location must be less than "
+ "(MAX_VERTEX_UNIFORM_VECTORS + "
+ "MAX_FRAGMENT_UNIFORM_VECTORS) * 4");
+ 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)))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
+ return false;
+ }
+
+ if (strncmp(name, "gl_", 3) == 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NameBeginsWithGL);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCoverageModulationCHROMIUM(Context *context, GLenum components)
+{
+ if (!context->getExtensions().framebufferMixedSamples)
+ {
+ context->handleError(InvalidOperation()
+ << "GL_CHROMIUM_framebuffer_mixed_samples is not available.");
+ return false;
+ }
+ switch (components)
+ {
+ case GL_RGB:
+ case GL_RGBA:
+ case GL_ALPHA:
+ case GL_NONE:
+ break;
+ default:
+ context->handleError(
+ InvalidEnum()
+ << "GLenum components is not one of GL_RGB, GL_RGBA, GL_ALPHA or GL_NONE.");
+ return false;
+ }
+
+ return true;
+}
+
+// CHROMIUM_path_rendering
+
+bool ValidateMatrix(Context *context, GLenum matrixMode, const GLfloat *matrix)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+ if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidMatrixMode);
+ return false;
+ }
+ if (matrix == nullptr)
+ {
+ context->handleError(InvalidOperation() << "Invalid matrix.");
+ return false;
+ }
+ return true;
+}
+
+bool ValidateMatrixMode(Context *context, GLenum matrixMode)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+ if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidMatrixMode);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateGenPaths(Context *context, GLsizei range)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidRange);
+ return false;
+ }
+
+ if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeletePaths(Context *context, GLuint path, GLsizei range)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidRange);
+ return false;
+ }
+
+ angle::CheckedNumeric<std::uint32_t> checkedRange(path);
+ checkedRange += range;
+
+ if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range) || !checkedRange.IsValid())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
+ return false;
+ }
+ return true;
+}
+
+bool ValidatePathCommands(Context *context,
+ GLuint path,
+ GLsizei numCommands,
+ const GLubyte *commands,
+ GLsizei numCoords,
+ GLenum coordType,
+ const void *coords)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+ if (!context->hasPath(path))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoSuchPath);
+ return false;
+ }
+
+ if (numCommands < 0)
+ {
+ context->handleError(InvalidValue() << "Invalid number of commands.");
+ return false;
+ }
+ else if (numCommands > 0)
+ {
+ if (!commands)
+ {
+ context->handleError(InvalidValue() << "No commands array given.");
+ return false;
+ }
+ }
+
+ if (numCoords < 0)
+ {
+ context->handleError(InvalidValue() << "Invalid number of coordinates.");
+ return false;
+ }
+ else if (numCoords > 0)
+ {
+ if (!coords)
+ {
+ context->handleError(InvalidValue() << "No coordinate array given.");
+ 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->handleError(InvalidEnum() << "Invalid coordinate type.");
+ return false;
+ }
+
+ angle::CheckedNumeric<std::uint32_t> checkedSize(numCommands);
+ checkedSize += (coordTypeSize * numCoords);
+ if (!checkedSize.IsValid())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
+ 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->handleError(InvalidEnum() << "Invalid command.");
+ return false;
+ }
+ }
+ if (expectedNumCoords != numCoords)
+ {
+ context->handleError(InvalidValue() << "Invalid number of coordinates.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateSetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat value)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+ if (!context->hasPath(path))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoSuchPath);
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_PATH_STROKE_WIDTH_CHROMIUM:
+ if (value < 0.0f)
+ {
+ context->handleError(InvalidValue() << "Invalid stroke width.");
+ 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->handleError(InvalidEnum() << "Invalid end caps.");
+ 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->handleError(InvalidEnum() << "Invalid join style.");
+ return false;
+ }
+ case GL_PATH_MITER_LIMIT_CHROMIUM:
+ if (value < 0.0f)
+ {
+ context->handleError(InvalidValue() << "Invalid miter limit.");
+ return false;
+ }
+ break;
+
+ case GL_PATH_STROKE_BOUND_CHROMIUM:
+ // no errors, only clamping.
+ break;
+
+ default:
+ context->handleError(InvalidEnum() << "Invalid path parameter.");
+ return false;
+ }
+ return true;
+}
+
+bool ValidateGetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat *value)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+
+ if (!context->hasPath(path))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoSuchPath);
+ return false;
+ }
+ if (!value)
+ {
+ context->handleError(InvalidValue() << "No value array.");
+ 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->handleError(InvalidEnum() << "Invalid path parameter.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePathStencilFunc(Context *context, GLenum func, GLint ref, GLuint mask)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ 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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ 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 ValidateStencilFillPath(Context *context, GLuint path, GLenum fillMode, GLuint mask)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+ if (context->hasPath(path) && !context->hasPathData(path))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoSuchPath);
+ return false;
+ }
+
+ switch (fillMode)
+ {
+ case GL_COUNT_UP_CHROMIUM:
+ case GL_COUNT_DOWN_CHROMIUM:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFillMode);
+ return false;
+ }
+
+ if (!isPow2(mask + 1))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidStencilBitMask);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilStrokePath(Context *context, GLuint path, GLint reference, GLuint mask)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+ if (context->hasPath(path) && !context->hasPathData(path))
+ {
+ context->handleError(InvalidOperation() << "No such path or path has no data.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCoverPath(Context *context, GLuint path, GLenum coverMode)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+ if (context->hasPath(path) && !context->hasPathData(path))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoSuchPath);
+ return false;
+ }
+
+ switch (coverMode)
+ {
+ case GL_CONVEX_HULL_CHROMIUM:
+ case GL_BOUNDING_BOX_CHROMIUM:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidCoverMode);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateStencilThenCoverFillPath(Context *context,
+ GLuint path,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum coverMode)
+{
+ return ValidateStencilFillPath(context, path, fillMode, mask) &&
+ ValidateCoverPath(context, path, coverMode);
+}
+
+bool ValidateStencilThenCoverStrokePath(Context *context,
+ GLuint path,
+ GLint reference,
+ GLuint mask,
+ GLenum coverMode)
+{
+ return ValidateStencilStrokePath(context, path, reference, mask) &&
+ ValidateCoverPath(context, path, coverMode);
+}
+
+bool ValidateIsPath(Context *context)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+ return true;
+}
+
+bool ValidateCoverFillPathInstanced(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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidCoverMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCoverStrokePathInstanced(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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidCoverMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilFillPathInstanced(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_COUNT_UP_CHROMIUM:
+ case GL_COUNT_DOWN_CHROMIUM:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFillMode);
+ return false;
+ }
+ if (!isPow2(mask + 1))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidStencilBitMask);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateStencilStrokePathInstanced(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 ValidateStencilThenCoverFillPathInstanced(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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidCoverMode);
+ return false;
+ }
+
+ switch (fillMode)
+ {
+ case GL_COUNT_UP_CHROMIUM:
+ case GL_COUNT_DOWN_CHROMIUM:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFillMode);
+ return false;
+ }
+ if (!isPow2(mask + 1))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidStencilBitMask);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilThenCoverStrokePathInstanced(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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidCoverMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindFragmentInputLocation(Context *context,
+ GLuint program,
+ GLint location,
+ const GLchar *name)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+
+ const GLint MaxLocation = context->getCaps().maxVaryingVectors * 4;
+ if (location >= MaxLocation)
+ {
+ context->handleError(InvalidValue() << "Location exceeds max varying.");
+ return false;
+ }
+
+ const auto *programObject = context->getProgram(program);
+ if (!programObject)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound);
+ return false;
+ }
+
+ if (!name)
+ {
+ context->handleError(InvalidValue() << "No name given.");
+ return false;
+ }
+
+ if (angle::BeginsWith(name, "gl_"))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NameBeginsWithGL);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateProgramPathFragmentInputGen(Context *context,
+ GLuint program,
+ GLint location,
+ GLenum genMode,
+ GLint components,
+ const GLfloat *coeffs)
+{
+ if (!context->getExtensions().pathRendering)
+ {
+ context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
+ return false;
+ }
+
+ const auto *programObject = context->getProgram(program);
+ if (!programObject || programObject->isFlaggedForDeletion())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramDoesNotExist);
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
+ return false;
+ }
+
+ switch (genMode)
+ {
+ case GL_NONE:
+ if (components != 0)
+ {
+ context->handleError(InvalidValue() << "Invalid components.");
+ return false;
+ }
+ break;
+
+ case GL_OBJECT_LINEAR_CHROMIUM:
+ case GL_EYE_LINEAR_CHROMIUM:
+ case GL_CONSTANT_CHROMIUM:
+ if (components < 1 || components > 4)
+ {
+ context->handleError(InvalidValue() << "Invalid components.");
+ return false;
+ }
+ if (!coeffs)
+ {
+ context->handleError(InvalidValue() << "No coefficients array given.");
+ return false;
+ }
+ break;
+
+ default:
+ context->handleError(InvalidEnum() << "Invalid gen mode.");
+ 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(context, location);
+
+ if (!binding.valid)
+ {
+ context->handleError(InvalidOperation() << "No such binding.");
+ 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->handleError(
+ InvalidOperation()
+ << "Fragment input type is not a floating point scalar or vector.");
+ return false;
+ }
+ if (expectedComponents != components && genMode != GL_NONE)
+ {
+ context->handleError(InvalidOperation() << "Unexpected number of components");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateCopyTextureCHROMIUM(Context *context,
+ GLuint sourceId,
+ GLint sourceLevel,
+ GLenum destTarget,
+ GLuint destId,
+ GLint destLevel,
+ GLint internalFormat,
+ GLenum destType,
+ GLboolean unpackFlipY,
+ GLboolean unpackPremultiplyAlpha,
+ GLboolean unpackUnmultiplyAlpha)
+{
+ if (!context->getExtensions().copyTexture)
+ {
+ context->handleError(InvalidOperation()
+ << "GL_CHROMIUM_copy_texture extension not available.");
+ return false;
+ }
+
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->handleError(InvalidValue() << "Source texture is not a valid texture object.");
+ return false;
+ }
+
+ if (!IsValidCopyTextureSourceTarget(context, source->getTarget()))
+ {
+ context->handleError(InvalidValue() << "Source texture a valid texture type.");
+ return false;
+ }
+
+ GLenum sourceTarget = source->getTarget();
+ ASSERT(sourceTarget != GL_TEXTURE_CUBE_MAP);
+
+ if (!IsValidCopyTextureSourceLevel(context, source->getTarget(), sourceLevel))
+ {
+ context->handleError(InvalidValue() << "Source texture level is not valid.");
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidInternalFormat);
+ return false;
+ }
+
+ const InternalFormat &sourceFormat = *source->getFormat(sourceTarget, sourceLevel).info;
+ if (!IsValidCopyTextureSourceInternalFormatEnum(sourceFormat.internalFormat))
+ {
+ context->handleError(InvalidOperation() << "Source texture internal format is invalid.");
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTargetEnum(context, destTarget))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
+ return false;
+ }
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->handleError(InvalidValue()
+ << "Destination texture is not a valid texture object.");
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTarget(context, dest->getTarget(), destTarget))
+ {
+ context->handleError(InvalidValue() << "Destination texture a valid texture type.");
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationLevel(context, destTarget, destLevel, sourceWidth,
+ sourceHeight))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationFormatType(context, internalFormat, destType))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
+ return false;
+ }
+
+ if (IsCubeMapTextureTarget(destTarget) && sourceWidth != sourceHeight)
+ {
+ context->handleError(
+ InvalidValue() << "Destination width and height must be equal for cube map textures.");
+ return false;
+ }
+
+ if (dest->getImmutableFormat())
+ {
+ context->handleError(InvalidOperation() << "Destination texture is immutable.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCopySubTextureCHROMIUM(Context *context,
+ GLuint sourceId,
+ GLint sourceLevel,
+ GLenum 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->handleError(InvalidOperation()
+ << "GL_CHROMIUM_copy_texture extension not available.");
+ return false;
+ }
+
+ const Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->handleError(InvalidValue() << "Source texture is not a valid texture object.");
+ return false;
+ }
+
+ if (!IsValidCopyTextureSourceTarget(context, source->getTarget()))
+ {
+ context->handleError(InvalidValue() << "Source texture a valid texture type.");
+ return false;
+ }
+
+ GLenum sourceTarget = source->getTarget();
+ ASSERT(sourceTarget != GL_TEXTURE_CUBE_MAP);
+
+ if (!IsValidCopyTextureSourceLevel(context, source->getTarget(), sourceLevel))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
+ return false;
+ }
+
+ if (source->getWidth(sourceTarget, sourceLevel) == 0 ||
+ source->getHeight(sourceTarget, sourceLevel) == 0)
+ {
+ context->handleError(InvalidValue()
+ << "The source level of the source texture must be defined.");
+ return false;
+ }
+
+ if (x < 0 || y < 0)
+ {
+ context->handleError(InvalidValue() << "x and y cannot be negative.");
+ return false;
+ }
+
+ if (width < 0 || height < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
+ return false;
+ }
+
+ if (static_cast<size_t>(x + width) > source->getWidth(sourceTarget, sourceLevel) ||
+ static_cast<size_t>(y + height) > source->getHeight(sourceTarget, sourceLevel))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), SourceTextureTooSmall);
+ return false;
+ }
+
+ const Format &sourceFormat = source->getFormat(sourceTarget, sourceLevel);
+ if (!IsValidCopySubTextureSourceInternalFormat(sourceFormat.info->internalFormat))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidInternalFormat);
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTargetEnum(context, destTarget))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
+ return false;
+ }
+
+ const Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->handleError(InvalidValue()
+ << "Destination texture is not a valid texture object.");
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationTarget(context, dest->getTarget(), destTarget))
+ {
+ context->handleError(InvalidValue() << "Destination texture a valid texture type.");
+ return false;
+ }
+
+ if (!IsValidCopyTextureDestinationLevel(context, destTarget, destLevel, width, height))
+ {
+ context->handleError(InvalidValue() << "Destination texture level is not valid.");
+ return false;
+ }
+
+ if (dest->getWidth(destTarget, destLevel) == 0 || dest->getHeight(destTarget, destLevel) == 0)
+ {
+ context
+ ->handleError(InvalidOperation()
+ << "The destination level of the destination texture must be defined.");
+ return false;
+ }
+
+ const InternalFormat &destFormat = *dest->getFormat(destTarget, destLevel).info;
+ if (!IsValidCopySubTextureDestionationInternalFormat(destFormat.internalFormat))
+ {
+ context->handleError(InvalidOperation()
+ << "Destination internal format and type combination is not valid.");
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeOffset);
+ return false;
+ }
+
+ if (static_cast<size_t>(xoffset + width) > dest->getWidth(destTarget, destLevel) ||
+ static_cast<size_t>(yoffset + height) > dest->getHeight(destTarget, destLevel))
+ {
+ context->handleError(InvalidValue() << "Destination texture not large enough to copy to.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCompressedCopyTextureCHROMIUM(Context *context, GLuint sourceId, GLuint destId)
+{
+ if (!context->getExtensions().copyCompressedTexture)
+ {
+ context->handleError(InvalidOperation()
+ << "GL_CHROMIUM_copy_compressed_texture extension not available.");
+ return false;
+ }
+
+ const gl::Texture *source = context->getTexture(sourceId);
+ if (source == nullptr)
+ {
+ context->handleError(InvalidValue() << "Source texture is not a valid texture object.");
+ return false;
+ }
+
+ if (source->getTarget() != GL_TEXTURE_2D)
+ {
+ context->handleError(InvalidValue() << "Source texture must be of type GL_TEXTURE_2D.");
+ return false;
+ }
+
+ if (source->getWidth(GL_TEXTURE_2D, 0) == 0 || source->getHeight(GL_TEXTURE_2D, 0) == 0)
+ {
+ context->handleError(InvalidValue() << "Source texture must level 0 defined.");
+ return false;
+ }
+
+ const gl::Format &sourceFormat = source->getFormat(GL_TEXTURE_2D, 0);
+ if (!sourceFormat.info->compressed)
+ {
+ context->handleError(InvalidOperation()
+ << "Source texture must have a compressed internal format.");
+ return false;
+ }
+
+ const gl::Texture *dest = context->getTexture(destId);
+ if (dest == nullptr)
+ {
+ context->handleError(InvalidValue()
+ << "Destination texture is not a valid texture object.");
+ return false;
+ }
+
+ if (dest->getTarget() != GL_TEXTURE_2D)
+ {
+ context->handleError(InvalidValue()
+ << "Destination texture must be of type GL_TEXTURE_2D.");
+ return false;
+ }
+
+ if (dest->getImmutableFormat())
+ {
+ context->handleError(InvalidOperation() << "Destination cannot be immutable.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCreateShader(Context *context, GLenum type)
+{
+ switch (type)
+ {
+ case GL_VERTEX_SHADER:
+ case GL_FRAGMENT_SHADER:
+ break;
+
+ case GL_COMPUTE_SHADER:
+ if (context->getClientVersion() < Version(3, 1))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), ES31Required);
+ return false;
+ }
+ break;
+
+ case GL_GEOMETRY_SHADER_EXT:
+ if (!context->getExtensions().geometryShader)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidShaderType);
+ return false;
+ }
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidShaderType);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBufferData(ValidationContext *context,
+ BufferBinding target,
+ GLsizeiptr size,
+ const void *data,
+ BufferUsage usage)
+{
+ if (size < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferUsage);
+ return false;
+ }
+ break;
+
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferUsage);
+ return false;
+ }
+
+ if (!ValidBufferType(context, target))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getGLState().getTargetBuffer(target);
+
+ if (!buffer)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), BufferNotBound);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBufferSubData(ValidationContext *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr size,
+ const void *data)
+{
+ if (size < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
+ return false;
+ }
+
+ if (offset < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeOffset);
+ return false;
+ }
+
+ if (!ValidBufferType(context, target))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferTypes);
+ return false;
+ }
+
+ Buffer *buffer = context->getGLState().getTargetBuffer(target);
+
+ if (!buffer)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), BufferNotBound);
+ return false;
+ }
+
+ if (buffer->isMapped())
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+
+ // Check for possible overflow of size + offset
+ angle::CheckedNumeric<size_t> checkedSize(size);
+ checkedSize += offset;
+ if (!checkedSize.IsValid())
+ {
+ context->handleError(OutOfMemory());
+ return false;
+ }
+
+ if (size + offset > buffer->getSize())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InsufficientBufferSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRequestExtensionANGLE(Context *context, const GLchar *name)
+{
+ if (!context->getExtensions().requestExtension)
+ {
+ context->handleError(InvalidOperation() << "GL_ANGLE_request_extension is not available.");
+ return false;
+ }
+
+ if (!context->isExtensionRequestable(name))
+ {
+ context->handleError(InvalidOperation() << "Extension " << name << " is not requestable.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateActiveTexture(ValidationContext *context, GLenum texture)
+{
+ if (texture < GL_TEXTURE0 ||
+ texture > GL_TEXTURE0 + context->getCaps().maxCombinedTextureImageUnits - 1)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateAttachShader(ValidationContext *context, GLuint program, GLuint shader)
+{
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ switch (shaderObject->getType())
+ {
+ case GL_VERTEX_SHADER:
+ {
+ if (programObject->getAttachedVertexShader())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderAttachmentHasShader);
+ return false;
+ }
+ break;
+ }
+ case GL_FRAGMENT_SHADER:
+ {
+ if (programObject->getAttachedFragmentShader())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderAttachmentHasShader);
+ return false;
+ }
+ break;
+ }
+ case GL_COMPUTE_SHADER:
+ {
+ if (programObject->getAttachedComputeShader())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderAttachmentHasShader);
+ return false;
+ }
+ break;
+ }
+ case GL_GEOMETRY_SHADER_EXT:
+ {
+ if (programObject->getAttachedGeometryShader())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderAttachmentHasShader);
+ return false;
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ return true;
+}
+
+bool ValidateBindAttribLocation(ValidationContext *context,
+ GLuint program,
+ GLuint index,
+ const GLchar *name)
+{
+ if (index >= MAX_VERTEX_ATTRIBS)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ if (strncmp(name, "gl_", 3) == 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NameBeginsWithGL);
+ 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
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
+ return false;
+ }
+
+ if (!ValidateWebGLNameLength(context, length) || !ValidateWebGLNamePrefix(context, name))
+ {
+ return false;
+ }
+ }
+
+ return GetValidProgram(context, program) != nullptr;
+}
+
+bool ValidateBindBuffer(ValidationContext *context, BufferBinding target, GLuint buffer)
+{
+ if (!ValidBufferType(context, target))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferTypes);
+ return false;
+ }
+
+ if (!context->getGLState().isBindGeneratesResourceEnabled() &&
+ !context->isBufferGenerated(buffer))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ObjectNotGenerated);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindFramebuffer(ValidationContext *context, GLenum target, GLuint framebuffer)
+{
+ if (!ValidFramebufferTarget(context, target))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
+ return false;
+ }
+
+ if (!context->getGLState().isBindGeneratesResourceEnabled() &&
+ !context->isFramebufferGenerated(framebuffer))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ObjectNotGenerated);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindRenderbuffer(ValidationContext *context, GLenum target, GLuint renderbuffer)
+{
+ if (target != GL_RENDERBUFFER)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidRenderbufferTarget);
+ return false;
+ }
+
+ if (!context->getGLState().isBindGeneratesResourceEnabled() &&
+ !context->isRenderbufferGenerated(renderbuffer))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ObjectNotGenerated);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidBlendEquationMode(const ValidationContext *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(ValidationContext *context,
+ GLfloat red,
+ GLfloat green,
+ GLfloat blue,
+ GLfloat alpha)
+{
+ return true;
+}
+
+bool ValidateBlendEquation(ValidationContext *context, GLenum mode)
+{
+ if (!ValidBlendEquationMode(context, mode))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendEquation);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBlendEquationSeparate(ValidationContext *context, GLenum modeRGB, GLenum modeAlpha)
+{
+ if (!ValidBlendEquationMode(context, modeRGB))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendEquation);
+ return false;
+ }
+
+ if (!ValidBlendEquationMode(context, modeAlpha))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendEquation);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBlendFunc(ValidationContext *context, GLenum sfactor, GLenum dfactor)
+{
+ return ValidateBlendFuncSeparate(context, sfactor, dfactor, sfactor, dfactor);
+}
+
+static bool ValidSrcBlendFunc(GLenum srcBlend)
+{
+ switch (srcBlend)
+ {
+ 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:
+ case GL_SRC_ALPHA_SATURATE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool ValidDstBlendFunc(GLenum dstBlend, GLint contextMajorVersion)
+{
+ switch (dstBlend)
+ {
+ 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;
+
+ case GL_SRC_ALPHA_SATURATE:
+ return (contextMajorVersion >= 3);
+
+ default:
+ return false;
+ }
+}
+
+bool ValidateBlendFuncSeparate(ValidationContext *context,
+ GLenum srcRGB,
+ GLenum dstRGB,
+ GLenum srcAlpha,
+ GLenum dstAlpha)
+{
+ if (!ValidSrcBlendFunc(srcRGB))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidDstBlendFunc(dstRGB, context->getClientMajorVersion()))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidSrcBlendFunc(srcAlpha))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
+ return false;
+ }
+
+ if (!ValidDstBlendFunc(dstAlpha, context->getClientMajorVersion()))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
+ 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)
+ {
+ const char *msg;
+ if (context->getExtensions().webglCompatibility)
+ {
+ msg = kErrorInvalidConstantColor;
+ }
+ else
+ {
+ msg =
+ "Simultaneous use of GL_CONSTANT_ALPHA/GL_ONE_MINUS_CONSTANT_ALPHA and "
+ "GL_CONSTANT_COLOR/GL_ONE_MINUS_CONSTANT_COLOR not supported by this "
+ "implementation.";
+ ERR() << msg;
+ }
+ context->handleError(InvalidOperation() << msg);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidName);
+ return false;
+ }
+ break;
+
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidName);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateLineWidth(ValidationContext *context, GLfloat width)
+{
+ if (width <= 0.0f || isNaN(width))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidWidth);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribPointer(ValidationContext *context,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const void *ptr)
+{
+ if (!ValidateVertexFormatBase(context, index, size, type, false))
+ {
+ return false;
+ }
+
+ if (stride < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeStride);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (context->getClientVersion() >= ES_3_1)
+ {
+ if (stride > caps.maxVertexAttribStride)
+ {
+ context->handleError(InvalidValue()
+ << "stride cannot be greater than MAX_VERTEX_ATTRIB_STRIDE.");
+ return false;
+ }
+
+ if (index >= caps.maxVertexAttribBindings)
+ {
+ context->handleError(InvalidValue()
+ << "index must be smaller than MAX_VERTEX_ATTRIB_BINDINGS.");
+ return false;
+ }
+ }
+
+ // [OpenGL ES 3.0.2] Section 2.8 page 24:
+ // An INVALID_OPERATION error is generated when a non-zero vertex array object
+ // is bound, zero is bound to the ARRAY_BUFFER buffer object binding point,
+ // and the pointer argument is not NULL.
+ bool nullBufferAllowed = context->getGLState().areClientArraysEnabled() &&
+ context->getGLState().getVertexArray()->id() == 0;
+ if (!nullBufferAllowed && context->getGLState().getTargetBuffer(BufferBinding::Array) == 0 &&
+ ptr != nullptr)
+ {
+ context
+ ->handleError(InvalidOperation()
+ << "Client data cannot be used with a non-default vertex array object.");
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibility)
+ {
+ // WebGL 1.0 [Section 6.14] Fixed point support
+ // The WebGL API does not support the GL_FIXED data type.
+ if (type == GL_FIXED)
+ {
+ context->handleError(InvalidEnum() << "GL_FIXED is not supported in WebGL.");
+ return false;
+ }
+
+ if (!ValidateWebGLVertexAttribPointer(context, type, normalized, stride, ptr, false))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDepthRangef(ValidationContext *context, GLfloat zNear, GLfloat zFar)
+{
+ if (context->getExtensions().webglCompatibility && zNear > zFar)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidDepthRange);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRenderbufferStorage(ValidationContext *context,
+ GLenum target,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ return ValidateRenderbufferStorageParametersBase(context, target, 0, internalformat, width,
+ height);
+}
+
+bool ValidateRenderbufferStorageMultisampleANGLE(ValidationContext *context,
+ GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (!context->getExtensions().framebufferMultisample)
+ {
+ context->handleError(InvalidOperation()
+ << "GL_ANGLE_framebuffer_multisample not available");
+ 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_OPERATION is
+ // generated.
+ if (static_cast<GLuint>(samples) > context->getCaps().maxSamples)
+ {
+ context->handleError(InvalidValue());
+ 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->handleError(OutOfMemory());
+ return false;
+ }
+ }
+
+ return ValidateRenderbufferStorageParametersBase(context, target, samples, internalformat,
+ width, height);
+}
+
+bool ValidateCheckFramebufferStatus(ValidationContext *context, GLenum target)
+{
+ if (!ValidFramebufferTarget(context, target))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateClearColor(ValidationContext *context,
+ GLfloat red,
+ GLfloat green,
+ GLfloat blue,
+ GLfloat alpha)
+{
+ return true;
+}
+
+bool ValidateClearDepthf(ValidationContext *context, GLfloat depth)
+{
+ return true;
+}
+
+bool ValidateClearStencil(ValidationContext *context, GLint s)
+{
+ return true;
+}
+
+bool ValidateColorMask(ValidationContext *context,
+ GLboolean red,
+ GLboolean green,
+ GLboolean blue,
+ GLboolean alpha)
+{
+ return true;
+}
+
+bool ValidateCompileShader(ValidationContext *context, GLuint shader)
+{
+ return true;
+}
+
+bool ValidateCreateProgram(ValidationContext *context)
+{
+ return true;
+}
+
+bool ValidateCullFace(ValidationContext *context, CullFaceMode mode)
+{
+ switch (mode)
+ {
+ case CullFaceMode::Front:
+ case CullFaceMode::Back:
+ case CullFaceMode::FrontAndBack:
+ break;
+
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidCullMode);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteProgram(ValidationContext *context, GLuint program)
+{
+ if (program == 0)
+ {
+ return false;
+ }
+
+ if (!context->getProgram(program))
+ {
+ if (context->getShader(program))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExpectedProgramName);
+ return false;
+ }
+ else
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidProgramName);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDeleteShader(ValidationContext *context, GLuint shader)
+{
+ if (shader == 0)
+ {
+ return false;
+ }
+
+ if (!context->getShader(shader))
+ {
+ if (context->getProgram(shader))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidShaderName);
+ return false;
+ }
+ else
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), ExpectedShaderName);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateDepthFunc(ValidationContext *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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDepthMask(ValidationContext *context, GLboolean flag)
+{
+ return true;
+}
+
+bool ValidateDetachShader(ValidationContext *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 = nullptr;
+
+ switch (shaderObject->getType())
+ {
+ case GL_VERTEX_SHADER:
+ {
+ attachedShader = programObject->getAttachedVertexShader();
+ break;
+ }
+ case GL_FRAGMENT_SHADER:
+ {
+ attachedShader = programObject->getAttachedFragmentShader();
+ break;
+ }
+ case GL_COMPUTE_SHADER:
+ {
+ attachedShader = programObject->getAttachedComputeShader();
+ break;
+ }
+ case GL_GEOMETRY_SHADER_EXT:
+ {
+ attachedShader = programObject->getAttachedGeometryShader();
+ break;
+ }
+ default:
+ UNREACHABLE();
+ return false;
+ }
+
+ if (attachedShader != shaderObject)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderToDetachMustBeAttached);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDisableVertexAttribArray(ValidationContext *context, GLuint index)
+{
+ if (index >= MAX_VERTEX_ATTRIBS)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateEnableVertexAttribArray(ValidationContext *context, GLuint index)
+{
+ if (index >= MAX_VERTEX_ATTRIBS)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMaxVertexAttribute);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFinish(ValidationContext *context)
+{
+ return true;
+}
+
+bool ValidateFlush(ValidationContext *context)
+{
+ return true;
+}
+
+bool ValidateFrontFace(ValidationContext *context, GLenum mode)
+{
+ switch (mode)
+ {
+ case GL_CW:
+ case GL_CCW:
+ break;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveAttrib(ValidationContext *context,
+ GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLint *size,
+ GLenum *type,
+ GLchar *name)
+{
+ if (bufsize < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(programObject->getActiveAttributeCount()))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMaxActiveUniform);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniform(ValidationContext *context,
+ GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLint *size,
+ GLenum *type,
+ GLchar *name)
+{
+ if (bufsize < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(programObject->getActiveUniformCount()))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMaxActiveUniform);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetAttachedShaders(ValidationContext *context,
+ GLuint program,
+ GLsizei maxcount,
+ GLsizei *count,
+ GLuint *shaders)
+{
+ if (maxcount < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeMaxCount);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetAttribLocation(ValidationContext *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)))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound);
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBooleanv(ValidationContext *context, GLenum pname, GLboolean *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetError(ValidationContext *context)
+{
+ return true;
+}
+
+bool ValidateGetFloatv(ValidationContext *context, GLenum pname, GLfloat *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetIntegerv(ValidationContext *context, GLenum pname, GLint *params)
+{
+ GLenum nativeType;
+ unsigned int numParams = 0;
+ return ValidateStateQuery(context, pname, &nativeType, &numParams);
+}
+
+bool ValidateGetProgramInfoLog(ValidationContext *context,
+ GLuint program,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLchar *infolog)
+{
+ if (bufsize < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderInfoLog(ValidationContext *context,
+ GLuint shader,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLchar *infolog)
+{
+ if (bufsize < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderPrecisionFormat(ValidationContext *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->handleError(InvalidOperation()
+ << "compute shader precision not yet implemented.");
+ return false;
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidShaderType);
+ 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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidPrecision);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetShaderSource(ValidationContext *context,
+ GLuint shader,
+ GLsizei bufsize,
+ GLsizei *length,
+ GLchar *source)
+{
+ if (bufsize < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
+ return false;
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformLocation(ValidationContext *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)))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateHint(ValidationContext *context, GLenum target, GLenum mode)
+{
+ switch (mode)
+ {
+ case GL_FASTEST:
+ case GL_NICEST:
+ case GL_DONT_CARE:
+ break;
+
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ 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->handleError(InvalidEnum() << "hint requires OES_standard_derivatives.");
+ return false;
+ }
+ break;
+
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsBuffer(ValidationContext *context, GLuint buffer)
+{
+ return true;
+}
+
+bool ValidateIsFramebuffer(ValidationContext *context, GLuint framebuffer)
+{
+ return true;
+}
+
+bool ValidateIsProgram(ValidationContext *context, GLuint program)
+{
+ return true;
+}
+
+bool ValidateIsRenderbuffer(ValidationContext *context, GLuint renderbuffer)
+{
+ return true;
+}
+
+bool ValidateIsShader(ValidationContext *context, GLuint shader)
+{
+ return true;
+}
+
+bool ValidateIsTexture(ValidationContext *context, GLuint texture)
+{
+ return true;
+}
+
+bool ValidatePixelStorei(ValidationContext *context, GLenum pname, GLint param)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ switch (pname)
+ {
+ case GL_UNPACK_IMAGE_HEIGHT:
+ case GL_UNPACK_SKIP_IMAGES:
+ context->handleError(InvalidEnum());
+ return false;
+
+ case GL_UNPACK_ROW_LENGTH:
+ case GL_UNPACK_SKIP_ROWS:
+ case GL_UNPACK_SKIP_PIXELS:
+ if (!context->getExtensions().unpackSubimage)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ break;
+
+ case GL_PACK_ROW_LENGTH:
+ case GL_PACK_SKIP_ROWS:
+ case GL_PACK_SKIP_PIXELS:
+ if (!context->getExtensions().packSubimage)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+ break;
+ }
+ }
+
+ if (param < 0)
+ {
+ context->handleError(InvalidValue() << "Cannot use negative values in PixelStorei");
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_UNPACK_ALIGNMENT:
+ if (param != 1 && param != 2 && param != 4 && param != 8)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidUnpackAlignment);
+ return false;
+ }
+ break;
+
+ case GL_PACK_ALIGNMENT:
+ if (param != 1 && param != 2 && param != 4 && param != 8)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidUnpackAlignment);
+ return false;
+ }
+ break;
+
+ case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
+ if (!context->getExtensions().packReverseRowOrder)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ }
+ 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:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePolygonOffset(ValidationContext *context, GLfloat factor, GLfloat units)
+{
+ return true;
+}
+
+bool ValidateReleaseShaderCompiler(ValidationContext *context)
+{
+ return true;
+}
+
+bool ValidateSampleCoverage(ValidationContext *context, GLfloat value, GLboolean invert)
+{
+ return true;
+}
+
+bool ValidateScissor(ValidationContext *context, GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ if (width < 0 || height < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateShaderBinary(ValidationContext *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->handleError(InvalidEnum() << "Invalid shader binary format.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateShaderSource(ValidationContext *context,
+ GLuint shader,
+ GLsizei count,
+ const GLchar *const *string,
+ const GLint *length)
+{
+ if (count < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeCount);
+ 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))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), ShaderSourceInvalidCharacters);
+ return false;
+ }
+ }
+ }
+
+ Shader *shaderObject = GetValidShader(context, shader);
+ if (!shaderObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilFunc(ValidationContext *context, GLenum func, GLint ref, GLuint mask)
+{
+ if (!IsValidStencilFunc(func))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilFuncSeparate(ValidationContext *context,
+ GLenum face,
+ GLenum func,
+ GLint ref,
+ GLuint mask)
+{
+ if (!IsValidStencilFace(face))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilFunc(func))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilMask(ValidationContext *context, GLuint mask)
+{
+ return true;
+}
+
+bool ValidateStencilMaskSeparate(ValidationContext *context, GLenum face, GLuint mask)
+{
+ if (!IsValidStencilFace(face))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilOp(ValidationContext *context, GLenum fail, GLenum zfail, GLenum zpass)
+{
+ if (!IsValidStencilOp(fail))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilOp(zfail))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ return false;
+ }
+
+ if (!IsValidStencilOp(zpass))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateStencilOpSeparate(ValidationContext *context,
+ GLenum face,
+ GLenum fail,
+ GLenum zfail,
+ GLenum zpass)
+{
+ if (!IsValidStencilFace(face))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
+ return false;
+ }
+
+ return ValidateStencilOp(context, fail, zfail, zpass);
+}
+
+bool ValidateUniform1f(ValidationContext *context, GLint location, GLfloat x)
+{
+ return ValidateUniform(context, GL_FLOAT, location, 1);
+}
+
+bool ValidateUniform1fv(ValidationContext *context, GLint location, GLsizei count, const GLfloat *v)
+{
+ return ValidateUniform(context, GL_FLOAT, location, count);
+}
+
+bool ValidateUniform1i(ValidationContext *context, GLint location, GLint x)
+{
+ return ValidateUniform1iv(context, location, 1, &x);
+}
+
+bool ValidateUniform2f(ValidationContext *context, GLint location, GLfloat x, GLfloat y)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC2, location, 1);
+}
+
+bool ValidateUniform2fv(ValidationContext *context, GLint location, GLsizei count, const GLfloat *v)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC2, location, count);
+}
+
+bool ValidateUniform2i(ValidationContext *context, GLint location, GLint x, GLint y)
+{
+ return ValidateUniform(context, GL_INT_VEC2, location, 1);
+}
+
+bool ValidateUniform2iv(ValidationContext *context, GLint location, GLsizei count, const GLint *v)
+{
+ return ValidateUniform(context, GL_INT_VEC2, location, count);
+}
+
+bool ValidateUniform3f(ValidationContext *context, GLint location, GLfloat x, GLfloat y, GLfloat z)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC3, location, 1);
+}
+
+bool ValidateUniform3fv(ValidationContext *context, GLint location, GLsizei count, const GLfloat *v)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC3, location, count);
+}
+
+bool ValidateUniform3i(ValidationContext *context, GLint location, GLint x, GLint y, GLint z)
+{
+ return ValidateUniform(context, GL_INT_VEC3, location, 1);
+}
+
+bool ValidateUniform3iv(ValidationContext *context, GLint location, GLsizei count, const GLint *v)
+{
+ return ValidateUniform(context, GL_INT_VEC3, location, count);
+}
+
+bool ValidateUniform4f(ValidationContext *context,
+ GLint location,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC4, location, 1);
+}
+
+bool ValidateUniform4fv(ValidationContext *context, GLint location, GLsizei count, const GLfloat *v)
+{
+ return ValidateUniform(context, GL_FLOAT_VEC4, location, count);
+}
+
+bool ValidateUniform4i(ValidationContext *context,
+ GLint location,
+ GLint x,
+ GLint y,
+ GLint z,
+ GLint w)
+{
+ return ValidateUniform(context, GL_INT_VEC4, location, 1);
+}
+
+bool ValidateUniform4iv(ValidationContext *context, GLint location, GLsizei count, const GLint *v)
+{
+ return ValidateUniform(context, GL_INT_VEC4, location, count);
+}
+
+bool ValidateUniformMatrix2fv(ValidationContext *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, GL_FLOAT_MAT2, location, count, transpose);
+}
+
+bool ValidateUniformMatrix3fv(ValidationContext *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, GL_FLOAT_MAT3, location, count, transpose);
+}
+
+bool ValidateUniformMatrix4fv(ValidationContext *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrix(context, GL_FLOAT_MAT4, location, count, transpose);
+}
+
+bool ValidateValidateProgram(ValidationContext *context, GLuint program)
+{
+ Program *programObject = GetValidProgram(context, program);
+
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttrib1f(ValidationContext *context, GLuint index, GLfloat x)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib1fv(ValidationContext *context, GLuint index, const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib2f(ValidationContext *context, GLuint index, GLfloat x, GLfloat y)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib2fv(ValidationContext *context, GLuint index, const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib3f(ValidationContext *context,
+ GLuint index,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib3fv(ValidationContext *context, GLuint index, const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib4f(ValidationContext *context,
+ GLuint index,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttrib4fv(ValidationContext *context, GLuint index, const GLfloat *values)
+{
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateViewport(ValidationContext *context, GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ if (width < 0 || height < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), ViewportNegativeSize);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawArrays(ValidationContext *context, GLenum mode, GLint first, GLsizei count)
+{
+ return ValidateDrawArraysCommon(context, mode, first, count, 1);
+}
+
+bool ValidateDrawElements(ValidationContext *context,
+ GLenum mode,
+ GLsizei count,
+ GLenum type,
+ const void *indices)
+{
+ return ValidateDrawElementsCommon(context, mode, count, type, indices, 1);
+}
+
+bool ValidateGetFramebufferAttachmentParameteriv(ValidationContext *context,
+ GLenum target,
+ GLenum attachment,
+ GLenum pname,
+ GLint *params)
+{
+ return ValidateGetFramebufferAttachmentParameterivBase(context, target, attachment, pname,
+ nullptr);
+}
+
+bool ValidateGetProgramiv(ValidationContext *context, GLuint program, GLenum pname, GLint *params)
+{
+ return ValidateGetProgramivBase(context, program, pname, nullptr);
+}
+
+bool ValidateCopyTexImage2D(ValidationContext *context,
+ GLenum 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,
+ GLenum 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 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))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateEnable(Context *context, GLenum cap)
+{
+ if (!ValidCap(context, cap, false))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+
+ if (context->getLimitations().noSampleAlphaToCoverageSupport &&
+ cap == GL_SAMPLE_ALPHA_TO_COVERAGE)
+ {
+ const char *errorMessage = "Current renderer doesn't support alpha-to-coverage";
+ context->handleError(InvalidOperation() << errorMessage);
+
+ // We also output an error message to the debugger window if tracing is active, so that
+ // developers can see the error message.
+ ERR() << errorMessage;
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferRenderbuffer(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer)
+{
+ if (!ValidFramebufferTarget(context, target))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
+ return false;
+ }
+
+ if (renderbuffertarget != GL_RENDERBUFFER && renderbuffer != 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidRenderbufferTarget);
+ return false;
+ }
+
+ return ValidateFramebufferRenderbufferParameters(context, target, attachment,
+ renderbuffertarget, renderbuffer);
+}
+
+bool ValidateFramebufferTexture2D(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLenum 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidFramebufferTextureLevel);
+ 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 GL_TEXTURE_2D:
+ {
+ if (level > gl::log2(caps.max2DTextureSize))
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ if (tex->getTarget() != GL_TEXTURE_2D)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidTextureTarget);
+ return false;
+ }
+ }
+ break;
+
+ case GL_TEXTURE_RECTANGLE_ANGLE:
+ {
+ if (level != 0)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ if (tex->getTarget() != GL_TEXTURE_RECTANGLE_ANGLE)
+ {
+ context->handleError(InvalidOperation()
+ << "Textarget must match the texture target type.");
+ return false;
+ }
+ }
+ break;
+
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ {
+ if (level > gl::log2(caps.maxCubeMapTextureSize))
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ if (tex->getTarget() != GL_TEXTURE_CUBE_MAP)
+ {
+ context->handleError(InvalidOperation()
+ << "Textarget must match the texture target type.");
+ return false;
+ }
+ }
+ break;
+
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ {
+ if (context->getClientVersion() < ES_3_1)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
+ return false;
+ }
+
+ if (level != 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), LevelNotZero);
+ return false;
+ }
+ if (tex->getTarget() != GL_TEXTURE_2D_MULTISAMPLE)
+ {
+ context->handleError(InvalidOperation()
+ << "Textarget must match the texture target type.");
+ return false;
+ }
+ }
+ break;
+
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
+ return false;
+ }
+
+ const Format &format = tex->getFormat(textarget, level);
+ if (format.info->compressed)
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+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, GLenum target)
+{
+ if (!ValidTextureTarget(context, target))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
+ return false;
+ }
+
+ Texture *texture = context->getTargetTexture(target);
+
+ if (texture == nullptr)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), TextureNotBound);
+ 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->handleError(InvalidOperation());
+ return false;
+ }
+
+ GLenum baseTarget = (target == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
+ const auto &format = *(texture->getFormat(baseTarget, effectiveBaseLevel).info);
+ if (format.sizedInternalFormat == GL_NONE || format.compressed || format.depthBits > 0 ||
+ format.stencilBits > 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), GenerateMipmapNotAllowed);
+ 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.renderSupport(context->getClientVersion(), context->getExtensions());
+ if (!formatUnsized && !formatColorRenderableAndFilterable)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), GenerateMipmapNotAllowed);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), GenerateMipmapNotAllowed);
+ return false;
+ }
+
+ // ES3 and WebGL grant mipmap generation for sRGBA (with alpha) textures but GL_EXT_sRGB does
+ // not.
+ bool supportsSRGBMipmapGeneration =
+ context->getClientVersion() >= ES_3_0 || context->getExtensions().webglCompatibility;
+ if (!supportsSRGBMipmapGeneration && format.colorEncoding == GL_SRGB)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), GenerateMipmapNotAllowed);
+ 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 == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE_ANGLE ||
+ target == GL_TEXTURE_CUBE_MAP);
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), TextureNotPow2);
+ return false;
+ }
+
+ // Cube completeness check
+ if (target == GL_TEXTURE_CUBE_MAP && !texture->getTextureState().isCubeComplete())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), CubemapIncomplete);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferParameteriv(ValidationContext *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, GLenum target, GLenum pname, GLfloat *params)
+{
+ return ValidateGetTexParameterBase(context, target, pname, nullptr);
+}
+
+bool ValidateGetTexParameteriv(Context *context, GLenum target, GLenum pname, GLint *params)
+{
+ 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))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateLinkProgram(Context *context, GLuint program)
+{
+ if (context->hasActiveTransformFeedback(program))
+ {
+ // ES 3.0.4 section 2.15 page 91
+ context->handleError(InvalidOperation() << "Cannot link program while program is "
+ "associated with an active transform "
+ "feedback object.");
+ 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, GLenum target, GLenum pname, GLfloat param)
+{
+ return ValidateTexParameterBase(context, target, pname, -1, &param);
+}
+
+bool ValidateTexParameterfv(Context *context, GLenum target, GLenum pname, const GLfloat *params)
+{
+ return ValidateTexParameterBase(context, target, pname, -1, params);
+}
+
+bool ValidateTexParameteri(Context *context, GLenum target, GLenum pname, GLint param)
+{
+ return ValidateTexParameterBase(context, target, pname, -1, &param);
+}
+
+bool ValidateTexParameteriv(Context *context, GLenum target, GLenum pname, const GLint *params)
+{
+ return ValidateTexParameterBase(context, target, pname, -1, params);
+}
+
+bool ValidateUseProgram(Context *context, GLuint program)
+{
+ if (program != 0)
+ {
+ Program *programObject = context->getProgram(program);
+ if (!programObject)
+ {
+ // ES 3.1.0 section 7.3 page 72
+ if (context->getShader(program))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExpectedProgramName);
+ return false;
+ }
+ else
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidProgramName);
+ return false;
+ }
+ }
+ if (!programObject->isLinked())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
+ return false;
+ }
+ }
+ if (context->getGLState().isTransformFeedbackActiveUnpaused())
+ {
+ // ES 3.0.4 section 2.15 page 91
+ context
+ ->handleError(InvalidOperation()
+ << "Cannot change active program while transform feedback is unpaused.");
+ return false;
+ }
+
+ return true;
+}
+
} // namespace gl