Source code

Revision control

Copy as Markdown

Other Tools

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