| // |
| // Copyright 2013 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // validationES2.cpp: Validation functions for OpenGL ES 2.0 entry point parameters |
| |
| #include "libANGLE/validationES2_autogen.h" |
| |
| #include <cstdint> |
| |
| #include "common/mathutil.h" |
| #include "common/string_utils.h" |
| #include "common/utilities.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/ErrorStrings.h" |
| #include "libANGLE/Fence.h" |
| #include "libANGLE/Framebuffer.h" |
| #include "libANGLE/FramebufferAttachment.h" |
| #include "libANGLE/Renderbuffer.h" |
| #include "libANGLE/Shader.h" |
| #include "libANGLE/Texture.h" |
| #include "libANGLE/Uniform.h" |
| #include "libANGLE/VertexArray.h" |
| #include "libANGLE/formatutils.h" |
| #include "libANGLE/validationES.h" |
| #include "libANGLE/validationES2.h" |
| #include "libANGLE/validationES3_autogen.h" |
| |
| namespace gl |
| { |
| using namespace err; |
| |
| namespace |
| { |
| |
| bool IsPartialBlit(gl::Context *context, |
| const FramebufferAttachment *readBuffer, |
| const FramebufferAttachment *writeBuffer, |
| GLint srcX0, |
| GLint srcY0, |
| GLint srcX1, |
| GLint srcY1, |
| GLint dstX0, |
| GLint dstY0, |
| GLint dstX1, |
| GLint dstY1) |
| { |
| const Extents &writeSize = writeBuffer->getSize(); |
| const Extents &readSize = readBuffer->getSize(); |
| |
| if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 || dstX1 != writeSize.width || |
| dstY1 != writeSize.height || srcX1 != readSize.width || srcY1 != readSize.height) |
| { |
| return true; |
| } |
| |
| if (context->getState().isScissorTestEnabled()) |
| { |
| const Rectangle &scissor = context->getState().getScissor(); |
| return scissor.x > 0 || scissor.y > 0 || scissor.width < writeSize.width || |
| scissor.height < writeSize.height; |
| } |
| |
| return false; |
| } |
| |
| template <typename T> |
| bool ValidatePathInstances(gl::Context *context, |
| GLsizei numPaths, |
| const void *paths, |
| PathID pathBase) |
| { |
| const auto *array = static_cast<const T *>(paths); |
| |
| for (GLsizei i = 0; i < numPaths; ++i) |
| { |
| const GLuint pathName = array[i] + pathBase.value; |
| if (context->isPathGenerated({pathName}) && !context->isPath({pathName})) |
| { |
| context->validationError(GL_INVALID_OPERATION, kNoSuchPath); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool ValidateInstancedPathParameters(gl::Context *context, |
| GLsizei numPaths, |
| GLenum pathNameType, |
| const void *paths, |
| PathID pathBase, |
| GLenum transformType, |
| const GLfloat *transformValues) |
| { |
| if (!context->getExtensions().pathRendering) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (paths == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidPathNameArray); |
| return false; |
| } |
| |
| if (numPaths < 0) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidPathNumPaths); |
| return false; |
| } |
| |
| if (!angle::IsValueInRangeForNumericType<std::uint32_t>(numPaths)) |
| { |
| context->validationError(GL_INVALID_OPERATION, kIntegerOverflow); |
| return false; |
| } |
| |
| std::uint32_t pathNameTypeSize = 0; |
| std::uint32_t componentCount = 0; |
| |
| switch (pathNameType) |
| { |
| case GL_UNSIGNED_BYTE: |
| pathNameTypeSize = sizeof(GLubyte); |
| if (!ValidatePathInstances<GLubyte>(context, numPaths, paths, pathBase)) |
| return false; |
| break; |
| |
| case GL_BYTE: |
| pathNameTypeSize = sizeof(GLbyte); |
| if (!ValidatePathInstances<GLbyte>(context, numPaths, paths, pathBase)) |
| return false; |
| break; |
| |
| case GL_UNSIGNED_SHORT: |
| pathNameTypeSize = sizeof(GLushort); |
| if (!ValidatePathInstances<GLushort>(context, numPaths, paths, pathBase)) |
| return false; |
| break; |
| |
| case GL_SHORT: |
| pathNameTypeSize = sizeof(GLshort); |
| if (!ValidatePathInstances<GLshort>(context, numPaths, paths, pathBase)) |
| return false; |
| break; |
| |
| case GL_UNSIGNED_INT: |
| pathNameTypeSize = sizeof(GLuint); |
| if (!ValidatePathInstances<GLuint>(context, numPaths, paths, pathBase)) |
| return false; |
| break; |
| |
| case GL_INT: |
| pathNameTypeSize = sizeof(GLint); |
| if (!ValidatePathInstances<GLint>(context, numPaths, paths, pathBase)) |
| return false; |
| break; |
| |
| default: |
| context->validationError(GL_INVALID_ENUM, kInvalidPathNameType); |
| return false; |
| } |
| |
| switch (transformType) |
| { |
| case GL_NONE: |
| componentCount = 0; |
| break; |
| case GL_TRANSLATE_X_CHROMIUM: |
| case GL_TRANSLATE_Y_CHROMIUM: |
| componentCount = 1; |
| break; |
| case GL_TRANSLATE_2D_CHROMIUM: |
| componentCount = 2; |
| break; |
| case GL_TRANSLATE_3D_CHROMIUM: |
| componentCount = 3; |
| break; |
| case GL_AFFINE_2D_CHROMIUM: |
| case GL_TRANSPOSE_AFFINE_2D_CHROMIUM: |
| componentCount = 6; |
| break; |
| case GL_AFFINE_3D_CHROMIUM: |
| case GL_TRANSPOSE_AFFINE_3D_CHROMIUM: |
| componentCount = 12; |
| break; |
| default: |
| context->validationError(GL_INVALID_ENUM, kInvalidTransformation); |
| return false; |
| } |
| if (componentCount != 0 && transformValues == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kNoTransformArray); |
| return false; |
| } |
| |
| angle::CheckedNumeric<std::uint32_t> checkedSize(0); |
| checkedSize += (numPaths * pathNameTypeSize); |
| checkedSize += (numPaths * sizeof(GLfloat) * componentCount); |
| if (!checkedSize.IsValid()) |
| { |
| context->validationError(GL_INVALID_OPERATION, kIntegerOverflow); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool IsValidCopyTextureSourceInternalFormatEnum(GLenum internalFormat) |
| { |
| // Table 1.1 from the CHROMIUM_copy_texture spec |
| switch (GetUnsizedFormat(internalFormat)) |
| { |
| case GL_RED: |
| case GL_ALPHA: |
| case GL_LUMINANCE: |
| case GL_LUMINANCE_ALPHA: |
| case GL_RGB: |
| case GL_RGBA: |
| case GL_RGB8: |
| case GL_RGBA8: |
| case GL_BGRA_EXT: |
| case GL_BGRA8_EXT: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool IsValidCopySubTextureSourceInternalFormat(GLenum internalFormat) |
| { |
| return IsValidCopyTextureSourceInternalFormatEnum(internalFormat); |
| } |
| |
| bool IsValidCopyTextureDestinationInternalFormatEnum(GLint internalFormat) |
| { |
| // Table 1.0 from the CHROMIUM_copy_texture spec |
| switch (internalFormat) |
| { |
| case GL_RGB: |
| case GL_RGBA: |
| case GL_RGB8: |
| case GL_RGBA8: |
| case GL_BGRA_EXT: |
| case GL_BGRA8_EXT: |
| case GL_SRGB_EXT: |
| case GL_SRGB_ALPHA_EXT: |
| case GL_R8: |
| case GL_R8UI: |
| case GL_RG8: |
| case GL_RG8UI: |
| case GL_SRGB8: |
| case GL_RGB565: |
| case GL_RGB8UI: |
| case GL_RGB10_A2: |
| case GL_SRGB8_ALPHA8: |
| case GL_RGB5_A1: |
| case GL_RGBA4: |
| case GL_RGBA8UI: |
| case GL_RGB9_E5: |
| case GL_R16F: |
| case GL_R32F: |
| case GL_RG16F: |
| case GL_RG32F: |
| case GL_RGB16F: |
| case GL_RGB32F: |
| case GL_RGBA16F: |
| case GL_RGBA32F: |
| case GL_R11F_G11F_B10F: |
| case GL_LUMINANCE: |
| case GL_LUMINANCE_ALPHA: |
| case GL_ALPHA: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool IsValidCopySubTextureDestionationInternalFormat(GLenum internalFormat) |
| { |
| return IsValidCopyTextureDestinationInternalFormatEnum(internalFormat); |
| } |
| |
| bool IsValidCopyTextureDestinationFormatType(Context *context, GLint internalFormat, GLenum type) |
| { |
| if (!IsValidCopyTextureDestinationInternalFormatEnum(internalFormat)) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat); |
| return false; |
| } |
| |
| if (!ValidES3FormatCombination(GetUnsizedFormat(internalFormat), type, internalFormat)) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| |
| const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat, type); |
| if (!internalFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions())) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool IsValidCopyTextureDestinationTargetEnum(Context *context, TextureTarget target) |
| { |
| switch (target) |
| { |
| case TextureTarget::_2D: |
| case TextureTarget::CubeMapNegativeX: |
| case TextureTarget::CubeMapNegativeY: |
| case TextureTarget::CubeMapNegativeZ: |
| case TextureTarget::CubeMapPositiveX: |
| case TextureTarget::CubeMapPositiveY: |
| case TextureTarget::CubeMapPositiveZ: |
| return true; |
| |
| case TextureTarget::Rectangle: |
| return context->getExtensions().textureRectangle; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool IsValidCopyTextureDestinationTarget(Context *context, |
| TextureType textureType, |
| TextureTarget target) |
| { |
| return TextureTargetToType(target) == textureType; |
| } |
| |
| bool IsValidCopyTextureSourceTarget(Context *context, TextureType type) |
| { |
| switch (type) |
| { |
| case TextureType::_2D: |
| return true; |
| case TextureType::Rectangle: |
| return context->getExtensions().textureRectangle; |
| case TextureType::External: |
| return context->getExtensions().eglImageExternal; |
| default: |
| return false; |
| } |
| } |
| |
| bool IsValidCopyTextureSourceLevel(Context *context, TextureType type, GLint level) |
| { |
| if (!ValidMipLevel(context, type, level)) |
| { |
| return false; |
| } |
| |
| if (level > 0 && context->getClientVersion() < ES_3_0) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool IsValidCopyTextureDestinationLevel(Context *context, |
| TextureType type, |
| GLint level, |
| GLsizei width, |
| GLsizei height, |
| bool isSubImage) |
| { |
| if (!ValidMipLevel(context, type, level)) |
| { |
| return false; |
| } |
| |
| if (!ValidImageSizeParameters(context, type, level, width, height, 1, isSubImage)) |
| { |
| return false; |
| } |
| |
| const Caps &caps = context->getCaps(); |
| switch (type) |
| { |
| case TextureType::_2D: |
| return width <= (caps.max2DTextureSize >> level) && |
| height <= (caps.max2DTextureSize >> level); |
| case TextureType::Rectangle: |
| ASSERT(level == 0); |
| return width <= (caps.max2DTextureSize >> level) && |
| height <= (caps.max2DTextureSize >> level); |
| |
| case TextureType::CubeMap: |
| return width <= (caps.maxCubeMapTextureSize >> level) && |
| height <= (caps.maxCubeMapTextureSize >> level); |
| default: |
| return true; |
| } |
| } |
| |
| bool IsValidStencilFunc(GLenum func) |
| { |
| switch (func) |
| { |
| case GL_NEVER: |
| case GL_ALWAYS: |
| case GL_LESS: |
| case GL_LEQUAL: |
| case GL_EQUAL: |
| case GL_GEQUAL: |
| case GL_GREATER: |
| case GL_NOTEQUAL: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool IsValidStencilFace(GLenum face) |
| { |
| switch (face) |
| { |
| case GL_FRONT: |
| case GL_BACK: |
| case GL_FRONT_AND_BACK: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool IsValidStencilOp(GLenum op) |
| { |
| switch (op) |
| { |
| case GL_ZERO: |
| case GL_KEEP: |
| case GL_REPLACE: |
| case GL_INCR: |
| case GL_DECR: |
| case GL_INVERT: |
| case GL_INCR_WRAP: |
| case GL_DECR_WRAP: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool ValidateES2CopyTexImageParameters(Context *context, |
| TextureTarget target, |
| GLint level, |
| GLenum internalformat, |
| bool isSubImage, |
| GLint xoffset, |
| GLint yoffset, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| GLint border) |
| { |
| if (!ValidTexture2DDestinationTarget(context, target)) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget); |
| return false; |
| } |
| |
| TextureType texType = TextureTargetToType(target); |
| if (!ValidImageSizeParameters(context, texType, level, width, height, 1, isSubImage)) |
| { |
| // Error is already handled. |
| return false; |
| } |
| |
| Format textureFormat = Format::Invalid(); |
| if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage, |
| xoffset, yoffset, 0, x, y, width, height, border, |
| &textureFormat)) |
| { |
| return false; |
| } |
| |
| const gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); |
| GLenum colorbufferFormat = |
| framebuffer->getReadColorAttachment()->getFormat().info->sizedInternalFormat; |
| const auto &formatInfo = *textureFormat.info; |
| |
| // [OpenGL ES 2.0.24] table 3.9 |
| if (isSubImage) |
| { |
| switch (formatInfo.format) |
| { |
| case GL_ALPHA: |
| if (colorbufferFormat != GL_ALPHA8_EXT && colorbufferFormat != GL_RGBA4 && |
| colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_RGBA8_OES && |
| colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_LUMINANCE: |
| if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT && |
| colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES && |
| colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 && |
| colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_BGRA8_EXT && |
| colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_RED_EXT: |
| if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT && |
| colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES && |
| colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 && |
| colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_R32F && |
| colorbufferFormat != GL_RG32F && colorbufferFormat != GL_RGB32F && |
| colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT && |
| colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_RG_EXT: |
| if (colorbufferFormat != GL_RG8_EXT && colorbufferFormat != GL_RGB565 && |
| colorbufferFormat != GL_RGB8_OES && colorbufferFormat != GL_RGBA4 && |
| colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_RGBA8_OES && |
| colorbufferFormat != GL_RG32F && colorbufferFormat != GL_RGB32F && |
| colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT && |
| colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_RGB: |
| if (colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES && |
| colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 && |
| colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_RGB32F && |
| colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT && |
| colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_LUMINANCE_ALPHA: |
| case GL_RGBA: |
| if (colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 && |
| colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_RGBA32F && |
| colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: |
| case GL_ETC1_RGB8_OES: |
| case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: |
| case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_RGBA_BPTC_UNORM_EXT: |
| case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT: |
| case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT: |
| case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT: |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| case GL_DEPTH_COMPONENT: |
| case GL_DEPTH_STENCIL_OES: |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| |
| if (formatInfo.type == GL_FLOAT && !context->getExtensions().textureFloat) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| } |
| else |
| { |
| switch (internalformat) |
| { |
| case GL_ALPHA: |
| if (colorbufferFormat != GL_ALPHA8_EXT && colorbufferFormat != GL_RGBA4 && |
| colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_BGRA8_EXT && |
| colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_LUMINANCE: |
| if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT && |
| colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES && |
| colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 && |
| colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES && |
| colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_RED_EXT: |
| if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT && |
| colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES && |
| colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 && |
| colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES && |
| colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_RG_EXT: |
| if (colorbufferFormat != GL_RG8_EXT && colorbufferFormat != GL_RGB565 && |
| colorbufferFormat != GL_RGB8_OES && colorbufferFormat != GL_RGBA4 && |
| colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_BGRA8_EXT && |
| colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_RGB: |
| if (colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES && |
| colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 && |
| colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES && |
| colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_LUMINANCE_ALPHA: |
| case GL_RGBA: |
| if (colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 && |
| colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES && |
| colorbufferFormat != GL_BGR5_A1_ANGLEX) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
| if (context->getExtensions().textureCompressionDXT1) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: |
| if (context->getExtensions().textureCompressionDXT3) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: |
| if (context->getExtensions().textureCompressionDXT5) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_ETC1_RGB8_OES: |
| if (context->getExtensions().compressedETC1RGB8Texture) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: |
| case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: |
| if (context->getExtensions().lossyETCDecode) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: |
| case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: |
| case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: |
| case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: |
| if (context->getExtensions().compressedTexturePVRTC) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: |
| if (context->getExtensions().compressedTexturePVRTCsRGB) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_DEPTH_COMPONENT: |
| case GL_DEPTH_COMPONENT16: |
| case GL_DEPTH_COMPONENT32_OES: |
| if (context->getExtensions().depthTextureAny()) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_DEPTH_STENCIL_OES: |
| case GL_DEPTH24_STENCIL8_OES: |
| if (context->getExtensions().depthTextureAny() || |
| context->getExtensions().packedDepthStencil) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| default: |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| } |
| |
| // If width or height is zero, it is a no-op. Return false without setting an error. |
| return (width > 0 && height > 0); |
| } |
| |
| bool ValidCap(const Context *context, GLenum cap, bool queryOnly) |
| { |
| switch (cap) |
| { |
| // EXT_multisample_compatibility |
| case GL_MULTISAMPLE_EXT: |
| case GL_SAMPLE_ALPHA_TO_ONE_EXT: |
| return context->getExtensions().multisampleCompatibility; |
| |
| case GL_CULL_FACE: |
| case GL_POLYGON_OFFSET_FILL: |
| case GL_SAMPLE_ALPHA_TO_COVERAGE: |
| case GL_SAMPLE_COVERAGE: |
| case GL_SCISSOR_TEST: |
| case GL_STENCIL_TEST: |
| case GL_DEPTH_TEST: |
| case GL_BLEND: |
| case GL_DITHER: |
| return true; |
| |
| case GL_PRIMITIVE_RESTART_FIXED_INDEX: |
| case GL_RASTERIZER_DISCARD: |
| return (context->getClientMajorVersion() >= 3); |
| |
| case GL_DEBUG_OUTPUT_SYNCHRONOUS: |
| case GL_DEBUG_OUTPUT: |
| return context->getExtensions().debug; |
| |
| case GL_BIND_GENERATES_RESOURCE_CHROMIUM: |
| return queryOnly && context->getExtensions().bindGeneratesResource; |
| |
| case GL_CLIENT_ARRAYS_ANGLE: |
| return queryOnly && context->getExtensions().clientArrays; |
| |
| case GL_FRAMEBUFFER_SRGB_EXT: |
| return context->getExtensions().sRGBWriteControl; |
| |
| case GL_SAMPLE_MASK: |
| return context->getClientVersion() >= Version(3, 1); |
| |
| case GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE: |
| return queryOnly && context->getExtensions().robustResourceInitialization; |
| |
| // GLES1 emulation: GLES1-specific caps |
| case GL_ALPHA_TEST: |
| case GL_VERTEX_ARRAY: |
| case GL_NORMAL_ARRAY: |
| case GL_COLOR_ARRAY: |
| case GL_TEXTURE_COORD_ARRAY: |
| case GL_TEXTURE_2D: |
| case GL_LIGHTING: |
| case GL_LIGHT0: |
| case GL_LIGHT1: |
| case GL_LIGHT2: |
| case GL_LIGHT3: |
| case GL_LIGHT4: |
| case GL_LIGHT5: |
| case GL_LIGHT6: |
| case GL_LIGHT7: |
| case GL_NORMALIZE: |
| case GL_RESCALE_NORMAL: |
| case GL_COLOR_MATERIAL: |
| case GL_CLIP_PLANE0: |
| case GL_CLIP_PLANE1: |
| case GL_CLIP_PLANE2: |
| case GL_CLIP_PLANE3: |
| case GL_CLIP_PLANE4: |
| case GL_CLIP_PLANE5: |
| case GL_FOG: |
| case GL_POINT_SMOOTH: |
| case GL_LINE_SMOOTH: |
| case GL_COLOR_LOGIC_OP: |
| return context->getClientVersion() < Version(2, 0); |
| case GL_POINT_SIZE_ARRAY_OES: |
| return context->getClientVersion() < Version(2, 0) && |
| context->getExtensions().pointSizeArray; |
| case GL_TEXTURE_CUBE_MAP: |
| return context->getClientVersion() < Version(2, 0) && |
| context->getExtensions().textureCubeMap; |
| case GL_POINT_SPRITE_OES: |
| return context->getClientVersion() < Version(2, 0) && |
| context->getExtensions().pointSprite; |
| default: |
| return false; |
| } |
| } |
| |
| // Return true if a character belongs to the ASCII subset as defined in GLSL ES 1.0 spec section |
| // 3.1. |
| bool IsValidESSLCharacter(unsigned char c) |
| { |
| // Printing characters are valid except " $ ` @ \ ' DEL. |
| if (c >= 32 && c <= 126 && c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && |
| c != '\'') |
| { |
| return true; |
| } |
| |
| // Horizontal tab, line feed, vertical tab, form feed, carriage return are also valid. |
| if (c >= 9 && c <= 13) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool IsValidESSLString(const char *str, size_t len) |
| { |
| for (size_t i = 0; i < len; i++) |
| { |
| if (!IsValidESSLCharacter(str[i])) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool IsValidESSLShaderSourceString(const char *str, size_t len, bool lineContinuationAllowed) |
| { |
| enum class ParseState |
| { |
| // Have not seen an ASCII non-whitespace character yet on |
| // this line. Possible that we might see a preprocessor |
| // directive. |
| BEGINING_OF_LINE, |
| |
| // Have seen at least one ASCII non-whitespace character |
| // on this line. |
| MIDDLE_OF_LINE, |
| |
| // Handling a preprocessor directive. Passes through all |
| // characters up to the end of the line. Disables comment |
| // processing. |
| IN_PREPROCESSOR_DIRECTIVE, |
| |
| // Handling a single-line comment. The comment text is |
| // replaced with a single space. |
| IN_SINGLE_LINE_COMMENT, |
| |
| // Handling a multi-line comment. Newlines are passed |
| // through to preserve line numbers. |
| IN_MULTI_LINE_COMMENT |
| }; |
| |
| ParseState state = ParseState::BEGINING_OF_LINE; |
| size_t pos = 0; |
| |
| while (pos < len) |
| { |
| char c = str[pos]; |
| char next = pos + 1 < len ? str[pos + 1] : 0; |
| |
| // Check for newlines |
| if (c == '\n' || c == '\r') |
| { |
| if (state != ParseState::IN_MULTI_LINE_COMMENT) |
| { |
| state = ParseState::BEGINING_OF_LINE; |
| } |
| |
| pos++; |
| continue; |
| } |
| |
| switch (state) |
| { |
| case ParseState::BEGINING_OF_LINE: |
| if (c == ' ') |
| { |
| // Maintain the BEGINING_OF_LINE state until a non-space is seen |
| pos++; |
| } |
| else if (c == '#') |
| { |
| state = ParseState::IN_PREPROCESSOR_DIRECTIVE; |
| pos++; |
| } |
| else |
| { |
| // Don't advance, re-process this character with the MIDDLE_OF_LINE state |
| state = ParseState::MIDDLE_OF_LINE; |
| } |
| break; |
| |
| case ParseState::MIDDLE_OF_LINE: |
| if (c == '/' && next == '/') |
| { |
| state = ParseState::IN_SINGLE_LINE_COMMENT; |
| pos++; |
| } |
| else if (c == '/' && next == '*') |
| { |
| state = ParseState::IN_MULTI_LINE_COMMENT; |
| pos++; |
| } |
| else if (lineContinuationAllowed && c == '\\' && (next == '\n' || next == '\r')) |
| { |
| // Skip line continuation characters |
| } |
| else if (!IsValidESSLCharacter(c)) |
| { |
| return false; |
| } |
| pos++; |
| break; |
| |
| case ParseState::IN_PREPROCESSOR_DIRECTIVE: |
| // Line-continuation characters may not be permitted. |
| // Otherwise, just pass it through. Do not parse comments in this state. |
| if (!lineContinuationAllowed && c == '\\') |
| { |
| return false; |
| } |
| pos++; |
| break; |
| |
| case ParseState::IN_SINGLE_LINE_COMMENT: |
| // Line-continuation characters are processed before comment processing. |
| // Advance string if a new line character is immediately behind |
| // line-continuation character. |
| if (c == '\\' && (next == '\n' || next == '\r')) |
| { |
| pos++; |
| } |
| pos++; |
| break; |
| |
| case ParseState::IN_MULTI_LINE_COMMENT: |
| if (c == '*' && next == '/') |
| { |
| state = ParseState::MIDDLE_OF_LINE; |
| pos++; |
| } |
| pos++; |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool ValidateWebGLNamePrefix(Context *context, const GLchar *name) |
| { |
| ASSERT(context->isWebGL()); |
| |
| // WebGL 1.0 [Section 6.16] GLSL Constructs |
| // Identifiers starting with "webgl_" and "_webgl_" are reserved for use by WebGL. |
| if (strncmp(name, "webgl_", 6) == 0 || strncmp(name, "_webgl_", 7) == 0) |
| { |
| context->validationError(GL_INVALID_OPERATION, kWebglBindAttribLocationReservedPrefix); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateWebGLNameLength(Context *context, size_t length) |
| { |
| ASSERT(context->isWebGL()); |
| |
| if (context->isWebGL1() && length > 256) |
| { |
| // WebGL 1.0 [Section 6.21] Maxmimum Uniform and Attribute Location Lengths |
| // WebGL imposes a limit of 256 characters on the lengths of uniform and attribute |
| // locations. |
| context->validationError(GL_INVALID_VALUE, kWebglNameLengthLimitExceeded); |
| |
| return false; |
| } |
| else if (length > 1024) |
| { |
| // WebGL 2.0 [Section 4.3.2] WebGL 2.0 imposes a limit of 1024 characters on the lengths of |
| // uniform and attribute locations. |
| context->validationError(GL_INVALID_VALUE, kWebgl2NameLengthLimitExceeded); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateMatrixMode(Context *context, GLenum matrixMode) |
| { |
| if (!context->getExtensions().pathRendering) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidMatrixMode); |
| return false; |
| } |
| return true; |
| } |
| |
| bool ValidBlendFunc(const Context *context, GLenum val) |
| { |
| const gl::Extensions &ext = context->getExtensions(); |
| |
| // these are always valid for src and dst. |
| switch (val) |
| { |
| case GL_ZERO: |
| case GL_ONE: |
| case GL_SRC_COLOR: |
| case GL_ONE_MINUS_SRC_COLOR: |
| case GL_DST_COLOR: |
| case GL_ONE_MINUS_DST_COLOR: |
| case GL_SRC_ALPHA: |
| case GL_ONE_MINUS_SRC_ALPHA: |
| case GL_DST_ALPHA: |
| case GL_ONE_MINUS_DST_ALPHA: |
| case GL_CONSTANT_COLOR: |
| case GL_ONE_MINUS_CONSTANT_COLOR: |
| case GL_CONSTANT_ALPHA: |
| case GL_ONE_MINUS_CONSTANT_ALPHA: |
| return true; |
| |
| // EXT_blend_func_extended. |
| case GL_SRC1_COLOR_EXT: |
| case GL_SRC1_ALPHA_EXT: |
| case GL_ONE_MINUS_SRC1_COLOR_EXT: |
| case GL_ONE_MINUS_SRC1_ALPHA_EXT: |
| case GL_SRC_ALPHA_SATURATE_EXT: |
| return ext.blendFuncExtended; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool ValidSrcBlendFunc(const Context *context, GLenum val) |
| { |
| if (ValidBlendFunc(context, val)) |
| return true; |
| |
| if (val == GL_SRC_ALPHA_SATURATE) |
| return true; |
| |
| return false; |
| } |
| |
| bool ValidDstBlendFunc(const Context *context, GLenum val) |
| { |
| if (ValidBlendFunc(context, val)) |
| return true; |
| |
| if (val == GL_SRC_ALPHA_SATURATE) |
| { |
| if (context->getClientMajorVersion() >= 3) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool IsValidImageLayout(ImageLayout layout) |
| { |
| switch (layout) |
| { |
| case ImageLayout::Undefined: |
| case ImageLayout::General: |
| case ImageLayout::ColorAttachment: |
| case ImageLayout::DepthStencilAttachment: |
| case ImageLayout::DepthStencilReadOnlyAttachment: |
| case ImageLayout::ShaderReadOnly: |
| case ImageLayout::TransferSrc: |
| case ImageLayout::TransferDst: |
| case ImageLayout::DepthReadOnlyStencilAttachment: |
| case ImageLayout::DepthAttachmentStencilReadOnly: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool ValidateES2TexImageParameters(Context *context, |
| TextureTarget target, |
| GLint level, |
| GLenum internalformat, |
| bool isCompressed, |
| bool isSubImage, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| GLsizei imageSize, |
| const void *pixels) |
| { |
| if (!ValidTexture2DDestinationTarget(context, target)) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget); |
| return false; |
| } |
| |
| return ValidateES2TexImageParametersBase(context, target, level, internalformat, isCompressed, |
| isSubImage, xoffset, yoffset, width, height, border, |
| format, type, imageSize, pixels); |
| } |
| |
| } // anonymous namespace |
| |
| bool ValidateES2TexImageParametersBase(Context *context, |
| TextureTarget target, |
| GLint level, |
| GLenum internalformat, |
| bool isCompressed, |
| bool isSubImage, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| GLsizei imageSize, |
| const void *pixels) |
| { |
| |
| TextureType texType = TextureTargetToType(target); |
| if (!ValidImageSizeParameters(context, texType, level, width, height, 1, isSubImage)) |
| { |
| // Error already handled. |
| return false; |
| } |
| |
| if (!ValidMipLevel(context, texType, level)) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidMipLevel); |
| return false; |
| } |
| |
| if (xoffset < 0 || std::numeric_limits<GLsizei>::max() - xoffset < width || |
| std::numeric_limits<GLsizei>::max() - yoffset < height) |
| { |
| context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize); |
| return false; |
| } |
| |
| const gl::Caps &caps = context->getCaps(); |
| |
| switch (texType) |
| { |
| case TextureType::_2D: |
| case TextureType::External: |
| if (width > (caps.max2DTextureSize >> level) || |
| height > (caps.max2DTextureSize >> level)) |
| { |
| context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize); |
| return false; |
| } |
| break; |
| |
| case TextureType::Rectangle: |
| ASSERT(level == 0); |
| if (width > caps.maxRectangleTextureSize || height > caps.maxRectangleTextureSize) |
| { |
| context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize); |
| return false; |
| } |
| if (isCompressed) |
| { |
| context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed); |
| return false; |
| } |
| break; |
| |
| case TextureType::CubeMap: |
| if (!isSubImage && width != height) |
| { |
| context->validationError(GL_INVALID_VALUE, kCubemapFacesEqualDimensions); |
| return false; |
| } |
| |
| if (width > (caps.maxCubeMapTextureSize >> level) || |
| height > (caps.maxCubeMapTextureSize >> level)) |
| { |
| context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize); |
| return false; |
| } |
| break; |
| |
| default: |
| context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget); |
| return false; |
| } |
| |
| gl::Texture *texture = context->getTextureByType(texType); |
| if (!texture) |
| { |
| context->validationError(GL_INVALID_OPERATION, kBufferNotBound); |
| return false; |
| } |
| |
| // Verify zero border |
| if (border != 0) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidBorder); |
| return false; |
| } |
| |
| bool nonEqualFormatsAllowed = false; |
| |
| if (isCompressed) |
| { |
| GLenum actualInternalFormat = |
| isSubImage ? texture->getFormat(target, level).info->sizedInternalFormat |
| : internalformat; |
| |
| const InternalFormat &internalFormatInfo = GetSizedInternalFormatInfo(actualInternalFormat); |
| |
| if (!internalFormatInfo.compressed) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidInternalFormat); |
| return false; |
| } |
| |
| if (!internalFormatInfo.textureSupport(context->getClientVersion(), |
| context->getExtensions())) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidInternalFormat); |
| return false; |
| } |
| |
| if (isSubImage) |
| { |
| // From the OES_compressed_ETC1_RGB8_texture spec: |
| // INVALID_OPERATION is generated by CompressedTexSubImage2D, TexSubImage2D, or |
| // CopyTexSubImage2D if the texture image <level> bound to <target> has internal format |
| // ETC1_RGB8_OES. |
| if (actualInternalFormat == GL_ETC1_RGB8_OES) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidInternalFormat); |
| return false; |
| } |
| |
| if (!ValidCompressedSubImageSize(context, actualInternalFormat, xoffset, yoffset, 0, |
| width, height, 1, texture->getWidth(target, level), |
| texture->getHeight(target, level), |
| texture->getDepth(target, level))) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidCompressedImageSize); |
| return false; |
| } |
| |
| if (format != actualInternalFormat) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| } |
| else |
| { |
| if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height, 1)) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidCompressedImageSize); |
| return false; |
| } |
| } |
| } |
| else |
| { |
| // validate <type> by itself (used as secondary key below) |
| switch (type) |
| { |
| case GL_UNSIGNED_BYTE: |
| case GL_UNSIGNED_SHORT_5_6_5: |
| case GL_UNSIGNED_SHORT_4_4_4_4: |
| case GL_UNSIGNED_SHORT_5_5_5_1: |
| case GL_UNSIGNED_SHORT: |
| case GL_UNSIGNED_INT: |
| case GL_UNSIGNED_INT_24_8_OES: |
| case GL_HALF_FLOAT_OES: |
| case GL_FLOAT: |
| break; |
| case GL_UNSIGNED_INT_2_10_10_10_REV_EXT: |
| if (!context->getExtensions().textureFormat2101010REV) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| default: |
| context->validationError(GL_INVALID_ENUM, kInvalidType); |
| return false; |
| } |
| |
| // validate <format> + <type> combinations |
| // - invalid <format> -> sets INVALID_ENUM |
| // - invalid <format>+<type> combination -> sets INVALID_OPERATION |
| switch (format) |
| { |
| case GL_ALPHA: |
| case GL_LUMINANCE: |
| case GL_LUMINANCE_ALPHA: |
| switch (type) |
| { |
| case GL_UNSIGNED_BYTE: |
| case GL_FLOAT: |
| case GL_HALF_FLOAT_OES: |
| break; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| case GL_RED: |
| case GL_RG: |
| if (!context->getExtensions().textureRG) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| switch (type) |
| { |
| case GL_UNSIGNED_BYTE: |
| break; |
| case GL_FLOAT: |
| case GL_HALF_FLOAT_OES: |
| if (!context->getExtensions().textureFloat) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_SHORT: |
| case GL_UNSIGNED_SHORT: |
| if (!context->getExtensions().textureNorm16) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| case GL_RGB: |
| switch (type) |
| { |
| case GL_UNSIGNED_BYTE: |
| case GL_UNSIGNED_SHORT_5_6_5: |
| case GL_UNSIGNED_INT_2_10_10_10_REV_EXT: |
| case GL_FLOAT: |
| case GL_HALF_FLOAT_OES: |
| break; |
| case GL_SHORT: |
| case GL_UNSIGNED_SHORT: |
| if (!context->getExtensions().textureNorm16) |
| { |
| context->validationError(GL_INVALID_OPERATION, |
| kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| case GL_RGBA: |
| switch (type) |
| { |
| case GL_UNSIGNED_BYTE: |
| case GL_UNSIGNED_SHORT_4_4_4_4: |
| case GL_UNSIGNED_SHORT_5_5_5_1: |
| case GL_FLOAT: |
| case GL_HALF_FLOAT_OES: |
| case GL_UNSIGNED_INT_2_10_10_10_REV_EXT: |
| break; |
| case GL_SHORT: |
| case GL_UNSIGNED_SHORT: |
| if (!context->getExtensions().textureNorm16) |
| { |
| context->validationError(GL_INVALID_OPERATION, |
| kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| case GL_BGRA_EXT: |
| if (!context->getExtensions().textureFormatBGRA8888) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| switch (type) |
| { |
| case GL_UNSIGNED_BYTE: |
| break; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| case GL_SRGB_EXT: |
| case GL_SRGB_ALPHA_EXT: |
| if (!context->getExtensions().sRGB) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| switch (type) |
| { |
| case GL_UNSIGNED_BYTE: |
| break; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: // error cases for compressed textures are |
| // handled below |
| case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: |
| break; |
| case GL_DEPTH_COMPONENT: |
| switch (type) |
| { |
| case GL_UNSIGNED_SHORT: |
| case GL_UNSIGNED_INT: |
| break; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| case GL_DEPTH_STENCIL_OES: |
| switch (type) |
| { |
| case GL_UNSIGNED_INT_24_8_OES: |
| break; |
| default: |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| default: |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| |
| switch (format) |
| { |
| case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
| if (context->getExtensions().textureCompressionDXT1) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: |
| if (context->getExtensions().textureCompressionDXT3) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: |
| if (context->getExtensions().textureCompressionDXT5) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_ETC1_RGB8_OES: |
| if (context->getExtensions().compressedETC1RGB8Texture) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: |
| case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: |
| if (context->getExtensions().lossyETCDecode) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: |
| case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: |
| case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: |
| case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: |
| if (context->getExtensions().compressedTexturePVRTC) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: |
| if (context->getExtensions().compressedTexturePVRTCsRGB) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormat); |
| return false; |
| } |
| else |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_DEPTH_COMPONENT: |
| case GL_DEPTH_STENCIL_OES: |
| if (!context->getExtensions().depthTextureANGLE && |
| !(context->getExtensions().packedDepthStencil && |
| context->getExtensions().depthTextureOES)) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| if (target != TextureTarget::_2D) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTargetAndFormat); |
| return false; |
| } |
| // OES_depth_texture supports loading depth data and multiple levels, |
| // but ANGLE_depth_texture does not |
| if (!context->getExtensions().depthTextureOES) |
| { |
| if (pixels != nullptr) |
| { |
| context->validationError(GL_INVALID_OPERATION, kPixelDataNotNull); |
| return false; |
| } |
| if (level != 0) |
| { |
| context->validationError(GL_INVALID_OPERATION, kLevelNotZero); |
| return false; |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (!isSubImage) |
| { |
| switch (internalformat) |
| { |
| // Core ES 2.0 formats |
| case GL_ALPHA: |
| case GL_LUMINANCE: |
| case GL_LUMINANCE_ALPHA: |
| case GL_RGB: |
| case GL_RGBA: |
| break; |
| |
| case GL_RGBA32F: |
| if (!context->getExtensions().colorBufferFloatRGBA) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidFormat); |
| return false; |
| } |
| |
| nonEqualFormatsAllowed = true; |
| |
| if (type != GL_FLOAT) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| if (format != GL_RGBA) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| |
| case GL_RGB32F: |
| if (!context->getExtensions().colorBufferFloatRGB) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidFormat); |
| return false; |
| } |
| |
| nonEqualFormatsAllowed = true; |
| |
| if (type != GL_FLOAT) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| if (format != GL_RGB) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| break; |
| |
| case GL_BGRA_EXT: |
| if (!context->getExtensions().textureFormatBGRA8888) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidFormat); |
| return false; |
| } |
| break; |
| |
| case GL_DEPTH_COMPONENT: |
| if (!(context->getExtensions().depthTextureAny())) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidFormat); |
| return false; |
| } |
| break; |
| |
| case GL_DEPTH_STENCIL: |
| if (!(context->getExtensions().depthTextureANGLE || |
| context->getExtensions().packedDepthStencil)) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidFormat); |
| return false; |
| } |
| break; |
| |
| case GL_RED: |
| case GL_RG: |
| if (!context->getExtensions().textureRG) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidFormat); |
| return false; |
| } |
| break; |
| |
| case GL_SRGB_EXT: |
| case GL_SRGB_ALPHA_EXT: |
| if (!context->getExtensions().sRGB) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| |
| case GL_RGB10_A2_EXT: |
| if (!context->getExtensions().textureFormat2101010REV) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| |
| if (type != GL_UNSIGNED_INT_2_10_10_10_REV_EXT || format != GL_RGBA) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMismatchedTypeAndFormat); |
| return false; |
| } |
| |
| nonEqualFormatsAllowed = true; |
| |
| break; |
| |
| case GL_RGB5_A1: |
| if (context->getExtensions().textureFormat2101010REV && |
| type == GL_UNSIGNED_INT_2_10_10_10_REV_EXT && format == GL_RGBA) |
| { |
| nonEqualFormatsAllowed = true; |
| } |
| |
| break; |
| |
| case GL_R16_EXT: |
| case GL_RG16_EXT: |
| case GL_RGB16_EXT: |
| case GL_RGBA16_EXT: |
| case GL_R16_SNORM_EXT: |
| case GL_RG16_SNORM_EXT: |
| case GL_RGB16_SNORM_EXT: |
| case GL_RGBA16_SNORM_EXT: |
| if (!context->getExtensions().textureNorm16) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| default: |
| context->validationError(GL_INVALID_VALUE, kInvalidInternalFormat); |
| return false; |
| } |
| } |
| |
| if (type == GL_FLOAT) |
| { |
| if (!context->getExtensions().textureFloat) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| } |
| else if (type == GL_HALF_FLOAT_OES) |
| { |
| if (!context->getExtensions().textureHalfFloat) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| } |
| } |
| |
| if (isSubImage) |
| { |
| const InternalFormat &textureInternalFormat = *texture->getFormat(target, level).info; |
| if (textureInternalFormat.internalFormat == GL_NONE) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidTextureLevel); |
| return false; |
| } |
| |
| if (format != textureInternalFormat.format) |
| { |
| context->validationError(GL_INVALID_OPERATION, err::kTextureFormatMismatch); |
| return false; |
| } |
| |
| if (context->getExtensions().webglCompatibility) |
| { |
| if (GetInternalFormatInfo(format, type).sizedInternalFormat != |
| textureInternalFormat.sizedInternalFormat) |
| { |
| context->validationError(GL_INVALID_OPERATION, kTextureTypeMismatch); |
| return false; |
| } |
| } |
| |
| if (static_cast<size_t>(xoffset + width) > texture->getWidth(target, level) || |
| static_cast<size_t>(yoffset + height) > texture->getHeight(target, level)) |
| { |
| context->validationError(GL_INVALID_VALUE, kOffsetOverflow); |
| return false; |
| } |
| |
| if (width > 0 && height > 0 && pixels == nullptr && |
| context->getState().getTargetBuffer(BufferBinding::PixelUnpack) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kPixelDataNull); |
| return false; |
| } |
| } |
| else |
| { |
| if (texture->getImmutableFormat()) |
| { |
| context->validationError(GL_INVALID_OPERATION, kTextureIsImmutable); |
| return false; |
| } |
| } |
| |
| // From GL_CHROMIUM_color_buffer_float_rgb[a]: |
| // GL_RGB[A] / GL_RGB[A]32F becomes an allowable format / internalformat parameter pair for |
| // TexImage2D. The restriction in section 3.7.1 of the OpenGL ES 2.0 spec that the |
| // internalformat parameter and format parameter of TexImage2D must match is lifted for this |
| // case. |
| if (!isSubImage && !isCompressed && internalformat != format && !nonEqualFormatsAllowed) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidFormatCombination); |
| return false; |
| } |
| |
| GLenum sizeCheckFormat = isSubImage ? format : internalformat; |
| return ValidImageDataSize(context, texType, width, height, 1, sizeCheckFormat, type, pixels, |
| imageSize); |
| } |
| |
| bool ValidateES2TexStorageParameters(Context *context, |
| TextureType target, |
| GLsizei levels, |
| GLenum internalformat, |
| GLsizei width, |
| GLsizei height) |
| { |
| if (target != TextureType::_2D && target != TextureType::CubeMap && |
| target != TextureType::Rectangle) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget); |
| return false; |
| } |
| |
| if (width < 1 || height < 1 || levels < 1) |
| { |
| context->validationError(GL_INVALID_VALUE, kTextureSizeTooSmall); |
| return false; |
| } |
| |
| if (target == TextureType::CubeMap && width != height) |
| { |
| context->validationError(GL_INVALID_VALUE, kCubemapFacesEqualDimensions); |
| return false; |
| } |
| |
| if (levels != 1 && levels != gl::log2(std::max(width, height)) + 1) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels); |
| return false; |
| } |
| |
| const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalformat); |
| if (formatInfo.format == GL_NONE || formatInfo.type == GL_NONE) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidFormat); |
| return false; |
| } |
| |
| const gl::Caps &caps = context->getCaps(); |
| |
| switch (target) |
| { |
| case TextureType::_2D: |
| if (width > caps.max2DTextureSize || height > caps.max2DTextureSize) |
| { |
| context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize); |
| return false; |
| } |
| break; |
| case TextureType::Rectangle: |
| if (levels != 1) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidMipLevel); |
| return false; |
| } |
| |
| if (width > caps.maxRectangleTextureSize || height > caps.maxRectangleTextureSize) |
| { |
| context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize); |
| return false; |
| } |
| if (formatInfo.compressed) |
| { |
| context->validationError(GL_INVALID_ENUM, kRectangleTextureCompressed); |
| return false; |
| } |
| break; |
| case TextureType::CubeMap: |
| if (width > caps.maxCubeMapTextureSize || height > caps.maxCubeMapTextureSize) |
| { |
| context->validationError(GL_INVALID_VALUE, kResourceMaxTextureSize); |
| return false; |
| } |
| break; |
| default: |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| |
| if (levels != 1 && !context->getExtensions().textureNPOT) |
| { |
| if (!gl::isPow2(width) || !gl::isPow2(height)) |
| { |
| context->validationError(GL_INVALID_OPERATION, kDimensionsMustBePow2); |
| return false; |
| } |
| } |
| |
| switch (internalformat) |
| { |
| case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
| case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
| if (!context->getExtensions().textureCompressionDXT1) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: |
| if (!context->getExtensions().textureCompressionDXT3) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: |
| if (!context->getExtensions().textureCompressionDXT5) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_ETC1_RGB8_OES: |
| if (!context->getExtensions().compressedETC1RGB8Texture) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: |
| case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: |
| case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE: |
| if (!context->getExtensions().lossyETCDecode) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: |
| case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: |
| case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: |
| case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: |
| if (!context->getExtensions().compressedTexturePVRTC) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: |
| case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: |
| if (!context->getExtensions().compressedTexturePVRTCsRGB) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_RGBA32F_EXT: |
| case GL_RGB32F_EXT: |
| case GL_ALPHA32F_EXT: |
| case GL_LUMINANCE32F_EXT: |
| case GL_LUMINANCE_ALPHA32F_EXT: |
| if (!context->getExtensions().textureFloat) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_RGBA16F_EXT: |
| case GL_RGB16F_EXT: |
| case GL_ALPHA16F_EXT: |
| case GL_LUMINANCE16F_EXT: |
| case GL_LUMINANCE_ALPHA16F_EXT: |
| if (!context->getExtensions().textureHalfFloat) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_R8_EXT: |
| case GL_RG8_EXT: |
| if (!context->getExtensions().textureRG) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_R16F_EXT: |
| case GL_RG16F_EXT: |
| if (!context->getExtensions().textureRG || !context->getExtensions().textureHalfFloat) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_R32F_EXT: |
| case GL_RG32F_EXT: |
| if (!context->getExtensions().textureRG || !context->getExtensions().textureFloat) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| break; |
| case GL_DEPTH_COMPONENT16: |
| case GL_DEPTH_COMPONENT32_OES: |
| if (!(context->getExtensions().depthTextureAny())) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| if (target != TextureType::_2D) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget); |
| return false; |
| } |
| // ANGLE_depth_texture only supports 1-level textures |
| if (!context->getExtensions().depthTextureOES) |
| { |
| if (levels != 1) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels); |
| return false; |
| } |
| } |
| break; |
| case GL_DEPTH24_STENCIL8_OES: |
| if (!(context->getExtensions().depthTextureANGLE || |
| (context->getExtensions().packedDepthStencil && |
| context->getExtensions().textureStorage))) |
| { |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| if (target != TextureType::_2D) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget); |
| return false; |
| } |
| if (!context->getExtensions().packedDepthStencil) |
| { |
| // ANGLE_depth_texture only supports 1-level textures |
| if (levels != 1) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels); |
| return false; |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| gl::Texture *texture = context->getTextureByType(target); |
| if (!texture || texture->id().value == 0) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMissingTexture); |
| return false; |
| } |
| |
| if (texture->getImmutableFormat()) |
| { |
| context->validationError(GL_INVALID_OPERATION, kTextureIsImmutable); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateDiscardFramebufferEXT(Context *context, |
| GLenum target, |
| GLsizei numAttachments, |
| const GLenum *attachments) |
| { |
| if (!context->getExtensions().discardFramebuffer) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| bool defaultFramebuffer = false; |
| |
| switch (target) |
| { |
| case GL_FRAMEBUFFER: |
| defaultFramebuffer = |
| (context->getState().getTargetFramebuffer(GL_FRAMEBUFFER)->isDefault()); |
| break; |
| default: |
| context->validationError(GL_INVALID_ENUM, kInvalidFramebufferTarget); |
| return false; |
| } |
| |
| return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments, |
| defaultFramebuffer); |
| } |
| |
| bool ValidateBindVertexArrayOES(Context *context, VertexArrayID array) |
| { |
| if (!context->getExtensions().vertexArrayObject) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| return ValidateBindVertexArrayBase(context, array); |
| } |
| |
| bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n, const VertexArrayID *arrays) |
| { |
| if (!context->getExtensions().vertexArrayObject) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| return ValidateGenOrDelete(context, n); |
| } |
| |
| bool ValidateGenVertexArraysOES(Context *context, GLsizei n, VertexArrayID *arrays) |
| { |
| if (!context->getExtensions().vertexArrayObject) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| return ValidateGenOrDelete(context, n); |
| } |
| |
| bool ValidateIsVertexArrayOES(Context *context, VertexArrayID array) |
| { |
| if (!context->getExtensions().vertexArrayObject) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateProgramBinaryOES(Context *context, |
| ShaderProgramID program, |
| GLenum binaryFormat, |
| const void *binary, |
| GLint length) |
| { |
| if (!context->getExtensions().getProgramBinary) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| return ValidateProgramBinaryBase(context, program, binaryFormat, binary, length); |
| } |
| |
| bool ValidateGetProgramBinaryOES(Context *context, |
| ShaderProgramID program, |
| GLsizei bufSize, |
| GLsizei *length, |
| GLenum *binaryFormat, |
| void *binary) |
| { |
| if (!context->getExtensions().getProgramBinary) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary); |
| } |
| |
| static bool ValidDebugSource(GLenum source, bool mustBeThirdPartyOrApplication) |
| { |
| switch (source) |
| { |
| case GL_DEBUG_SOURCE_API: |
| case GL_DEBUG_SOURCE_SHADER_COMPILER: |
| case GL_DEBUG_SOURCE_WINDOW_SYSTEM: |
| case GL_DEBUG_SOURCE_OTHER: |
| // Only THIRD_PARTY and APPLICATION sources are allowed to be manually inserted |
| return !mustBeThirdPartyOrApplication; |
| |
| case GL_DEBUG_SOURCE_THIRD_PARTY: |
| case GL_DEBUG_SOURCE_APPLICATION: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| static bool ValidDebugType(GLenum type) |
| { |
| switch (type) |
| { |
| case GL_DEBUG_TYPE_ERROR: |
| case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: |
| case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: |
| case GL_DEBUG_TYPE_PERFORMANCE: |
| case GL_DEBUG_TYPE_PORTABILITY: |
| case GL_DEBUG_TYPE_OTHER: |
| case GL_DEBUG_TYPE_MARKER: |
| case GL_DEBUG_TYPE_PUSH_GROUP: |
| case GL_DEBUG_TYPE_POP_GROUP: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| static bool ValidDebugSeverity(GLenum severity) |
| { |
| switch (severity) |
| { |
| case GL_DEBUG_SEVERITY_HIGH: |
| case GL_DEBUG_SEVERITY_MEDIUM: |
| case GL_DEBUG_SEVERITY_LOW: |
| case GL_DEBUG_SEVERITY_NOTIFICATION: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| bool ValidateDebugMessageControlKHR(Context *context, |
| GLenum source, |
| GLenum type, |
| GLenum severity, |
| GLsizei count, |
| const GLuint *ids, |
| GLboolean enabled) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (!ValidDebugSource(source, false) && source != GL_DONT_CARE) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidDebugSource); |
| return false; |
| } |
| |
| if (!ValidDebugType(type) && type != GL_DONT_CARE) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidDebugType); |
| return false; |
| } |
| |
| if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidDebugSeverity); |
| return false; |
| } |
| |
| if (count > 0) |
| { |
| if (source == GL_DONT_CARE || type == GL_DONT_CARE) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidDebugSourceType); |
| return false; |
| } |
| |
| if (severity != GL_DONT_CARE) |
| { |
| context->validationError(GL_INVALID_OPERATION, kInvalidDebugSeverity); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool ValidateDebugMessageInsertKHR(Context *context, |
| GLenum source, |
| GLenum type, |
| GLuint id, |
| GLenum severity, |
| GLsizei length, |
| const GLchar *buf) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (!context->getState().getDebug().isOutputEnabled()) |
| { |
| // If the DEBUG_OUTPUT state is disabled calls to DebugMessageInsert are discarded and do |
| // not generate an error. |
| return false; |
| } |
| |
| if (!ValidDebugSeverity(severity)) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidDebugSource); |
| return false; |
| } |
| |
| if (!ValidDebugType(type)) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidDebugType); |
| return false; |
| } |
| |
| if (!ValidDebugSource(source, true)) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidDebugSource); |
| return false; |
| } |
| |
| size_t messageLength = (length < 0) ? strlen(buf) : length; |
| if (messageLength > context->getExtensions().maxDebugMessageLength) |
| { |
| context->validationError(GL_INVALID_VALUE, kExceedsMaxDebugMessageLength); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateDebugMessageCallbackKHR(Context *context, |
| GLDEBUGPROCKHR callback, |
| const void *userParam) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateGetDebugMessageLogKHR(Context *context, |
| GLuint count, |
| GLsizei bufSize, |
| GLenum *sources, |
| GLenum *types, |
| GLuint *ids, |
| GLenum *severities, |
| GLsizei *lengths, |
| GLchar *messageLog) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (bufSize < 0 && messageLog != nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kNegativeBufferSize); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidatePushDebugGroupKHR(Context *context, |
| GLenum source, |
| GLuint id, |
| GLsizei length, |
| const GLchar *message) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (!ValidDebugSource(source, true)) |
| { |
| context->validationError(GL_INVALID_ENUM, kInvalidDebugSource); |
| return false; |
| } |
| |
| size_t messageLength = (length < 0) ? strlen(message) : length; |
| if (messageLength > context->getExtensions().maxDebugMessageLength) |
| { |
| context->validationError(GL_INVALID_VALUE, kExceedsMaxDebugMessageLength); |
| return false; |
| } |
| |
| size_t currentStackSize = context->getState().getDebug().getGroupStackDepth(); |
| if (currentStackSize >= context->getExtensions().maxDebugGroupStackDepth) |
| { |
| context->validationError(GL_STACK_OVERFLOW, kExceedsMaxDebugGroupStackDepth); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidatePopDebugGroupKHR(Context *context) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| size_t currentStackSize = context->getState().getDebug().getGroupStackDepth(); |
| if (currentStackSize <= 1) |
| { |
| context->validationError(GL_STACK_UNDERFLOW, kCannotPopDefaultDebugGroup); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier, GLuint name) |
| { |
| switch (identifier) |
| { |
| case GL_BUFFER: |
| if (context->getBuffer({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidBufferName); |
| return false; |
| } |
| return true; |
| |
| case GL_SHADER: |
| if (context->getShader({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidShaderName); |
| return false; |
| } |
| return true; |
| |
| case GL_PROGRAM: |
| if (context->getProgramNoResolveLink({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidProgramName); |
| return false; |
| } |
| return true; |
| |
| case GL_VERTEX_ARRAY: |
| if (context->getVertexArray({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidVertexArrayName); |
| return false; |
| } |
| return true; |
| |
| case GL_QUERY: |
| if (context->getQuery({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidQueryName); |
| return false; |
| } |
| return true; |
| |
| case GL_TRANSFORM_FEEDBACK: |
| if (context->getTransformFeedback({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidTransformFeedbackName); |
| return false; |
| } |
| return true; |
| |
| case GL_SAMPLER: |
| if (context->getSampler({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidSamplerName); |
| return false; |
| } |
| return true; |
| |
| case GL_TEXTURE: |
| if (context->getTexture({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidTextureName); |
| return false; |
| } |
| return true; |
| |
| case GL_RENDERBUFFER: |
| if (!context->isRenderbuffer({name})) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidRenderbufferName); |
| return false; |
| } |
| return true; |
| |
| case GL_FRAMEBUFFER: |
| if (context->getFramebuffer({name}) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidFramebufferName); |
| return false; |
| } |
| return true; |
| |
| default: |
| context->validationError(GL_INVALID_ENUM, kInvalidIndentifier); |
| return false; |
| } |
| } |
| |
| static bool ValidateLabelLength(Context *context, GLsizei length, const GLchar *label) |
| { |
| size_t labelLength = 0; |
| |
| if (length < 0) |
| { |
| if (label != nullptr) |
| { |
| labelLength = strlen(label); |
| } |
| } |
| else |
| { |
| labelLength = static_cast<size_t>(length); |
| } |
| |
| if (labelLength > context->getExtensions().maxLabelLength) |
| { |
| context->validationError(GL_INVALID_VALUE, kExceedsMaxLabelLength); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateObjectLabelKHR(Context *context, |
| GLenum identifier, |
| GLuint name, |
| GLsizei length, |
| const GLchar *label) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (!ValidateObjectIdentifierAndName(context, identifier, name)) |
| { |
| return false; |
| } |
| |
| if (!ValidateLabelLength(context, length, label)) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateGetObjectLabelKHR(Context *context, |
| GLenum identifier, |
| GLuint name, |
| GLsizei bufSize, |
| GLsizei *length, |
| GLchar *label) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (bufSize < 0) |
| { |
| context->validationError(GL_INVALID_VALUE, kNegativeBufferSize); |
| return false; |
| } |
| |
| if (!ValidateObjectIdentifierAndName(context, identifier, name)) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool ValidateObjectPtrName(Context *context, const void *ptr) |
| { |
| if (context->getSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidSyncPointer); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateObjectPtrLabelKHR(Context *context, |
| const void *ptr, |
| GLsizei length, |
| const GLchar *label) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (!ValidateObjectPtrName(context, ptr)) |
| { |
| return false; |
| } |
| |
| if (!ValidateLabelLength(context, length, label)) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateGetObjectPtrLabelKHR(Context *context, |
| const void *ptr, |
| GLsizei bufSize, |
| GLsizei *length, |
| GLchar *label) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| if (bufSize < 0) |
| { |
| context->validationError(GL_INVALID_VALUE, kNegativeBufferSize); |
| return false; |
| } |
| |
| if (!ValidateObjectPtrName(context, ptr)) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params) |
| { |
| if (!context->getExtensions().debug) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| // TODO: represent this in Context::getQueryParameterInfo. |
| switch (pname) |
| { |
| case GL_DEBUG_CALLBACK_FUNCTION: |
| case GL_DEBUG_CALLBACK_USER_PARAM: |
| break; |
| |
| default: |
| context->validationError(GL_INVALID_ENUM, kEnumNotSupported); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateGetPointervRobustANGLERobustANGLE(Context *context, |
| GLenum pname, |
| GLsizei bufSize, |
| GLsizei *length, |
| void **params) |
| { |
| UNIMPLEMENTED(); |
| return false; |
| } |
| |
| bool ValidateBlitFramebufferANGLE(Context *context, |
| GLint srcX0, |
| GLint srcY0, |
| GLint srcX1, |
| GLint srcY1, |
| GLint dstX0, |
| GLint dstY0, |
| GLint dstX1, |
| GLint dstY1, |
| GLbitfield mask, |
| GLenum filter) |
| { |
| if (!context->getExtensions().framebufferBlit) |
| { |
| context->validationError(GL_INVALID_OPERATION, kBlitExtensionNotAvailable); |
| return false; |
| } |
| |
| if (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0) |
| { |
| // TODO(jmadill): Determine if this should be available on other implementations. |
| context->validationError(GL_INVALID_OPERATION, kBlitExtensionScaleOrFlip); |
| return false; |
| } |
| |
| if (filter == GL_LINEAR) |
| { |
| context->validationError(GL_INVALID_ENUM, kBlitExtensionLinear); |
| return false; |
| } |
| |
| Framebuffer *readFramebuffer = context->getState().getReadFramebuffer(); |
| Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer(); |
| |
| if (mask & GL_COLOR_BUFFER_BIT) |
| { |
| const FramebufferAttachment *readColorAttachment = |
| readFramebuffer->getReadColorAttachment(); |
| const FramebufferAttachment *drawColorAttachment = |
| drawFramebuffer->getFirstColorAttachment(); |
| |
| if (readColorAttachment && drawColorAttachment) |
| { |
| if (!(readColorAttachment->type() == GL_TEXTURE && |
| (readColorAttachment->getTextureImageIndex().getType() == TextureType::_2D || |
| readColorAttachment->getTextureImageIndex().getType() == |
| TextureType::Rectangle)) && |
| readColorAttachment->type() != GL_RENDERBUFFER && |
| readColorAttachment->type() != GL_FRAMEBUFFER_DEFAULT) |
| { |
| context->validationError(GL_INVALID_OPERATION, |
| kBlitExtensionFromInvalidAttachmentType); |
| return false; |
| } |
| |
| for (size_t drawbufferIdx = 0; |
| drawbufferIdx < drawFramebuffer->getDrawbufferStateCount(); ++drawbufferIdx) |
| { |
| const FramebufferAttachment *attachment = |
| drawFramebuffer->getDrawBuffer(drawbufferIdx); |
| if (attachment) |
| { |
| if (!(attachment->type() == GL_TEXTURE && |
| (attachment->getTextureImageIndex().getType() == TextureType::_2D || |
| attachment->getTextureImageIndex().getType() == |
| TextureType::Rectangle)) && |
| attachment->type() != GL_RENDERBUFFER && |
| attachment->type() != GL_FRAMEBUFFER_DEFAULT) |
| { |
| context->validationError(GL_INVALID_OPERATION, |
| kBlitExtensionToInvalidAttachmentType); |
| return false; |
| } |
| |
| // Return an error if the destination formats do not match |
| if (!Format::EquivalentForBlit(attachment->getFormat(), |
| readColorAttachment->getFormat())) |
| { |
| context->validationError(GL_INVALID_OPERATION, |
| kBlitExtensionFormatMismatch); |
| return false; |
| } |
| } |
| } |
| |
| GLint samples = readFramebuffer->getSamples(context); |
| if (samples != 0 && |
| IsPartialBlit(context, readColorAttachment, drawColorAttachment, srcX0, srcY0, |
| srcX1, srcY1, dstX0, dstY0, dstX1, dstY1)) |
| { |
| context->validationError(GL_INVALID_OPERATION, |
| kBlitExtensionMultisampledWholeBufferBlit); |
| return false; |
| } |
| } |
| } |
| |
| GLenum masks[] = {GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT}; |
| GLenum attachments[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT}; |
| for (size_t i = 0; i < 2; i++) |
| { |
| if (mask & masks[i]) |
| { |
| const FramebufferAttachment *readBuffer = |
| readFramebuffer->getAttachment(context, attachments[i]); |
| const FramebufferAttachment *drawBuffer = |
| drawFramebuffer->getAttachment(context, attachments[i]); |
| |
| if (readBuffer && drawBuffer) |
| { |
| if (IsPartialBlit(context, readBuffer, drawBuffer, srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1)) |
| { |
| // only whole-buffer copies are permitted |
| context->validationError(GL_INVALID_OPERATION, |
| kBlitExtensionDepthStencilWholeBufferBlit); |
| return false; |
| } |
| |
| if (readBuffer->getSamples() != 0 || drawBuffer->getSamples() != 0) |
| { |
| context->validationError(GL_INVALID_OPERATION, |
| kBlitExtensionMultisampledDepthOrStencil); |
| return false; |
| } |
| } |
| } |
| } |
| |
| return ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, |
| dstX1, dstY1, mask, filter); |
| } |
| |
| bool ValidateClear(Context *context, GLbitfield mask) |
| { |
| Framebuffer *fbo = context->getState().getDrawFramebuffer(); |
| const Extensions &extensions = context->getExtensions(); |
| |
| if (!ValidateFramebufferComplete(context, fbo)) |
| { |
| return false; |
| } |
| |
| if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) |
| { |
| context->validationError(GL_INVALID_VALUE, kInvalidClearMask); |
| return false; |
| } |
| |
| if (extensions.webglCompatibility && (mask & GL_COLOR_BUFFER_BIT) != 0) |
| { |
| constexpr GLenum validComponentTypes[] = {GL_FLOAT, GL_UNSIGNED_NORMALIZED, |
| GL_SIGNED_NORMALIZED}; |
| |
| for (GLuint drawBufferIdx = 0; drawBufferIdx < fbo->getDrawbufferStateCount(); |
| drawBufferIdx++) |
| { |
| if (!ValidateWebGLFramebufferAttachmentClearType( |
| context, drawBufferIdx, validComponentTypes, ArraySize(validComponentTypes))) |
| { |
| return false; |
| } |
| } |
| } |
| |
| if ((extensions.multiview || extensions.multiview2) && extensions.disjointTimerQuery) |
| { |
| const State &state = context->getState(); |
| Framebuffer *framebuffer = state.getDrawFramebuffer(); |
| if (framebuffer->getNumViews() > 1 && state.isQueryActive(QueryType::TimeElapsed)) |
| { |
| context->validationError(GL_INVALID_OPERATION, kMultiviewTimerQuery); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool ValidateDrawBuffersEXT(Context *context, GLsizei n, const GLenum *bufs) |
| { |
| if (!context->getExtensions().drawBuffers) |
| { |
| context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); |
| return false; |
| } |
| |
| return ValidateDrawBuffersBase(context, n, bufs); |
| } |
| |
| bool ValidateTexImage2D(Context *context, |
| TextureTarget target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| const void *pixels) |
| { |
| if (context->getClientMajorVersion() < 3) |
| { |
| return ValidateES2TexImageParameters(context, target, level, internalformat, false, false, |
| 0, 0, width, height, border, format, type, -1, pixels); |
| } |
| |
| ASSERT(context->getClientMajorVersion() >= 3); |
| return ValidateES3TexImage2DParameters(context, target, level, internalformat, false, false, 0, |
| 0, 0, width, height, 1, border, format, type, -1, |
| pixels); |
| } |
| |
| bool ValidateTexImage2DRobustANGLE(Context *context, |
| TextureTarget target, |
| GLint level, |
| GLint internalformat, |
| GLsizei width, |
| GLsizei height, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| GLsizei bufSize, |
| const void *pixels) |
| { |
| if (!ValidateRobustEntryPoint(context, bufSize)) |
| { |
| return false; |
| } |
| |
| if (context->getClientMajorVersion() < 3) |
| { |
| return ValidateES2TexImageParameters(context, target, level, internalformat, false, false, |
| 0, 0, width, height, border, format, type, bufSize, |
| pixels); |
| } |
| |
| ASSERT(context->getClientMajorVersion() >= 3); |
| return ValidateES3TexImage2DParameters(context, target, level, internalformat, false, false, 0, |
| 0, 0, width, height, 1, border, format, type, bufSize, |
| pixels); |
| } |
| |
| bool ValidateTexSubImage2D(Context *context, |
| TextureTarget target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| const void *pixels) |
| { |
| |
| if (context->getClientMajorVersion() < 3) |
| { |
| return ValidateES2TexImageParameters(context, target, level, GL_NONE, false, true, xoffset, |
| yoffset, width, height, 0, format, type, -1, pixels); |
| } |
| |
| ASSERT(context->getClientMajorVersion() >= 3); |
| return ValidateES3TexImage2DParameters(context, target, level, GL_NONE, false, true, xoffset, |
| yoffset, 0, width, height, 1, 0, format, type, -1, |
| pixels); |
| } |
| |
| bool ValidateTexSubImage2DRobustANGLE(Context *context, |
| TextureTarget target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| GLsizei bufSize, |
| const void |