//
// 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.
//
// validationES3.cpp: Validation functions for OpenGL ES 3.0 entry point parameters
#include "libANGLE/validationES3_autogen.h"
#include "anglebase/numerics/safe_conversions.h"
#include "common/mathutil.h"
#include "common/utilities.h"
#include "libANGLE/Context.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/PixelLocalStorage.h"
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/Texture.h"
#include "libANGLE/VertexArray.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/validationES.h"
using namespace angle;
namespace gl
{
using namespace err;
namespace
{
bool ValidateFramebufferTextureMultiviewBaseANGLE(const Context *context,
angle::EntryPoint entryPoint,
GLenum target,
GLenum attachment,
TextureID texture,
GLint level,
GLsizei numViews)
{
if (!(context->getExtensions().multiviewOVR || context->getExtensions().multiview2OVR))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kMultiviewNotAvailable);
return false;
}
if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
{
return false;
}
if (texture.value != 0 && numViews < 1)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kMultiviewViewsTooSmall);
return false;
}
if (static_cast<GLuint>(numViews) > context->getCaps().maxViews)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kMultiviewViewsTooLarge);
return false;
}
return true;
}
bool ValidateFramebufferTextureMultiviewLevelAndFormat(const Context *context,
angle::EntryPoint entryPoint,
const Texture *texture,
GLint level)
{
TextureType type = texture->getType();
if (!ValidMipLevel(context, type, level))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
return false;
}
const auto &format = texture->getFormat(NonCubeTextureTypeToTarget(type), level);
if (format.info->compressed)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kCompressedTexturesNotAttachable);
return false;
}
return true;
}
bool ValidateUniformES3(const Context *context,
angle::EntryPoint entryPoint,
GLenum uniformType,
UniformLocation location,
GLint count)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
return ValidateUniform(context, entryPoint, uniformType, location, count);
}
bool ValidateUniformMatrixES3(const Context *context,
angle::EntryPoint entryPoint,
GLenum valueType,
UniformLocation location,
GLsizei count,
GLboolean transpose)
{
// Check for ES3 uniform entry points
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
return ValidateUniformMatrix(context, entryPoint, valueType, location, count, transpose);
}
bool ValidateGenOrDeleteES3(const Context *context, angle::EntryPoint entryPoint, GLint n)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
return ValidateGenOrDelete(context, entryPoint, n);
}
bool ValidateGenOrDeleteCountES3(const Context *context, angle::EntryPoint entryPoint, GLint count)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
if (count < 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount);
return false;
}
return true;
}
bool ValidateCopyTexture3DCommon(const Context *context,
angle::EntryPoint entryPoint,
const Texture *source,
GLint sourceLevel,
GLint srcInternalFormat,
const Texture *dest,
GLint destLevel,
GLint internalFormat,
TextureTarget destTarget)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
if (!context->getExtensions().copyTexture3dANGLE)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kANGLECopyTexture3DUnavailable);
return false;
}
if (!ValidTexture3DTarget(context, source->getType()))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
// Table 1.1 from the ANGLE_copy_texture_3d spec
switch (GetUnsizedFormat(srcInternalFormat))
{
case GL_ALPHA:
case GL_LUMINANCE:
case GL_LUMINANCE_ALPHA:
case GL_RED:
case GL_RED_INTEGER:
case GL_RG:
case GL_RG_INTEGER:
case GL_RGB:
case GL_RGB_INTEGER:
case GL_RGBA:
case GL_RGBA_INTEGER:
case GL_DEPTH_COMPONENT:
case GL_DEPTH_STENCIL:
break;
default:
context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
srcInternalFormat);
return false;
}
if (!ValidTexture3DTarget(context, TextureTargetToType(destTarget)))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
// Table 1.0 from the ANGLE_copy_texture_3d spec
switch (internalFormat)
{
case GL_RGB:
case GL_RGBA:
case GL_LUMINANCE:
case GL_LUMINANCE_ALPHA:
case GL_ALPHA:
case GL_R8:
case GL_R8_SNORM:
case GL_R16F:
case GL_R32F:
case GL_R8UI:
case GL_R8I:
case GL_R16UI:
case GL_R16I:
case GL_R32UI:
case GL_R32I:
case GL_RG:
case GL_RG8:
case GL_RG8_SNORM:
case GL_RG16F:
case GL_RG32F:
case GL_RG8UI:
case GL_RG8I:
case GL_RG16UI:
case GL_RG16I:
case GL_RG32UI:
case GL_RG32I:
case GL_RGB8:
case GL_RGBX8_ANGLE:
case GL_SRGB8:
case GL_RGB565:
case GL_RGB8_SNORM:
case GL_R11F_G11F_B10F:
case GL_RGB9_E5:
case GL_RGB16F:
case GL_RGB32F:
case GL_RGB8UI:
case GL_RGB8I:
case GL_RGB16UI:
case GL_RGB16I:
case GL_RGB32UI:
case GL_RGB32I:
case GL_RGBA8:
case GL_SRGB8_ALPHA8:
case GL_RGBA8_SNORM:
case GL_RGB5_A1:
case GL_RGBA4:
case GL_RGB10_A2:
case GL_RGBA16F:
case GL_RGBA32F:
case GL_RGBA8UI:
case GL_RGBA8I:
case GL_RGB10_A2UI:
case GL_RGBA16UI:
case GL_RGBA16I:
case GL_RGBA32I:
case GL_RGBA32UI:
break;
default:
context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
internalFormat);
return false;
}
return true;
}
} // anonymous namespace
static bool ValidateTexImageFormatCombination(const Context *context,
angle::EntryPoint entryPoint,
TextureType target,
GLenum internalFormat,
GLenum format,
GLenum type)
{
// Different validation if on desktop api
if (context->getClientType() == EGL_OPENGL_API)
{
// The type and format are valid if any supported internal format has that type and format
if (!ValidDesktopFormat(format))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
return false;
}
if (!ValidDesktopType(type))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidType);
return false;
}
}
else
{
// The type and format are valid if any supported internal format has that type and format.
// ANGLE_texture_external_yuv_sampling extension adds support for YUV formats
if (gl::IsYuvFormat(format))
{
if (!context->getExtensions().yuvInternalFormatANGLE)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
return false;
}
}
else
{
if (!ValidES3Format(format))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
return false;
}
}
if (!ValidES3Type(type) || (type == GL_HALF_FLOAT_OES && context->isWebGL()))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidType);
return false;
}
}
// For historical reasons, glTexImage2D and glTexImage3D pass in their internal format as a
// GLint instead of a GLenum. Therefor an invalid internal format gives a GL_INVALID_VALUE
// error instead of a GL_INVALID_ENUM error. As this validation function is only called in
// the validation codepaths for glTexImage2D/3D, we record a GL_INVALID_VALUE error.
if (!ValidES3InternalFormat(internalFormat))
{
context->validationErrorF(entryPoint, GL_INVALID_VALUE, kInvalidInternalFormat,
internalFormat);
return false;
}
// From the ES 3.0 spec section 3.8.3:
// Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL are supported by
// texture image specification commands only if target is TEXTURE_2D, TEXTURE_2D_ARRAY, or
// TEXTURE_CUBE_MAP.Using these formats in conjunction with any other target will result in an
// INVALID_OPERATION error.
if (target == TextureType::_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, k3DDepthStencil);
return false;
}
if (context->getClientType() == EGL_OPENGL_API)
{
// Check if this is a valid format combination to load texture data
if (!ValidDesktopFormatCombination(format, type, internalFormat))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormatCombination);
return false;
}
}
else
{
// Check if this is a valid format combination to load texture data
// ANGLE_texture_external_yuv_sampling extension adds support for YUV formats
if (gl::IsYuvFormat(format))
{
if (type != GL_UNSIGNED_BYTE)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidFormatCombination);
return false;
}
}
else
{
if (!ValidES3FormatCombination(format, type, internalFormat))
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidFormatCombination);
return false;
}
}
}
const InternalFormat &formatInfo = GetInternalFormatInfo(internalFormat, type);
if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
{
context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
internalFormat);
return false;
}
return true;
}
static bool ValidateES3CompressedFormatForTexture2DArray(const Context *context,
angle::EntryPoint entryPoint,
GLenum format)
{
if (IsETC1Format(format) || IsPVRTC1Format(format))
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInternalFormatRequiresTexture2D);
return false;
}
return true;
}
static bool ValidateES3CompressedFormatForTexture3D(const Context *context,
angle::EntryPoint entryPoint,
GLenum format)
{
if (IsETC1Format(format) || IsPVRTC1Format(format))
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInternalFormatRequiresTexture2D);
return false;
}
if (IsETC2EACFormat(format))
{
// ES 3.1, Section 8.7, page 169.
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInternalFormatRequiresTexture2DArray);
return false;
}
if (IsASTC2DFormat(format) && !(context->getExtensions().textureCompressionAstcHdrKHR ||
context->getExtensions().textureCompressionAstcSliced3dKHR))
{
// GL_KHR_texture_compression_astc_hdr, TEXTURE_3D is not supported without HDR profile
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInternalFormatRequiresTexture2DArrayASTC);
return false;
}
if (IsS3TCFormat(format))
{
// GL_EXT_texture_compression_s3tc
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInternalFormatRequiresTexture2DArrayS3TC);
return false;
}
if (IsRGTCFormat(format))
{
// GL_EXT_texture_compression_rgtc
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInternalFormatRequiresTexture2DArrayRGTC);
return false;
}
if (IsBPTCFormat(format) && (context->getLimitations().noCompressedTexture3D))
{
// GL_EXT_texture_compression_bptc
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInternalFormatRequiresTexture2DArrayBPTC);
return false;
}
return true;
}
bool ValidateES3TexImageParametersBase(const Context *context,
angle::EntryPoint entryPoint,
TextureTarget target,
GLint level,
GLenum internalformat,
bool isCompressed,
bool isSubImage,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLsizei width,
GLsizei height,
GLsizei depth,
GLint border,
GLenum format,
GLenum type,
GLsizei imageSize,
const void *pixels)
{
TextureType texType = TextureTargetToType(target);
if (gl::IsYuvFormat(format))
{
// According to ANGLE_yuv_internal_format, the texture needs to be an immutable
// texture, texture target can only be TEXTURE_2D and there is no mipmap support
if (!context->getExtensions().yuvInternalFormatANGLE || !isSubImage)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
return false;
}
if (target != TextureTarget::_2D)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
if (level != 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
return false;
}
}
// Validate image size
if (!ValidImageSizeParameters(context, entryPoint, texType, level, width, height, depth,
isSubImage))
{
// Error already processed.
return false;
}
// Verify zero border
if (border != 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidBorder);
return false;
}
if (xoffset < 0 || yoffset < 0 || zoffset < 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeOffset);
return false;
}
if (std::numeric_limits<GLsizei>::max() - xoffset < width ||
std::numeric_limits<GLsizei>::max() - yoffset < height ||
std::numeric_limits<GLsizei>::max() - zoffset < depth)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kOffsetOverflow);
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))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
break;
case TextureType::_3D:
if (width > (caps.max3DTextureSize >> level) ||
height > (caps.max3DTextureSize >> level) ||
depth > (caps.max3DTextureSize >> level))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
break;
case TextureType::_2DArray:
if (width > (caps.max2DTextureSize >> level) ||
height > (caps.max2DTextureSize >> level) || depth > caps.maxArrayTextureLayers)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
break;
case TextureType::CubeMapArray:
if (!isSubImage && width != height)
{
context->validationError(entryPoint, GL_INVALID_VALUE,
kCubemapFacesEqualDimensions);
return false;
}
if (width > (caps.maxCubeMapTextureSize >> level))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
if (width > (caps.max3DTextureSize >> level) ||
height > (caps.max3DTextureSize >> level) ||
depth > (caps.max3DTextureSize >> level))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
if (!isSubImage && depth % 6 != 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kCubemapInvalidDepth);
return false;
}
break;
case TextureType::InvalidEnum:
context->validationError(entryPoint, GL_INVALID_ENUM, kEnumInvalid);
return false;
default:
context->validationErrorF(entryPoint, GL_INVALID_ENUM, kEnumNotSupported,
ToGLenum(texType));
return false;
}
Texture *texture = context->getTextureByType(texType);
if (!texture)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kMissingTexture);
return false;
}
if (texture->getImmutableFormat() && !isSubImage)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kTextureIsImmutable);
return false;
}
// Validate texture formats
GLenum actualInternalFormat =
isSubImage ? texture->getFormat(target, level).info->internalFormat : internalformat;
if (isSubImage && actualInternalFormat == GL_NONE)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidMipLevel);
return false;
}
const InternalFormat &actualFormatInfo = isSubImage
? *texture->getFormat(target, level).info
: GetInternalFormatInfo(internalformat, type);
if (isCompressed)
{
// compressedTexSubImage does not generate GL_INVALID_ENUM when format is unknown or invalid
if (!isSubImage)
{
if (!actualFormatInfo.compressed && !actualFormatInfo.paletted)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kCompressedMismatch);
return false;
}
if (!actualFormatInfo.textureSupport(context->getClientVersion(),
context->getExtensions()))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
return false;
}
}
if (texType == TextureType::_2DArray)
{
GLenum compressedDataFormat = isSubImage ? format : internalformat;
if (!ValidateES3CompressedFormatForTexture2DArray(context, entryPoint,
compressedDataFormat))
{
// Error already generated.
return false;
}
}
if (texType == TextureType::_3D)
{
GLenum compressedDataFormat = isSubImage ? format : internalformat;
if (!ValidateES3CompressedFormatForTexture3D(context, entryPoint, compressedDataFormat))
{
// Error already generated.
return false;
}
}
if (isSubImage)
{
if (!ValidCompressedSubImageSize(
context, actualFormatInfo.internalFormat, xoffset, yoffset, zoffset, width,
height, depth, 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, kMismatchedFormat);
return false;
}
// GL_EXT_compressed_ETC1_RGB8_sub_texture allows this format
if (IsETC1Format(actualInternalFormat) &&
!context->getExtensions().compressedETC1RGB8SubTextureEXT)
{
context->validationErrorF(entryPoint, GL_INVALID_OPERATION, kInvalidInternalFormat,
internalformat);
return false;
}
}
else
{
if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height,
depth))
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidCompressedImageSize);
return false;
}
}
// Disallow 3D-only compressed formats from being set on 2D textures
if (actualFormatInfo.compressedBlockDepth > 1 && texType != TextureType::_2DArray)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidTextureTarget);
return false;
}
}
else
{
// Compressed formats are not valid internal formats for glTexImage*D
if (!isSubImage)
{
const InternalFormat &internalFormatInfo = GetSizedInternalFormatInfo(internalformat);
if (internalFormatInfo.compressed)
{
context->validationErrorF(entryPoint, GL_INVALID_VALUE, kInvalidInternalFormat,
internalformat);
return false;
}
}
if (!ValidateTexImageFormatCombination(context, entryPoint, texType, actualInternalFormat,
format, type))
{
return false;
}
}
// Validate sub image parameters
if (isSubImage)
{
if (isCompressed != actualFormatInfo.compressed)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kCompressedMismatch);
return false;
}
if (xoffset < 0 || yoffset < 0 || zoffset < 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeOffset);
return false;
}
if (std::numeric_limits<GLsizei>::max() - xoffset < width ||
std::numeric_limits<GLsizei>::max() - yoffset < height ||
std::numeric_limits<GLsizei>::max() - zoffset < depth)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kOffsetOverflow);
return false;
}
if (static_cast<size_t>(xoffset + width) > texture->getWidth(target, level) ||
static_cast<size_t>(yoffset + height) > texture->getHeight(target, level) ||
static_cast<size_t>(zoffset + depth) > texture->getDepth(target, level))
{
context->validationError(entryPoint, GL_INVALID_VALUE, kOffsetOverflow);
return false;
}
if (width > 0 && height > 0 && depth > 0 && pixels == nullptr &&
context->getState().getTargetBuffer(BufferBinding::PixelUnpack) == nullptr)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kPixelDataNull);
return false;
}
}
GLenum sizeCheckFormat = isSubImage ? format : internalformat;
if (!ValidImageDataSize(context, entryPoint, texType, width, height, depth, sizeCheckFormat,
type, pixels, imageSize))
{
return false;
}
// Check for pixel unpack buffer related API errors
Buffer *pixelUnpackBuffer = context->getState().getTargetBuffer(BufferBinding::PixelUnpack);
if (pixelUnpackBuffer != nullptr)
{
// ...data is not evenly divisible into the number of bytes needed to store in memory a
// datum
// indicated by type.
if (!isCompressed)
{
size_t offset = reinterpret_cast<size_t>(pixels);
size_t dataBytesPerPixel = static_cast<size_t>(GetTypeInfo(type).bytes);
if ((offset % dataBytesPerPixel) != 0)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kDataTypeNotAligned);
return false;
}
}
// ...the buffer object's data store is currently mapped but not persistently.
if (pixelUnpackBuffer->isMapped() && !pixelUnpackBuffer->isPersistentlyMapped())
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferMapped);
return false;
}
}
if (context->getExtensions().webglCompatibilityANGLE)
{
// Define:
// DataStoreWidth = (GL_UNPACK_ROW_LENGTH ? GL_UNPACK_ROW_LENGTH : width)
// DataStoreHeight = (GL_UNPACK_IMAGE_HEIGHT ? GL_UNPACK_IMAGE_HEIGHT : height)
//
// WebGL 2.0 imposes the following additional constraints:
//
// 1) texImage2D and texSubImage2D generate INVALID_OPERATION if:
// GL_UNPACK_SKIP_PIXELS + width > DataStoreWidth
// except for texImage2D if no GL_PIXEL_UNPACK_BUFFER is
// bound and _pixels_ is null.
//
// 2) texImage3D and texSubImage3D generate INVALID_OPERATION if:
// GL_UNPACK_SKIP_PIXELS + width > DataStoreWidth
// GL_UNPACK_SKIP_ROWS + height > DataStoreHeight
// except for texImage3D if no GL_PIXEL_UNPACK_BUFFER is
// bound and _pixels_ is null.
if (!pixelUnpackBuffer && !pixels && !isSubImage)
{
// Exception case for texImage2D or texImage3D, above.
}
else
{
const auto &unpack = context->getState().getUnpackState();
GLint dataStoreWidth = unpack.rowLength ? unpack.rowLength : width;
if (unpack.skipPixels + width > dataStoreWidth)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidUnpackParametersForWebGL);
return false;
}
if (target == TextureTarget::_3D || target == TextureTarget::_2DArray)
{
GLint dataStoreHeight = unpack.imageHeight ? unpack.imageHeight : height;
if (unpack.skipRows + height > dataStoreHeight)
{
context->validationError(entryPoint, GL_INVALID_OPERATION,
kInvalidUnpackParametersForWebGL);
return false;
}
}
}
}
return true;
}
bool ValidateES3TexImage2DParameters(const Context *context,
angle::EntryPoint entryPoint,
TextureTarget target,
GLint level,
GLenum internalformat,
bool isCompressed,
bool isSubImage,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLsizei width,
GLsizei height,
GLsizei depth,
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 ValidateES3TexImageParametersBase(
context, entryPoint, target, level, internalformat, isCompressed, isSubImage, xoffset,
yoffset, zoffset, width, height, depth, border, format, type, imageSize, pixels);
}
bool ValidateES3TexImage3DParameters(const Context *context,
angle::EntryPoint entryPoint,
TextureTarget target,
GLint level,
GLenum internalformat,
bool isCompressed,
bool isSubImage,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLsizei width,
GLsizei height,
GLsizei depth,
GLint border,
GLenum format,
GLenum type,
GLsizei bufSize,
const void *pixels)
{
if (!ValidTexture3DDestinationTarget(context, target))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
return ValidateES3TexImageParametersBase(
context, entryPoint, target, level, internalformat, isCompressed, isSubImage, xoffset,
yoffset, zoffset, width, height, depth, border, format, type, bufSize, pixels);
}
struct EffectiveInternalFormatInfo
{
GLenum effectiveFormat;
GLenum destFormat;
GLuint minRedBits;
GLuint maxRedBits;
GLuint minGreenBits;
GLuint maxGreenBits;
GLuint minBlueBits;
GLuint maxBlueBits;
GLuint minAlphaBits;
GLuint maxAlphaBits;
};
static bool QueryEffectiveFormatList(const InternalFormat &srcFormat,
GLenum targetFormat,
const EffectiveInternalFormatInfo *list,
size_t size,
GLenum *outEffectiveFormat)
{
for (size_t curFormat = 0; curFormat < size; ++curFormat)
{
const EffectiveInternalFormatInfo &formatInfo = list[curFormat];
if ((formatInfo.destFormat == targetFormat) &&
(formatInfo.minRedBits <= srcFormat.redBits &&
formatInfo.maxRedBits >= srcFormat.redBits) &&
(formatInfo.minGreenBits <= srcFormat.greenBits &&
formatInfo.maxGreenBits >= srcFormat.greenBits) &&
(formatInfo.minBlueBits <= srcFormat.blueBits &&
formatInfo.maxBlueBits >= srcFormat.blueBits) &&
(formatInfo.minAlphaBits <= srcFormat.alphaBits &&
formatInfo.maxAlphaBits >= srcFormat.alphaBits))
{
*outEffectiveFormat = formatInfo.effectiveFormat;
return true;
}
}
*outEffectiveFormat = GL_NONE;
return false;
}
bool GetSizedEffectiveInternalFormatInfo(const InternalFormat &srcFormat,
GLenum *outEffectiveFormat)
{
// OpenGL ES 3.0.3 Specification, Table 3.17, pg 141:
// Effective internal format coresponding to destination internal format and linear source
// buffer component sizes.
// | Source channel min/max sizes |
// Effective Internal Format | N/A | R | G | B | A |
// clang-format off
constexpr EffectiveInternalFormatInfo list[] = {
{ GL_ALPHA8_EXT, GL_NONE, 0, 0, 0, 0, 0, 0, 1, 8 },
{ GL_R8, GL_NONE, 1, 8, 0, 0, 0, 0, 0, 0 },
{ GL_RG8, GL_NONE, 1, 8, 1, 8, 0, 0, 0, 0 },
{ GL_RGB565, GL_NONE, 1, 5, 1, 6, 1, 5, 0, 0 },
{ GL_RGB8, GL_NONE, 6, 8, 7, 8, 6, 8, 0, 0 },
{ GL_RGBA4, GL_NONE, 1, 4, 1, 4, 1, 4, 1, 4 },
{ GL_RGB5_A1, GL_NONE, 5, 5, 5, 5, 5, 5, 1, 1 },
{ GL_RGBA8, GL_NONE, 5, 8, 5, 8, 5, 8, 2, 8 },
{ GL_RGB10_A2, GL_NONE, 9, 10, 9, 10, 9, 10, 2, 2 },
};
// clang-format on
return QueryEffectiveFormatList(srcFormat, GL_NONE, list, ArraySize(list), outEffectiveFormat);
}
bool GetUnsizedEffectiveInternalFormatInfo(const InternalFormat &srcFormat,
const InternalFormat &destFormat,
GLenum *outEffectiveFormat)
{
constexpr GLuint umax = UINT_MAX;
// OpenGL ES 3.0.3 Specification, Table 3.17, pg 141:
// Effective internal format coresponding to destination internal format andlinear source buffer
// component sizes.
// | Source channel min/max sizes |
// Effective Internal Format | Dest Format | R | G | B | A |
// clang-format off
constexpr EffectiveInternalFormatInfo list[] = {
{ GL_ALPHA8_EXT, GL_ALPHA, 0, umax, 0, umax, 0, umax, 1, 8 },
{ GL_LUMINANCE8_EXT, GL_LUMINANCE, 1, 8, 0, umax, 0, umax, 0, umax },
{ GL_LUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, 1, 8, 0, umax, 0, umax, 1, 8 },
{ GL_RGB565, GL_RGB, 1, 5, 1, 6, 1, 5, 0, umax },
{ GL_RGB8, GL_RGB, 6, 8, 7, 8, 6, 8, 0, umax },
{ GL_RGBA4, GL_RGBA, 1, 4, 1, 4, 1, 4, 1, 4 },
{ GL_RGB5_A1, GL_RGBA, 5, 5, 5, 5, 5, 5, 1, 1 },
{ GL_RGBA8, GL_RGBA, 5, 8, 5, 8, 5, 8, 5, 8 },
};
// clang-format on
return QueryEffectiveFormatList(srcFormat, destFormat.format, list, ArraySize(list),
outEffectiveFormat);
}
static bool GetEffectiveInternalFormat(const InternalFormat &srcFormat,
const InternalFormat &destFormat,
GLenum *outEffectiveFormat)
{
if (destFormat.sized)
{
return GetSizedEffectiveInternalFormatInfo(srcFormat, outEffectiveFormat);
}
else
{
return GetUnsizedEffectiveInternalFormatInfo(srcFormat, destFormat, outEffectiveFormat);
}
}
static bool EqualOrFirstZero(GLuint first, GLuint second)
{
return first == 0 || first == second;
}
static bool IsValidES3CopyTexImageCombination(const InternalFormat &textureFormatInfo,
const InternalFormat &framebufferFormatInfo,
FramebufferID readBufferHandle)
{
if (!ValidES3CopyConversion(textureFormatInfo.format, framebufferFormatInfo.format))
{
return false;
}
// Section 3.8.5 of the GLES 3.0.3 spec states that source and destination formats
// must both be signed, unsigned, or fixed point and both source and destinations
// must be either both SRGB or both not SRGB. EXT_color_buffer_float adds allowed
// conversion between fixed and floating point.
if ((textureFormatInfo.colorEncoding == GL_SRGB) !=
(framebufferFormatInfo.colorEncoding == GL_SRGB))
{
return false;
}
if (((textureFormatInfo.componentType == GL_INT) !=
(framebufferFormatInfo.componentType == GL_INT)) ||
((textureFormatInfo.componentType == GL_UNSIGNED_INT) !=
(framebufferFormatInfo.componentType == GL_UNSIGNED_INT)))
{
return false;
}
if ((textureFormatInfo.componentType == GL_UNSIGNED_NORMALIZED ||
textureFormatInfo.componentType == GL_SIGNED_NORMALIZED) &&
!(framebufferFormatInfo.componentType == GL_UNSIGNED_NORMALIZED ||
framebufferFormatInfo.componentType == GL_SIGNED_NORMALIZED))
{
return false;
}
// SNORM is not supported (e.g. is not in the tables of "effective internal format" that
// correspond to internal formats.
if (textureFormatInfo.componentType == GL_SIGNED_NORMALIZED)
{
return false;
}
// Section 3.8.5 of the GLES 3.0.3 (and section 8.6 of the GLES 3.2) spec has a caveat, that
// the KHR dEQP tests enforce:
//
// Note that the above rules disallow matches where some components sizes are smaller and
// others are larger (such as RGB10_A2).
if (!textureFormatInfo.sized && (framebufferFormatInfo.internalFormat == GL_RGB10_A2))
{
return false;
}
// GLES specification 3.0.3, sec 3.8.5, pg 139-140:
// The effective internal format of the source buffer is determined with the following rules
// applied in order:
// * If the source buffer is a texture or renderbuffer that was created with a sized internal
// format then the effective internal format is the source buffer's sized internal format.
// * If the source buffer is a texture that was created with an unsized base internal format,
// then the effective internal format is the source image array's effective internal
// format, as specified by table 3.12, which is determined from the <format> and <type>
// that were used when the source image array was specified by TexImage*.
// * Otherwise the effective internal format is determined by the row in table 3.17 or 3.18
// where Destination Internal Format matches internalformat and where the [source channel
// sizes] are consistent with the values of the source buffer's [channel sizes]. Table 3.17
// is used if the FRAMEBUFFER_ATTACHMENT_ENCODING is LINEAR and table 3.18 is used if the
// FRAMEBUFFER_ATTACHMENT_ENCODING is SRGB.
const InternalFormat *sourceEffectiveFormat = nullptr;
if (readBufferHandle.value != 0)
{
// Not the default framebuffer, therefore the read buffer must be a user-created texture or
// renderbuffer
if (framebufferFormatInfo.sized)
{
sourceEffectiveFormat = &framebufferFormatInfo;
}
else
{
// Renderbuffers cannot be created with an unsized internal format, so this must be an
// unsized-format texture. We can use the same table we use when creating textures to
// get its effective sized format.
sourceEffectiveFormat =
&GetSizedInternalFormatInfo(framebufferFormatInfo.sizedInternalFormat);
}
}
else
{
// The effective internal format must be derived from the source framebuffer's channel
// sizes. This is done in GetEffectiveInternalFormat for linear buffers (table 3.17)
if (framebufferFormatInfo.colorEncoding == GL_LINEAR)
{
GLenum effectiveFormat;
if (GetEffectiveInternalFormat(framebufferFormatInfo, textureFormatInfo,
&effectiveFormat))
{
sourceEffectiveFormat = &GetSizedInternalFormatInfo(effectiveFormat);
}
else
{
return false;
}
}
else if (framebufferFormatInfo.colorEncoding == GL_SRGB)
{
// SRGB buffers can only be copied to sized format destinations according to table 3.18
if (textureFormatInfo.sized &&
(framebufferFormatInfo.redBits >= 1 && framebufferFormatInfo.redBits <= 8) &&
(framebufferFormatInfo.greenBits >= 1 && framebufferFormatInfo.greenBits <= 8) &&
(framebufferFormatInfo.blueBits >= 1 && framebufferFormatInfo.blueBits <= 8) &&
(framebufferFormatInfo.alphaBits >= 1 && framebufferFormatInfo.alphaBits <= 8))
{
sourceEffectiveFormat = &GetSizedInternalFormatInfo(GL_SRGB8_ALPHA8);
}
else
{
return false;
}
}
else
{
UNREACHABLE();
return false;
}
}
if (textureFormatInfo.sized)
{
// Section 3.8.5 of the GLES 3.0.3 spec, pg 139, requires that, if the destination format is
// sized, component sizes of the source and destination formats must exactly match if the
// destination format exists.
if (!EqualOrFirstZero(textureFormatInfo.redBits, sourceEffectiveFormat->redBits) ||
!EqualOrFirstZero(textureFormatInfo.greenBits, sourceEffectiveFormat->greenBits) ||
!EqualOrFirstZero(textureFormatInfo.blueBits, sourceEffectiveFormat->blueBits) ||
!EqualOrFirstZero(textureFormatInfo.alphaBits, sourceEffectiveFormat->alphaBits))
{
return false;
}
}
return true; // A conversion function exists, and no rule in the specification has precluded
// conversion between these formats.
}
bool ValidateES3CopyTexImageParametersBase(const Context *context,
angle::EntryPoint entryPoint,
TextureTarget target,
GLint level,
GLenum internalformat,
bool isSubImage,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLint border)
{
Format textureFormat = Format::Invalid();
if (!ValidateCopyTexImageParametersBase(context, entryPoint, target, level, internalformat,
isSubImage, xoffset, yoffset, zoffset, x, y, width,
height, border, &textureFormat))
{
return false;
}
ASSERT(textureFormat.valid() || !isSubImage);
const auto &state = context->getState();
Framebuffer *framebuffer = state.getReadFramebuffer();
FramebufferID readFramebufferID = framebuffer->id();
if (!ValidateFramebufferComplete(context, entryPoint, framebuffer))
{
return false;
}
// needIntrinsic = true. Treat renderToTexture textures as single sample since they will be
// resolved before copying
if (!framebuffer->isDefault() &&
!ValidateFramebufferNotMultisampled(context, entryPoint, framebuffer, true))
{
return false;
}
const FramebufferAttachment *source = framebuffer->getReadColorAttachment();
// According to ES 3.x spec, if the internalformat of the texture
// is RGB9_E5 and copy to such a texture, generate INVALID_OPERATION.
if (textureFormat.info->internalFormat == GL_RGB9_E5)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidFormat);
return false;
}
if (isSubImage)
{
if (!IsValidES3CopyTexImageCombination(*textureFormat.info, *source->getFormat().info,
readFramebufferID))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidCopyCombination);
return false;
}
}
else
{
// Use format/type from the source FBO. (Might not be perfect for all cases?)
const InternalFormat &framebufferFormat = *source->getFormat().info;
const InternalFormat ©Format = GetInternalFormatInfo(internalformat, GL_UNSIGNED_BYTE);
if (!IsValidES3CopyTexImageCombination(copyFormat, framebufferFormat, readFramebufferID))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidCopyCombination);
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 ValidateES3CopyTexImage2DParameters(const Context *context,
angle::EntryPoint entryPoint,
TextureTarget target,
GLint level,
GLenum internalformat,
bool isSubImage,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLint border)
{
if (!ValidTexture2DDestinationTarget(context, target))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
return ValidateES3CopyTexImageParametersBase(context, entryPoint, target, level, internalformat,
isSubImage, xoffset, yoffset, zoffset, x, y, width,
height, border);
}
bool ValidateES3CopyTexImage3DParameters(const Context *context,
angle::EntryPoint entryPoint,
TextureTarget target,
GLint level,
GLenum internalformat,
bool isSubImage,
GLint xoffset,
GLint yoffset,
GLint zoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLint border)
{
if (!ValidTexture3DDestinationTarget(context, target))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
return ValidateES3CopyTexImageParametersBase(context, entryPoint, target, level, internalformat,
isSubImage, xoffset, yoffset, zoffset, x, y, width,
height, border);
}
bool ValidateES3TexStorageParametersLevel(const Context *context,
angle::EntryPoint entryPoint,
TextureType target,
GLsizei levels,
GLsizei width,
GLsizei height,
GLsizei depth)
{
GLsizei maxDim = std::max(width, height);
if (target != TextureType::_2DArray)
{
maxDim = std::max(maxDim, depth);
}
if (levels > log2(maxDim) + 1)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidMipLevels);
return false;
}
return true;
}
bool ValidateES3TexStorageParametersExtent(const Context *context,
angle::EntryPoint entryPoint,
TextureType target,
GLsizei levels,
GLsizei width,
GLsizei height,
GLsizei depth)
{
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, kInvalidMipLevels);
return false;
}
if (width > caps.maxRectangleTextureSize || height > caps.maxRectangleTextureSize)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
}
break;
case TextureType::CubeMap:
{
if (width != height)
{
context->validationError(entryPoint, GL_INVALID_VALUE,
kCubemapFacesEqualDimensions);
return false;
}
if (width > caps.maxCubeMapTextureSize)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
}
break;
case TextureType::_3D:
{
if (width > caps.max3DTextureSize || height > caps.max3DTextureSize ||
depth > caps.max3DTextureSize)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
}
break;
case TextureType::_2DArray:
{
if (width > caps.max2DTextureSize || height > caps.max2DTextureSize ||
depth > caps.maxArrayTextureLayers)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
}
break;
case TextureType::CubeMapArray:
{
if (width != height)
{
context->validationError(entryPoint, GL_INVALID_VALUE,
kCubemapFacesEqualDimensions);
return false;
}
if (width > caps.maxCubeMapTextureSize)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
if (width > caps.max3DTextureSize || height > caps.max3DTextureSize ||
depth > caps.max3DTextureSize)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kResourceMaxTextureSize);
return false;
}
if (depth % 6 != 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kCubemapInvalidDepth);
return false;
}
}
break;
default:
UNREACHABLE();
return false;
}
return true;
}
bool ValidateES3TexStorageParametersTexObject(const Context *context,
angle::EntryPoint entryPoint,
TextureType target)
{
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 ValidateES3TexStorageParametersFormat(const Context *context,
angle::EntryPoint entryPoint,
TextureType target,
GLsizei levels,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLsizei depth)
{
// From ANGLE_texture_external_yuv_sampling:
// Texture target can only be TEXTURE_2D, there is no mipmap support
if (gl::IsYuvFormat(internalformat))
{
if (!context->getExtensions().yuvInternalFormatANGLE)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
return false;
}
if (target != TextureType::_2D)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
if (levels != 1)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel);
return false;
}
}
const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
return false;
}
if (!formatInfo.sized)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidFormat);
return false;
}
if (formatInfo.compressed)
{
if (target == TextureType::Rectangle)
{
context->validationError(entryPoint, GL_INVALID_ENUM, kRectangleTextureCompressed);
return false;
}
if (target == TextureType::_2DArray)
{
if (!ValidateES3CompressedFormatForTexture2DArray(context, entryPoint,
formatInfo.internalFormat))
{
// Error already generated.
return false;
}
}
if (target == TextureType::_3D)
{
if (!ValidateES3CompressedFormatForTexture3D(context, entryPoint,
formatInfo.internalFormat))
{
// Error already generated.
return false;
}
}
if (!ValidCompressedImageSize(context, formatInfo.internalFormat, 0, width, height, depth))
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidCompressedImageSize);
return false;
}
}
return true;
}
bool ValidateES3TexStorageParametersBase(const Context *context,
angle::EntryPoint entryPoint,
TextureType target,
GLsizei levels,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLsizei depth)
{
if (width < 1 || height < 1 || depth < 1 || levels < 1)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kTextureSizeTooSmall);
return false;
}
if (!ValidateES3TexStorageParametersLevel(context, entryPoint, target, levels, width, height,
depth))
{
// Error already generated.
return false;
}
if (!ValidateES3TexStorageParametersExtent(context, entryPoint, target, levels, width, height,
depth))
{
// Error already generated.
return false;
}
if (!ValidateES3TexStorageParametersTexObject(context, entryPoint, target))
{
// Error already generated.
return false;
}
if (!ValidateES3TexStorageParametersFormat(context, entryPoint, target, levels, internalformat,
width, height, depth))
{
// Error already generated.
return false;
}
return true;
}
bool ValidateES3TexStorage2DParameters(const Context *context,
angle::EntryPoint entryPoint,
TextureType target,
GLsizei levels,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLsizei depth)
{
if (!ValidTexture2DTarget(context, target))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
return ValidateES3TexStorageParametersBase(context, entryPoint, target, levels, internalformat,
width, height, depth);
}
bool ValidateES3TexStorage3DParameters(const Context *context,
angle::EntryPoint entryPoint,
TextureType target,
GLsizei levels,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLsizei depth)
{
if (!ValidTexture3DTarget(context, target))
{
context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
return ValidateES3TexStorageParametersBase(context, entryPoint, target, levels, internalformat,
width, height, depth);
}
bool ValidateBeginQuery(const Context *context,
angle::EntryPoint entryPoint,
QueryType target,
QueryID id)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
return ValidateBeginQueryBase(context, entryPoint, target, id);
}
bool ValidateEndQuery(const Context *context, angle::EntryPoint entryPoint, QueryType target)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
return ValidateEndQueryBase(context, entryPoint, target);
}
bool ValidateGetQueryiv(const Context *context,
angle::EntryPoint entryPoint,
QueryType target,
GLenum pname,
const GLint *params)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
return ValidateGetQueryivBase(context, entryPoint, target, pname, nullptr);
}
bool ValidateGetQueryObjectuiv(const Context *context,
angle::EntryPoint entryPoint,
QueryID id,
GLenum pname,
const GLuint *params)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
return ValidateGetQueryObjectValueBase(context, entryPoint, id, pname, nullptr);
}
bool ValidateFramebufferTextureLayer(const Context *context,
angle::EntryPoint entryPoint,
GLenum target,
GLenum attachment,
TextureID texture,
GLint level,
GLint layer)
{
if (context->getClientMajorVersion() < 3)
{
context->validationError(entryPoint, GL_INVALID_OPERATION, kES3Required);
return false;
}
if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level))
{
return false;
}
const Caps &caps = context->getCaps();
if (texture.value != 0)
{
if (layer < 0)
{
context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeLayer);
return false;
}
Texture *tex = context->getTexture(texture);
ASSERT(tex);
switch (tex->getType())
{
case TextureType::_2DArray:
{
if (level > log2(caps.max2DTextureSize))
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland
|
|