// // 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
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: returntrue;
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: returntrue;
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: returntrue;
case TextureTarget::Rectangle: return context->getExtensions().textureRectangleANGLE;
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: returntrue;
default: returnfalse;
}
}
bool IsValidStencilFace(GLenum face)
{ switch (face)
{ case GL_FRONT: case GL_BACK: case GL_FRONT_AND_BACK: returntrue;
default: returnfalse;
}
}
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: returntrue;
default: returnfalse;
}
}
staticinlinebool 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));
}
staticinlinebool 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));
}
// 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: returntrue;
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)
{ returntrue;
} 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)
{ returnfalse;
}
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: returnfalse;
}
}
// Return true if a character belongs to the ASCII subset as defined in GLSL ES 1.0 spec section // 3.1. bool IsValidESSLCharacter(unsignedchar c)
{ // Printing characters are valid except " $ ` @ \ ' DEL. if (c >= 32 && c <= 126 && c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' &&
c != '\'')
{ returntrue;
}
// Horizontal tab, line feed, vertical tab, form feed, carriage return are also valid. if (c >= 9 && c <= 13)
{ returntrue;
}
returnfalse;
}
bool IsValidESSLString(constchar *str, size_t len)
{ for (size_t i = 0; i < len; i++)
{ if (!IsValidESSLCharacter(str[i]))
{ returnfalse;
}
}
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);
returnfalse;
} elseif (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); returnfalse;
}
// 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: returntrue;
// 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;
if (!internalFormatInfo.textureSupport(context->getClientVersion(),
context->getExtensions()))
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kInvalidInternalFormat,
internalformat); returnfalse;
}
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); returnfalse;
}
// 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); returnfalse;
}
if (format != actualInternalFormat)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat); returnfalse;
}
} else
{ if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height, 1))
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidCompressedImageSize); returnfalse;
}
}
} 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); returnfalse;
} break; default:
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidType); returnfalse;
}
// 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); returnfalse;
} break; case GL_RED: case GL_RG: if (!context->getExtensions().textureRgEXT)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
format); returnfalse;
} switch (type)
{ case GL_UNSIGNED_BYTE: break; case GL_FLOAT: if (!context->getExtensions().textureFloatOES)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM,
kEnumNotSupported, type); returnfalse;
} break; case GL_HALF_FLOAT_OES: if (!context->getExtensions().textureFloatOES &&
!context->getExtensions().textureHalfFloatOES)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM,
kEnumNotSupported, type); returnfalse;
} break; case GL_SHORT: case GL_UNSIGNED_SHORT: if (!context->getExtensions().textureNorm16EXT)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM,
kEnumNotSupported, type); returnfalse;
} break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
} 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); returnfalse;
} break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
} 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); returnfalse;
} break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
} break; case GL_BGRA_EXT: if (!context->getExtensions().textureFormatBGRA8888EXT)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
format); returnfalse;
} switch (type)
{ case GL_UNSIGNED_BYTE: break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
} break; case GL_SRGB_EXT: case GL_SRGB_ALPHA_EXT: if (!context->getExtensions().sRGBEXT)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
format); returnfalse;
} switch (type)
{ case GL_UNSIGNED_BYTE: break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
} 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); returnfalse;
} break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
} break; case GL_DEPTH_STENCIL_OES: switch (type)
{ case GL_UNSIGNED_INT_24_8_OES: break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
} break; case GL_STENCIL_INDEX: switch (type)
{ case GL_UNSIGNED_BYTE: break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
} break; default:
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, format); returnfalse;
}
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); returnfalse;
}
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); returnfalse;
} break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTargetAndFormat); returnfalse;
}
// 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); returnfalse;
} if (level != 0)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kLevelNotZero); returnfalse;
}
} break; case GL_STENCIL_INDEX: if (!context->getExtensions().textureStencil8OES)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat); returnfalse;
}
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); returnfalse;
}
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); returnfalse;
}
case GL_RGB32F: if (!context->getExtensions().colorBufferFloatRgbCHROMIUM)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat); returnfalse;
}
case GL_BGRA_EXT: if (!context->getExtensions().textureFormatBGRA8888EXT)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat); returnfalse;
} break;
case GL_DEPTH_COMPONENT: if (!(context->getExtensions().depthTextureAny()))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat); returnfalse;
} break;
case GL_DEPTH_STENCIL: if (!(context->getExtensions().depthTextureANGLE ||
context->getExtensions().packedDepthStencilOES ||
context->getExtensions().depthTextureCubeMapOES))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat); returnfalse;
} break;
case GL_STENCIL_INDEX8: if (!context->getExtensions().textureStencil8OES)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat); returnfalse;
} break;
case GL_RED: case GL_RG: if (!context->getExtensions().textureRgEXT)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat); returnfalse;
} break;
case GL_SRGB_EXT: case GL_SRGB_ALPHA_EXT: if (!context->getExtensions().sRGBEXT)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
internalformat); returnfalse;
} break;
case GL_RGB10_A2_EXT: if (!context->getExtensions().textureType2101010REVEXT)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
internalformat); returnfalse;
}
if (type != GL_UNSIGNED_INT_2_10_10_10_REV_EXT || format != GL_RGBA)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMismatchedTypeAndFormat); returnfalse;
}
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); returnfalse;
} break; default: // Compressed formats are not valid internal formats for glTexImage*D
context->validationErrorF(entryPoint, GL_INVALID_VALUE, kInvalidInternalFormat,
internalformat); returnfalse;
}
}
// 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); returnfalse;
}
if (levels != 1 && !context->getExtensions().textureNpotOES)
{ if (!isPow2(width) || !isPow2(height))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kDimensionsMustBePow2); returnfalse;
}
}
if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat); returnfalse;
}
// 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); returnfalse;
}
}
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); returnfalse;
} break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidTextureTarget); returnfalse;
}
// ANGLE_depth_texture only supports 1-level textures if (!context->getExtensions().depthTextureOES)
{ if (levels != 1)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidMipLevels); returnfalse;
}
} 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); returnfalse;
} break; default:
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidTextureTarget); returnfalse;
}
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); returnfalse;
}
} break;
staticbool 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: returntrue;
default: returnfalse;
}
}
staticbool 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: returntrue;
default: returnfalse;
}
}
staticbool 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: returntrue;
if (!context->getState().getDebug().isOutputEnabled())
{ // If the DEBUG_OUTPUT state is disabled calls to DebugMessageInsert are discarded and do // not generate an error. returnfalse;
}
if (!ValidDebugSeverity(severity))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugSource); returnfalse;
}
if (!ValidDebugType(type))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugType); returnfalse;
}
if (!ValidDebugSource(source, true))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidDebugSource); returnfalse;
}
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); returnfalse;
}
if (filter == GL_LINEAR)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kBlitExtensionLinear); returnfalse;
}
// 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); returnfalse;
}
}
}
if (buffer == nullptr)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotMappable); returnfalse;
}
if (access != GL_WRITE_ONLY_OES)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidAccessBits); returnfalse;
}
// 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); returnfalse;
}
if (buffer->isMapped())
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferAlreadyMapped); returnfalse;
}
// 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++)
{ constauto &transformFeedbackBuffer = transformFeedback->getIndexedBuffer(i); if (transformFeedbackBuffer.get() == buffer)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kBufferBoundForTransformFeedback); returnfalse;
}
}
}
if (buffer->hasWebGLXFBBindingConflict(context->isWebGL()))
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kBufferBoundForTransformFeedback); returnfalse;
}
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); returnfalse;
} break;
if (!buffer)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotBound); returnfalse;
}
// 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); returnfalse;
}
if (buffer->hasWebGLXFBBindingConflict(context->isWebGL()))
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kBufferBoundForTransformFeedback); returnfalse;
}
staticbool ValidBlendEquationMode(const Context *context, GLenum mode)
{ switch (mode)
{ case GL_FUNC_ADD: case GL_FUNC_SUBTRACT: case GL_FUNC_REVERSE_SUBTRACT: returntrue;
case GL_MIN: case GL_MAX: return context->getClientVersion() >= ES_3_0 || context->getExtensions().blendMinmaxEXT;
default: returnfalse;
}
}
staticbool 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;
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); returnfalse;
} break;
case GL_SERIALIZED_CONTEXT_STRING_ANGLE: if (!context->getExtensions().getSerializedContextStringANGLE)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidName); returnfalse;
} break;
// 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); returnfalse;
}
// 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); returnfalse;
}
}
if (!context->getShader(shader))
{ if (context->getProgramResolveLink(shader))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidShaderName); returnfalse;
} else
{
context->validationError(entryPoint, GL_INVALID_VALUE, kExpectedShaderName); returnfalse;
}
}
returntrue;
}
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;
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); returnfalse;
} break;
case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: if (context->getClientVersion() < ES_3_0 &&
!context->getExtensions().standardDerivativesOES)
{
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported, target); returnfalse;
} 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); returnfalse;
} break;
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); returnfalse;
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); returnfalse;
} 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); returnfalse;
} break;
}
}
if (param < 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeParam); returnfalse;
}
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;
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; returnfalse;
}
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); returnfalse;
}
if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
{ returnfalse;
}
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); returnfalse;
} if (tex->getType() != TextureType::_2D)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidTextureTarget); returnfalse;
}
} break;
case TextureTarget::Rectangle:
{ if (level != 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel); returnfalse;
} if (tex->getType() != TextureType::Rectangle)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kTextureTargetMismatch); returnfalse;
}
} 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); returnfalse;
} if (tex->getType() != TextureType::CubeMap)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kTextureTargetMismatch); returnfalse;
}
} break;
case TextureTarget::_2DMultisample:
{ if (context->getClientVersion() < ES_3_1 &&
!context->getExtensions().textureMultisampleANGLE)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kMultisampleTextureExtensionOrES31Required); returnfalse;
}
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); returnfalse;
}
// 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); returnfalse;
}
if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
{ returnfalse;
}
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); returnfalse;
} if (zoffset >= caps.max3DTextureSize)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidZOffset); returnfalse;
} if (tex->getType() != TextureType::_3D)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidTextureType); returnfalse;
}
} break;
if (index >= static_cast<GLuint>(context->getCaps().maxVertexAttributes))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute); returnfalse;
}
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; returnfalse;
}
}
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); returnfalse;
}
// 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); returnfalse;
}
if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
{ returnfalse;
}
// 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); returnfalse;
}
}
// 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); returnfalse;
}
if (!ValidTexture2DDestinationTarget(context, textarget))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget); returnfalse;
}
// 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); returnfalse;
}
// 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); returnfalse;
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.210Bemerkung:
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.