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