summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/libANGLE/validationES3.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/angle/src/libANGLE/validationES3.cpp')
-rw-r--r--src/3rdparty/angle/src/libANGLE/validationES3.cpp3580
1 files changed, 2749 insertions, 831 deletions
diff --git a/src/3rdparty/angle/src/libANGLE/validationES3.cpp b/src/3rdparty/angle/src/libANGLE/validationES3.cpp
index 2db64ec4cc..1aadfc876e 100644
--- a/src/3rdparty/angle/src/libANGLE/validationES3.cpp
+++ b/src/3rdparty/angle/src/libANGLE/validationES3.cpp
@@ -7,255 +7,190 @@
// validationES3.cpp: Validation functions for OpenGL ES 3.0 entry point parameters
#include "libANGLE/validationES3.h"
-#include "libANGLE/validationES.h"
+
+#include "anglebase/numerics/safe_conversions.h"
+#include "common/mathutil.h"
+#include "common/utilities.h"
#include "libANGLE/Context.h"
-#include "libANGLE/Texture.h"
+#include "libANGLE/ErrorStrings.h"
#include "libANGLE/Framebuffer.h"
+#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Renderbuffer.h"
+#include "libANGLE/Texture.h"
#include "libANGLE/formatutils.h"
-#include "libANGLE/FramebufferAttachment.h"
+#include "libANGLE/validationES.h"
-#include "common/mathutil.h"
-#include "common/utilities.h"
+using namespace angle;
namespace gl
{
-struct ES3FormatCombination
+namespace
{
- GLenum internalFormat;
- GLenum format;
- GLenum type;
-};
+bool ValidateFramebufferTextureMultiviewBaseANGLE(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLsizei numViews)
+{
+ if (!context->getExtensions().multiview)
+ {
+ context->handleError(InvalidOperation() << "ANGLE_multiview is not available.");
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
+ {
+ return false;
+ }
-bool operator<(const ES3FormatCombination& a, const ES3FormatCombination& b)
-{
- return memcmp(&a, &b, sizeof(ES3FormatCombination)) < 0;
-}
-
-typedef std::set<ES3FormatCombination> ES3FormatCombinationSet;
-
-static inline void InsertES3FormatCombo(ES3FormatCombinationSet *set, GLenum internalFormat, GLenum format, GLenum type)
-{
- ES3FormatCombination info;
- info.internalFormat = internalFormat;
- info.format = format;
- info.type = type;
- set->insert(info);
-}
-
-ES3FormatCombinationSet BuildES3FormatSet()
-{
- ES3FormatCombinationSet set;
-
- // Format combinations from ES 3.0.1 spec, table 3.2
-
- // | Internal format | Format | Type |
- // | | | |
- InsertES3FormatCombo(&set, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGBA8_SNORM, GL_RGBA, GL_BYTE );
- InsertES3FormatCombo(&set, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 );
- InsertES3FormatCombo(&set, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV );
- InsertES3FormatCombo(&set, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV );
- InsertES3FormatCombo(&set, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 );
- InsertES3FormatCombo(&set, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_RGBA32F, GL_RGBA, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RGBA16F, GL_RGBA, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE );
- InsertES3FormatCombo(&set, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT );
- InsertES3FormatCombo(&set, GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT );
- InsertES3FormatCombo(&set, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT );
- InsertES3FormatCombo(&set, GL_RGBA32I, GL_RGBA_INTEGER, GL_INT );
- InsertES3FormatCombo(&set, GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV );
- InsertES3FormatCombo(&set, GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGB565, GL_RGB, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_SRGB8, GL_RGB, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGB8_SNORM, GL_RGB, GL_BYTE );
- InsertES3FormatCombo(&set, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5 );
- InsertES3FormatCombo(&set, GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV );
- InsertES3FormatCombo(&set, GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV );
- InsertES3FormatCombo(&set, GL_RGB16F, GL_RGB, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_RGB16F, GL_RGB, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_R11F_G11F_B10F, GL_RGB, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_R11F_G11F_B10F, GL_RGB, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_RGB9_E5, GL_RGB, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_RGB9_E5, GL_RGB, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_RGB32F, GL_RGB, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RGB16F, GL_RGB, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_R11F_G11F_B10F, GL_RGB, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RGB9_E5, GL_RGB, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGB8I, GL_RGB_INTEGER, GL_BYTE );
- InsertES3FormatCombo(&set, GL_RGB16UI, GL_RGB_INTEGER, GL_UNSIGNED_SHORT );
- InsertES3FormatCombo(&set, GL_RGB16I, GL_RGB_INTEGER, GL_SHORT );
- InsertES3FormatCombo(&set, GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT );
- InsertES3FormatCombo(&set, GL_RGB32I, GL_RGB_INTEGER, GL_INT );
- InsertES3FormatCombo(&set, GL_RG8, GL_RG, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RG8_SNORM, GL_RG, GL_BYTE );
- InsertES3FormatCombo(&set, GL_RG16F, GL_RG, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_RG16F, GL_RG, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_RG32F, GL_RG, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RG16F, GL_RG, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RG8I, GL_RG_INTEGER, GL_BYTE );
- InsertES3FormatCombo(&set, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT );
- InsertES3FormatCombo(&set, GL_RG16I, GL_RG_INTEGER, GL_SHORT );
- InsertES3FormatCombo(&set, GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT );
- InsertES3FormatCombo(&set, GL_RG32I, GL_RG_INTEGER, GL_INT );
- InsertES3FormatCombo(&set, GL_R8, GL_RED, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_R8_SNORM, GL_RED, GL_BYTE );
- InsertES3FormatCombo(&set, GL_R16F, GL_RED, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_R16F, GL_RED, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_R32F, GL_RED, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_R16F, GL_RED, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_R8I, GL_RED_INTEGER, GL_BYTE );
- InsertES3FormatCombo(&set, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT );
- InsertES3FormatCombo(&set, GL_R16I, GL_RED_INTEGER, GL_SHORT );
- InsertES3FormatCombo(&set, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT );
- InsertES3FormatCombo(&set, GL_R32I, GL_RED_INTEGER, GL_INT );
-
- // Unsized formats
- InsertES3FormatCombo(&set, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 );
- InsertES3FormatCombo(&set, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 );
- InsertES3FormatCombo(&set, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5 );
- InsertES3FormatCombo(&set, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_SRGB_ALPHA_EXT, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_SRGB_EXT, GL_SRGB_EXT, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RG, GL_RG, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RG, GL_RG, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RG, GL_RG, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_RG, GL_RG, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_RED, GL_RED, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_RED, GL_RED, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RED, GL_RED, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_RED, GL_RED, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_DEPTH_STENCIL, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 );
-
- // Depth stencil formats
- InsertES3FormatCombo(&set, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT );
- InsertES3FormatCombo(&set, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT );
- InsertES3FormatCombo(&set, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT );
- InsertES3FormatCombo(&set, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 );
- InsertES3FormatCombo(&set, GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV);
-
- // From GL_EXT_sRGB
- InsertES3FormatCombo(&set, GL_SRGB8_ALPHA8_EXT, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_SRGB8, GL_SRGB_EXT, GL_UNSIGNED_BYTE );
-
- // From GL_OES_texture_float
- InsertES3FormatCombo(&set, GL_RGBA, GL_RGBA, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_RGB, GL_RGB, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_LUMINANCE, GL_LUMINANCE, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_ALPHA, GL_ALPHA, GL_FLOAT );
-
- // From GL_OES_texture_half_float
- InsertES3FormatCombo(&set, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_LUMINANCE, GL_LUMINANCE, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_LUMINANCE, GL_LUMINANCE, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_ALPHA, GL_ALPHA, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_ALPHA, GL_ALPHA, GL_HALF_FLOAT_OES );
-
- // From GL_EXT_texture_format_BGRA8888
- InsertES3FormatCombo(&set, GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE );
-
- // From GL_EXT_texture_storage
- // | Internal format | Format | Type |
- // | | | |
- InsertES3FormatCombo(&set, GL_ALPHA8_EXT, GL_ALPHA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_LUMINANCE8_EXT, GL_LUMINANCE, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_LUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_ALPHA32F_EXT, GL_ALPHA, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_LUMINANCE32F_EXT, GL_LUMINANCE, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_LUMINANCE_ALPHA32F_EXT, GL_LUMINANCE_ALPHA, GL_FLOAT );
- InsertES3FormatCombo(&set, GL_ALPHA16F_EXT, GL_ALPHA, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_ALPHA16F_EXT, GL_ALPHA, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_LUMINANCE16F_EXT, GL_LUMINANCE, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_LUMINANCE16F_EXT, GL_LUMINANCE, GL_HALF_FLOAT_OES );
- InsertES3FormatCombo(&set, GL_LUMINANCE_ALPHA16F_EXT, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT );
- InsertES3FormatCombo(&set, GL_LUMINANCE_ALPHA16F_EXT, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT_OES );
-
- // From GL_EXT_texture_storage and GL_EXT_texture_format_BGRA8888
- InsertES3FormatCombo(&set, GL_BGRA8_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_BGRA4_ANGLEX, GL_BGRA_EXT, GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT);
- InsertES3FormatCombo(&set, GL_BGRA4_ANGLEX, GL_BGRA_EXT, GL_UNSIGNED_BYTE );
- InsertES3FormatCombo(&set, GL_BGR5_A1_ANGLEX, GL_BGRA_EXT, GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT);
- InsertES3FormatCombo(&set, GL_BGR5_A1_ANGLEX, GL_BGRA_EXT, GL_UNSIGNED_BYTE );
-
- // From GL_ANGLE_depth_texture
- InsertES3FormatCombo(&set, GL_DEPTH_COMPONENT32_OES, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT_24_8_OES );
-
- return set;
-}
-
-static bool ValidateTexImageFormatCombination(gl::Context *context, GLenum internalFormat, GLenum format, GLenum type)
+ if (texture != 0 && numViews < 1)
+ {
+ context->handleError(InvalidValue() << "numViews cannot be less than 1.");
+ return false;
+ }
+
+ const Extensions &extensions = context->getExtensions();
+ if (static_cast<GLuint>(numViews) > extensions.maxViews)
+ {
+ context->handleError(InvalidValue()
+ << "numViews cannot be greater than GL_MAX_VIEWS_ANGLE.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferTextureMultiviewLevelAndFormat(Context *context,
+ Texture *texture,
+ GLint level)
{
- // 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()))
+ GLenum texTarget = texture->getTarget();
+ if (!ValidMipLevel(context, texTarget, level))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
+ return false;
+ }
+
+ const auto &format = texture->getFormat(texTarget, level);
+ if (format.info->compressed)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), CompressedTexturesNotAttachable);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateUniformES3(Context *context, GLenum uniformType, GLint location, GLint count)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateUniform(context, uniformType, location, count);
+}
+
+bool ValidateUniformMatrixES3(Context *context,
+ GLenum valueType,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose)
+{
+ // Check for ES3 uniform entry points
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateUniformMatrix(context, valueType, location, count, transpose);
+}
+
+bool ValidateGenOrDeleteES3(Context *context, GLint n)
+{
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
+ return ValidateGenOrDelete(context, n);
+}
+
+bool ValidateGenOrDeleteCountES3(Context *context, GLint count)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+ if (count < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeCount);
+ return false;
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+static bool ValidateTexImageFormatCombination(gl::Context *context,
+ GLenum target,
+ GLenum internalFormat,
+ GLenum format,
+ GLenum type)
+{
// The type and format are valid if any supported internal format has that type and format
- bool formatSupported = false;
- bool typeSupported = false;
+ if (!ValidES3Format(format))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFormat);
+ return false;
+ }
- static const ES3FormatCombinationSet es3FormatSet = BuildES3FormatSet();
- for (ES3FormatCombinationSet::const_iterator i = es3FormatSet.begin(); i != es3FormatSet.end(); i++)
+ if (!ValidES3Type(type))
{
- if (i->format == format || i->type == type)
- {
- const gl::InternalFormat &info = gl::GetInternalFormatInfo(i->internalFormat);
- bool supported = info.textureSupport(context->getClientVersion(), context->getExtensions());
- if (supported && i->type == type)
- {
- typeSupported = true;
- }
- if (supported && i->format == format)
- {
- formatSupported = true;
- }
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidType);
+ return false;
+ }
- // Early-out if both type and format are supported now
- if (typeSupported && formatSupported)
- {
- break;
- }
- }
+ // For historical reasons, glTexImage2D and glTexImage3D pass in their internal format as a
+ // GLint instead of a GLenum. Therefor an invalid internal format gives a GL_INVALID_VALUE
+ // error instead of a GL_INVALID_ENUM error. As this validation function is only called in
+ // the validation codepaths for glTexImage2D/3D, we record a GL_INVALID_VALUE error.
+ if (!ValidES3InternalFormat(internalFormat))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidInternalFormat);
+ return false;
}
- if (!typeSupported || !formatSupported)
+ // From the ES 3.0 spec section 3.8.3:
+ // Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL are supported by
+ // texture image specification commands only if target is TEXTURE_2D, TEXTURE_2D_ARRAY, or
+ // TEXTURE_CUBE_MAP.Using these formats in conjunction with any other target will result in an
+ // INVALID_OPERATION error.
+ if (target == GL_TEXTURE_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidOperation() << "Format cannot be GL_DEPTH_COMPONENT or "
+ "GL_DEPTH_STENCIL if target is "
+ "GL_TEXTURE_3D");
return false;
}
// Check if this is a valid format combination to load texture data
- ES3FormatCombination searchFormat;
- searchFormat.internalFormat = internalFormat;
- searchFormat.format = format;
- searchFormat.type = type;
+ if (!ValidES3FormatCombination(format, type, internalFormat))
+ {
+ context->handleError(InvalidOperation()
+ << "Invalid combination of format, type and internalFormat.");
+ return false;
+ }
- if (es3FormatSet.find(searchFormat) == es3FormatSet.end())
+ const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type);
+ if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation() << "Unsupported internal format.");
return false;
}
@@ -277,19 +212,20 @@ bool ValidateES3TexImageParametersBase(Context *context,
GLint border,
GLenum format,
GLenum type,
- const GLvoid *pixels)
+ GLsizei imageSize,
+ const void *pixels)
{
// Validate image size
if (!ValidImageSizeParameters(context, target, level, width, height, depth, isSubImage))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
// Verify zero border
if (border != 0)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
@@ -298,7 +234,7 @@ bool ValidateES3TexImageParametersBase(Context *context,
std::numeric_limits<GLsizei>::max() - yoffset < height ||
std::numeric_limits<GLsizei>::max() - zoffset < depth)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
@@ -306,112 +242,158 @@ bool ValidateES3TexImageParametersBase(Context *context,
switch (target)
{
- case GL_TEXTURE_2D:
- if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
- static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
- {
- context->recordError(Error(GL_INVALID_VALUE));
- return false;
- }
- break;
+ case GL_TEXTURE_2D:
+ if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
+ static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
+ {
+ context->handleError(InvalidValue());
+ 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->recordError(Error(GL_INVALID_VALUE));
- return false;
- }
+ case GL_TEXTURE_RECTANGLE_ANGLE:
+ ASSERT(level == 0);
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ if (isCompressed)
+ {
+ context->handleError(InvalidEnum()
+ << "Rectangle texture cannot have a compressed format.");
+ return false;
+ }
+ break;
- if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level))
- {
- context->recordError(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(InvalidValue());
+ return false;
+ }
- 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->recordError(Error(GL_INVALID_VALUE));
- return false;
- }
- break;
+ if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level))
+ {
+ context->handleError(InvalidValue());
+ 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->recordError(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(InvalidValue());
+ return false;
+ }
+ break;
- default:
- context->recordError(Error(GL_INVALID_ENUM));
- return false;
+ 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(InvalidValue());
+ return false;
+ }
+ break;
+
+ default:
+ context->handleError(InvalidEnum());
+ return false;
}
- gl::Texture *texture = context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target);
+ gl::Texture *texture =
+ context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target);
if (!texture)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
if (texture->getImmutableFormat() && !isSubImage)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
// Validate texture formats
- GLenum actualInternalFormat = isSubImage ? texture->getInternalFormat(target, level) : internalformat;
- const gl::InternalFormat &actualFormatInfo = gl::GetInternalFormatInfo(actualInternalFormat);
+ GLenum actualInternalFormat =
+ isSubImage ? texture->getFormat(target, level).info->internalFormat : internalformat;
+ if (isSubImage && actualInternalFormat == GL_NONE)
+ {
+ context->handleError(InvalidOperation() << "Texture level does not exist.");
+ return false;
+ }
+
+ const gl::InternalFormat &actualFormatInfo = isSubImage
+ ? *texture->getFormat(target, level).info
+ : GetInternalFormatInfo(internalformat, type);
if (isCompressed)
{
if (!actualFormatInfo.compressed)
{
- context->recordError(Error(
- GL_INVALID_ENUM, "internalformat is not a supported compressed internal format."));
+ context->handleError(
+ InvalidEnum() << "internalformat is not a supported compressed internal format.");
return false;
}
- if (!ValidCompressedImageSize(context, actualInternalFormat, width, height))
+ if (isSubImage)
{
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
+ if (!ValidCompressedSubImageSize(
+ context, actualFormatInfo.internalFormat, xoffset, yoffset, width, height,
+ texture->getWidth(target, level), texture->getHeight(target, level)))
+ {
+ context->handleError(InvalidOperation() << "Invalid compressed format dimension.");
+ return false;
+ }
+
+ if (format != actualInternalFormat)
+ {
+ context->handleError(InvalidOperation()
+ << "Format must match the internal format of the texture.");
+ return false;
+ }
+
+ if (actualInternalFormat == GL_ETC1_RGB8_OES)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidInternalFormat);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height))
+ {
+ context->handleError(InvalidOperation() << "Invalid compressed format dimension.");
+ return false;
+ }
}
if (!actualFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
if (target == GL_TEXTURE_3D)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
}
else
{
- if (!ValidateTexImageFormatCombination(context, actualInternalFormat, format, type))
- {
- return false;
- }
-
- if (target == GL_TEXTURE_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL))
+ if (!ValidateTexImageFormatCombination(context, target, actualInternalFormat, format, type))
{
- context->recordError(Error(GL_INVALID_OPERATION));
return false;
}
}
@@ -421,18 +403,13 @@ bool ValidateES3TexImageParametersBase(Context *context,
{
if (isCompressed != actualFormatInfo.compressed)
{
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
-
- if (width == 0 || height == 0 || depth == 0)
- {
+ context->handleError(InvalidOperation());
return false;
}
if (xoffset < 0 || yoffset < 0 || zoffset < 0)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
@@ -440,7 +417,7 @@ bool ValidateES3TexImageParametersBase(Context *context,
std::numeric_limits<GLsizei>::max() - yoffset < height ||
std::numeric_limits<GLsizei>::max() - zoffset < depth)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
@@ -448,54 +425,42 @@ bool ValidateES3TexImageParametersBase(Context *context,
static_cast<size_t>(yoffset + height) > texture->getHeight(target, level) ||
static_cast<size_t>(zoffset + depth) > texture->getDepth(target, level))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
- }
- // Check for pixel unpack buffer related API errors
- gl::Buffer *pixelUnpackBuffer = context->getState().getTargetBuffer(GL_PIXEL_UNPACK_BUFFER);
- if (pixelUnpackBuffer != NULL)
- {
- // ...the data would be unpacked from the buffer object such that the memory reads required
- // would exceed the data store size.
- size_t widthSize = static_cast<size_t>(width);
- size_t heightSize = static_cast<size_t>(height);
- size_t depthSize = static_cast<size_t>(depth);
- GLenum sizedFormat = GetSizedInternalFormat(actualInternalFormat, type);
-
- size_t pixelBytes = static_cast<size_t>(gl::GetInternalFormatInfo(sizedFormat).pixelBytes);
-
- if (!rx::IsUnsignedMultiplicationSafe(widthSize, heightSize) ||
- !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize, depthSize) ||
- !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize * depthSize, pixelBytes))
+ if (width > 0 && height > 0 && depth > 0 && pixels == nullptr &&
+ context->getGLState().getTargetBuffer(gl::BufferBinding::PixelUnpack) == nullptr)
{
- // Overflow past the end of the buffer
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), PixelDataNull);
return false;
}
+ }
- const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(sizedFormat);
- size_t copyBytes = formatInfo.computeBlockSize(type, width, height);
- size_t offset = reinterpret_cast<size_t>(pixels);
-
- if (!rx::IsUnsignedAdditionSafe(offset, copyBytes) ||
- ((offset + copyBytes) > static_cast<size_t>(pixelUnpackBuffer->getSize())))
- {
- // Overflow past the end of the buffer
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
+ GLenum sizeCheckFormat = isSubImage ? format : internalformat;
+ if (!ValidImageDataSize(context, target, width, height, depth, sizeCheckFormat, type, pixels,
+ imageSize))
+ {
+ return false;
+ }
- // ...data is not evenly divisible into the number of bytes needed to store in memory a datum
+ // Check for pixel unpack buffer related API errors
+ gl::Buffer *pixelUnpackBuffer =
+ context->getGLState().getTargetBuffer(BufferBinding::PixelUnpack);
+ if (pixelUnpackBuffer != nullptr)
+ {
+ // ...data is not evenly divisible into the number of bytes needed to store in memory a
+ // datum
// indicated by type.
if (!isCompressed)
{
+ size_t offset = reinterpret_cast<size_t>(pixels);
size_t dataBytesPerPixel = static_cast<size_t>(gl::GetTypeInfo(type).bytes);
if ((offset % dataBytesPerPixel) != 0)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation()
+ << "Reads would overflow the pixel unpack buffer.");
return false;
}
}
@@ -503,7 +468,7 @@ bool ValidateES3TexImageParametersBase(Context *context,
// ...the buffer object's data store is currently mapped.
if (pixelUnpackBuffer->isMapped())
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation() << "Pixel unpack buffer is mapped.");
return false;
}
}
@@ -526,17 +491,18 @@ bool ValidateES3TexImage2DParameters(Context *context,
GLint border,
GLenum format,
GLenum type,
- const GLvoid *pixels)
+ GLsizei imageSize,
+ const void *pixels)
{
if (!ValidTexture2DDestinationTarget(context, target))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
return ValidateES3TexImageParametersBase(context, target, level, internalformat, isCompressed,
isSubImage, xoffset, yoffset, zoffset, width, height,
- depth, border, format, type, pixels);
+ depth, border, format, type, imageSize, pixels);
}
bool ValidateES3TexImage3DParameters(Context *context,
@@ -554,301 +520,260 @@ bool ValidateES3TexImage3DParameters(Context *context,
GLint border,
GLenum format,
GLenum type,
- const GLvoid *pixels)
+ GLsizei bufSize,
+ const void *pixels)
{
if (!ValidTexture3DDestinationTarget(context, target))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
return ValidateES3TexImageParametersBase(context, target, level, internalformat, isCompressed,
isSubImage, xoffset, yoffset, zoffset, width, height,
- depth, border, format, type, pixels);
+ depth, border, format, type, bufSize, pixels);
}
struct EffectiveInternalFormatInfo
{
- GLenum mEffectiveFormat;
- GLenum mDestFormat;
- GLuint mMinRedBits;
- GLuint mMaxRedBits;
- GLuint mMinGreenBits;
- GLuint mMaxGreenBits;
- GLuint mMinBlueBits;
- GLuint mMaxBlueBits;
- GLuint mMinAlphaBits;
- GLuint mMaxAlphaBits;
-
- EffectiveInternalFormatInfo(GLenum effectiveFormat, GLenum destFormat, GLuint minRedBits, GLuint maxRedBits,
- GLuint minGreenBits, GLuint maxGreenBits, GLuint minBlueBits, GLuint maxBlueBits,
- GLuint minAlphaBits, GLuint maxAlphaBits)
- : mEffectiveFormat(effectiveFormat), mDestFormat(destFormat), mMinRedBits(minRedBits),
- mMaxRedBits(maxRedBits), mMinGreenBits(minGreenBits), mMaxGreenBits(maxGreenBits),
- mMinBlueBits(minBlueBits), mMaxBlueBits(maxBlueBits), mMinAlphaBits(minAlphaBits),
- mMaxAlphaBits(maxAlphaBits) {};
+ GLenum effectiveFormat;
+ GLenum destFormat;
+ GLuint minRedBits;
+ GLuint maxRedBits;
+ GLuint minGreenBits;
+ GLuint maxGreenBits;
+ GLuint minBlueBits;
+ GLuint maxBlueBits;
+ GLuint minAlphaBits;
+ GLuint maxAlphaBits;
};
-typedef std::vector<EffectiveInternalFormatInfo> EffectiveInternalFormatList;
-
-static EffectiveInternalFormatList BuildSizedEffectiveInternalFormatList()
+static bool QueryEffectiveFormatList(const InternalFormat &srcFormat,
+ GLenum targetFormat,
+ const EffectiveInternalFormatInfo *list,
+ size_t size,
+ GLenum *outEffectiveFormat)
{
- EffectiveInternalFormatList list;
-
- // 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 |
- list.push_back(EffectiveInternalFormatInfo(GL_ALPHA8_EXT, GL_NONE, 0, 0, 0, 0, 0, 0, 1, 8));
- list.push_back(EffectiveInternalFormatInfo(GL_R8, GL_NONE, 1, 8, 0, 0, 0, 0, 0, 0));
- list.push_back(EffectiveInternalFormatInfo(GL_RG8, GL_NONE, 1, 8, 1, 8, 0, 0, 0, 0));
- list.push_back(EffectiveInternalFormatInfo(GL_RGB565, GL_NONE, 1, 5, 1, 6, 1, 5, 0, 0));
- list.push_back(EffectiveInternalFormatInfo(GL_RGB8, GL_NONE, 6, 8, 7, 8, 6, 8, 0, 0));
- list.push_back(EffectiveInternalFormatInfo(GL_RGBA4, GL_NONE, 1, 4, 1, 4, 1, 4, 1, 4));
- list.push_back(EffectiveInternalFormatInfo(GL_RGB5_A1, GL_NONE, 5, 5, 5, 5, 5, 5, 1, 1));
- list.push_back(EffectiveInternalFormatInfo(GL_RGBA8, GL_NONE, 5, 8, 5, 8, 5, 8, 2, 8));
- list.push_back(EffectiveInternalFormatInfo(GL_RGB10_A2, GL_NONE, 9, 10, 9, 10, 9, 10, 2, 2));
+ 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;
+ }
+ }
- return list;
+ *outEffectiveFormat = GL_NONE;
+ return false;
}
-static EffectiveInternalFormatList BuildUnsizedEffectiveInternalFormatList()
+bool GetSizedEffectiveInternalFormatInfo(const InternalFormat &srcFormat,
+ GLenum *outEffectiveFormat)
{
- EffectiveInternalFormatList list;
-
- // 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 | Dest Format | R | G | B | A |
- list.push_back(EffectiveInternalFormatInfo(GL_ALPHA8_EXT, GL_ALPHA, 0, UINT_MAX, 0, UINT_MAX, 0, UINT_MAX, 1, 8));
- list.push_back(EffectiveInternalFormatInfo(GL_LUMINANCE8_EXT, GL_LUMINANCE, 1, 8, 0, UINT_MAX, 0, UINT_MAX, 0, UINT_MAX));
- list.push_back(EffectiveInternalFormatInfo(GL_LUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, 1, 8, 0, UINT_MAX, 0, UINT_MAX, 1, 8));
- list.push_back(EffectiveInternalFormatInfo(GL_RGB565, GL_RGB, 1, 5, 1, 6, 1, 5, 0, UINT_MAX));
- list.push_back(EffectiveInternalFormatInfo(GL_RGB8, GL_RGB, 6, 8, 7, 8, 6, 8, 0, UINT_MAX));
- list.push_back(EffectiveInternalFormatInfo(GL_RGBA4, GL_RGBA, 1, 4, 1, 4, 1, 4, 1, 4));
- list.push_back(EffectiveInternalFormatInfo(GL_RGB5_A1, GL_RGBA, 5, 5, 5, 5, 5, 5, 1, 1));
- list.push_back(EffectiveInternalFormatInfo(GL_RGBA8, GL_RGBA, 5, 8, 5, 8, 5, 8, 5, 8));
+ // 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);
+}
- return list;
+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,
+static bool GetEffectiveInternalFormat(const InternalFormat &srcFormat,
+ const InternalFormat &destFormat,
GLenum *outEffectiveFormat)
{
- const EffectiveInternalFormatList *list = NULL;
- GLenum targetFormat = GL_NONE;
-
- if (destFormat.pixelBytes > 0)
+ if (destFormat.sized)
{
- static const EffectiveInternalFormatList sizedList = BuildSizedEffectiveInternalFormatList();
- list = &sizedList;
+ return GetSizedEffectiveInternalFormatInfo(srcFormat, outEffectiveFormat);
}
else
{
- static const EffectiveInternalFormatList unsizedList = BuildUnsizedEffectiveInternalFormatList();
- list = &unsizedList;
- targetFormat = destFormat.format;
- }
-
- for (size_t curFormat = 0; curFormat < list->size(); ++curFormat)
- {
- const EffectiveInternalFormatInfo& formatInfo = list->at(curFormat);
- if ((formatInfo.mDestFormat == targetFormat) &&
- (formatInfo.mMinRedBits <= srcFormat.redBits && formatInfo.mMaxRedBits >= srcFormat.redBits) &&
- (formatInfo.mMinGreenBits <= srcFormat.greenBits && formatInfo.mMaxGreenBits >= srcFormat.greenBits) &&
- (formatInfo.mMinBlueBits <= srcFormat.blueBits && formatInfo.mMaxBlueBits >= srcFormat.blueBits) &&
- (formatInfo.mMinAlphaBits <= srcFormat.alphaBits && formatInfo.mMaxAlphaBits >= srcFormat.alphaBits))
- {
- *outEffectiveFormat = formatInfo.mEffectiveFormat;
- return true;
- }
+ return GetUnsizedEffectiveInternalFormatInfo(srcFormat, destFormat, outEffectiveFormat);
}
+}
- return false;
+static bool EqualOrFirstZero(GLuint first, GLuint second)
+{
+ return first == 0 || first == second;
}
-struct CopyConversion
+static bool IsValidES3CopyTexImageCombination(const InternalFormat &textureFormatInfo,
+ const InternalFormat &framebufferFormatInfo,
+ GLuint readBufferHandle)
{
- GLenum mTextureFormat;
- GLenum mFramebufferFormat;
+ if (!ValidES3CopyConversion(textureFormatInfo.format, framebufferFormatInfo.format))
+ {
+ return false;
+ }
- CopyConversion(GLenum textureFormat, GLenum framebufferFormat)
- : mTextureFormat(textureFormat), mFramebufferFormat(framebufferFormat) { }
+ // 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.
- bool operator<(const CopyConversion& other) const
+ if ((textureFormatInfo.colorEncoding == GL_SRGB) !=
+ (framebufferFormatInfo.colorEncoding == GL_SRGB))
{
- return memcmp(this, &other, sizeof(CopyConversion)) < 0;
+ return false;
}
-};
-typedef std::set<CopyConversion> CopyConversionSet;
-
-static CopyConversionSet BuildValidES3CopyTexImageCombinations()
-{
- CopyConversionSet set;
-
- // From ES 3.0.1 spec, table 3.15
- set.insert(CopyConversion(GL_ALPHA, GL_RGBA));
- set.insert(CopyConversion(GL_LUMINANCE, GL_RED));
- set.insert(CopyConversion(GL_LUMINANCE, GL_RG));
- set.insert(CopyConversion(GL_LUMINANCE, GL_RGB));
- set.insert(CopyConversion(GL_LUMINANCE, GL_RGBA));
- set.insert(CopyConversion(GL_LUMINANCE_ALPHA, GL_RGBA));
- set.insert(CopyConversion(GL_RED, GL_RED));
- set.insert(CopyConversion(GL_RED, GL_RG));
- set.insert(CopyConversion(GL_RED, GL_RGB));
- set.insert(CopyConversion(GL_RED, GL_RGBA));
- set.insert(CopyConversion(GL_RG, GL_RG));
- set.insert(CopyConversion(GL_RG, GL_RGB));
- set.insert(CopyConversion(GL_RG, GL_RGBA));
- set.insert(CopyConversion(GL_RGB, GL_RGB));
- set.insert(CopyConversion(GL_RGB, GL_RGBA));
- set.insert(CopyConversion(GL_RGBA, GL_RGBA));
-
- // Necessary for ANGLE back-buffers
- set.insert(CopyConversion(GL_ALPHA, GL_BGRA_EXT));
- set.insert(CopyConversion(GL_LUMINANCE, GL_BGRA_EXT));
- set.insert(CopyConversion(GL_LUMINANCE_ALPHA, GL_BGRA_EXT));
- set.insert(CopyConversion(GL_RED, GL_BGRA_EXT));
- set.insert(CopyConversion(GL_RG, GL_BGRA_EXT));
- set.insert(CopyConversion(GL_RGB, GL_BGRA_EXT));
- set.insert(CopyConversion(GL_RGBA, GL_BGRA_EXT));
-
- set.insert(CopyConversion(GL_RED_INTEGER, GL_RED_INTEGER));
- set.insert(CopyConversion(GL_RED_INTEGER, GL_RG_INTEGER));
- set.insert(CopyConversion(GL_RED_INTEGER, GL_RGB_INTEGER));
- set.insert(CopyConversion(GL_RED_INTEGER, GL_RGBA_INTEGER));
- set.insert(CopyConversion(GL_RG_INTEGER, GL_RG_INTEGER));
- set.insert(CopyConversion(GL_RG_INTEGER, GL_RGB_INTEGER));
- set.insert(CopyConversion(GL_RG_INTEGER, GL_RGBA_INTEGER));
- set.insert(CopyConversion(GL_RGB_INTEGER, GL_RGB_INTEGER));
- set.insert(CopyConversion(GL_RGB_INTEGER, GL_RGBA_INTEGER));
- set.insert(CopyConversion(GL_RGBA_INTEGER, GL_RGBA_INTEGER));
-
- return set;
-}
-
-static bool IsValidES3CopyTexImageCombination(GLenum textureInternalFormat, GLenum frameBufferInternalFormat, GLuint readBufferHandle)
-{
- const InternalFormat &textureInternalFormatInfo = GetInternalFormatInfo(textureInternalFormat);
- const InternalFormat &framebufferInternalFormatInfo = GetInternalFormatInfo(frameBufferInternalFormat);
-
- static const CopyConversionSet conversionSet = BuildValidES3CopyTexImageCombinations();
- if (conversionSet.find(CopyConversion(textureInternalFormatInfo.format, framebufferInternalFormatInfo.format)) != conversionSet.end())
- {
- // 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 ((textureInternalFormatInfo.colorEncoding == GL_SRGB) != (framebufferInternalFormatInfo.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;
+ }
- if (((textureInternalFormatInfo.componentType == GL_INT) != (framebufferInternalFormatInfo.componentType == GL_INT )) ||
- ((textureInternalFormatInfo.componentType == GL_UNSIGNED_INT) != (framebufferInternalFormatInfo.componentType == GL_UNSIGNED_INT)))
+ // GLES specification 3.0.3, sec 3.8.5, pg 139-140:
+ // The effective internal format of the source buffer is determined with the following rules
+ // applied in order:
+ // * If the source buffer is a texture or renderbuffer that was created with a sized internal
+ // format then the effective internal format is the source buffer's sized internal format.
+ // * If the source buffer is a texture that was created with an unsized base internal format,
+ // then the effective internal format is the source image array's effective internal
+ // format, as specified by table 3.12, which is determined from the <format> and <type>
+ // that were used when the source image array was specified by TexImage*.
+ // * Otherwise the effective internal format is determined by the row in table 3.17 or 3.18
+ // where Destination Internal Format matches internalformat and where the [source channel
+ // sizes] are consistent with the values of the source buffer's [channel sizes]. Table 3.17
+ // is used if the FRAMEBUFFER_ATTACHMENT_ENCODING is LINEAR and table 3.18 is used if the
+ // FRAMEBUFFER_ATTACHMENT_ENCODING is SRGB.
+ const InternalFormat *sourceEffectiveFormat = nullptr;
+ if (readBufferHandle != 0)
+ {
+ // Not the default framebuffer, therefore the read buffer must be a user-created texture or
+ // renderbuffer
+ if (framebufferFormatInfo.sized)
{
- return false;
+ sourceEffectiveFormat = &framebufferFormatInfo;
}
-
- if ((textureInternalFormatInfo.componentType == GL_UNSIGNED_NORMALIZED ||
- textureInternalFormatInfo.componentType == GL_SIGNED_NORMALIZED ||
- textureInternalFormatInfo.componentType == GL_FLOAT) &&
- !(framebufferInternalFormatInfo.componentType == GL_UNSIGNED_NORMALIZED ||
- framebufferInternalFormatInfo.componentType == GL_SIGNED_NORMALIZED ||
- framebufferInternalFormatInfo.componentType == GL_FLOAT))
+ else
{
- return false;
+ // Renderbuffers cannot be created with an unsized internal format, so this must be an
+ // unsized-format texture. We can use the same table we use when creating textures to
+ // get its effective sized format.
+ sourceEffectiveFormat =
+ &GetSizedInternalFormatInfo(framebufferFormatInfo.sizedInternalFormat);
}
-
- // 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.
- InternalFormat sourceEffectiveFormat;
- if (readBufferHandle != 0)
+ }
+ 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)
{
- // Not the default framebuffer, therefore the read buffer must be a user-created texture or renderbuffer
- if (framebufferInternalFormatInfo.pixelBytes > 0)
+ GLenum effectiveFormat;
+ if (GetEffectiveInternalFormat(framebufferFormatInfo, textureFormatInfo,
+ &effectiveFormat))
{
- sourceEffectiveFormat = framebufferInternalFormatInfo;
+ sourceEffectiveFormat = &GetSizedInternalFormatInfo(effectiveFormat);
}
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(framebufferInternalFormatInfo.format, framebufferInternalFormatInfo.type);
- sourceEffectiveFormat = GetInternalFormatInfo(sizedInternalFormat);
+ return false;
}
}
- else
+ else if (framebufferFormatInfo.colorEncoding == GL_SRGB)
{
- // 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 (framebufferInternalFormatInfo.colorEncoding == GL_LINEAR)
- {
- GLenum effectiveFormat;
- if (GetEffectiveInternalFormat(framebufferInternalFormatInfo, textureInternalFormatInfo, &effectiveFormat))
- {
- sourceEffectiveFormat = GetInternalFormatInfo(effectiveFormat);
- }
- else
- {
- return false;
- }
- }
- else if (framebufferInternalFormatInfo.colorEncoding == GL_SRGB)
+ // SRGB buffers can only be copied to sized format destinations according to table 3.18
+ if (textureFormatInfo.sized &&
+ (framebufferFormatInfo.redBits >= 1 && framebufferFormatInfo.redBits <= 8) &&
+ (framebufferFormatInfo.greenBits >= 1 && framebufferFormatInfo.greenBits <= 8) &&
+ (framebufferFormatInfo.blueBits >= 1 && framebufferFormatInfo.blueBits <= 8) &&
+ (framebufferFormatInfo.alphaBits >= 1 && framebufferFormatInfo.alphaBits <= 8))
{
- // SRGB buffers can only be copied to sized format destinations according to table 3.18
- if ((textureInternalFormatInfo.pixelBytes > 0) &&
- (framebufferInternalFormatInfo.redBits >= 1 && framebufferInternalFormatInfo.redBits <= 8) &&
- (framebufferInternalFormatInfo.greenBits >= 1 && framebufferInternalFormatInfo.greenBits <= 8) &&
- (framebufferInternalFormatInfo.blueBits >= 1 && framebufferInternalFormatInfo.blueBits <= 8) &&
- (framebufferInternalFormatInfo.alphaBits >= 1 && framebufferInternalFormatInfo.alphaBits <= 8))
- {
- sourceEffectiveFormat = GetInternalFormatInfo(GL_SRGB8_ALPHA8);
- }
- else
- {
- return false;
- }
+ sourceEffectiveFormat = &GetSizedInternalFormatInfo(GL_SRGB8_ALPHA8);
}
else
{
- UNREACHABLE();
return false;
}
}
-
- if (textureInternalFormatInfo.pixelBytes > 0)
+ else
{
- // 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 (textureInternalFormatInfo.redBits != sourceEffectiveFormat.redBits ||
- textureInternalFormatInfo.greenBits != sourceEffectiveFormat.greenBits ||
- textureInternalFormatInfo.blueBits != sourceEffectiveFormat.blueBits ||
- textureInternalFormatInfo.alphaBits != sourceEffectiveFormat.alphaBits)
- {
- return false;
- }
+ UNREACHABLE();
+ return false;
}
+ }
-
- return true; // A conversion function exists, and no rule in the specification has precluded conversion
- // between these formats.
+ if (textureFormatInfo.sized)
+ {
+ // Section 3.8.5 of the GLES 3.0.3 spec, pg 139, requires that, if the destination format is
+ // sized, component sizes of the source and destination formats must exactly match if the
+ // destination format exists.
+ if (!EqualOrFirstZero(textureFormatInfo.redBits, sourceEffectiveFormat->redBits) ||
+ !EqualOrFirstZero(textureFormatInfo.greenBits, sourceEffectiveFormat->greenBits) ||
+ !EqualOrFirstZero(textureFormatInfo.blueBits, sourceEffectiveFormat->blueBits) ||
+ !EqualOrFirstZero(textureFormatInfo.alphaBits, sourceEffectiveFormat->alphaBits))
+ {
+ return false;
+ }
}
- return false;
+ return true; // A conversion function exists, and no rule in the specification has precluded
+ // conversion between these formats.
}
bool ValidateES3CopyTexImageParametersBase(ValidationContext *context,
@@ -865,48 +790,50 @@ bool ValidateES3CopyTexImageParametersBase(ValidationContext *context,
GLsizei height,
GLint border)
{
- GLenum textureInternalFormat;
+ Format textureFormat = Format::Invalid();
if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
- xoffset, yoffset, zoffset, x, y, width, height,
- border, &textureInternalFormat))
+ xoffset, yoffset, zoffset, x, y, width, height, border,
+ &textureFormat))
{
return false;
}
+ ASSERT(textureFormat.valid() || !isSubImage);
- const auto &state = context->getState();
- const gl::Framebuffer *framebuffer = state.getReadFramebuffer();
- GLuint readFramebufferID = framebuffer->id();
+ const auto &state = context->getGLState();
+ gl::Framebuffer *framebuffer = state.getReadFramebuffer();
+ GLuint readFramebufferID = framebuffer->id();
- if (framebuffer->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE)
+ if (framebuffer->checkStatus(context) != GL_FRAMEBUFFER_COMPLETE)
{
- context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION));
+ context->handleError(InvalidFramebufferOperation());
return false;
}
- if (readFramebufferID != 0 && framebuffer->getSamples(context->getData()) != 0)
+ if (readFramebufferID != 0 && framebuffer->getSamples(context) != 0)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
- const gl::FramebufferAttachment *source = framebuffer->getReadColorbuffer();
- GLenum colorbufferInternalFormat = source->getInternalFormat();
+ const FramebufferAttachment *source = framebuffer->getReadColorbuffer();
if (isSubImage)
{
- if (!IsValidES3CopyTexImageCombination(textureInternalFormat, colorbufferInternalFormat,
+ if (!IsValidES3CopyTexImageCombination(*textureFormat.info, *source->getFormat().info,
readFramebufferID))
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
}
else
{
- if (!gl::IsValidES3CopyTexImageCombination(internalformat, colorbufferInternalFormat,
- readFramebufferID))
+ // Use format/type from the source FBO. (Might not be perfect for all cases?)
+ const InternalFormat &framebufferFormat = *source->getFormat().info;
+ const InternalFormat &copyFormat = GetInternalFormatInfo(internalformat, GL_UNSIGNED_BYTE);
+ if (!IsValidES3CopyTexImageCombination(copyFormat, framebufferFormat, readFramebufferID))
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
}
@@ -931,7 +858,7 @@ bool ValidateES3CopyTexImage2DParameters(ValidationContext *context,
{
if (!ValidTexture2DDestinationTarget(context, target))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
@@ -956,7 +883,7 @@ bool ValidateES3CopyTexImage3DParameters(ValidationContext *context,
{
if (!ValidTexture3DDestinationTarget(context, target))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
@@ -975,7 +902,7 @@ bool ValidateES3TexStorageParametersBase(Context *context,
{
if (width < 1 || height < 1 || depth < 1 || levels < 1)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
@@ -987,7 +914,7 @@ bool ValidateES3TexStorageParametersBase(Context *context,
if (levels > gl::log2(maxDim) + 1)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
@@ -995,85 +922,102 @@ bool ValidateES3TexStorageParametersBase(Context *context,
switch (target)
{
- case GL_TEXTURE_2D:
+ case GL_TEXTURE_2D:
{
if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
static_cast<GLuint>(height) > caps.max2DTextureSize)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
}
break;
- case GL_TEXTURE_CUBE_MAP:
+ case GL_TEXTURE_RECTANGLE_ANGLE:
+ {
+ if (static_cast<GLuint>(width) > caps.maxRectangleTextureSize ||
+ static_cast<GLuint>(height) > caps.maxRectangleTextureSize || levels != 1)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ }
+ break;
+
+ case GL_TEXTURE_CUBE_MAP:
{
if (width != height)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
if (static_cast<GLuint>(width) > caps.maxCubeMapTextureSize)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
}
break;
- case GL_TEXTURE_3D:
+ case GL_TEXTURE_3D:
{
if (static_cast<GLuint>(width) > caps.max3DTextureSize ||
static_cast<GLuint>(height) > caps.max3DTextureSize ||
static_cast<GLuint>(depth) > caps.max3DTextureSize)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
}
break;
- case GL_TEXTURE_2D_ARRAY:
+ 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->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
}
break;
- default:
- UNREACHABLE();
- return false;
+ default:
+ UNREACHABLE();
+ return false;
}
gl::Texture *texture = context->getTargetTexture(target);
if (!texture || texture->id() == 0)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
if (texture->getImmutableFormat())
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
- const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat);
+ const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalformat);
if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
- if (formatInfo.pixelBytes == 0)
+ if (!formatInfo.sized)
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
+ return false;
+ }
+
+ if (formatInfo.compressed && target == GL_TEXTURE_RECTANGLE_ANGLE)
+ {
+ context->handleError(InvalidEnum() << "Rectangle texture cannot have a compressed format.");
return false;
}
@@ -1090,7 +1034,7 @@ bool ValidateES3TexStorage2DParameters(Context *context,
{
if (!ValidTexture2DTarget(context, target))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
@@ -1108,7 +1052,7 @@ bool ValidateES3TexStorage3DParameters(Context *context,
{
if (!ValidTexture3DTarget(context, target))
{
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
@@ -1116,33 +1060,11 @@ bool ValidateES3TexStorage3DParameters(Context *context,
height, depth);
}
-bool ValidateGenQueries(gl::Context *context, GLsizei n, const GLuint *ids)
-{
- if (context->getClientVersion() < 3)
- {
- context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
- return false;
- }
-
- return ValidateGenQueriesBase(context, n, ids);
-}
-
-bool ValidateDeleteQueries(gl::Context *context, GLsizei n, const GLuint *ids)
-{
- if (context->getClientVersion() < 3)
- {
- context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
- return false;
- }
-
- return ValidateDeleteQueriesBase(context, n, ids);
-}
-
bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1151,9 +1073,9 @@ bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id)
bool ValidateEndQuery(gl::Context *context, GLenum target)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1162,38 +1084,36 @@ bool ValidateEndQuery(gl::Context *context, GLenum target)
bool ValidateGetQueryiv(Context *context, GLenum target, GLenum pname, GLint *params)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
- return ValidateGetQueryivBase(context, target, pname);
+ return ValidateGetQueryivBase(context, target, pname, nullptr);
}
bool ValidateGetQueryObjectuiv(Context *context, GLuint id, GLenum pname, GLuint *params)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION, "GLES version < 3.0"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
- return ValidateGetQueryObjectValueBase(context, id, pname);
+ return ValidateGetQueryObjectValueBase(context, id, pname, nullptr);
}
-bool ValidateFramebufferTextureLayer(Context *context, GLenum target, GLenum attachment,
- GLuint texture, GLint level, GLint layer)
+bool ValidateFramebufferTextureLayer(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLint layer)
{
- if (context->getClientVersion() < 3)
- {
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
- }
-
- if (layer < 0)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1205,52 +1125,58 @@ bool ValidateFramebufferTextureLayer(Context *context, GLenum target, GLenum att
const gl::Caps &caps = context->getCaps();
if (texture != 0)
{
+ if (layer < 0)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
gl::Texture *tex = context->getTexture(texture);
ASSERT(tex);
switch (tex->getTarget())
{
- case GL_TEXTURE_2D_ARRAY:
+ case GL_TEXTURE_2D_ARRAY:
{
if (level > gl::log2(caps.max2DTextureSize))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
if (static_cast<GLuint>(layer) >= caps.maxArrayTextureLayers)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
}
break;
- case GL_TEXTURE_3D:
+ case GL_TEXTURE_3D:
{
if (level > gl::log2(caps.max3DTextureSize))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
if (static_cast<GLuint>(layer) >= caps.max3DTextureSize)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
}
break;
- default:
- context->recordError(Error(GL_INVALID_OPERATION));
- return false;
+ default:
+ context->handleError(InvalidOperation());
+ return false;
}
- const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(tex->getInternalFormat(tex->getTarget(), level));
- if (internalFormatInfo.compressed)
+ const auto &format = tex->getFormat(tex->getTarget(), level);
+ if (format.info->compressed)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidOperation());
return false;
}
}
@@ -1258,161 +1184,114 @@ bool ValidateFramebufferTextureLayer(Context *context, GLenum target, GLenum att
return true;
}
-bool ValidES3ReadFormatType(Context *context, GLenum internalFormat, GLenum format, GLenum type)
+bool ValidateInvalidateFramebuffer(Context *context,
+ GLenum target,
+ GLsizei numAttachments,
+ const GLenum *attachments)
{
- const gl::InternalFormat &internalFormatInfo = gl::GetInternalFormatInfo(internalFormat);
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
- switch (format)
+ bool defaultFramebuffer = false;
+
+ switch (target)
{
- case GL_RGBA:
- switch (type)
- {
- case GL_UNSIGNED_BYTE:
- break;
- case GL_UNSIGNED_INT_2_10_10_10_REV:
- if (internalFormat != GL_RGB10_A2)
- {
- return false;
- }
- break;
- case GL_FLOAT:
- if (internalFormatInfo.componentType != GL_FLOAT)
- {
- return false;
- }
- break;
- default:
- return false;
- }
- break;
- case GL_RGBA_INTEGER:
- switch (type)
- {
- case GL_INT:
- if (internalFormatInfo.componentType != GL_INT)
- {
- return false;
- }
+ case GL_DRAW_FRAMEBUFFER:
+ case GL_FRAMEBUFFER:
+ defaultFramebuffer = context->getGLState().getDrawFramebuffer()->id() == 0;
break;
- case GL_UNSIGNED_INT:
- if (internalFormatInfo.componentType != GL_UNSIGNED_INT)
- {
- return false;
- }
- 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:
+ case GL_READ_FRAMEBUFFER:
+ defaultFramebuffer = context->getGLState().getReadFramebuffer()->id() == 0;
break;
default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
return false;
- }
- break;
- default:
- return false;
}
- return true;
+
+ return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments,
+ defaultFramebuffer);
}
-bool ValidateES3RenderbufferStorageParameters(gl::Context *context, GLenum target, GLsizei samples,
- GLenum internalformat, GLsizei width, GLsizei height)
+bool ValidateInvalidateSubFramebuffer(Context *context,
+ GLenum target,
+ GLsizei numAttachments,
+ const GLenum *attachments,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height)
{
- if (!ValidateRenderbufferStorageParametersBase(context, target, samples, internalformat, width, height))
- {
- return false;
- }
+ return ValidateInvalidateFramebuffer(context, target, numAttachments, attachments);
+}
- //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)
+bool ValidateClearBuffer(ValidationContext *context)
+{
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
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())
+ if (context->getGLState().getDrawFramebuffer()->checkStatus(context) != GL_FRAMEBUFFER_COMPLETE)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidFramebufferOperation());
return false;
}
return true;
}
-bool ValidateInvalidateFramebuffer(Context *context, GLenum target, GLsizei numAttachments,
- const GLenum *attachments)
+bool ValidateDrawRangeElements(Context *context,
+ GLenum mode,
+ GLuint start,
+ GLuint end,
+ GLsizei count,
+ GLenum type,
+ const void *indices)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Operation only supported on ES 3.0 and above"));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
- bool defaultFramebuffer = false;
-
- switch (target)
+ if (end < start)
{
- case GL_DRAW_FRAMEBUFFER:
- case GL_FRAMEBUFFER:
- defaultFramebuffer = context->getState().getDrawFramebuffer()->id() == 0;
- break;
- case GL_READ_FRAMEBUFFER:
- defaultFramebuffer = context->getState().getReadFramebuffer()->id() == 0;
- break;
- default:
- context->recordError(Error(GL_INVALID_ENUM, "Invalid framebuffer target"));
+ context->handleError(InvalidValue() << "end < start");
return false;
}
- return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments, defaultFramebuffer);
-}
-
-bool ValidateClearBuffer(ValidationContext *context)
-{
- if (context->getClientVersion() < 3)
+ if (!ValidateDrawElementsCommon(context, mode, count, type, indices, 0))
{
- context->recordError(Error(GL_INVALID_OPERATION));
return false;
}
- const gl::Framebuffer *fbo = context->getState().getDrawFramebuffer();
- if (!fbo || fbo->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE)
+ // Use the parameter buffer to retrieve and cache the index range.
+ const auto &params = context->getParams<HasIndexRange>();
+ const auto &indexRangeOpt = params.getIndexRange();
+ if (!indexRangeOpt.valid())
{
- context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION));
+ // Unexpected error.
return false;
}
+ if (indexRangeOpt.value().end > end || indexRangeOpt.value().start < start)
+ {
+ // GL spec says that behavior in this case is undefined - generating an error is fine.
+ context->handleError(InvalidOperation() << "Indices are out of the start, end range.");
+ return false;
+ }
return true;
}
-bool ValidateGetUniformuiv(Context *context, GLuint program, GLint location, GLuint* params)
+bool ValidateGetUniformuiv(Context *context, GLuint program, GLint location, GLuint *params)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1421,17 +1300,17 @@ bool ValidateGetUniformuiv(Context *context, GLuint program, GLint location, GLu
bool ValidateReadBuffer(Context *context, GLenum src)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
- Framebuffer *readFBO = context->getState().getReadFramebuffer();
+ const Framebuffer *readFBO = context->getGLState().getReadFramebuffer();
if (readFBO == nullptr)
{
- context->recordError(gl::Error(GL_INVALID_OPERATION, "No active read framebuffer."));
+ context->handleError(InvalidOperation() << "No active read framebuffer.");
return false;
}
@@ -1440,9 +1319,9 @@ bool ValidateReadBuffer(Context *context, GLenum src)
return true;
}
- if (src != GL_BACK && (src < GL_COLOR_ATTACHMENT0 || src > GL_COLOR_ATTACHMENT15))
+ if (src != GL_BACK && (src < GL_COLOR_ATTACHMENT0 || src > GL_COLOR_ATTACHMENT31))
{
- context->recordError(gl::Error(GL_INVALID_ENUM, "Unknown enum for 'src' in ReadBuffer"));
+ context->handleError(InvalidEnum() << "Unknown enum for 'src' in ReadBuffer");
return false;
}
@@ -1450,8 +1329,9 @@ bool ValidateReadBuffer(Context *context, GLenum src)
{
if (src != GL_BACK)
{
- const char *errorMsg = "'src' must be GL_NONE or GL_BACK when reading from the default framebuffer.";
- context->recordError(gl::Error(GL_INVALID_OPERATION, errorMsg));
+ context->handleError(
+ InvalidOperation()
+ << "'src' must be GL_NONE or GL_BACK when reading from the default framebuffer.");
return false;
}
}
@@ -1461,8 +1341,7 @@ bool ValidateReadBuffer(Context *context, GLenum src)
if (drawBuffer >= context->getCaps().maxDrawBuffers)
{
- const char *errorMsg = "'src' is greater than MAX_DRAW_BUFFERS.";
- context->recordError(gl::Error(GL_INVALID_OPERATION, errorMsg));
+ context->handleError(InvalidOperation() << "'src' is greater than MAX_DRAW_BUFFERS.");
return false;
}
}
@@ -1479,34 +1358,57 @@ bool ValidateCompressedTexImage3D(Context *context,
GLsizei depth,
GLint border,
GLsizei imageSize,
- const GLvoid *data)
+ const void *data)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
- const InternalFormat &formatInfo = GetInternalFormatInfo(internalformat);
- if (imageSize < 0 ||
- static_cast<GLuint>(imageSize) !=
- formatInfo.computeBlockSize(GL_UNSIGNED_BYTE, width, height))
+ if (!ValidTextureTarget(context, target))
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidEnum());
+ return false;
+ }
+
+ // Validate image size
+ if (!ValidImageSizeParameters(context, target, level, width, height, depth, false))
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
+ if (!formatInfo.compressed)
+ {
+ context->handleError(InvalidEnum() << "Not a valid compressed texture format");
+ return false;
+ }
+
+ auto blockSizeOrErr = formatInfo.computeCompressedImageSize(gl::Extents(width, height, depth));
+ if (blockSizeOrErr.isError())
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSizeOrErr.getResult())
+ {
+ context->handleError(InvalidValue());
return false;
}
// 3D texture target validation
if (target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY)
{
- context->recordError(
- Error(GL_INVALID_ENUM, "Must specify a valid 3D texture destination target"));
+ context->handleError(InvalidEnum() << "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, data))
+ 0, width, height, depth, border, GL_NONE, GL_NONE, -1,
+ data))
{
return false;
}
@@ -1514,59 +1416,207 @@ bool ValidateCompressedTexImage3D(Context *context,
return true;
}
-bool ValidateBindVertexArray(Context *context, GLuint array)
+bool ValidateCompressedTexImage3DRobustANGLE(Context *context,
+ GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
{
- if (context->getClientVersion() < 3)
+ if (!ValidateRobustCompressedTexImageBase(context, imageSize, dataSize))
{
- context->recordError(Error(GL_INVALID_OPERATION));
return false;
}
- return ValidateBindVertexArrayBase(context, array);
+ return ValidateCompressedTexImage3D(context, target, level, internalformat, width, height,
+ depth, border, imageSize, data);
}
-bool ValidateDeleteVertexArrays(Context *context, GLsizei n)
+bool ValidateBindVertexArray(Context *context, GLuint array)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
- return ValidateDeleteVertexArraysBase(context, n);
+ return ValidateBindVertexArrayBase(context, array);
}
-bool ValidateGenVertexArrays(Context *context, GLsizei n)
+bool ValidateIsVertexArray(Context *context, GLuint array)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
- return ValidateGenVertexArraysBase(context, n);
+ return true;
}
-bool ValidateIsVertexArray(Context *context)
+static bool ValidateBindBufferCommon(Context *context,
+ BufferBinding target,
+ GLuint index,
+ GLuint buffer,
+ GLintptr offset,
+ GLsizeiptr size)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (buffer != 0 && offset < 0)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ context->handleError(InvalidValue() << "buffer is non-zero and offset is negative.");
return false;
}
+ if (!context->getGLState().isBindGeneratesResourceEnabled() &&
+ !context->isBufferGenerated(buffer))
+ {
+ context->handleError(InvalidOperation() << "Buffer was not generated.");
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ switch (target)
+ {
+ case BufferBinding::TransformFeedback:
+ {
+ if (index >= caps.maxTransformFeedbackSeparateAttributes)
+ {
+ context->handleError(InvalidValue() << "index is greater than or equal to the "
+ "number of TRANSFORM_FEEDBACK_BUFFER "
+ "indexed binding points.");
+ return false;
+ }
+ if (buffer != 0 && ((offset % 4) != 0 || (size % 4) != 0))
+ {
+ context->handleError(InvalidValue() << "offset and size must be multiple of 4.");
+ return false;
+ }
+
+ TransformFeedback *curTransformFeedback =
+ context->getGLState().getCurrentTransformFeedback();
+ if (curTransformFeedback && curTransformFeedback->isActive())
+ {
+ context->handleError(InvalidOperation()
+ << "target is TRANSFORM_FEEDBACK_BUFFER and transform "
+ "feedback is currently active.");
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::Uniform:
+ {
+ if (index >= caps.maxUniformBufferBindings)
+ {
+ context->handleError(InvalidValue() << "index is greater than or equal to the "
+ "number of UNIFORM_BUFFER indexed "
+ "binding points.");
+ return false;
+ }
+
+ if (buffer != 0 && (offset % caps.uniformBufferOffsetAlignment) != 0)
+ {
+ context->handleError(
+ InvalidValue()
+ << "offset must be multiple of value of UNIFORM_BUFFER_OFFSET_ALIGNMENT.");
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::AtomicCounter:
+ {
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->handleError(InvalidEnum()
+ << "ATOMIC_COUNTER_BUFFER is not supported before GLES 3.1");
+ return false;
+ }
+ if (index >= caps.maxAtomicCounterBufferBindings)
+ {
+ context->handleError(InvalidValue() << "index is greater than or equal to the "
+ "number of ATOMIC_COUNTER_BUFFER "
+ "indexed binding points.");
+ return false;
+ }
+ if (buffer != 0 && (offset % 4) != 0)
+ {
+ context->handleError(InvalidValue() << "offset must be a multiple of 4.");
+ return false;
+ }
+ break;
+ }
+ case BufferBinding::ShaderStorage:
+ {
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->handleError(InvalidEnum()
+ << "SHADER_STORAGE_BUFFER is not supported in GLES3.");
+ return false;
+ }
+ if (index >= caps.maxShaderStorageBufferBindings)
+ {
+ context->handleError(InvalidValue() << "index is greater than or equal to the "
+ "number of SHADER_STORAGE_BUFFER "
+ "indexed binding points.");
+ return false;
+ }
+ if (buffer != 0 && (offset % caps.shaderStorageBufferOffsetAlignment) != 0)
+ {
+ context->handleError(InvalidValue() << "offset must be multiple of value of "
+ "SHADER_STORAGE_BUFFER_OFFSET_"
+ "ALIGNMENT.");
+ return false;
+ }
+ break;
+ }
+ default:
+ context->handleError(InvalidEnum() << "the target is not supported.");
+ return false;
+ }
+
return true;
}
+bool ValidateBindBufferBase(Context *context, BufferBinding target, GLuint index, GLuint buffer)
+{
+ return ValidateBindBufferCommon(context, target, index, buffer, 0, 0);
+}
+
+bool ValidateBindBufferRange(Context *context,
+ BufferBinding target,
+ GLuint index,
+ GLuint buffer,
+ GLintptr offset,
+ GLsizeiptr size)
+{
+ if (buffer != 0 && size <= 0)
+ {
+ context->handleError(InvalidValue()
+ << "buffer is non-zero and size is less than or equal to zero.");
+ return false;
+ }
+ return ValidateBindBufferCommon(context, target, index, buffer, offset, size);
+}
+
bool ValidateProgramBinary(Context *context,
GLuint program,
GLenum binaryFormat,
const void *binary,
GLint length)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1580,20 +1630,20 @@ bool ValidateGetProgramBinary(Context *context,
GLenum *binaryFormat,
void *binary)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary);
}
-bool ValidateProgramParameter(Context *context, GLuint program, GLenum pname, GLint value)
+bool ValidateProgramParameteri(Context *context, GLuint program, GLenum pname, GLint value)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1605,10 +1655,33 @@ bool ValidateProgramParameter(Context *context, GLuint program, GLenum pname, GL
switch (pname)
{
case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
+ if (value != GL_FALSE && value != GL_TRUE)
+ {
+ context->handleError(InvalidValue()
+ << "Invalid value, expected GL_FALSE or GL_TRUE: " << value);
+ return false;
+ }
+ break;
+
+ case GL_PROGRAM_SEPARABLE:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->handleError(InvalidEnum()
+ << "PROGRAM_SEPARABLE is not supported before GLES 3.1");
+ return false;
+ }
+
+ if (value != GL_FALSE && value != GL_TRUE)
+ {
+ context->handleError(InvalidValue()
+ << "Invalid value, expected GL_FALSE or GL_TRUE: " << value);
+ return false;
+ }
break;
default:
- context->recordError(Error(GL_INVALID_ENUM, "Invalid pname: 0x%X", pname));
+ context->handleError(InvalidEnum()
+ << "Invalid pname: 0x" << std::hex << std::uppercase << pname);
return false;
}
@@ -1627,9 +1700,9 @@ bool ValidateBlitFramebuffer(Context *context,
GLbitfield mask,
GLenum filter)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1648,21 +1721,30 @@ bool ValidateClearBufferiv(ValidationContext *context,
if (drawbuffer < 0 ||
static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
+ if (context->getExtensions().webglCompatibility)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_INT};
+ if (!ValidateWebGLFramebufferAttachmentClearType(
+ context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
break;
case GL_STENCIL:
if (drawbuffer != 0)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
break;
default:
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
@@ -1680,13 +1762,22 @@ bool ValidateClearBufferuiv(ValidationContext *context,
if (drawbuffer < 0 ||
static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
+ if (context->getExtensions().webglCompatibility)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_UNSIGNED_INT};
+ if (!ValidateWebGLFramebufferAttachmentClearType(
+ context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
break;
default:
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
@@ -1704,21 +1795,31 @@ bool ValidateClearBufferfv(ValidationContext *context,
if (drawbuffer < 0 ||
static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
+ if (context->getExtensions().webglCompatibility)
+ {
+ constexpr GLenum validComponentTypes[] = {GL_FLOAT, GL_UNSIGNED_NORMALIZED,
+ GL_SIGNED_NORMALIZED};
+ if (!ValidateWebGLFramebufferAttachmentClearType(
+ context, drawbuffer, validComponentTypes, ArraySize(validComponentTypes)))
+ {
+ return false;
+ }
+ }
break;
case GL_DEPTH:
if (drawbuffer != 0)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
break;
default:
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
@@ -1736,13 +1837,13 @@ bool ValidateClearBufferfi(ValidationContext *context,
case GL_DEPTH_STENCIL:
if (drawbuffer != 0)
{
- context->recordError(Error(GL_INVALID_VALUE));
+ context->handleError(InvalidValue());
return false;
}
break;
default:
- context->recordError(Error(GL_INVALID_ENUM));
+ context->handleError(InvalidEnum());
return false;
}
@@ -1751,9 +1852,9 @@ bool ValidateClearBufferfi(ValidationContext *context,
bool ValidateDrawBuffers(ValidationContext *context, GLsizei n, const GLenum *bufs)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION, "Context does not support GLES3."));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1771,9 +1872,9 @@ bool ValidateCopyTexSubImage3D(Context *context,
GLsizei width,
GLsizei height)
{
- if (context->getClientVersion() < 3)
+ if (context->getClientMajorVersion() < 3)
{
- context->recordError(Error(GL_INVALID_OPERATION));
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
return false;
}
@@ -1781,4 +1882,1821 @@ bool ValidateCopyTexSubImage3D(Context *context,
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 void *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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 void *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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 void *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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 void *pixels)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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 void *data)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ const InternalFormat &formatInfo = GetSizedInternalFormatInfo(format);
+ if (!formatInfo.compressed)
+ {
+ context->handleError(InvalidEnum() << "Not a valid compressed texture format");
+ return false;
+ }
+
+ 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(InvalidValue());
+ return false;
+ }
+
+ if (!data)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return ValidateES3TexImage3DParameters(context, target, level, GL_NONE, true, true, xoffset,
+ yoffset, zoffset, width, height, depth, 0, format,
+ GL_NONE, -1, data);
+}
+bool ValidateCompressedTexSubImage3DRobustANGLE(Context *context,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLsizei imageSize,
+ GLsizei dataSize,
+ const void *data)
+{
+ if (!ValidateRobustCompressedTexImageBase(context, imageSize, dataSize))
+ {
+ return false;
+ }
+
+ return ValidateCompressedTexSubImage3D(context, target, level, xoffset, yoffset, zoffset, width,
+ height, depth, format, imageSize, 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(InvalidOperation()
+ << "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 ValidateBeginTransformFeedback(Context *context, GLenum primitiveMode)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+ switch (primitiveMode)
+ {
+ case GL_TRIANGLES:
+ case GL_LINES:
+ case GL_POINTS:
+ break;
+
+ default:
+ context->handleError(InvalidEnum() << "Invalid primitive mode.");
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ if (transformFeedback->isActive())
+ {
+ context->handleError(InvalidOperation() << "Transform feedback is already active.");
+ return false;
+ }
+
+ for (size_t i = 0; i < transformFeedback->getIndexedBufferCount(); i++)
+ {
+ const auto &buffer = transformFeedback->getIndexedBuffer(i);
+ if (buffer.get() && buffer->isMapped())
+ {
+ context->handleError(InvalidOperation() << "Transform feedback has a mapped buffer.");
+ return false;
+ }
+ }
+
+ Program *program = context->getGLState().getProgram();
+
+ if (!program)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound);
+ return false;
+ }
+
+ if (program->getTransformFeedbackVaryingCount() == 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoTransformFeedbackOutputVariables);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferPointerv(Context *context, BufferBinding target, GLenum pname, void **params)
+{
+ return ValidateGetBufferPointervBase(context, target, pname, nullptr, params);
+}
+
+bool ValidateGetBufferPointervRobustANGLE(Context *context,
+ BufferBinding target,
+ GLenum pname,
+ GLsizei bufSize,
+ GLsizei *length,
+ void **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, BufferBinding target)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateUnmapBufferBase(context, target);
+}
+
+bool ValidateMapBufferRange(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length,
+ GLbitfield access)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateMapBufferRangeBase(context, target, offset, length, access);
+}
+
+bool ValidateFlushMappedBufferRange(Context *context,
+ BufferBinding target,
+ GLintptr offset,
+ GLsizeiptr length)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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(InvalidEnum());
+ 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(InvalidValue());
+ return false;
+ }
+ break;
+
+ case GL_UNIFORM_BUFFER_START:
+ case GL_UNIFORM_BUFFER_SIZE:
+ case GL_UNIFORM_BUFFER_BINDING:
+ if (index >= caps.maxUniformBufferBindings)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ break;
+
+ case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
+ case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
+ if (index >= 3u)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ break;
+
+ case GL_ATOMIC_COUNTER_BUFFER_START:
+ case GL_ATOMIC_COUNTER_BUFFER_SIZE:
+ case GL_ATOMIC_COUNTER_BUFFER_BINDING:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->handleError(
+ InvalidEnum()
+ << "Atomic Counter buffers are not supported in this version of GL");
+ return false;
+ }
+ if (index >= caps.maxAtomicCounterBufferBindings)
+ {
+ context->handleError(
+ InvalidValue()
+ << "index is outside the valid range for GL_ATOMIC_COUNTER_BUFFER_BINDING");
+ return false;
+ }
+ break;
+
+ case GL_SHADER_STORAGE_BUFFER_START:
+ case GL_SHADER_STORAGE_BUFFER_SIZE:
+ case GL_SHADER_STORAGE_BUFFER_BINDING:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->handleError(
+ InvalidEnum()
+ << "Shader storage buffers are not supported in this version of GL");
+ return false;
+ }
+ if (index >= caps.maxShaderStorageBufferBindings)
+ {
+ context->handleError(
+ InvalidValue()
+ << "index is outside the valid range for GL_SHADER_STORAGE_BUFFER_BINDING");
+ return false;
+ }
+ break;
+
+ case GL_VERTEX_BINDING_BUFFER:
+ case GL_VERTEX_BINDING_DIVISOR:
+ case GL_VERTEX_BINDING_OFFSET:
+ case GL_VERTEX_BINDING_STRIDE:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ context->handleError(
+ InvalidEnum()
+ << "Vertex Attrib Bindings are not supported in this version of GL");
+ return false;
+ }
+ if (index >= caps.maxVertexAttribBindings)
+ {
+ context->handleError(
+ InvalidValue()
+ << "bindingindex must be smaller than MAX_VERTEX_ATTRIB_BINDINGS.");
+ return false;
+ }
+ break;
+ case GL_SAMPLE_MASK_VALUE:
+ if (context->getClientVersion() < ES_3_1)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumRequiresGLES31);
+ return false;
+ }
+ if (index >= caps.maxSampleMaskWords)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidSampleMaskNumber);
+ return false;
+ }
+ break;
+ default:
+ context->handleError(InvalidEnum());
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ 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,
+ BufferBinding readTarget,
+ BufferBinding writeTarget,
+ GLintptr readOffset,
+ GLintptr writeOffset,
+ GLsizeiptr size)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (!ValidBufferType(context, readTarget) || !ValidBufferType(context, writeTarget))
+ {
+ context->handleError(InvalidEnum() << "Invalid buffer target");
+ return false;
+ }
+
+ Buffer *readBuffer = context->getGLState().getTargetBuffer(readTarget);
+ Buffer *writeBuffer = context->getGLState().getTargetBuffer(writeTarget);
+
+ if (!readBuffer || !writeBuffer)
+ {
+ context->handleError(InvalidOperation() << "No buffer bound to target");
+ return false;
+ }
+
+ // Verify that readBuffer and writeBuffer are not currently mapped
+ if (readBuffer->isMapped() || writeBuffer->isMapped())
+ {
+ context->handleError(InvalidOperation()
+ << "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(InvalidValue() << "Integer overflow when validating copy offsets.");
+ return false;
+ }
+
+ if (readOffset < 0 || writeOffset < 0 || size < 0)
+ {
+ context->handleError(InvalidValue()
+ << "readOffset, writeOffset and size must all be non-negative");
+ return false;
+ }
+
+ if (checkedReadSum.ValueOrDie() > readBuffer->getSize() ||
+ checkedWriteSum.ValueOrDie() > writeBuffer->getSize())
+ {
+ context->handleError(InvalidValue() << "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(InvalidValue()
+ << "Integer overflow when validating same buffer copy.");
+ return false;
+ }
+
+ if (checkedOffsetDiff.ValueOrDie() < size)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetStringi(Context *context, GLenum name, GLuint index)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ switch (name)
+ {
+ case GL_EXTENSIONS:
+ if (index >= context->getExtensionStringCount())
+ {
+ context->handleError(InvalidValue()
+ << "index must be less than the number of extension strings.");
+ return false;
+ }
+ break;
+
+ case GL_REQUESTABLE_EXTENSIONS_ANGLE:
+ if (!context->getExtensions().requestExtension)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidName);
+ return false;
+ }
+ if (index >= context->getRequestableExtensionStringCount())
+ {
+ context->handleError(
+ InvalidValue()
+ << "index must be less than the number of requestable extension strings.");
+ return false;
+ }
+ break;
+
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidName);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRenderbufferStorageMultisample(ValidationContext *context,
+ GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ 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::GetSizedInternalFormatInfo(internalformat);
+ if ((formatInfo.componentType == GL_UNSIGNED_INT || formatInfo.componentType == GL_INT) &&
+ samples > 0)
+ {
+ context->handleError(InvalidOperation());
+ 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(
+ InvalidOperation()
+ << "Samples must not be greater than maximum supported value for the format.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribIPointer(ValidationContext *context,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLsizei stride,
+ const void *pointer)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (!ValidateVertexFormatBase(context, index, size, type, true))
+ {
+ return false;
+ }
+
+ if (stride < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeStride);
+ return false;
+ }
+
+ const Caps &caps = context->getCaps();
+ if (context->getClientVersion() >= ES_3_1)
+ {
+ if (stride > caps.maxVertexAttribStride)
+ {
+ context->handleError(InvalidValue()
+ << "stride cannot be greater than MAX_VERTEX_ATTRIB_STRIDE.");
+ return false;
+ }
+
+ // [OpenGL ES 3.1] Section 10.3.1 page 245:
+ // glVertexAttribBinding is part of the equivalent code of VertexAttribIPointer, so its
+ // validation should be inherited.
+ if (index >= caps.maxVertexAttribBindings)
+ {
+ context->handleError(InvalidValue()
+ << "index must be smaller than MAX_VERTEX_ATTRIB_BINDINGS.");
+ return false;
+ }
+ }
+
+ // [OpenGL ES 3.0.2] Section 2.8 page 24:
+ // An INVALID_OPERATION error is generated when a non-zero vertex array object
+ // is bound, zero is bound to the ARRAY_BUFFER buffer object binding point,
+ // and the pointer argument is not NULL.
+ if (context->getGLState().getVertexArrayId() != 0 &&
+ context->getGLState().getTargetBuffer(BufferBinding::Array) == 0 && pointer != nullptr)
+ {
+ context
+ ->handleError(InvalidOperation()
+ << "Client data cannot be used with a non-default vertex array object.");
+ return false;
+ }
+
+ if (context->getExtensions().webglCompatibility)
+ {
+ if (!ValidateWebGLVertexAttribPointer(context, type, false, stride, pointer, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetSynciv(Context *context,
+ GLsync sync,
+ GLenum pname,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLint *values)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
+ return false;
+ }
+
+ Sync *syncObject = context->getSync(sync);
+ if (!syncObject)
+ {
+ context->handleError(InvalidValue() << "Invalid sync object.");
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_OBJECT_TYPE:
+ case GL_SYNC_CONDITION:
+ case GL_SYNC_FLAGS:
+ case GL_SYNC_STATUS:
+ break;
+
+ default:
+ ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidPname);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawElementsInstanced(ValidationContext *context,
+ GLenum mode,
+ GLsizei count,
+ GLenum type,
+ const void *indices,
+ GLsizei instanceCount)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateDrawElementsInstancedCommon(context, mode, count, type, indices, instanceCount);
+}
+
+bool ValidateFramebufferTextureMultiviewLayeredANGLE(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLint baseViewIndex,
+ GLsizei numViews)
+{
+
+ if (!ValidateFramebufferTextureMultiviewBaseANGLE(context, target, attachment, texture, level,
+ numViews))
+ {
+ return false;
+ }
+
+ if (texture != 0)
+ {
+ if (baseViewIndex < 0)
+ {
+ context->handleError(InvalidValue() << "baseViewIndex cannot be less than 0.");
+ return false;
+ }
+
+ Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ switch (tex->getTarget())
+ {
+ case GL_TEXTURE_2D_ARRAY:
+ {
+ const Caps &caps = context->getCaps();
+ if (static_cast<GLuint>(baseViewIndex + numViews) > caps.maxArrayTextureLayers)
+ {
+ context->handleError(InvalidValue() << "baseViewIndex+numViews cannot be "
+ "greater than "
+ "GL_MAX_ARRAY_TEXTURE_LAYERS.");
+ return false;
+ }
+ }
+ break;
+ default:
+ context->handleError(InvalidOperation()
+ << "Texture's target must be GL_TEXTURE_2D_ARRAY.");
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureMultiviewLevelAndFormat(context, tex, level))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateFramebufferTextureMultiviewSideBySideANGLE(Context *context,
+ GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLsizei numViews,
+ const GLint *viewportOffsets)
+{
+ if (!ValidateFramebufferTextureMultiviewBaseANGLE(context, target, attachment, texture, level,
+ numViews))
+ {
+ return false;
+ }
+
+ if (texture != 0)
+ {
+ const GLsizei numViewportOffsetValues = numViews * 2;
+ for (GLsizei i = 0; i < numViewportOffsetValues; ++i)
+ {
+ if (viewportOffsets[i] < 0)
+ {
+ context->handleError(InvalidValue()
+ << "viewportOffsets cannot contain negative values.");
+ return false;
+ }
+ }
+
+ Texture *tex = context->getTexture(texture);
+ ASSERT(tex);
+
+ switch (tex->getTarget())
+ {
+ case GL_TEXTURE_2D:
+ break;
+ default:
+ context->handleError(InvalidOperation()
+ << "Texture's target must be GL_TEXTURE_2D.");
+ return false;
+ }
+
+ if (!ValidateFramebufferTextureMultiviewLevelAndFormat(context, tex, level))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateUniform1ui(Context *context, GLint location, GLuint v0)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT, location, 1);
+}
+
+bool ValidateUniform2ui(Context *context, GLint location, GLuint v0, GLuint v1)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC2, location, 1);
+}
+
+bool ValidateUniform3ui(Context *context, GLint location, GLuint v0, GLuint v1, GLuint v2)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC3, location, 1);
+}
+
+bool ValidateUniform4ui(Context *context,
+ GLint location,
+ GLuint v0,
+ GLuint v1,
+ GLuint v2,
+ GLuint v3)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC4, location, 1);
+}
+
+bool ValidateUniform1uiv(Context *context, GLint location, GLsizei count, const GLuint *value)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT, location, count);
+}
+
+bool ValidateUniform2uiv(Context *context, GLint location, GLsizei count, const GLuint *value)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC2, location, count);
+}
+
+bool ValidateUniform3uiv(Context *context, GLint location, GLsizei count, const GLuint *value)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC3, location, count);
+}
+
+bool ValidateUniform4uiv(Context *context, GLint location, GLsizei count, const GLuint *value)
+{
+ return ValidateUniformES3(context, GL_UNSIGNED_INT_VEC4, location, count);
+}
+
+bool ValidateIsQuery(Context *context, GLuint id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateUniformMatrix2x3fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT2x3, location, count, transpose);
+}
+
+bool ValidateUniformMatrix3x2fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT3x2, location, count, transpose);
+}
+
+bool ValidateUniformMatrix2x4fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT2x4, location, count, transpose);
+}
+
+bool ValidateUniformMatrix4x2fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT4x2, location, count, transpose);
+}
+
+bool ValidateUniformMatrix3x4fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT3x4, location, count, transpose);
+}
+
+bool ValidateUniformMatrix4x3fv(Context *context,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat *value)
+{
+ return ValidateUniformMatrixES3(context, GL_FLOAT_MAT4x3, location, count, transpose);
+}
+
+bool ValidateEndTransformFeedback(Context *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ if (!transformFeedback->isActive())
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTransformFeedbackVaryings(Context *context,
+ GLuint program,
+ GLsizei count,
+ const GLchar *const *varyings,
+ GLenum bufferMode)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (count < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeCount);
+ return false;
+ }
+
+ switch (bufferMode)
+ {
+ case GL_INTERLEAVED_ATTRIBS:
+ break;
+ case GL_SEPARATE_ATTRIBS:
+ {
+ const Caps &caps = context->getCaps();
+ if (static_cast<GLuint>(count) > caps.maxTransformFeedbackSeparateAttributes)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ break;
+ }
+ default:
+ context->handleError(InvalidEnum());
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetTransformFeedbackVarying(Context *context,
+ GLuint program,
+ GLuint index,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLsizei *size,
+ GLenum *type,
+ GLchar *name)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (bufSize < 0)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (index >= static_cast<GLuint>(programObject->getTransformFeedbackVaryingCount()))
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindTransformFeedback(Context *context, GLenum target, GLuint id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ switch (target)
+ {
+ case GL_TRANSFORM_FEEDBACK:
+ {
+ // Cannot bind a transform feedback object if the current one is started and not
+ // paused (3.0.2 pg 85 section 2.14.1)
+ TransformFeedback *curTransformFeedback =
+ context->getGLState().getCurrentTransformFeedback();
+ if (curTransformFeedback && curTransformFeedback->isActive() &&
+ !curTransformFeedback->isPaused())
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+
+ // Cannot bind a transform feedback object that does not exist (3.0.2 pg 85 section
+ // 2.14.1)
+ if (!context->isTransformFeedbackGenerated(id))
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), TransformFeedbackDoesNotExist);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ context->handleError(InvalidEnum());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsTransformFeedback(Context *context, GLuint id)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidatePauseTransformFeedback(Context *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ // Current transform feedback must be active and not paused in order to pause (3.0.2 pg 86)
+ if (!transformFeedback->isActive() || transformFeedback->isPaused())
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateResumeTransformFeedback(Context *context)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+ ASSERT(transformFeedback != nullptr);
+
+ // Current transform feedback must be active and paused in order to resume (3.0.2 pg 86)
+ if (!transformFeedback->isActive() || !transformFeedback->isPaused())
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribI4i(Context *context, GLuint index, GLint x, GLint y, GLint z, GLint w)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttribI4ui(Context *context,
+ GLuint index,
+ GLuint x,
+ GLuint y,
+ GLuint z,
+ GLuint w)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttribI4iv(Context *context, GLuint index, const GLint *v)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateVertexAttribI4uiv(Context *context, GLuint index, const GLuint *v)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateGetFragDataLocation(Context *context, GLuint program, const GLchar *name)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (!programObject->isLinked())
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformIndices(Context *context,
+ GLuint program,
+ GLsizei uniformCount,
+ const GLchar *const *uniformNames,
+ GLuint *uniformIndices)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (uniformCount < 0)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniformsiv(Context *context,
+ GLuint program,
+ GLsizei uniformCount,
+ const GLuint *uniformIndices,
+ GLenum pname,
+ GLint *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (uniformCount < 0)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ switch (pname)
+ {
+ case GL_UNIFORM_TYPE:
+ case GL_UNIFORM_SIZE:
+ case GL_UNIFORM_NAME_LENGTH:
+ case GL_UNIFORM_BLOCK_INDEX:
+ case GL_UNIFORM_OFFSET:
+ case GL_UNIFORM_ARRAY_STRIDE:
+ case GL_UNIFORM_MATRIX_STRIDE:
+ case GL_UNIFORM_IS_ROW_MAJOR:
+ break;
+
+ default:
+ context->handleError(InvalidEnum());
+ return false;
+ }
+
+ if (uniformCount > programObject->getActiveUniformCount())
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ for (int uniformId = 0; uniformId < uniformCount; uniformId++)
+ {
+ const GLuint index = uniformIndices[uniformId];
+
+ if (index >= static_cast<GLuint>(programObject->getActiveUniformCount()))
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateGetUniformBlockIndex(Context *context, GLuint program, const GLchar *uniformBlockName)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetActiveUniformBlockiv(Context *context,
+ GLuint program,
+ GLuint uniformBlockIndex,
+ GLenum pname,
+ GLint *params)
+{
+ return ValidateGetActiveUniformBlockivBase(context, program, uniformBlockIndex, pname, nullptr);
+}
+
+bool ValidateGetActiveUniformBlockName(Context *context,
+ GLuint program,
+ GLuint uniformBlockIndex,
+ GLsizei bufSize,
+ GLsizei *length,
+ GLchar *uniformBlockName)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ if (uniformBlockIndex >= programObject->getActiveUniformBlockCount())
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateUniformBlockBinding(Context *context,
+ GLuint program,
+ GLuint uniformBlockIndex,
+ GLuint uniformBlockBinding)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (uniformBlockBinding >= context->getCaps().maxUniformBufferBindings)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ Program *programObject = GetValidProgram(context, program);
+ if (!programObject)
+ {
+ return false;
+ }
+
+ // if never linked, there won't be any uniform blocks
+ if (uniformBlockIndex >= programObject->getActiveUniformBlockCount())
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDrawArraysInstanced(Context *context,
+ GLenum mode,
+ GLint first,
+ GLsizei count,
+ GLsizei primcount)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateDrawArraysInstancedBase(context, mode, first, count, primcount);
+}
+
+bool ValidateFenceSync(Context *context, GLenum condition, GLbitfield flags)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (condition != GL_SYNC_GPU_COMMANDS_COMPLETE)
+ {
+ context->handleError(InvalidEnum());
+ return false;
+ }
+
+ if (flags != 0)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsSync(Context *context, GLsync sync)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateDeleteSync(Context *context, GLsync sync)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (sync != static_cast<GLsync>(0) && !context->getSync(sync))
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateClientWaitSync(Context *context, GLsync sync, GLbitfield flags, GLuint64 timeout)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if ((flags & ~(GL_SYNC_FLUSH_COMMANDS_BIT)) != 0)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ Sync *clientWaitSync = context->getSync(sync);
+ if (!clientWaitSync)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateWaitSync(Context *context, GLsync sync, GLbitfield flags, GLuint64 timeout)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (flags != 0)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ if (timeout != GL_TIMEOUT_IGNORED)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ Sync *waitSync = context->getSync(sync);
+ if (!waitSync)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetInteger64v(Context *context, GLenum pname, GLint64 *params)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ GLenum nativeType = GL_NONE;
+ unsigned int numParams = 0;
+ if (!ValidateStateQuery(context, pname, &nativeType, &numParams))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateIsSampler(Context *context, GLuint sampler)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateBindSampler(Context *context, GLuint unit, GLuint sampler)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (sampler != 0 && !context->isSampler(sampler))
+ {
+ context->handleError(InvalidOperation());
+ return false;
+ }
+
+ if (unit >= context->getCaps().maxCombinedTextureImageUnits)
+ {
+ context->handleError(InvalidValue());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateVertexAttribDivisor(Context *context, GLuint index, GLuint divisor)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ return ValidateVertexAttribIndex(context, index);
+}
+
+bool ValidateTexStorage2D(Context *context,
+ GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (!ValidateES3TexStorage2DParameters(context, target, levels, internalformat, width, height,
+ 1))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateTexStorage3D(Context *context,
+ GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth)
+{
+ if (context->getClientMajorVersion() < 3)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+ return false;
+ }
+
+ if (!ValidateES3TexStorage3DParameters(context, target, levels, internalformat, width, height,
+ depth))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateGetBufferParameteri64v(ValidationContext *context,
+ BufferBinding target,
+ GLenum pname,
+ GLint64 *params)
+{
+ return ValidateGetBufferParameterBase(context, target, pname, false, nullptr);
+}
+
+bool ValidateGetSamplerParameterfv(Context *context, GLuint sampler, GLenum pname, GLfloat *params)
+{
+ return ValidateGetSamplerParameterBase(context, sampler, pname, nullptr);
+}
+
+bool ValidateGetSamplerParameteriv(Context *context, GLuint sampler, GLenum pname, GLint *params)
+{
+ return ValidateGetSamplerParameterBase(context, sampler, pname, nullptr);
+}
+
+bool ValidateSamplerParameterf(Context *context, GLuint sampler, GLenum pname, GLfloat param)
+{
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, &param);
+}
+
+bool ValidateSamplerParameterfv(Context *context,
+ GLuint sampler,
+ GLenum pname,
+ const GLfloat *params)
+{
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, params);
+}
+
+bool ValidateSamplerParameteri(Context *context, GLuint sampler, GLenum pname, GLint param)
+{
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, &param);
+}
+
+bool ValidateSamplerParameteriv(Context *context, GLuint sampler, GLenum pname, const GLint *params)
+{
+ return ValidateSamplerParameterBase(context, sampler, pname, -1, params);
+}
+
+bool ValidateGetVertexAttribIiv(Context *context, GLuint index, GLenum pname, GLint *params)
+{
+ return ValidateGetVertexAttribBase(context, index, pname, nullptr, false, true);
+}
+
+bool ValidateGetVertexAttribIuiv(Context *context, GLuint index, GLenum pname, GLuint *params)
+{
+ return ValidateGetVertexAttribBase(context, index, pname, nullptr, false, true);
+}
+
+bool ValidateGetInternalformativ(Context *context,
+ GLenum target,
+ GLenum internalformat,
+ GLenum pname,
+ GLsizei bufSize,
+ GLint *params)
+{
+ return ValidateGetInternalFormativBase(context, target, internalformat, pname, bufSize,
+ nullptr);
+}
+
} // namespace gl