summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/libANGLE/validationES3.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/src/libANGLE/validationES3.cpp')
-rwxr-xr-xgfx/angle/src/libANGLE/validationES3.cpp2038
1 files changed, 2038 insertions, 0 deletions
diff --git a/gfx/angle/src/libANGLE/validationES3.cpp b/gfx/angle/src/libANGLE/validationES3.cpp
new file mode 100755
index 000000000..a4cb71e6a
--- /dev/null
+++ b/gfx/angle/src/libANGLE/validationES3.cpp
@@ -0,0 +1,2038 @@
+//
+// 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.
+//
+
+// validationES3.cpp: Validation functions for OpenGL ES 3.0 entry point parameters
+
+#include "libANGLE/validationES3.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "common/mathutil.h"
+#include "common/utilities.h"
+#include "libANGLE/validationES.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"
+
+using namespace angle;
+
+namespace gl
+{
+
+static bool ValidateTexImageFormatCombination(gl::Context *context, GLenum internalFormat, GLenum format, GLenum type)
+{
+ // 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.
+ const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat);
+ if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ // The type and format are valid if any supported internal format has that type and format
+ if (!ValidES3Format(format) || !ValidES3Type(type))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ // Check if this is a valid format combination to load texture data
+ if (!ValidES3FormatCombination(format, type, internalFormat))
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexImageParametersBase(Context *context,
+ GLenum 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 GLvoid *pixels)
+{
+ // Validate image size
+ if (!ValidImageSizeParameters(context, target, level, width, height, depth, isSubImage))
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ // Verify zero border
+ if (border != 0)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0 || zoffset < 0 ||
+ std::numeric_limits<GLsizei>::max() - xoffset < width ||
+ std::numeric_limits<GLsizei>::max() - yoffset < height ||
+ std::numeric_limits<GLsizei>::max() - zoffset < depth)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ const gl::Caps &caps = context->getCaps();
+
+ switch (target)
+ {
+ case 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;
+ }
+ break;
+
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ if (!isSubImage && width != height)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level))
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ case GL_TEXTURE_3D:
+ if (static_cast<GLuint>(width) > (caps.max3DTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.max3DTextureSize >> level) ||
+ static_cast<GLuint>(depth) > (caps.max3DTextureSize >> level))
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ case GL_TEXTURE_2D_ARRAY:
+ if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.max2DTextureSize >> level) ||
+ static_cast<GLuint>(depth) > caps.maxArrayTextureLayers)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ default:
+ 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 (texture->getImmutableFormat() && !isSubImage)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ // Validate texture formats
+ GLenum actualInternalFormat =
+ isSubImage ? texture->getFormat(target, level).asSized() : internalformat;
+ if (isSubImage && actualInternalFormat == GL_NONE)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Texture level does not exist."));
+ return false;
+ }
+
+ const gl::InternalFormat &actualFormatInfo = gl::GetInternalFormatInfo(actualInternalFormat);
+ if (isCompressed)
+ {
+ if (!actualFormatInfo.compressed)
+ {
+ 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;
+ }
+
+ if (!actualFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ if (target == GL_TEXTURE_3D)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidateTexImageFormatCombination(context, actualInternalFormat, format, type))
+ {
+ return false;
+ }
+
+ if (target == GL_TEXTURE_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL))
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+ }
+
+ // Validate sub image parameters
+ if (isSubImage)
+ {
+ if (isCompressed != actualFormatInfo.compressed)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ if (width == 0 || height == 0 || depth == 0)
+ {
+ return false;
+ }
+
+ if (xoffset < 0 || yoffset < 0 || zoffset < 0)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ 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->handleError(Error(GL_INVALID_VALUE));
+ 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->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ }
+
+ if (!ValidImageDataSize(context, target, width, height, 1, actualInternalFormat, type, pixels,
+ imageSize))
+ {
+ return false;
+ }
+
+ // Check for pixel unpack buffer related API errors
+ gl::Buffer *pixelUnpackBuffer = context->getGLState().getTargetBuffer(GL_PIXEL_UNPACK_BUFFER);
+ 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>(gl::GetTypeInfo(type).bytes);
+
+ if ((offset % dataBytesPerPixel) != 0)
+ {
+ context->handleError(
+ Error(GL_INVALID_OPERATION, "Reads would overflow the pixel unpack buffer."));
+ return false;
+ }
+ }
+
+ // ...the buffer object's data store is currently mapped.
+ if (pixelUnpackBuffer->isMapped())
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Pixel unpack buffer is mapped."));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateES3TexImage2DParameters(Context *context,
+ GLenum 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 GLvoid *pixels)
+{
+ if (!ValidTexture2DDestinationTarget(context, target))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateES3TexImageParametersBase(context, target, level, internalformat, isCompressed,
+ isSubImage, xoffset, yoffset, zoffset, width, height,
+ depth, border, format, type, imageSize, pixels);
+}
+
+bool ValidateES3TexImage3DParameters(Context *context,
+ GLenum 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 GLvoid *pixels)
+{
+ if (!ValidTexture3DDestinationTarget(context, target))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateES3TexImageParametersBase(context, 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.pixelBytes > 0)
+ {
+ 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 Format &textureFormat,
+ const Format &framebufferFormat,
+ GLuint readBufferHandle)
+{
+ const auto &textureFormatInfo = *textureFormat.info;
+ const auto &framebufferFormatInfo = *framebufferFormat.info;
+
+ 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 ||
+ textureFormatInfo.componentType == GL_FLOAT) &&
+ !(framebufferFormatInfo.componentType == GL_UNSIGNED_NORMALIZED ||
+ framebufferFormatInfo.componentType == GL_SIGNED_NORMALIZED ||
+ framebufferFormatInfo.componentType == GL_FLOAT))
+ {
+ 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 = NULL;
+ if (readBufferHandle != 0)
+ {
+ // Not the default framebuffer, therefore the read buffer must be a user-created texture or
+ // renderbuffer
+ if (framebufferFormat.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.
+ GLenum sizedInternalFormat =
+ GetSizedInternalFormat(framebufferFormatInfo.format, framebufferFormatInfo.type);
+ sourceEffectiveFormat = &GetInternalFormatInfo(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 = &GetInternalFormatInfo(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 (textureFormat.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 = &GetInternalFormatInfo(GL_SRGB8_ALPHA8);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ UNREACHABLE();
+ return false;
+ }
+ }
+
+ if (textureFormat.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(ValidationContext *context,
+ GLenum 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, target, level, internalformat, isSubImage,
+ xoffset, yoffset, zoffset, x, y, width, height, border,
+ &textureFormat))
+ {
+ return false;
+ }
+ ASSERT(textureFormat.valid() || !isSubImage);
+
+ const auto &state = context->getGLState();
+ gl::Framebuffer *framebuffer = state.getReadFramebuffer();
+ GLuint readFramebufferID = framebuffer->id();
+
+ if (framebuffer->checkStatus(context->getContextState()) != GL_FRAMEBUFFER_COMPLETE)
+ {
+ context->handleError(Error(GL_INVALID_FRAMEBUFFER_OPERATION));
+ return false;
+ }
+
+ if (readFramebufferID != 0 && framebuffer->getSamples(context->getContextState()) != 0)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ const FramebufferAttachment *source = framebuffer->getReadColorbuffer();
+
+ if (isSubImage)
+ {
+ if (!IsValidES3CopyTexImageCombination(textureFormat, source->getFormat(),
+ readFramebufferID))
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+ }
+ else
+ {
+ // Use format/type from the source FBO. (Might not be perfect for all cases?)
+ const auto framebufferFormat = source->getFormat();
+ Format copyFormat(internalformat, framebufferFormat.format, framebufferFormat.type);
+ if (!IsValidES3CopyTexImageCombination(copyFormat, framebufferFormat, readFramebufferID))
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ 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(ValidationContext *context,
+ GLenum 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->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateES3CopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
+ xoffset, yoffset, zoffset, x, y, width, height,
+ border);
+}
+
+bool ValidateES3CopyTexImage3DParameters(ValidationContext *context,
+ GLenum 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->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateES3CopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
+ xoffset, yoffset, zoffset, x, y, width, height,
+ border);
+}
+
+bool ValidateES3TexStorageParametersBase(Context *context,
+ GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (width < 1 || height < 1 || depth < 1 || levels < 1)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ GLsizei maxDim = std::max(width, height);
+ if (target != GL_TEXTURE_2D_ARRAY)
+ {
+ maxDim = std::max(maxDim, depth);
+ }
+
+ if (levels > gl::log2(maxDim) + 1)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ 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 (width != height)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ if (static_cast<GLuint>(width) > caps.maxCubeMapTextureSize)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ }
+ break;
+
+ case GL_TEXTURE_3D:
+ {
+ if (static_cast<GLuint>(width) > caps.max3DTextureSize ||
+ static_cast<GLuint>(height) > caps.max3DTextureSize ||
+ static_cast<GLuint>(depth) > caps.max3DTextureSize)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ }
+ break;
+
+ case GL_TEXTURE_2D_ARRAY:
+ {
+ if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
+ static_cast<GLuint>(height) > caps.max2DTextureSize ||
+ static_cast<GLuint>(depth) > caps.maxArrayTextureLayers)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ return false;
+ }
+
+ 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;
+ }
+
+ const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat);
+ if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ if (formatInfo.pixelBytes == 0)
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateES3TexStorage2DParameters(Context *context,
+ GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (!ValidTexture2DTarget(context, target))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateES3TexStorageParametersBase(context, target, levels, internalformat, width,
+ height, depth);
+}
+
+bool ValidateES3TexStorage3DParameters(Context *context,
+ GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (!ValidTexture3DTarget(context, target))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateES3TexStorageParametersBase(context, target, levels, internalformat, width,
+ height, depth);
+}
+
+bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
+ return false;
+ }
+
+ return ValidateBeginQueryBase(context, target, id);
+}
+
+bool ValidateEndQuery(gl::Context *context, GLenum target)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
+ return false;
+ }
+
+ return ValidateEndQueryBase(context, target);
+}
+
+bool ValidateGetQueryiv(Context *context, GLenum target, GLenum pname, GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
+ return false;
+ }
+
+ return ValidateGetQueryivBase(context, target, pname, nullptr);
+}
+
+bool ValidateGetQueryObjectuiv(Context *context, GLuint id, GLenum pname, GLuint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
+ return false;
+ }
+
+ return ValidateGetQueryObjectValueBase(context, id, pname, nullptr);
+}
+
+bool ValidateFramebufferTextureLayer(Context *context, GLenum target, GLenum attachment,
+ GLuint texture, GLint level, GLint layer)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ if (layer < 0)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
+ {
+ return false;
+ }
+
+ const gl::Caps &caps = context->getCaps();
+ if (texture != 0)
+ {
+ gl::Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ switch (tex->getTarget())
+ {
+ case GL_TEXTURE_2D_ARRAY:
+ {
+ if (level > gl::log2(caps.max2DTextureSize))
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ if (static_cast<GLuint>(layer) >= caps.maxArrayTextureLayers)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ }
+ break;
+
+ case GL_TEXTURE_3D:
+ {
+ if (level > gl::log2(caps.max3DTextureSize))
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ if (static_cast<GLuint>(layer) >= caps.max3DTextureSize)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ const auto &format = tex->getFormat(tex->getTarget(), level);
+ if (format.info->compressed)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateES3RenderbufferStorageParameters(gl::Context *context, GLenum target, GLsizei samples,
+ GLenum internalformat, GLsizei width, GLsizei height)
+{
+ if (!ValidateRenderbufferStorageParametersBase(context, target, samples, internalformat, width, height))
+ {
+ return false;
+ }
+
+ //The ES3 spec(section 4.4.2) states that the internal format must be sized and not an integer format if samples is greater than zero.
+ const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat);
+ if ((formatInfo.componentType == GL_UNSIGNED_INT || formatInfo.componentType == GL_INT) && samples > 0)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ // The behavior is different than the ANGLE version, which would generate a GL_OUT_OF_MEMORY.
+ const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
+ if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
+ {
+ context->handleError(
+ Error(GL_INVALID_OPERATION,
+ "Samples must not be greater than maximum supported value for the format."));
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateInvalidateFramebuffer(Context *context, GLenum target, GLsizei numAttachments,
+ const GLenum *attachments)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(
+ Error(GL_INVALID_OPERATION, "Operation only supported on ES 3.0 and above"));
+ return false;
+ }
+
+ bool defaultFramebuffer = false;
+
+ switch (target)
+ {
+ case GL_DRAW_FRAMEBUFFER:
+ case GL_FRAMEBUFFER:
+ defaultFramebuffer = context->getGLState().getDrawFramebuffer()->id() == 0;
+ break;
+ case GL_READ_FRAMEBUFFER:
+ defaultFramebuffer = context->getGLState().getReadFramebuffer()->id() == 0;
+ break;
+ default:
+ context->handleError(Error(GL_INVALID_ENUM, "Invalid framebuffer target"));
+ return false;
+ }
+
+ return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments, defaultFramebuffer);
+}
+
+bool ValidateClearBuffer(ValidationContext *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ if (context->getGLState().getDrawFramebuffer()->checkStatus(context->getContextState()) !=
+ GL_FRAMEBUFFER_COMPLETE)
+ {
+ context->handleError(Error(GL_INVALID_FRAMEBUFFER_OPERATION));
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawRangeElements(Context *context,
+ GLenum mode,
+ GLuint start,
+ GLuint end,
+ GLsizei count,
+ GLenum type,
+ const GLvoid *indices,
+ IndexRange *indexRange)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ return false;
+ }
+
+ if (end < start)
+ {
+ context->handleError(Error(GL_INVALID_VALUE, "end < start"));
+ return false;
+ }
+
+ if (!ValidateDrawElements(context, mode, count, type, indices, 0, indexRange))
+ {
+ return false;
+ }
+
+ if (indexRange->end > end || indexRange->start < start)
+ {
+ // GL spec says that behavior in this case is undefined - generating an error is fine.
+ context->handleError(
+ Error(GL_INVALID_OPERATION, "Indices are out of the start, end range."));
+ return false;
+ }
+ return true;
+}
+
+bool ValidateGetUniformuiv(Context *context, GLuint program, GLint location, GLuint* params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateGetUniformBase(context, program, location);
+}
+
+bool ValidateReadBuffer(Context *context, GLenum src)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ const Framebuffer *readFBO = context->getGLState().getReadFramebuffer();
+
+ if (readFBO == nullptr)
+ {
+ context->handleError(gl::Error(GL_INVALID_OPERATION, "No active read framebuffer."));
+ return false;
+ }
+
+ if (src == GL_NONE)
+ {
+ return true;
+ }
+
+ if (src != GL_BACK && (src < GL_COLOR_ATTACHMENT0 || src > GL_COLOR_ATTACHMENT31))
+ {
+ context->handleError(gl::Error(GL_INVALID_ENUM, "Unknown enum for 'src' in ReadBuffer"));
+ return false;
+ }
+
+ if (readFBO->id() == 0)
+ {
+ if (src != GL_BACK)
+ {
+ const char *errorMsg = "'src' must be GL_NONE or GL_BACK when reading from the default framebuffer.";
+ context->handleError(gl::Error(GL_INVALID_OPERATION, errorMsg));
+ return false;
+ }
+ }
+ else
+ {
+ GLuint drawBuffer = static_cast<GLuint>(src - GL_COLOR_ATTACHMENT0);
+
+ if (drawBuffer >= context->getCaps().maxDrawBuffers)
+ {
+ const char *errorMsg = "'src' is greater than MAX_DRAW_BUFFERS.";
+ context->handleError(gl::Error(GL_INVALID_OPERATION, errorMsg));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateCompressedTexImage3D(Context *context,
+ GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLsizei imageSize,
+ const GLvoid *data)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ if (!ValidTextureTarget(context, target))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ // Validate image size
+ if (!ValidImageSizeParameters(context, target, level, width, height, depth, false))
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetInternalFormatInfo(internalformat);
+ if (!formatInfo.compressed)
+ {
+ context->handleError(Error(GL_INVALID_ENUM, "Not a valid compressed texture format"));
+ return false;
+ }
+
+ auto blockSizeOrErr =
+ formatInfo.computeCompressedImageSize(gl::Extents(width, height, depth));
+ if (blockSizeOrErr.isError())
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSizeOrErr.getResult())
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ // 3D texture target validation
+ if (target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY)
+ {
+ context->handleError(
+ Error(GL_INVALID_ENUM, "Must specify a valid 3D texture destination target"));
+ return false;
+ }
+
+ // validateES3TexImageFormat sets the error code if there is an error
+ if (!ValidateES3TexImage3DParameters(context, target, level, internalformat, true, false, 0, 0,
+ 0, width, height, depth, border, GL_NONE, GL_NONE, -1,
+ data))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindVertexArray(Context *context, GLuint array)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateBindVertexArrayBase(context, array);
+}
+
+bool ValidateIsVertexArray(Context *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateProgramBinary(Context *context,
+ GLuint program,
+ GLenum binaryFormat,
+ const void *binary,
+ GLint length)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateProgramBinaryBase(context, program, binaryFormat, binary, length);
+}
+
+bool ValidateGetProgramBinary(Context *context,
+ GLuint program,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLenum *binaryFormat,
+ void *binary)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary);
+}
+
+bool ValidateProgramParameteri(Context *context, GLuint program, GLenum pname, GLint value)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ return false;
+ }
+
+ if (GetValidProgram(context, program) == nullptr)
+ {
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
+ if (value != GL_FALSE && value != GL_TRUE)
+ {
+ context->handleError(Error(
+ GL_INVALID_VALUE, "Invalid value, expected GL_FALSE or GL_TRUE: %i", value));
+ return false;
+ }
+ break;
+
+ default:
+ context->handleError(Error(GL_INVALID_ENUM, "Invalid pname: 0x%X", pname));
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBlitFramebuffer(Context *context,
+ GLint srcX0,
+ GLint srcY0,
+ GLint srcX1,
+ GLint srcY1,
+ GLint dstX0,
+ GLint dstY0,
+ GLint dstX1,
+ GLint dstY1,
+ GLbitfield mask,
+ GLenum filter)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0,
+ dstX1, dstY1, mask, filter);
+}
+
+bool ValidateClearBufferiv(ValidationContext *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLint *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 ||
+ static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ case GL_STENCIL:
+ if (drawbuffer != 0)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ default:
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateClearBuffer(context);
+}
+
+bool ValidateClearBufferuiv(ValidationContext *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLuint *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 ||
+ static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ default:
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateClearBuffer(context);
+}
+
+bool ValidateClearBufferfv(ValidationContext *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLfloat *value)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ if (drawbuffer < 0 ||
+ static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ case GL_DEPTH:
+ if (drawbuffer != 0)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ default:
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateClearBuffer(context);
+}
+
+bool ValidateClearBufferfi(ValidationContext *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ GLfloat depth,
+ GLint stencil)
+{
+ switch (buffer)
+ {
+ case GL_DEPTH_STENCIL:
+ if (drawbuffer != 0)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ default:
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ return ValidateClearBuffer(context);
+}
+
+bool ValidateDrawBuffers(ValidationContext *context, GLsizei n, const GLenum *bufs)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ return false;
+ }
+
+ return ValidateDrawBuffersBase(context, n, bufs);
+}
+
+bool ValidateCopyTexSubImage3D(Context *context,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateES3CopyTexImage3DParameters(context, target, level, GL_NONE, true, xoffset,
+ yoffset, zoffset, x, y, width, height, 0);
+}
+
+bool ValidateTexImage3D(Context *context,
+ GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const GLvoid *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, internalformat, false, false, 0,
+ 0, 0, width, height, depth, border, format, type, -1,
+ pixels);
+}
+
+bool ValidateTexImage3DRobustANGLE(Context *context,
+ GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const GLvoid *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, internalformat, false, false, 0,
+ 0, 0, width, height, depth, border, format, type,
+ bufSize, pixels);
+}
+
+bool ValidateTexSubImage3D(Context *context,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLenum type,
+ const GLvoid *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, GL_NONE, false, true, xoffset,
+ yoffset, zoffset, width, height, depth, 0, format, type,
+ -1, pixels);
+}
+
+bool ValidateTexSubImage3DRobustANGLE(Context *context,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLenum type,
+ GLsizei bufSize,
+ const GLvoid *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, GL_NONE, false, true, xoffset,
+ yoffset, zoffset, width, height, depth, 0, format, type,
+ bufSize, pixels);
+}
+
+bool ValidateCompressedTexSubImage3D(Context *context,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLsizei imageSize,
+ const GLvoid *data)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetInternalFormatInfo(format);
+ auto blockSizeOrErr =
+ formatInfo.computeCompressedImageSize(gl::Extents(width, height, depth));
+ 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;
+ }
+
+ if (!data)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, GL_NONE, true, true, 0, 0, 0,
+ width, height, depth, 0, GL_NONE, GL_NONE, -1, data);
+}
+
+bool ValidateGenQueries(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateDeleteQueries(Context *context, GLint n, const GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateGenSamplers(Context *context, GLint count, GLuint *)
+{
+ return ValidateGenOrDeleteCountES3(context, count);
+}
+
+bool ValidateDeleteSamplers(Context *context, GLint count, const GLuint *)
+{
+ return ValidateGenOrDeleteCountES3(context, count);
+}
+
+bool ValidateGenTransformFeedbacks(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateDeleteTransformFeedbacks(Context *context, GLint n, const GLuint *ids)
+{
+ if (!ValidateGenOrDeleteES3(context, n))
+ {
+ return false;
+ }
+ for (GLint i = 0; i < n; ++i)
+ {
+ auto *transformFeedback = context->getTransformFeedback(ids[i]);
+ if (transformFeedback != nullptr && transformFeedback->isActive())
+ {
+ // ES 3.0.4 section 2.15.1 page 86
+ context->handleError(
+ Error(GL_INVALID_OPERATION, "Attempt to delete active transform feedback."));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ValidateGenVertexArrays(Context *context, GLint n, GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateDeleteVertexArrays(Context *context, GLint n, const GLuint *)
+{
+ return ValidateGenOrDeleteES3(context, n);
+}
+
+bool ValidateGenOrDeleteES3(Context *context, GLint n)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ return false;
+ }
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenOrDeleteCountES3(Context *context, GLint count)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ return false;
+ }
+ if (count < 0)
+ {
+ context->handleError(Error(GL_INVALID_VALUE, "count < 0"));
+ return false;
+ }
+ return true;
+}
+
+bool ValidateBeginTransformFeedback(Context *context, GLenum primitiveMode)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ return false;
+ }
+ switch (primitiveMode)
+ {
+ case GL_TRIANGLES:
+ case GL_LINES:
+ case GL_POINTS:
+ break;
+
+ default:
+ context->handleError(Error(GL_INVALID_ENUM, "Invalid primitive mode."));
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ if (transformFeedback->isActive())
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Transform feedback is already active."));
+ return false;
+ }
+ return true;
+}
+
+bool ValidateGetBufferPointerv(Context *context, GLenum target, GLenum pname, GLvoid **params)
+{
+ return ValidateGetBufferPointervBase(context, target, pname, nullptr, params);
+}
+
+bool ValidateGetBufferPointervRobustANGLE(Context *context,
+ GLenum target,
+ GLenum pname,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLvoid **params)
+{
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ if (!ValidateGetBufferPointervBase(context, target, pname, length, params))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, bufSize, *length))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateUnmapBuffer(Context *context, GLenum target)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION));
+ return false;
+ }
+
+ return ValidateUnmapBufferBase(context, target);
+}
+
+bool ValidateMapBufferRange(Context *context,
+ GLenum target,
+ GLintptr offset,
+ GLsizeiptr length,
+ GLbitfield access)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ return false;
+ }
+
+ return ValidateMapBufferRangeBase(context, target, offset, length, access);
+}
+
+bool ValidateFlushMappedBufferRange(Context *context,
+ GLenum target,
+ GLintptr offset,
+ GLsizeiptr length)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ return false;
+ }
+
+ return ValidateFlushMappedBufferRangeBase(context, target, offset, length);
+}
+
+bool ValidateIndexedStateQuery(ValidationContext *context,
+ GLenum pname,
+ GLuint index,
+ GLsizei *length)
+{
+ if (length)
+ {
+ *length = 0;
+ }
+
+ GLenum nativeType;
+ unsigned int numParams;
+ if (!context->getIndexedQueryParameterInfo(pname, &nativeType, &numParams))
+ {
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ switch (pname)
+ {
+ case GL_TRANSFORM_FEEDBACK_BUFFER_START:
+ case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
+ case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
+ if (index >= caps.maxTransformFeedbackSeparateAttributes)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+
+ case GL_UNIFORM_BUFFER_START:
+ case GL_UNIFORM_BUFFER_SIZE:
+ case GL_UNIFORM_BUFFER_BINDING:
+ if (index >= caps.maxUniformBufferBindings)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+ case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
+ case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
+ if (index >= 3u)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ break;
+ default:
+ context->handleError(Error(GL_INVALID_ENUM));
+ return false;
+ }
+
+ if (length)
+ {
+ *length = 1;
+ }
+
+ return true;
+}
+
+bool ValidateGetIntegeri_v(ValidationContext *context, GLenum target, GLuint index, GLint *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3.0"));
+ return false;
+ }
+ return ValidateIndexedStateQuery(context, target, index, nullptr);
+}
+
+bool ValidateGetIntegeri_vRobustANGLE(ValidationContext *context,
+ GLenum target,
+ GLuint index,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLint *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3.0"));
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ if (!ValidateIndexedStateQuery(context, target, index, length))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, bufSize, *length))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetInteger64i_v(ValidationContext *context, GLenum target, GLuint index, GLint64 *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3.0"));
+ return false;
+ }
+ return ValidateIndexedStateQuery(context, target, index, nullptr);
+}
+
+bool ValidateGetInteger64i_vRobustANGLE(ValidationContext *context,
+ GLenum target,
+ GLuint index,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLint64 *data)
+{
+ if (context->getClientVersion() < ES_3_0)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "Context does not support GLES3.0"));
+ return false;
+ }
+
+ if (!ValidateRobustEntryPoint(context, bufSize))
+ {
+ return false;
+ }
+
+ if (!ValidateIndexedStateQuery(context, target, index, length))
+ {
+ return false;
+ }
+
+ if (!ValidateRobustBufferSize(context, bufSize, *length))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateCopyBufferSubData(ValidationContext *context,
+ GLenum readTarget,
+ GLenum writeTarget,
+ GLintptr readOffset,
+ GLintptr writeOffset,
+ GLsizeiptr size)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ context->handleError(
+ Error(GL_INVALID_OPERATION, "CopyBufferSubData requires ES 3 or greater"));
+ return false;
+ }
+
+ if (!ValidBufferTarget(context, readTarget) || !ValidBufferTarget(context, writeTarget))
+ {
+ context->handleError(Error(GL_INVALID_ENUM, "Invalid buffer target"));
+ return false;
+ }
+
+ Buffer *readBuffer = context->getGLState().getTargetBuffer(readTarget);
+ Buffer *writeBuffer = context->getGLState().getTargetBuffer(writeTarget);
+
+ if (!readBuffer || !writeBuffer)
+ {
+ context->handleError(Error(GL_INVALID_OPERATION, "No buffer bound to target"));
+ return false;
+ }
+
+ // Verify that readBuffer and writeBuffer are not currently mapped
+ if (readBuffer->isMapped() || writeBuffer->isMapped())
+ {
+ context->handleError(
+ Error(GL_INVALID_OPERATION, "Cannot call CopyBufferSubData on a mapped buffer"));
+ return false;
+ }
+
+ CheckedNumeric<GLintptr> checkedReadOffset(readOffset);
+ CheckedNumeric<GLintptr> checkedWriteOffset(writeOffset);
+ CheckedNumeric<GLintptr> checkedSize(size);
+
+ auto checkedReadSum = checkedReadOffset + checkedSize;
+ auto checkedWriteSum = checkedWriteOffset + checkedSize;
+
+ if (!checkedReadSum.IsValid() || !checkedWriteSum.IsValid() ||
+ !IsValueInRangeForNumericType<GLintptr>(readBuffer->getSize()) ||
+ !IsValueInRangeForNumericType<GLintptr>(writeBuffer->getSize()))
+ {
+ context->handleError(
+ Error(GL_INVALID_VALUE, "Integer overflow when validating copy offsets."));
+ return false;
+ }
+
+ if (readOffset < 0 || writeOffset < 0 || size < 0)
+ {
+ context->handleError(
+ Error(GL_INVALID_VALUE, "readOffset, writeOffset and size must all be non-negative"));
+ return false;
+ }
+
+ if (checkedReadSum.ValueOrDie() > readBuffer->getSize() ||
+ checkedWriteSum.ValueOrDie() > writeBuffer->getSize())
+ {
+ context->handleError(
+ Error(GL_INVALID_VALUE, "Buffer offset overflow in CopyBufferSubData"));
+ return false;
+ }
+
+ if (readBuffer == writeBuffer)
+ {
+ auto checkedOffsetDiff = (checkedReadOffset - checkedWriteOffset).Abs();
+ if (!checkedOffsetDiff.IsValid())
+ {
+ // This shold not be possible.
+ UNREACHABLE();
+ context->handleError(
+ Error(GL_INVALID_VALUE, "Integer overflow when validating same buffer copy."));
+ return false;
+ }
+
+ if (checkedOffsetDiff.ValueOrDie() < size)
+ {
+ context->handleError(Error(GL_INVALID_VALUE));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace gl