blob: 68285cecfda200e74fce7b52026535291d4a7d35 [file] [log] [blame]
//
// Copyright (c) 2013-2014 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.h"
#include <cstdint>
#include "libANGLE/validationES.h"
#include "libANGLE/validationES3.h"
#include "libANGLE/Context.h"
#include "libANGLE/Texture.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Uniform.h"
#include "common/mathutil.h"
#include "common/string_utils.h"
#include "common/utilities.h"
namespace gl
{
namespace
{
bool IsPartialBlit(gl::Context *context,
const FramebufferAttachment *readBuffer,
const FramebufferAttachment *writeBuffer,
GLint srcX0,
GLint srcY0,
GLint srcX1,
GLint srcY1,
GLint dstX0,
GLint dstY0,
GLint dstX1,
GLint dstY1)
{
const Extents &writeSize = writeBuffer->getSize();
const Extents &readSize = readBuffer->getSize();
if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 || dstX1 != writeSize.width ||
dstY1 != writeSize.height || srcX1 != readSize.width || srcY1 != readSize.height)
{
return true;
}
if (context->getGLState().isScissorTestEnabled())
{
const Rectangle &scissor = context->getGLState().getScissor();
return scissor.x > 0 || scissor.y > 0 || scissor.width < writeSize.width ||
scissor.height < writeSize.height;
}
return false;
}
template <typename T>
bool ValidatePathInstances(gl::Context *context,
GLsizei numPaths,
const void *paths,
GLuint pathBase)
{
const auto *array = static_cast<const T *>(paths);
for (GLsizei i = 0; i < numPaths; ++i)
{
const GLuint pathName = array[i] + pathBase;
if (context->hasPath(pathName) && !context->hasPathData(pathName))
{
context->handleError(gl::Error(GL_INVALID_OPERATION, "No such path object."));
return false;
}
}
return true;
}
bool ValidateInstancedPathParameters(gl::Context *context,
GLsizei numPaths,
GLenum pathNameType,
const void *paths,
GLuint pathBase,
GLenum transformType,
const GLfloat *transformValues)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
gl::Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (paths == nullptr)
{
context->handleError(gl::Error(GL_INVALID_VALUE, "No path name array."));
return false;
}
if (numPaths < 0)
{
context->handleError(gl::Error(GL_INVALID_VALUE, "Invalid (negative) numPaths."));
return false;
}
if (!angle::IsValueInRangeForNumericType<std::uint32_t>(numPaths))
{
context->handleError(gl::Error(GL_INVALID_OPERATION, "Overflow in numPaths."));
return false;
}
std::uint32_t pathNameTypeSize = 0;
std::uint32_t componentCount = 0;
switch (pathNameType)
{
case GL_UNSIGNED_BYTE:
pathNameTypeSize = sizeof(GLubyte);
if (!ValidatePathInstances<GLubyte>(context, numPaths, paths, pathBase))
return false;
break;
case GL_BYTE:
pathNameTypeSize = sizeof(GLbyte);
if (!ValidatePathInstances<GLbyte>(context, numPaths, paths, pathBase))
return false;
break;
case GL_UNSIGNED_SHORT:
pathNameTypeSize = sizeof(GLushort);
if (!ValidatePathInstances<GLushort>(context, numPaths, paths, pathBase))
return false;
break;
case GL_SHORT:
pathNameTypeSize = sizeof(GLshort);
if (!ValidatePathInstances<GLshort>(context, numPaths, paths, pathBase))
return false;
break;
case GL_UNSIGNED_INT:
pathNameTypeSize = sizeof(GLuint);
if (!ValidatePathInstances<GLuint>(context, numPaths, paths, pathBase))
return false;
break;
case GL_INT:
pathNameTypeSize = sizeof(GLint);
if (!ValidatePathInstances<GLint>(context, numPaths, paths, pathBase))
return false;
break;
default:
context->handleError(gl::Error(GL_INVALID_ENUM, "Invalid path name type."));
return false;
}
switch (transformType)
{
case GL_NONE:
componentCount = 0;
break;
case GL_TRANSLATE_X_CHROMIUM:
case GL_TRANSLATE_Y_CHROMIUM:
componentCount = 1;
break;
case GL_TRANSLATE_2D_CHROMIUM:
componentCount = 2;
break;
case GL_TRANSLATE_3D_CHROMIUM:
componentCount = 3;
break;
case GL_AFFINE_2D_CHROMIUM:
case GL_TRANSPOSE_AFFINE_2D_CHROMIUM:
componentCount = 6;
break;
case GL_AFFINE_3D_CHROMIUM:
case GL_TRANSPOSE_AFFINE_3D_CHROMIUM:
componentCount = 12;
break;
default:
context->handleError(gl::Error(GL_INVALID_ENUM, "Invalid transformation."));
return false;
}
if (componentCount != 0 && transformValues == nullptr)
{
context->handleError(gl::Error(GL_INVALID_VALUE, "No transform array given."));
return false;
}
angle::CheckedNumeric<std::uint32_t> checkedSize(0);
checkedSize += (numPaths * pathNameTypeSize);
checkedSize += (numPaths * sizeof(GLfloat) * componentCount);
if (!checkedSize.IsValid())
{
context->handleError(gl::Error(GL_INVALID_OPERATION, "Overflow in num paths."));
return false;
}
return true;
}
bool IsValidCopyTextureFormat(Context *context, GLenum internalFormat)
{
const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat);
switch (internalFormatInfo.format)
{
case GL_ALPHA:
case GL_LUMINANCE:
case GL_LUMINANCE_ALPHA:
case GL_RGB:
case GL_RGBA:
return true;
case GL_RED:
return context->getClientMajorVersion() >= 3 || context->getExtensions().textureRG;
case GL_BGRA_EXT:
return context->getExtensions().textureFormatBGRA8888;
default:
return false;
}
}
bool IsValidCopyTextureDestinationFormatType(Context *context, GLint internalFormat, GLenum type)
{
switch (internalFormat)
{
case GL_RGB:
case GL_RGBA:
break;
case GL_BGRA_EXT:
return context->getExtensions().textureFormatBGRA8888;
default:
return false;
}
switch (type)
{
case GL_UNSIGNED_BYTE:
break;
default:
return false;
}
return true;
}
bool IsValidCopyTextureDestinationTarget(Context *context, GLenum target)
{
switch (target)
{
case GL_TEXTURE_2D:
return true;
// TODO(geofflang): accept GL_TEXTURE_RECTANGLE_ARB if the texture_rectangle extension is
// supported
default:
return false;
}
}
bool IsValidCopyTextureSourceTarget(Context *context, GLenum target)
{
if (IsValidCopyTextureDestinationTarget(context, target))
{
return true;
}
// TODO(geofflang): accept GL_TEXTURE_EXTERNAL_OES if the texture_external extension is
// supported
return false;
}
} // anonymous namespace
bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage,
GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
GLint border, GLenum format, GLenum type, const GLvoid *pixels)
{
if (!ValidTexture2DDestinationTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
if (!ValidImageSizeParameters(context, target, level, width, height, 1, isSubImage))
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (level < 0 || xoffset < 0 ||
std::numeric_limits<GLsizei>::max() - xoffset < width ||
std::numeric_limits<GLsizei>::max() - yoffset < height)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (!isSubImage && !isCompressed && internalformat != format)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
const gl::Caps &caps = context->getCaps();
if (target == GL_TEXTURE_2D)
{
if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
}
else if (IsCubeMapTextureTarget(target))
{
if (!isSubImage && width != height)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level) ||
static_cast<GLuint>(height) > (caps.maxCubeMapTextureSize >> level))
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
gl::Texture *texture = context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target);
if (!texture)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
if (isSubImage)
{
if (format != GL_NONE)
{
if (gl::GetSizedInternalFormat(format, type) !=
texture->getFormat(target, level).asSized())
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
}
if (static_cast<size_t>(xoffset + width) > texture->getWidth(target, level) ||
static_cast<size_t>(yoffset + height) > texture->getHeight(target, level))
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
}
else
{
if (texture->getImmutableFormat())
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
}
// Verify zero border
if (border != 0)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (isCompressed)
{
GLenum actualInternalFormat =
isSubImage ? texture->getFormat(target, level).asSized() : internalformat;
switch (actualInternalFormat)
{
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
if (!context->getExtensions().textureCompressionDXT1)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
if (!context->getExtensions().textureCompressionDXT1)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
if (!context->getExtensions().textureCompressionDXT5)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_ETC1_RGB8_OES:
if (!context->getExtensions().compressedETC1RGB8Texture)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
if (!context->getExtensions().lossyETCDecode)
{
context->handleError(
Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported"));
return false;
}
break;
default:
context->handleError(Error(
GL_INVALID_ENUM, "internalformat is not a supported compressed internal format"));
return false;
}
if (!ValidCompressedImageSize(context, actualInternalFormat, width, height))
{
context->handleError(Error(GL_INVALID_OPERATION));
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;
default:
context->handleError(Error(GL_INVALID_ENUM));
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->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_RED:
case GL_RG:
if (!context->getExtensions().textureRG)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
switch (type)
{
case GL_UNSIGNED_BYTE:
case GL_FLOAT:
case GL_HALF_FLOAT_OES:
break;
default:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_RGB:
switch (type)
{
case GL_UNSIGNED_BYTE:
case GL_UNSIGNED_SHORT_5_6_5:
case GL_FLOAT:
case GL_HALF_FLOAT_OES:
break;
default:
context->handleError(Error(GL_INVALID_OPERATION));
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:
break;
default:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_BGRA_EXT:
switch (type)
{
case GL_UNSIGNED_BYTE:
break;
default:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_SRGB_EXT:
case GL_SRGB_ALPHA_EXT:
if (!context->getExtensions().sRGB)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
switch (type)
{
case GL_UNSIGNED_BYTE:
break;
default:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: // error cases for compressed textures are handled below
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
break;
case GL_DEPTH_COMPONENT:
switch (type)
{
case GL_UNSIGNED_SHORT:
case GL_UNSIGNED_INT:
break;
default:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_DEPTH_STENCIL_OES:
switch (type)
{
case GL_UNSIGNED_INT_24_8_OES:
break;
default:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
switch (format)
{
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
if (context->getExtensions().textureCompressionDXT1)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
if (context->getExtensions().textureCompressionDXT3)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
if (context->getExtensions().textureCompressionDXT5)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_ETC1_RGB8_OES:
if (context->getExtensions().compressedETC1RGB8Texture)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
if (context->getExtensions().lossyETCDecode)
{
context->handleError(
Error(GL_INVALID_OPERATION,
"ETC1_RGB8_LOSSY_DECODE_ANGLE can't work with this type."));
return false;
}
else
{
context->handleError(
Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
return false;
}
break;
case GL_DEPTH_COMPONENT:
case GL_DEPTH_STENCIL_OES:
if (!context->getExtensions().depthTextures)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (target != GL_TEXTURE_2D)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
// OES_depth_texture supports loading depth data and multiple levels,
// but ANGLE_depth_texture does not
if (pixels != NULL || level != 0)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
default:
break;
}
if (type == GL_FLOAT)
{
if (!context->getExtensions().textureFloat)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
}
else if (type == GL_HALF_FLOAT_OES)
{
if (!context->getExtensions().textureHalfFloat)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
}
}
return true;
}
bool ValidateES2CopyTexImageParameters(ValidationContext *context,
GLenum 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->handleError(Error(GL_INVALID_ENUM, "Invalid texture target"));
return false;
}
Format textureFormat = Format::Invalid();
if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
xoffset, yoffset, 0, x, y, width, height, border,
&textureFormat))
{
return false;
}
const gl::Framebuffer *framebuffer = context->getGLState().getReadFramebuffer();
GLenum colorbufferFormat = framebuffer->getReadColorbuffer()->getFormat().asSized();
const auto &formatInfo = *textureFormat.info;
// [OpenGL ES 2.0.24] table 3.9
if (isSubImage)
{
switch (formatInfo.format)
{
case GL_ALPHA:
if (colorbufferFormat != GL_ALPHA8_EXT &&
colorbufferFormat != GL_RGBA4 &&
colorbufferFormat != GL_RGB5_A1 &&
colorbufferFormat != GL_RGBA8_OES)
{
context->handleError(Error(GL_INVALID_OPERATION));
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)
{
context->handleError(Error(GL_INVALID_OPERATION));
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)
{
context->handleError(Error(GL_INVALID_OPERATION));
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)
{
context->handleError(Error(GL_INVALID_OPERATION));
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)
{
context->handleError(Error(GL_INVALID_OPERATION));
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)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
case GL_ETC1_RGB8_OES:
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
case GL_DEPTH_COMPONENT:
case GL_DEPTH_STENCIL_OES:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
default:
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
if (formatInfo.type == GL_FLOAT && !context->getExtensions().textureFloat)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
}
else
{
switch (internalformat)
{
case GL_ALPHA:
if (colorbufferFormat != GL_ALPHA8_EXT &&
colorbufferFormat != GL_RGBA4 &&
colorbufferFormat != GL_RGB5_A1 &&
colorbufferFormat != GL_BGRA8_EXT &&
colorbufferFormat != GL_RGBA8_OES &&
colorbufferFormat != GL_BGR5_A1_ANGLEX)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_LUMINANCE:
if (colorbufferFormat != GL_R8_EXT &&
colorbufferFormat != GL_RG8_EXT &&
colorbufferFormat != GL_RGB565 &&
colorbufferFormat != GL_RGB8_OES &&
colorbufferFormat != GL_RGBA4 &&
colorbufferFormat != GL_RGB5_A1 &&
colorbufferFormat != GL_BGRA8_EXT &&
colorbufferFormat != GL_RGBA8_OES &&
colorbufferFormat != GL_BGR5_A1_ANGLEX)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_RED_EXT:
if (colorbufferFormat != GL_R8_EXT &&
colorbufferFormat != GL_RG8_EXT &&
colorbufferFormat != GL_RGB565 &&
colorbufferFormat != GL_RGB8_OES &&
colorbufferFormat != GL_RGBA4 &&
colorbufferFormat != GL_RGB5_A1 &&
colorbufferFormat != GL_BGRA8_EXT &&
colorbufferFormat != GL_RGBA8_OES &&
colorbufferFormat != GL_BGR5_A1_ANGLEX)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_RG_EXT:
if (colorbufferFormat != GL_RG8_EXT &&
colorbufferFormat != GL_RGB565 &&
colorbufferFormat != GL_RGB8_OES &&
colorbufferFormat != GL_RGBA4 &&
colorbufferFormat != GL_RGB5_A1 &&
colorbufferFormat != GL_BGRA8_EXT &&
colorbufferFormat != GL_RGBA8_OES &&
colorbufferFormat != GL_BGR5_A1_ANGLEX)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_RGB:
if (colorbufferFormat != GL_RGB565 &&
colorbufferFormat != GL_RGB8_OES &&
colorbufferFormat != GL_RGBA4 &&
colorbufferFormat != GL_RGB5_A1 &&
colorbufferFormat != GL_BGRA8_EXT &&
colorbufferFormat != GL_RGBA8_OES &&
colorbufferFormat != GL_BGR5_A1_ANGLEX)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_LUMINANCE_ALPHA:
case GL_RGBA:
if (colorbufferFormat != GL_RGBA4 &&
colorbufferFormat != GL_RGB5_A1 &&
colorbufferFormat != GL_BGRA8_EXT &&
colorbufferFormat != GL_RGBA8_OES &&
colorbufferFormat != GL_BGR5_A1_ANGLEX)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
if (context->getExtensions().textureCompressionDXT1)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
if (context->getExtensions().textureCompressionDXT3)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
if (context->getExtensions().textureCompressionDXT5)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_ETC1_RGB8_OES:
if (context->getExtensions().compressedETC1RGB8Texture)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
if (context->getExtensions().lossyETCDecode)
{
context->handleError(Error(GL_INVALID_OPERATION,
"ETC1_RGB8_LOSSY_DECODE_ANGLE can't be copied to."));
return false;
}
else
{
context->handleError(
Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
return false;
}
break;
case GL_DEPTH_COMPONENT:
case GL_DEPTH_COMPONENT16:
case GL_DEPTH_COMPONENT32_OES:
case GL_DEPTH_STENCIL_OES:
case GL_DEPTH24_STENCIL8_OES:
if (context->getExtensions().depthTextures)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
else
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
default:
context->handleError(Error(GL_INVALID_ENUM));
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 ValidateES2TexStorageParameters(Context *context, GLenum target, GLsizei levels, GLenum internalformat,
GLsizei width, GLsizei height)
{
if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
if (width < 1 || height < 1 || levels < 1)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (target == GL_TEXTURE_CUBE_MAP && width != height)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (levels != 1 && levels != gl::log2(std::max(width, height)) + 1)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat);
if (formatInfo.format == GL_NONE || formatInfo.type == GL_NONE)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
const gl::Caps &caps = context->getCaps();
switch (target)
{
case GL_TEXTURE_2D:
if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
static_cast<GLuint>(height) > caps.max2DTextureSize)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
break;
case GL_TEXTURE_CUBE_MAP:
if (static_cast<GLuint>(width) > caps.maxCubeMapTextureSize ||
static_cast<GLuint>(height) > caps.maxCubeMapTextureSize)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
if (levels != 1 && !context->getExtensions().textureNPOT)
{
if (!gl::isPow2(width) || !gl::isPow2(height))
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
}
switch (internalformat)
{
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
if (!context->getExtensions().textureCompressionDXT1)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
if (!context->getExtensions().textureCompressionDXT3)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
if (!context->getExtensions().textureCompressionDXT5)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_ETC1_RGB8_OES:
if (!context->getExtensions().compressedETC1RGB8Texture)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
if (!context->getExtensions().lossyETCDecode)
{
context->handleError(
Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
return false;
}
break;
case GL_RGBA32F_EXT:
case GL_RGB32F_EXT:
case GL_ALPHA32F_EXT:
case GL_LUMINANCE32F_EXT:
case GL_LUMINANCE_ALPHA32F_EXT:
if (!context->getExtensions().textureFloat)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_RGBA16F_EXT:
case GL_RGB16F_EXT:
case GL_ALPHA16F_EXT:
case GL_LUMINANCE16F_EXT:
case GL_LUMINANCE_ALPHA16F_EXT:
if (!context->getExtensions().textureHalfFloat)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_R8_EXT:
case GL_RG8_EXT:
case GL_R16F_EXT:
case GL_RG16F_EXT:
case GL_R32F_EXT:
case GL_RG32F_EXT:
if (!context->getExtensions().textureRG)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
case GL_DEPTH_COMPONENT16:
case GL_DEPTH_COMPONENT32_OES:
case GL_DEPTH24_STENCIL8_OES:
if (!context->getExtensions().depthTextures)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
if (target != GL_TEXTURE_2D)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
// ANGLE_depth_texture only supports 1-level textures
if (levels != 1)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
break;
default:
break;
}
gl::Texture *texture = context->getTargetTexture(target);
if (!texture || texture->id() == 0)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
if (texture->getImmutableFormat())
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
return true;
}
// check for combinations of format and type that are valid for ReadPixels
bool ValidES2ReadFormatType(ValidationContext *context, GLenum format, GLenum type)
{
switch (format)
{
case GL_RGBA:
switch (type)
{
case GL_UNSIGNED_BYTE:
break;
default:
return false;
}
break;
case GL_BGRA_EXT:
switch (type)
{
case GL_UNSIGNED_BYTE:
case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT:
case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT:
break;
default:
return false;
}
break;
case GL_RG_EXT:
case GL_RED_EXT:
if (!context->getExtensions().textureRG)
{
return false;
}
switch (type)
{
case GL_UNSIGNED_BYTE:
break;
default:
return false;
}
break;
default:
return false;
}
return true;
}
bool ValidateDiscardFramebufferEXT(Context *context, GLenum target, GLsizei numAttachments,
const GLenum *attachments)
{
if (!context->getExtensions().discardFramebuffer)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
bool defaultFramebuffer = false;
switch (target)
{
case GL_FRAMEBUFFER:
defaultFramebuffer =
(context->getGLState().getTargetFramebuffer(GL_FRAMEBUFFER)->id() == 0);
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid framebuffer target"));
return false;
}
return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments, defaultFramebuffer);
}
bool ValidateBindVertexArrayOES(Context *context, GLuint array)
{
if (!context->getExtensions().vertexArrayObject)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
return ValidateBindVertexArrayBase(context, array);
}
bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n)
{
if (!context->getExtensions().vertexArrayObject)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
return ValidateGenOrDelete(context, n);
}
bool ValidateGenVertexArraysOES(Context *context, GLsizei n)
{
if (!context->getExtensions().vertexArrayObject)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
return ValidateGenOrDelete(context, n);
}
bool ValidateIsVertexArrayOES(Context *context)
{
if (!context->getExtensions().vertexArrayObject)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
return true;
}
bool ValidateProgramBinaryOES(Context *context,
GLuint program,
GLenum binaryFormat,
const void *binary,
GLint length)
{
if (!context->getExtensions().getProgramBinary)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
return ValidateProgramBinaryBase(context, program, binaryFormat, binary, length);
}
bool ValidateGetProgramBinaryOES(Context *context,
GLuint program,
GLsizei bufSize,
GLsizei *length,
GLenum *binaryFormat,
void *binary)
{
if (!context->getExtensions().getProgramBinary)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary);
}
static bool ValidDebugSource(GLenum source, bool mustBeThirdPartyOrApplication)
{
switch (source)
{
case GL_DEBUG_SOURCE_API:
case GL_DEBUG_SOURCE_SHADER_COMPILER:
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
case GL_DEBUG_SOURCE_OTHER:
// Only THIRD_PARTY and APPLICATION sources are allowed to be manually inserted
return !mustBeThirdPartyOrApplication;
case GL_DEBUG_SOURCE_THIRD_PARTY:
case GL_DEBUG_SOURCE_APPLICATION:
return true;
default:
return false;
}
}
static bool ValidDebugType(GLenum type)
{
switch (type)
{
case GL_DEBUG_TYPE_ERROR:
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
case GL_DEBUG_TYPE_PERFORMANCE:
case GL_DEBUG_TYPE_PORTABILITY:
case GL_DEBUG_TYPE_OTHER:
case GL_DEBUG_TYPE_MARKER:
case GL_DEBUG_TYPE_PUSH_GROUP:
case GL_DEBUG_TYPE_POP_GROUP:
return true;
default:
return false;
}
}
static bool ValidDebugSeverity(GLenum severity)
{
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH:
case GL_DEBUG_SEVERITY_MEDIUM:
case GL_DEBUG_SEVERITY_LOW:
case GL_DEBUG_SEVERITY_NOTIFICATION:
return true;
default:
return false;
}
}
bool ValidateDebugMessageControlKHR(Context *context,
GLenum source,
GLenum type,
GLenum severity,
GLsizei count,
const GLuint *ids,
GLboolean enabled)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
if (!ValidDebugSource(source, false) && source != GL_DONT_CARE)
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid debug source."));
return false;
}
if (!ValidDebugType(type) && type != GL_DONT_CARE)
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid debug type."));
return false;
}
if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE)
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid debug severity."));
return false;
}
if (count > 0)
{
if (source == GL_DONT_CARE || type == GL_DONT_CARE)
{
context->handleError(Error(
GL_INVALID_OPERATION,
"If count is greater than zero, source and severity cannot be GL_DONT_CARE."));
return false;
}
if (severity != GL_DONT_CARE)
{
context->handleError(
Error(GL_INVALID_OPERATION,
"If count is greater than zero, severity must be GL_DONT_CARE."));
return false;
}
}
return true;
}
bool ValidateDebugMessageInsertKHR(Context *context,
GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *buf)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
if (!context->getGLState().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->handleError(Error(GL_INVALID_ENUM, "Invalid debug severity."));
return false;
}
if (!ValidDebugType(type))
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid debug type."));
return false;
}
if (!ValidDebugSource(source, true))
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid debug source."));
return false;
}
size_t messageLength = (length < 0) ? strlen(buf) : length;
if (messageLength > context->getExtensions().maxDebugMessageLength)
{
context->handleError(
Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH."));
return false;
}
return true;
}
bool ValidateDebugMessageCallbackKHR(Context *context,
GLDEBUGPROCKHR callback,
const void *userParam)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
return true;
}
bool ValidateGetDebugMessageLogKHR(Context *context,
GLuint count,
GLsizei bufSize,
GLenum *sources,
GLenum *types,
GLuint *ids,
GLenum *severities,
GLsizei *lengths,
GLchar *messageLog)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
if (bufSize < 0 && messageLog != nullptr)
{
context->handleError(
Error(GL_INVALID_VALUE, "bufSize must be positive if messageLog is not null."));
return false;
}
return true;
}
bool ValidatePushDebugGroupKHR(Context *context,
GLenum source,
GLuint id,
GLsizei length,
const GLchar *message)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
if (!ValidDebugSource(source, true))
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid debug source."));
return false;
}
size_t messageLength = (length < 0) ? strlen(message) : length;
if (messageLength > context->getExtensions().maxDebugMessageLength)
{
context->handleError(
Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH."));
return false;
}
size_t currentStackSize = context->getGLState().getDebug().getGroupStackDepth();
if (currentStackSize >= context->getExtensions().maxDebugGroupStackDepth)
{
context->handleError(
Error(GL_STACK_OVERFLOW,
"Cannot push more than GL_MAX_DEBUG_GROUP_STACK_DEPTH debug groups."));
return false;
}
return true;
}
bool ValidatePopDebugGroupKHR(Context *context)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
size_t currentStackSize = context->getGLState().getDebug().getGroupStackDepth();
if (currentStackSize <= 1)
{
context->handleError(Error(GL_STACK_UNDERFLOW, "Cannot pop the default debug group."));
return false;
}
return true;
}
static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier, GLuint name)
{
switch (identifier)
{
case GL_BUFFER:
if (context->getBuffer(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid buffer."));
return false;
}
return true;
case GL_SHADER:
if (context->getShader(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid shader."));
return false;
}
return true;
case GL_PROGRAM:
if (context->getProgram(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid program."));
return false;
}
return true;
case GL_VERTEX_ARRAY:
if (context->getVertexArray(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid vertex array."));
return false;
}
return true;
case GL_QUERY:
if (context->getQuery(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid query."));
return false;
}
return true;
case GL_TRANSFORM_FEEDBACK:
if (context->getTransformFeedback(name) == nullptr)
{
context->handleError(
Error(GL_INVALID_VALUE, "name is not a valid transform feedback."));
return false;
}
return true;
case GL_SAMPLER:
if (context->getSampler(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid sampler."));
return false;
}
return true;
case GL_TEXTURE:
if (context->getTexture(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid texture."));
return false;
}
return true;
case GL_RENDERBUFFER:
if (context->getRenderbuffer(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid renderbuffer."));
return false;
}
return true;
case GL_FRAMEBUFFER:
if (context->getFramebuffer(name) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid framebuffer."));
return false;
}
return true;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid identifier."));
return false;
}
}
static bool ValidateLabelLength(Context *context, GLsizei length, const GLchar *label)
{
size_t labelLength = 0;
if (length < 0)
{
if (label != nullptr)
{
labelLength = strlen(label);
}
}
else
{
labelLength = static_cast<size_t>(length);
}
if (labelLength > context->getExtensions().maxLabelLength)
{
context->handleError(
Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH."));
return false;
}
return true;
}
bool ValidateObjectLabelKHR(Context *context,
GLenum identifier,
GLuint name,
GLsizei length,
const GLchar *label)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
if (!ValidateObjectIdentifierAndName(context, identifier, name))
{
return false;
}
if (!ValidateLabelLength(context, length, label))
{
return false;
}
return true;
}
bool ValidateGetObjectLabelKHR(Context *context,
GLenum identifier,
GLuint name,
GLsizei bufSize,
GLsizei *length,
GLchar *label)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
if (bufSize < 0)
{
context->handleError(Error(GL_INVALID_VALUE, "bufSize cannot be negative."));
return false;
}
if (!ValidateObjectIdentifierAndName(context, identifier, name))
{
return false;
}
return true;
}
static bool ValidateObjectPtrName(Context *context, const void *ptr)
{
if (context->getFenceSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr)
{
context->handleError(Error(GL_INVALID_VALUE, "name is not a valid sync."));
return false;
}
return true;
}
bool ValidateObjectPtrLabelKHR(Context *context,
const void *ptr,
GLsizei length,
const GLchar *label)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
if (!ValidateObjectPtrName(context, ptr))
{
return false;
}
if (!ValidateLabelLength(context, length, label))
{
return false;
}
return true;
}
bool ValidateGetObjectPtrLabelKHR(Context *context,
const void *ptr,
GLsizei bufSize,
GLsizei *length,
GLchar *label)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
if (bufSize < 0)
{
context->handleError(Error(GL_INVALID_VALUE, "bufSize cannot be negative."));
return false;
}
if (!ValidateObjectPtrName(context, ptr))
{
return false;
}
return true;
}
bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params)
{
if (!context->getExtensions().debug)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not enabled"));
return false;
}
// TODO: represent this in Context::getQueryParameterInfo.
switch (pname)
{
case GL_DEBUG_CALLBACK_FUNCTION:
case GL_DEBUG_CALLBACK_USER_PARAM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid pname."));
return false;
}
return true;
}
bool ValidateBlitFramebufferANGLE(Context *context,
GLint srcX0,
GLint srcY0,
GLint srcX1,
GLint srcY1,
GLint dstX0,
GLint dstY0,
GLint dstX1,
GLint dstY1,
GLbitfield mask,
GLenum filter)
{
if (!context->getExtensions().framebufferBlit)
{
context->handleError(Error(GL_INVALID_OPERATION, "Blit extension not available."));
return false;
}
if (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0)
{
// TODO(jmadill): Determine if this should be available on other implementations.
context->handleError(Error(
GL_INVALID_OPERATION,
"Scaling and flipping in BlitFramebufferANGLE not supported by this implementation."));
return false;
}
if (filter == GL_LINEAR)
{
context->handleError(Error(GL_INVALID_ENUM, "Linear blit not supported in this extension"));
return false;
}
Framebuffer *readFramebuffer = context->getGLState().getReadFramebuffer();
Framebuffer *drawFramebuffer = context->getGLState().getDrawFramebuffer();
if (mask & GL_COLOR_BUFFER_BIT)
{
const FramebufferAttachment *readColorAttachment = readFramebuffer->getReadColorbuffer();
const FramebufferAttachment *drawColorAttachment = drawFramebuffer->getFirstColorbuffer();
if (readColorAttachment && drawColorAttachment)
{
if (!(readColorAttachment->type() == GL_TEXTURE &&
readColorAttachment->getTextureImageIndex().type == GL_TEXTURE_2D) &&
readColorAttachment->type() != GL_RENDERBUFFER &&
readColorAttachment->type() != GL_FRAMEBUFFER_DEFAULT)
{
context->handleError(Error(GL_INVALID_OPERATION));
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().type == GL_TEXTURE_2D) &&
attachment->type() != GL_RENDERBUFFER &&
attachment->type() != GL_FRAMEBUFFER_DEFAULT)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
// Return an error if the destination formats do not match
if (!Format::SameSized(attachment->getFormat(),
readColorAttachment->getFormat()))
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
}
}
if (readFramebuffer->getSamples(context->getContextState()) != 0 &&
IsPartialBlit(context, readColorAttachment, drawColorAttachment, srcX0, srcY0,
srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
{
context->handleError(Error(GL_INVALID_OPERATION));
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(attachments[i]);
const FramebufferAttachment *drawBuffer =
drawFramebuffer->getAttachment(attachments[i]);
if (readBuffer && drawBuffer)
{
if (IsPartialBlit(context, readBuffer, drawBuffer, srcX0, srcY0, srcX1, srcY1,
dstX0, dstY0, dstX1, dstY1))
{
// only whole-buffer copies are permitted
ERR(
"Only whole-buffer depth and stencil blits are supported by this "
"implementation.");
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
if (readBuffer->getSamples() != 0 || drawBuffer->getSamples() != 0)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
}
}
}
return ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0,
dstX1, dstY1, mask, filter);
}
bool ValidateClear(ValidationContext *context, GLbitfield mask)
{
auto fbo = context->getGLState().getDrawFramebuffer();
if (fbo->checkStatus(context->getContextState()) != GL_FRAMEBUFFER_COMPLETE)
{
context->handleError(Error(GL_INVALID_FRAMEBUFFER_OPERATION));
return false;
}
if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
return true;
}
bool ValidateDrawBuffersEXT(ValidationContext *context, GLsizei n, const GLenum *bufs)
{
if (!context->getExtensions().drawBuffers)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension not supported."));
return false;
}
return ValidateDrawBuffersBase(context, n, bufs);
}
bool ValidateTexImage2D(Context *context,
GLenum target,
GLint level,
GLint internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const GLvoid *pixels)
{
if (context->getClientMajorVersion() < 3)
{
return ValidateES2TexImageParameters(context, target, level, internalformat, false, false,
0, 0, width, height, border, format, type, pixels);
}
ASSERT(context->getClientMajorVersion() >= 3);
return ValidateES3TexImage2DParameters(context, target, level, internalformat, false, false, 0,
0, 0, width, height, 1, border, format, type, pixels);
}
bool ValidateTexSubImage2D(Context *context,
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
const GLvoid *pixels)
{
if (context->getClientMajorVersion() < 3)
{
return ValidateES2TexImageParameters(context, target, level, GL_NONE, false, true, xoffset,
yoffset, width, height, 0, format, type, pixels);
}
ASSERT(context->getClientMajorVersion() >= 3);
return ValidateES3TexImage2DParameters(context, target, level, GL_NONE, false, true, xoffset,
yoffset, 0, width, height, 1, 0, format, type, pixels);
}
bool ValidateCompressedTexImage2D(Context *context,
GLenum target,
GLint level,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLsizei imageSize,
const GLvoid *data)
{
if (context->getClientMajorVersion() < 3)
{
if (!ValidateES2TexImageParameters(context, target, level, internalformat, true, false, 0,
0, width, height, border, GL_NONE, GL_NONE, data))
{
return false;
}
}
else
{
ASSERT(context->getClientMajorVersion() >= 3);
if (!ValidateES3TexImage2DParameters(context, target, level, internalformat, true, false, 0,
0, 0, width, height, 1, border, GL_NONE, GL_NONE,
data))
{
return false;
}
}
const InternalFormat &formatInfo = GetInternalFormatInfo(internalformat);
auto blockSizeOrErr =
formatInfo.computeCompressedImageSize(GL_UNSIGNED_BYTE, gl::Extents(width, height, 1));
if (blockSizeOrErr.isError())
{
context->handleError(blockSizeOrErr.getError());
return false;
}
if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSizeOrErr.getResult())
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
return true;
}
bool ValidateCompressedTexSubImage2D(Context *context,
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLsizei imageSize,
const GLvoid *data)
{
if (context->getClientMajorVersion() < 3)
{
if (!ValidateES2TexImageParameters(context, target, level, GL_NONE, true, true, xoffset,
yoffset, width, height, 0, GL_NONE, GL_NONE, data))
{
return false;
}
}
else
{
ASSERT(context->getClientMajorVersion() >= 3);
if (!ValidateES3TexImage2DParameters(context, target, level, GL_NONE, true, true, xoffset,
yoffset, 0, width, height, 1, 0, GL_NONE, GL_NONE,
data))
{
return false;
}
}
const InternalFormat &formatInfo = GetInternalFormatInfo(format);
auto blockSizeOrErr =
formatInfo.computeCompressedImageSize(GL_UNSIGNED_BYTE, gl::Extents(width, height, 1));
if (blockSizeOrErr.isError())
{
context->handleError(blockSizeOrErr.getError());
return false;
}
if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSizeOrErr.getResult())
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
return true;
}
bool ValidateGetBufferPointervOES(Context *context, GLenum target, GLenum pname, void **params)
{
if (!context->getExtensions().mapBuffer)
{
context->handleError(Error(GL_INVALID_OPERATION, "Map buffer extension not available."));
return false;
}
return ValidateGetBufferPointervBase(context, target, pname, params);
}
bool ValidateMapBufferOES(Context *context, GLenum target, GLenum access)
{
if (!context->getExtensions().mapBuffer)
{
context->handleError(Error(GL_INVALID_OPERATION, "Map buffer extension not available."));
return false;
}
if (!ValidBufferTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid buffer target."));
return false;
}
Buffer *buffer = context->getGLState().getTargetBuffer(target);
if (buffer == nullptr)
{
context->handleError(Error(GL_INVALID_OPERATION, "Attempted to map buffer object zero."));
return false;
}
if (access != GL_WRITE_ONLY_OES)
{
context->handleError(Error(GL_INVALID_ENUM, "Non-write buffer mapping not supported."));
return false;
}
if (buffer->isMapped())
{
context->handleError(Error(GL_INVALID_OPERATION, "Buffer is already mapped."));
return false;
}
return true;
}
bool ValidateUnmapBufferOES(Context *context, GLenum target)
{
if (!context->getExtensions().mapBuffer)
{
context->handleError(Error(GL_INVALID_OPERATION, "Map buffer extension not available."));
return false;
}
return ValidateUnmapBufferBase(context, target);
}
bool ValidateMapBufferRangeEXT(Context *context,
GLenum target,
GLintptr offset,
GLsizeiptr length,
GLbitfield access)
{
if (!context->getExtensions().mapBufferRange)
{
context->handleError(
Error(GL_INVALID_OPERATION, "Map buffer range extension not available."));
return false;
}
return ValidateMapBufferRangeBase(context, target, offset, length, access);
}
bool ValidateFlushMappedBufferRangeEXT(Context *context,
GLenum target,
GLintptr offset,
GLsizeiptr length)
{
if (!context->getExtensions().mapBufferRange)
{
context->handleError(
Error(GL_INVALID_OPERATION, "Map buffer range extension not available."));
return false;
}
return ValidateFlushMappedBufferRangeBase(context, target, offset, length);
}
bool ValidateBindTexture(Context *context, GLenum target, GLuint texture)
{
Texture *textureObject = context->getTexture(texture);
if (textureObject && textureObject->getTarget() != target && texture != 0)
{
context->handleError(Error(GL_INVALID_OPERATION, "Invalid texture"));
return false;
}
switch (target)
{
case GL_TEXTURE_2D:
case GL_TEXTURE_CUBE_MAP:
break;
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
if (context->getClientMajorVersion() < 3)
{
context->handleError(Error(GL_INVALID_ENUM, "GLES 3.0 disabled"));
return false;
}
break;
case GL_TEXTURE_EXTERNAL_OES:
if (!context->getExtensions().eglImageExternal &&
!context->getExtensions().eglStreamConsumerExternal)
{
context->handleError(
Error(GL_INVALID_ENUM, "External texture extension not enabled"));
return false;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid target"));
return false;
}
return true;
}
bool ValidateBindUniformLocationCHROMIUM(Context *context,
GLuint program,
GLint location,
const GLchar *name)
{
if (!context->getExtensions().bindUniformLocation)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_bind_uniform_location is not available."));
return false;
}
Program *programObject = GetValidProgram(context, program);
if (!programObject)
{
return false;
}
if (location < 0)
{
context->handleError(Error(GL_INVALID_VALUE, "Location cannot be less than 0."));
return false;
}
const Caps &caps = context->getCaps();
if (static_cast<size_t>(location) >=
(caps.maxVertexUniformVectors + caps.maxFragmentUniformVectors) * 4)
{
context->handleError(Error(GL_INVALID_VALUE,
"Location must be less than (MAX_VERTEX_UNIFORM_VECTORS + "
"MAX_FRAGMENT_UNIFORM_VECTORS) * 4"));
return false;
}
if (strncmp(name, "gl_", 3) == 0)
{
context->handleError(
Error(GL_INVALID_OPERATION, "Name cannot start with the reserved \"gl_\" prefix."));
return false;
}
return true;
}
bool ValidateCoverageModulationCHROMIUM(Context *context, GLenum components)
{
if (!context->getExtensions().framebufferMixedSamples)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_framebuffer_mixed_samples is not available."));
return false;
}
switch (components)
{
case GL_RGB:
case GL_RGBA:
case GL_ALPHA:
case GL_NONE:
break;
default:
context->handleError(
Error(GL_INVALID_ENUM,
"GLenum components is not one of GL_RGB, GL_RGBA, GL_ALPHA or GL_NONE."));
return false;
}
return true;
}
// CHROMIUM_path_rendering
bool ValidateMatrix(Context *context, GLenum matrixMode, const GLfloat *matrix)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid matrix mode."));
return false;
}
if (matrix == nullptr)
{
context->handleError(Error(GL_INVALID_OPERATION, "Invalid matrix."));
return false;
}
return true;
}
bool ValidateMatrixMode(Context *context, GLenum matrixMode)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
{
context->handleError(Error(GL_INVALID_ENUM, "Invalid matrix mode."));
return false;
}
return true;
}
bool ValidateGenPaths(Context *context, GLsizei range)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
// range = 0 is undefined in NV_path_rendering.
// we add stricter semantic check here and require a non zero positive range.
if (range <= 0)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid range."));
return false;
}
if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range))
{
context->handleError(Error(GL_INVALID_OPERATION, "Range overflow."));
return false;
}
return true;
}
bool ValidateDeletePaths(Context *context, GLuint path, GLsizei range)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
// range = 0 is undefined in NV_path_rendering.
// we add stricter semantic check here and require a non zero positive range.
if (range <= 0)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid range."));
return false;
}
angle::CheckedNumeric<std::uint32_t> checkedRange(path);
checkedRange += range;
if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range) || !checkedRange.IsValid())
{
context->handleError(Error(GL_INVALID_OPERATION, "Range overflow."));
return false;
}
return true;
}
bool ValidatePathCommands(Context *context,
GLuint path,
GLsizei numCommands,
const GLubyte *commands,
GLsizei numCoords,
GLenum coordType,
const void *coords)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (!context->hasPath(path))
{
context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
return false;
}
if (numCommands < 0)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid number of commands."));
return false;
}
else if (numCommands > 0)
{
if (!commands)
{
context->handleError(Error(GL_INVALID_VALUE, "No commands array given."));
return false;
}
}
if (numCoords < 0)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid number of coordinates."));
return false;
}
else if (numCoords > 0)
{
if (!coords)
{
context->handleError(Error(GL_INVALID_VALUE, "No coordinate array given."));
return false;
}
}
std::uint32_t coordTypeSize = 0;
switch (coordType)
{
case GL_BYTE:
coordTypeSize = sizeof(GLbyte);
break;
case GL_UNSIGNED_BYTE:
coordTypeSize = sizeof(GLubyte);
break;
case GL_SHORT:
coordTypeSize = sizeof(GLshort);
break;
case GL_UNSIGNED_SHORT:
coordTypeSize = sizeof(GLushort);
break;
case GL_FLOAT:
coordTypeSize = sizeof(GLfloat);
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid coordinate type."));
return false;
}
angle::CheckedNumeric<std::uint32_t> checkedSize(numCommands);
checkedSize += (coordTypeSize * numCoords);
if (!checkedSize.IsValid())
{
context->handleError(Error(GL_INVALID_OPERATION, "Coord size overflow."));
return false;
}
// early return skips command data validation when it doesn't exist.
if (!commands)
return true;
GLsizei expectedNumCoords = 0;
for (GLsizei i = 0; i < numCommands; ++i)
{
switch (commands[i])
{
case GL_CLOSE_PATH_CHROMIUM: // no coordinates.
break;
case GL_MOVE_TO_CHROMIUM:
case GL_LINE_TO_CHROMIUM:
expectedNumCoords += 2;
break;
case GL_QUADRATIC_CURVE_TO_CHROMIUM:
expectedNumCoords += 4;
break;
case GL_CUBIC_CURVE_TO_CHROMIUM:
expectedNumCoords += 6;
break;
case GL_CONIC_CURVE_TO_CHROMIUM:
expectedNumCoords += 5;
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid command."));
return false;
}
}
if (expectedNumCoords != numCoords)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid number of coordinates."));
return false;
}
return true;
}
bool ValidateSetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat value)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (!context->hasPath(path))
{
context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
return false;
}
switch (pname)
{
case GL_PATH_STROKE_WIDTH_CHROMIUM:
if (value < 0.0f)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid stroke width."));
return false;
}
break;
case GL_PATH_END_CAPS_CHROMIUM:
switch (static_cast<GLenum>(value))
{
case GL_FLAT_CHROMIUM:
case GL_SQUARE_CHROMIUM:
case GL_ROUND_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid end caps."));
return false;
}
break;
case GL_PATH_JOIN_STYLE_CHROMIUM:
switch (static_cast<GLenum>(value))
{
case GL_MITER_REVERT_CHROMIUM:
case GL_BEVEL_CHROMIUM:
case GL_ROUND_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid join style."));
return false;
}
case GL_PATH_MITER_LIMIT_CHROMIUM:
if (value < 0.0f)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid miter limit."));
return false;
}
break;
case GL_PATH_STROKE_BOUND_CHROMIUM:
// no errors, only clamping.
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid path parameter."));
return false;
}
return true;
}
bool ValidateGetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat *value)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (!context->hasPath(path))
{
context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
return false;
}
if (!value)
{
context->handleError(Error(GL_INVALID_VALUE, "No value array."));
return false;
}
switch (pname)
{
case GL_PATH_STROKE_WIDTH_CHROMIUM:
case GL_PATH_END_CAPS_CHROMIUM:
case GL_PATH_JOIN_STYLE_CHROMIUM:
case GL_PATH_MITER_LIMIT_CHROMIUM:
case GL_PATH_STROKE_BOUND_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid path parameter."));
return false;
}
return true;
}
bool ValidatePathStencilFunc(Context *context, GLenum func, GLint ref, GLuint mask)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
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:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid stencil function."));
return false;
}
return true;
}
// Note that the spec specifies that for the path drawing commands
// if the path object is not an existing path object the command
// does nothing and no error is generated.
// However if the path object exists but has not been specified any
// commands then an error is generated.
bool ValidateStencilFillPath(Context *context, GLuint path, GLenum fillMode, GLuint mask)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (context->hasPath(path) && !context->hasPathData(path))
{
context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
return false;
}
switch (fillMode)
{
case GL_COUNT_UP_CHROMIUM:
case GL_COUNT_DOWN_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid fill mode."));
return false;
}
if (!isPow2(mask + 1))
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid stencil bit mask."));
return false;
}
return true;
}
bool ValidateStencilStrokePath(Context *context, GLuint path, GLint reference, GLuint mask)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (context->hasPath(path) && !context->hasPathData(path))
{
context->handleError(Error(GL_INVALID_OPERATION, "No such path or path has no data."));
return false;
}
return true;
}
bool ValidateCoverPath(Context *context, GLuint path, GLenum coverMode)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
if (context->hasPath(path) && !context->hasPathData(path))
{
context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
return false;
}
switch (coverMode)
{
case GL_CONVEX_HULL_CHROMIUM:
case GL_BOUNDING_BOX_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid cover mode."));
return false;
}
return true;
}
bool ValidateStencilThenCoverFillPath(Context *context,
GLuint path,
GLenum fillMode,
GLuint mask,
GLenum coverMode)
{
return ValidateStencilFillPath(context, path, fillMode, mask) &&
ValidateCoverPath(context, path, coverMode);
}
bool ValidateStencilThenCoverStrokePath(Context *context,
GLuint path,
GLint reference,
GLuint mask,
GLenum coverMode)
{
return ValidateStencilStrokePath(context, path, reference, mask) &&
ValidateCoverPath(context, path, coverMode);
}
bool ValidateIsPath(Context *context)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
return true;
}
bool ValidateCoverFillPathInstanced(Context *context,
GLsizei numPaths,
GLenum pathNameType,
const void *paths,
GLuint pathBase,
GLenum coverMode,
GLenum transformType,
const GLfloat *transformValues)
{
if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
transformType, transformValues))
return false;
switch (coverMode)
{
case GL_CONVEX_HULL_CHROMIUM:
case GL_BOUNDING_BOX_CHROMIUM:
case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid cover mode."));
return false;
}
return true;
}
bool ValidateCoverStrokePathInstanced(Context *context,
GLsizei numPaths,
GLenum pathNameType,
const void *paths,
GLuint pathBase,
GLenum coverMode,
GLenum transformType,
const GLfloat *transformValues)
{
if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
transformType, transformValues))
return false;
switch (coverMode)
{
case GL_CONVEX_HULL_CHROMIUM:
case GL_BOUNDING_BOX_CHROMIUM:
case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid cover mode."));
return false;
}
return true;
}
bool ValidateStencilFillPathInstanced(Context *context,
GLsizei numPaths,
GLenum pathNameType,
const void *paths,
GLuint pathBase,
GLenum fillMode,
GLuint mask,
GLenum transformType,
const GLfloat *transformValues)
{
if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
transformType, transformValues))
return false;
switch (fillMode)
{
case GL_COUNT_UP_CHROMIUM:
case GL_COUNT_DOWN_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid fill mode."));
return false;
}
if (!isPow2(mask + 1))
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid stencil bit mask."));
return false;
}
return true;
}
bool ValidateStencilStrokePathInstanced(Context *context,
GLsizei numPaths,
GLenum pathNameType,
const void *paths,
GLuint pathBase,
GLint reference,
GLuint mask,
GLenum transformType,
const GLfloat *transformValues)
{
if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
transformType, transformValues))
return false;
// no more validation here.
return true;
}
bool ValidateStencilThenCoverFillPathInstanced(Context *context,
GLsizei numPaths,
GLenum pathNameType,
const void *paths,
GLuint pathBase,
GLenum fillMode,
GLuint mask,
GLenum coverMode,
GLenum transformType,
const GLfloat *transformValues)
{
if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
transformType, transformValues))
return false;
switch (coverMode)
{
case GL_CONVEX_HULL_CHROMIUM:
case GL_BOUNDING_BOX_CHROMIUM:
case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid cover mode."));
return false;
}
switch (fillMode)
{
case GL_COUNT_UP_CHROMIUM:
case GL_COUNT_DOWN_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid fill mode."));
return false;
}
if (!isPow2(mask + 1))
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid stencil bit mask."));
return false;
}
return true;
}
bool ValidateStencilThenCoverStrokePathInstanced(Context *context,
GLsizei numPaths,
GLenum pathNameType,
const void *paths,
GLuint pathBase,
GLint reference,
GLuint mask,
GLenum coverMode,
GLenum transformType,
const GLfloat *transformValues)
{
if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
transformType, transformValues))
return false;
switch (coverMode)
{
case GL_CONVEX_HULL_CHROMIUM:
case GL_BOUNDING_BOX_CHROMIUM:
case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid cover mode."));
return false;
}
return true;
}
bool ValidateBindFragmentInputLocation(Context *context,
GLuint program,
GLint location,
const GLchar *name)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
const GLint MaxLocation = context->getCaps().maxVaryingVectors * 4;
if (location >= MaxLocation)
{
context->handleError(Error(GL_INVALID_VALUE, "Location exceeds max varying."));
return false;
}
const auto *programObject = context->getProgram(program);
if (!programObject)
{
context->handleError(Error(GL_INVALID_OPERATION, "No such program."));
return false;
}
if (!name)
{
context->handleError(Error(GL_INVALID_VALUE, "No name given."));
return false;
}
if (angle::BeginsWith(name, "gl_"))
{
context->handleError(Error(GL_INVALID_OPERATION, "Cannot bind a built-in variable."));
return false;
}
return true;
}
bool ValidateProgramPathFragmentInputGen(Context *context,
GLuint program,
GLint location,
GLenum genMode,
GLint components,
const GLfloat *coeffs)
{
if (!context->getExtensions().pathRendering)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
return false;
}
const auto *programObject = context->getProgram(program);
if (!programObject || programObject->isFlaggedForDeletion())
{
context->handleError(Error(GL_INVALID_OPERATION, "No such program."));
return false;
}
if (!programObject->isLinked())
{
context->handleError(Error(GL_INVALID_OPERATION, "Program is not linked."));
return false;
}
switch (genMode)
{
case GL_NONE:
if (components != 0)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid components."));
return false;
}
break;
case GL_OBJECT_LINEAR_CHROMIUM:
case GL_EYE_LINEAR_CHROMIUM:
case GL_CONSTANT_CHROMIUM:
if (components < 1 || components > 4)
{
context->handleError(Error(GL_INVALID_VALUE, "Invalid components."));
return false;
}
if (!coeffs)
{
context->handleError(Error(GL_INVALID_VALUE, "No coefficients array given."));
return false;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM, "Invalid gen mode."));
return false;
}
// If the location is -1 then the command is silently ignored
// and no further validation is needed.
if (location == -1)
return true;
const auto &binding = programObject->getFragmentInputBindingInfo(location);
if (!binding.valid)
{
context->handleError(Error(GL_INVALID_OPERATION, "No such binding."));
return false;
}
if (binding.type != GL_NONE)
{
GLint expectedComponents = 0;
switch (binding.type)
{
case GL_FLOAT:
expectedComponents = 1;
break;
case GL_FLOAT_VEC2:
expectedComponents = 2;
break;
case GL_FLOAT_VEC3:
expectedComponents = 3;
break;
case GL_FLOAT_VEC4:
expectedComponents = 4;
break;
default:
context->handleError(Error(GL_INVALID_OPERATION,
"Fragment input type is not a floating point scalar or vector."));
return false;
}
if (expectedComponents != components && genMode != GL_NONE)
{
context->handleError(Error(GL_INVALID_OPERATION, "Unexpected number of components"));
return false;
}
}
return true;
}
bool ValidateCopyTextureCHROMIUM(Context *context,
GLuint sourceId,
GLuint destId,
GLint internalFormat,
GLenum destType,
GLboolean unpackFlipY,
GLboolean unpackPremultiplyAlpha,
GLboolean unpackUnmultiplyAlpha)
{
if (!context->getExtensions().copyTexture)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_copy_texture extension not available."));
return false;
}
const gl::Texture *source = context->getTexture(sourceId);
if (source == nullptr)
{
context->handleError(
Error(GL_INVALID_VALUE, "Source texture is not a valid texture object."));
return false;
}
if (!IsValidCopyTextureSourceTarget(context, source->getTarget()))
{
context->handleError(Error(GL_INVALID_VALUE, "Source texture a valid texture type."));
return false;
}
GLenum sourceTarget = source->getTarget();
ASSERT(sourceTarget != GL_TEXTURE_CUBE_MAP);
if (source->getWidth(sourceTarget, 0) == 0 || source->getHeight(sourceTarget, 0) == 0)
{
context->handleError(
Error(GL_INVALID_VALUE, "Level 0 of the source texture must be defined."));
return false;
}
const gl::Format &sourceFormat = source->getFormat(sourceTarget, 0);
if (!IsValidCopyTextureFormat(context, sourceFormat.format))
{
context->handleError(
Error(GL_INVALID_OPERATION, "Source texture internal format is invalid."));
return false;
}
const gl::Texture *dest = context->getTexture(destId);
if (dest == nullptr)
{
context->handleError(
Error(GL_INVALID_VALUE, "Destination texture is not a valid texture object."));
return false;
}
if (!IsValidCopyTextureDestinationTarget(context, dest->getTarget()))
{
context->handleError(Error(GL_INVALID_VALUE, "Destination texture a valid texture type."));
return false;
}
if (!IsValidCopyTextureDestinationFormatType(context, internalFormat, destType))
{
context->handleError(
Error(GL_INVALID_OPERATION,
"Destination internal format and type combination is not valid."));
return false;
}
if (dest->getImmutableFormat())
{
context->handleError(Error(GL_INVALID_OPERATION, "Destination texture is immutable."));
return false;
}
return true;
}
bool ValidateCopySubTextureCHROMIUM(Context *context,
GLuint sourceId,
GLuint destId,
GLint xoffset,
GLint yoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLboolean unpackFlipY,
GLboolean unpackPremultiplyAlpha,
GLboolean unpackUnmultiplyAlpha)
{
if (!context->getExtensions().copyTexture)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_CHROMIUM_copy_texture extension not available."));
return false;
}
const gl::Texture *source = context->getTexture(sourceId);
if (source == nullptr)
{
context->handleError(
Error(GL_INVALID_VALUE, "Source texture is not a valid texture object."));
return false;
}
if (!IsValidCopyTextureSourceTarget(context, source->getTarget()))
{
context->handleError(Error(GL_INVALID_VALUE, "Source texture a valid texture type."));
return false;
}
GLenum sourceTarget = source->getTarget();
ASSERT(sourceTarget != GL_TEXTURE_CUBE_MAP);
if (source->getWidth(sourceTarget, 0) == 0 || source->getHeight(sourceTarget, 0) == 0)
{
context->handleError(
Error(GL_INVALID_VALUE, "Level 0 of the source texture must be defined."));
return false;
}
if (x < 0 || y < 0)
{
context->handleError(Error(GL_INVALID_VALUE, "x and y cannot be negative."));
return false;
}
if (width < 0 || height < 0)
{
context->handleError(Error(GL_INVALID_VALUE, "width and height cannot be negative."));
return false;
}
if (static_cast<size_t>(x + width) > source->getWidth(sourceTarget, 0) ||
static_cast<size_t>(y + height) > source->getHeight(sourceTarget, 0))
{
context->handleError(
Error(GL_INVALID_VALUE, "Source texture not large enough to copy from."));
return false;
}
const gl::Format &sourceFormat = source->getFormat(sourceTarget, 0);
if (!IsValidCopyTextureFormat(context, sourceFormat.format))
{
context->handleError(
Error(GL_INVALID_OPERATION, "Source texture internal format is invalid."));
return false;
}
const gl::Texture *dest = context->getTexture(destId);
if (dest == nullptr)
{
context->handleError(
Error(GL_INVALID_VALUE, "Destination texture is not a valid texture object."));
return false;
}
if (!IsValidCopyTextureDestinationTarget(context, dest->getTarget()))
{
context->handleError(Error(GL_INVALID_VALUE, "Destination texture a valid texture type."));
return false;
}
GLenum destTarget = dest->getTarget();
ASSERT(destTarget != GL_TEXTURE_CUBE_MAP);
if (dest->getWidth(sourceTarget, 0) == 0 || dest->getHeight(sourceTarget, 0) == 0)
{
context->handleError(
Error(GL_INVALID_VALUE, "Level 0 of the destination texture must be defined."));
return false;
}
const gl::Format &destFormat = dest->getFormat(destTarget, 0);
if (!IsValidCopyTextureDestinationFormatType(context, destFormat.format, destFormat.type))
{
context->handleError(
Error(GL_INVALID_OPERATION,
"Destination internal format and type combination is not valid."));
return false;
}
if (xoffset < 0 || yoffset < 0)
{
context->handleError(Error(GL_INVALID_VALUE, "xoffset and yoffset cannot be negative."));
return false;
}
if (static_cast<size_t>(xoffset + width) > dest->getWidth(destTarget, 0) ||
static_cast<size_t>(yoffset + height) > dest->getHeight(destTarget, 0))
{
context->handleError(
Error(GL_INVALID_VALUE, "Destination texture not large enough to copy to."));
return false;
}
return true;
}
bool ValidateCreateShader(Context *context, GLenum type)
{
switch (type)
{
case GL_VERTEX_SHADER:
case GL_FRAGMENT_SHADER:
break;
case GL_COMPUTE_SHADER:
if (context->getGLVersion().isES31())
{
break;
}
default:
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
return true;
}
bool ValidateBufferData(ValidationContext *context,
GLenum target,
GLsizeiptr size,
const GLvoid *data,
GLenum usage)
{
if (size < 0)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
switch (usage)
{
case GL_STREAM_DRAW:
case GL_STATIC_DRAW:
case GL_DYNAMIC_DRAW:
break;
case GL_STREAM_READ:
case GL_STREAM_COPY:
case GL_STATIC_READ:
case GL_STATIC_COPY:
case GL_DYNAMIC_READ:
case GL_DYNAMIC_COPY:
if (context->getClientMajorVersion() < 3)
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
break;
default:
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
if (!ValidBufferTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
Buffer *buffer = context->getGLState().getTargetBuffer(target);
if (!buffer)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
return true;
}
bool ValidateBufferSubData(ValidationContext *context,
GLenum target,
GLintptr offset,
GLsizeiptr size,
const GLvoid *data)
{
if (size < 0 || offset < 0)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
if (!ValidBufferTarget(context, target))
{
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
Buffer *buffer = context->getGLState().getTargetBuffer(target);
if (!buffer)
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
if (buffer->isMapped())
{
context->handleError(Error(GL_INVALID_OPERATION));
return false;
}
// Check for possible overflow of size + offset
angle::CheckedNumeric<size_t> checkedSize(size);
checkedSize += offset;
if (!checkedSize.IsValid())
{
context->handleError(Error(GL_OUT_OF_MEMORY));
return false;
}
if (size + offset > buffer->getSize())
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
return true;
}
bool ValidateEnableExtensionANGLE(ValidationContext *context, const GLchar *name)
{
if (!context->getExtensions().webglCompatibility)
{
context->handleError(
Error(GL_INVALID_OPERATION, "GL_ANGLE_webgl_compatibility is not available."));
return false;
}
const ExtensionInfoMap &extensionInfos = GetExtensionInfoMap();
auto extension = extensionInfos.find(name);
if (extension == extensionInfos.end() || !extension->second.Enableable)
{
context->handleError(Error(GL_INVALID_OPERATION, "Extension %s is not enableable.", name));
return false;
}
return true;
}
} // namespace gl