diff options
Diffstat (limited to 'src/3rdparty/angle/src/libGLESv2/validationES.cpp')
-rw-r--r-- | src/3rdparty/angle/src/libGLESv2/validationES.cpp | 757 |
1 files changed, 547 insertions, 210 deletions
diff --git a/src/3rdparty/angle/src/libGLESv2/validationES.cpp b/src/3rdparty/angle/src/libGLESv2/validationES.cpp index 309c4daedb..f79bc97e4f 100644 --- a/src/3rdparty/angle/src/libGLESv2/validationES.cpp +++ b/src/3rdparty/angle/src/libGLESv2/validationES.cpp @@ -1,4 +1,3 @@ -#include "precompiled.h" // // Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be @@ -20,6 +19,7 @@ #include "libGLESv2/ProgramBinary.h" #include "libGLESv2/TransformFeedback.h" #include "libGLESv2/VertexArray.h" +#include "libGLESv2/renderer/BufferImpl.h" #include "common/mathutil.h" #include "common/utilities.h" @@ -168,7 +168,7 @@ bool ValidMipLevel(const Context *context, GLenum target, GLint level) return level <= gl::log2(maxDimension); } -bool ValidImageSize(const gl::Context *context, GLenum target, GLint level, +bool ValidImageSize(const Context *context, GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth) { if (level < 0 || width < 0 || height < 0 || depth < 0) @@ -190,17 +190,16 @@ bool ValidImageSize(const gl::Context *context, GLenum target, GLint level, return true; } -bool ValidCompressedImageSize(const gl::Context *context, GLenum internalFormat, GLsizei width, GLsizei height) +bool ValidCompressedImageSize(const Context *context, GLenum internalFormat, GLsizei width, GLsizei height) { - if (!IsFormatCompressed(internalFormat)) + const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); + if (!formatInfo.compressed) { return false; } - GLint blockWidth = GetCompressedBlockWidth(internalFormat); - GLint blockHeight = GetCompressedBlockHeight(internalFormat); - if (width < 0 || (width > blockWidth && width % blockWidth != 0) || - height < 0 || (height > blockHeight && height % blockHeight != 0)) + if (width < 0 || (static_cast<GLuint>(width) > formatInfo.compressedBlockWidth && width % formatInfo.compressedBlockWidth != 0) || + height < 0 || (static_cast<GLuint>(height) > formatInfo.compressedBlockHeight && height % formatInfo.compressedBlockHeight != 0)) { return false; } @@ -225,7 +224,7 @@ bool ValidQueryType(const Context *context, GLenum queryType) } } -bool ValidProgram(const Context *context, GLuint id) +bool ValidProgram(Context *context, GLuint id) { // ES3 spec (section 2.11.1) -- "Commands that accept shader or program object names will generate the // error INVALID_VALUE if the provided name is not the name of either a shader or program object and @@ -238,16 +237,18 @@ bool ValidProgram(const Context *context, GLuint id) else if (context->getShader(id) != NULL) { // ID is the wrong type - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } else { // No shader/program object has this ID - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } } -bool ValidateAttachmentTarget(const gl::Context *context, GLenum attachment) +bool ValidateAttachmentTarget(gl::Context *context, GLenum attachment) { if (attachment >= GL_COLOR_ATTACHMENT0_EXT && attachment <= GL_COLOR_ATTACHMENT15_EXT) { @@ -255,7 +256,8 @@ bool ValidateAttachmentTarget(const gl::Context *context, GLenum attachment) if (colorAttachment >= context->getCaps().maxColorAttachments) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } } else @@ -269,19 +271,21 @@ bool ValidateAttachmentTarget(const gl::Context *context, GLenum attachment) case GL_DEPTH_STENCIL_ATTACHMENT: if (context->getClientVersion() < 3) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } } return true; } -bool ValidateRenderbufferStorageParameters(const gl::Context *context, GLenum target, GLsizei samples, +bool ValidateRenderbufferStorageParameters(gl::Context *context, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, bool angleExtension) { @@ -290,43 +294,44 @@ bool ValidateRenderbufferStorageParameters(const gl::Context *context, GLenum ta case GL_RENDERBUFFER: break; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } if (width < 0 || height < 0 || samples < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } - if (!gl::IsValidInternalFormat(internalformat, context->getExtensions(), context->getClientVersion())) + const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat); + if (!formatCaps.renderable) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } // ANGLE_framebuffer_multisample does not explicitly state that the internal format must be // sized but it does state that the format must be in the ES2.0 spec table 4.5 which contains // only sized internal formats. The ES3 spec (section 4.4.2) does, however, state that the // internal format must be sized and not an integer format if samples is greater than zero. - if (!gl::IsSizedInternalFormat(internalformat)) + const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat); + if (formatInfo.pixelBytes == 0) { - return gl::error(GL_INVALID_ENUM, false); - } - - GLenum componentType = gl::GetComponentType(internalformat); - if ((componentType == GL_UNSIGNED_INT || componentType == GL_INT) && samples > 0) - { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } - const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat); - if (!formatCaps.renderable) + if ((formatInfo.componentType == GL_UNSIGNED_INT || formatInfo.componentType == GL_INT) && samples > 0) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (static_cast<GLuint>(std::max(width, height)) > context->getCaps().maxRenderbufferSize) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } // ANGLE_framebuffer_multisample states that the value of samples must be less than or equal @@ -335,23 +340,34 @@ bool ValidateRenderbufferStorageParameters(const gl::Context *context, GLenum ta // internal format. if (angleExtension) { - if (samples > context->getMaxSupportedSamples()) + ASSERT(context->getExtensions().framebufferMultisample); + if (static_cast<GLuint>(samples) > context->getExtensions().maxSamples) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + + // Check if this specific format supports enough samples + if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples()) + { + context->recordError(Error(GL_OUT_OF_MEMORY)); + return false; } } else { - if (samples > context->getMaxSupportedFormatSamples(internalformat)) + if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples()) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } } GLuint handle = context->getState().getRenderbufferId(); if (handle == 0) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } return true; @@ -362,7 +378,8 @@ bool ValidateFramebufferRenderbufferParameters(gl::Context *context, GLenum targ { if (!ValidFramebufferTarget(target)) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } gl::Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); @@ -370,7 +387,8 @@ bool ValidateFramebufferRenderbufferParameters(gl::Context *context, GLenum targ if (!framebuffer || (framebufferHandle == 0 && renderbuffer != 0)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (!ValidateAttachmentTarget(context, attachment)) @@ -386,7 +404,8 @@ bool ValidateFramebufferRenderbufferParameters(gl::Context *context, GLenum targ { if (!context->getRenderbuffer(renderbuffer)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } @@ -428,16 +447,19 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint case GL_LINEAR: if (fromAngleExtension) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)) != 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } if (mask == 0) @@ -450,14 +472,16 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint if (fromAngleExtension && (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0)) { ERR("Scaling and flipping in BlitFramebufferANGLE not supported by this implementation."); - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } // ES3.0 spec, section 4.3.2 states that linear filtering is only available for the // color buffer, leaving only nearest being unfiltered from above if ((mask & ~GL_COLOR_BUFFER_BIT) != 0 && filter != GL_NEAREST) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (context->getState().getReadFramebuffer()->id() == context->getState().getDrawFramebuffer()->id()) @@ -467,7 +491,8 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint ERR("Blits with the same source and destination framebuffer are not supported by this " "implementation."); } - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } gl::Framebuffer *readFramebuffer = context->getState().getReadFramebuffer(); @@ -475,12 +500,14 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint if (!readFramebuffer || readFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE || !drawFramebuffer || drawFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE) { - return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false); + context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION)); + return false; } if (drawFramebuffer->getSamples() != 0) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } bool sameBounds = srcX0 == dstX0 && srcY0 == dstY0 && srcX1 == dstX1 && srcY1 == dstY1; @@ -493,45 +520,50 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint if (readColorBuffer && drawColorBuffer) { GLenum readInternalFormat = readColorBuffer->getActualFormat(); - GLenum readComponentType = gl::GetComponentType(readInternalFormat); + const InternalFormat &readFormatInfo = GetInternalFormatInfo(readInternalFormat); for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; i++) { if (drawFramebuffer->isEnabledColorAttachment(i)) { GLenum drawInternalFormat = drawFramebuffer->getColorbuffer(i)->getActualFormat(); - GLenum drawComponentType = gl::GetComponentType(drawInternalFormat); + const InternalFormat &drawFormatInfo = GetInternalFormatInfo(drawInternalFormat); // The GL ES 3.0.2 spec (pg 193) states that: // 1) If the read buffer is fixed point format, the draw buffer must be as well // 2) If the read buffer is an unsigned integer format, the draw buffer must be as well // 3) If the read buffer is a signed integer format, the draw buffer must be as well - if ( (readComponentType == GL_UNSIGNED_NORMALIZED || readComponentType == GL_SIGNED_NORMALIZED) && - !(drawComponentType == GL_UNSIGNED_NORMALIZED || drawComponentType == GL_SIGNED_NORMALIZED)) + if ( (readFormatInfo.componentType == GL_UNSIGNED_NORMALIZED || readFormatInfo.componentType == GL_SIGNED_NORMALIZED) && + !(drawFormatInfo.componentType == GL_UNSIGNED_NORMALIZED || drawFormatInfo.componentType == GL_SIGNED_NORMALIZED)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - if (readComponentType == GL_UNSIGNED_INT && drawComponentType != GL_UNSIGNED_INT) + if (readFormatInfo.componentType == GL_UNSIGNED_INT && drawFormatInfo.componentType != GL_UNSIGNED_INT) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - if (readComponentType == GL_INT && drawComponentType != GL_INT) + if (readFormatInfo.componentType == GL_INT && drawFormatInfo.componentType != GL_INT) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (readColorBuffer->getSamples() > 0 && (readInternalFormat != drawInternalFormat || !sameBounds)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } } - if ((readComponentType == GL_INT || readComponentType == GL_UNSIGNED_INT) && filter == GL_LINEAR) + if ((readFormatInfo.componentType == GL_INT || readFormatInfo.componentType == GL_UNSIGNED_INT) && filter == GL_LINEAR) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (fromAngleExtension) @@ -539,7 +571,8 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint const GLenum readColorbufferType = readFramebuffer->getReadColorbufferType(); if (readColorbufferType != GL_TEXTURE_2D && readColorbufferType != GL_RENDERBUFFER) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++) @@ -551,12 +584,14 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint if (attachment->type() != GL_TEXTURE_2D && attachment->type() != GL_RENDERBUFFER) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (attachment->getActualFormat() != readColorBuffer->getActualFormat()) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } } @@ -564,7 +599,8 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } } @@ -579,12 +615,14 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint { if (readDepthBuffer->getActualFormat() != drawDepthBuffer->getActualFormat()) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (readDepthBuffer->getSamples() > 0 && !sameBounds) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (fromAngleExtension) @@ -593,12 +631,14 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1)) { ERR("Only whole-buffer depth and stencil blits are supported by this implementation."); - return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted + context->recordError(Error(GL_INVALID_OPERATION)); // only whole-buffer copies are permitted + return false; } if (readDepthBuffer->getSamples() != 0 || drawDepthBuffer->getSamples() != 0) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } } @@ -613,12 +653,14 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint { if (readStencilBuffer->getActualFormat() != drawStencilBuffer->getActualFormat()) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (readStencilBuffer->getSamples() > 0 && !sameBounds) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (fromAngleExtension) @@ -627,12 +669,14 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1)) { ERR("Only whole-buffer depth and stencil blits are supported by this implementation."); - return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted + context->recordError(Error(GL_INVALID_OPERATION)); // only whole-buffer copies are permitted + return false; } if (readStencilBuffer->getSamples() != 0 || drawStencilBuffer->getSamples() != 0) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } } @@ -641,7 +685,7 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint return true; } -bool ValidateGetVertexAttribParameters(GLenum pname, int clientVersion) +bool ValidateGetVertexAttribParameters(Context *context, GLenum pname) { switch (pname) { @@ -661,10 +705,16 @@ bool ValidateGetVertexAttribParameters(GLenum pname, int clientVersion) return true; case GL_VERTEX_ATTRIB_ARRAY_INTEGER: - return ((clientVersion >= 3) ? true : gl::error(GL_INVALID_ENUM, false)); + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } } @@ -685,7 +735,8 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_TEXTURE_MAX_LOD: if (context->getClientVersion() < 3) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; @@ -704,7 +755,8 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_MIRRORED_REPEAT: return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } case GL_TEXTURE_MIN_FILTER: @@ -718,7 +770,8 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_LINEAR_MIPMAP_LINEAR: return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; @@ -729,7 +782,8 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_LINEAR: return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; @@ -740,20 +794,23 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_FRAMEBUFFER_ATTACHMENT_ANGLE: return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; case GL_TEXTURE_MAX_ANISOTROPY_EXT: if (!context->getExtensions().textureFilterAnisotropic) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } // we assume the parameter passed to this validation method is truncated, not rounded if (param < 1) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } return true; @@ -770,7 +827,8 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_COMPARE_REF_TO_TEXTURE: return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; @@ -788,7 +846,8 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_NEVER: return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; @@ -806,7 +865,8 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_ONE: return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; @@ -814,16 +874,18 @@ bool ValidateTexParamParameters(gl::Context *context, GLenum pname, GLint param) case GL_TEXTURE_MAX_LEVEL: if (param < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } } -bool ValidateSamplerObjectParameter(GLenum pname) +bool ValidateSamplerObjectParameter(gl::Context *context, GLenum pname) { switch (pname) { @@ -839,7 +901,8 @@ bool ValidateSamplerObjectParameter(GLenum pname) return true; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } } @@ -851,17 +914,20 @@ bool ValidateReadPixelsParameters(gl::Context *context, GLint x, GLint y, GLsize if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE) { - return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false); + context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION)); + return false; } if (context->getState().getReadFramebuffer()->id() != 0 && framebuffer->getSamples() != 0) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (!framebuffer->getReadColorbuffer()) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } GLenum currentInternalFormat, currentFormat, currentType; @@ -874,20 +940,22 @@ bool ValidateReadPixelsParameters(gl::Context *context, GLint x, GLint y, GLsize if (!(currentFormat == format && currentType == type) && !validReadFormat) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - GLenum sizedInternalFormat = IsSizedInternalFormat(format) ? format - : GetSizedInternalFormat(format, type); + GLenum sizedInternalFormat = GetSizedInternalFormat(format, type); + const InternalFormat &sizedFormatInfo = GetInternalFormatInfo(sizedInternalFormat); - GLsizei outputPitch = GetRowPitch(sizedInternalFormat, type, width, context->getState().getPackAlignment()); + GLsizei outputPitch = sizedFormatInfo.computeRowPitch(type, width, context->getState().getPackAlignment()); // sized query sanity check if (bufSize) { int requiredSize = outputPitch * height; if (requiredSize > *bufSize) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } @@ -898,12 +966,14 @@ bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) { if (!ValidQueryType(context, target)) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } if (id == 0) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } // From EXT_occlusion_query_boolean: If BeginQueryEXT is called with an <id> @@ -923,7 +993,8 @@ bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) // no query may be active for either if glBeginQuery targets either. if (context->getState().isQueryActive()) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } Query *queryObject = context->getQuery(id, true, target); @@ -931,13 +1002,15 @@ bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) // check that name was obtained with glGenQueries if (!queryObject) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } // check for type mismatch if (queryObject->getType() != target) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } return true; @@ -947,19 +1020,16 @@ bool ValidateEndQuery(gl::Context *context, GLenum target) { if (!ValidQueryType(context, target)) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } const Query *queryObject = context->getState().getActiveQuery(target); if (queryObject == NULL) { - return gl::error(GL_INVALID_OPERATION, false); - } - - if (!queryObject->isStarted()) - { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } return true; @@ -970,13 +1040,15 @@ static bool ValidateUniformCommonBase(gl::Context *context, GLenum targetUniform { if (count < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary(); if (!programBinary) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (location == -1) @@ -987,7 +1059,8 @@ static bool ValidateUniformCommonBase(gl::Context *context, GLenum targetUniform if (!programBinary->isValidUniformLocation(location)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } LinkedUniform *uniform = programBinary->getUniformByLocation(location); @@ -995,7 +1068,8 @@ static bool ValidateUniformCommonBase(gl::Context *context, GLenum targetUniform // attempting to write an array to a non-array uniform is an INVALID_OPERATION if (uniform->elementCount() == 1 && count > 1) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } *uniformOut = uniform; @@ -1007,7 +1081,8 @@ bool ValidateUniform(gl::Context *context, GLenum uniformType, GLint location, G // Check for ES3 uniform entry points if (VariableComponentType(uniformType) == GL_UNSIGNED_INT && context->getClientVersion() < 3) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } LinkedUniform *uniform = NULL; @@ -1020,7 +1095,8 @@ bool ValidateUniform(gl::Context *context, GLenum uniformType, GLint location, G bool samplerUniformCheck = (IsSampler(uniform->type) && uniformType == GL_INT); if (!samplerUniformCheck && uniformType != uniform->type && targetBoolType != uniform->type) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } return true; @@ -1034,12 +1110,14 @@ bool ValidateUniformMatrix(gl::Context *context, GLenum matrixType, GLint locati int cols = VariableColumnCount(matrixType); if (rows != cols && context->getClientVersion() < 3) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (transpose != GL_FALSE && context->getClientVersion() < 3) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } LinkedUniform *uniform = NULL; @@ -1050,7 +1128,8 @@ bool ValidateUniformMatrix(gl::Context *context, GLenum matrixType, GLint locati if (uniform->type != matrixType) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } return true; @@ -1060,7 +1139,8 @@ bool ValidateStateQuery(gl::Context *context, GLenum pname, GLenum *nativeType, { if (!context->getQueryParameterInfo(pname, nativeType, numParams)) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } if (pname >= GL_DRAW_BUFFER0 && pname <= GL_DRAW_BUFFER15) @@ -1069,7 +1149,8 @@ bool ValidateStateQuery(gl::Context *context, GLenum pname, GLenum *nativeType, if (colorAttachment >= context->getCaps().maxDrawBuffers) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } @@ -1079,9 +1160,10 @@ bool ValidateStateQuery(gl::Context *context, GLenum pname, GLenum *nativeType, case GL_TEXTURE_BINDING_CUBE_MAP: case GL_TEXTURE_BINDING_3D: case GL_TEXTURE_BINDING_2D_ARRAY: - if (context->getState().getActiveSampler() >= context->getMaximumCombinedTextureImageUnits()) + if (context->getState().getActiveSampler() >= context->getCaps().maxCombinedTextureImageUnits) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } break; @@ -1092,13 +1174,15 @@ bool ValidateStateQuery(gl::Context *context, GLenum pname, GLenum *nativeType, ASSERT(framebuffer); if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } FramebufferAttachment *attachment = framebuffer->getReadColorbuffer(); if (!attachment) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } break; @@ -1123,46 +1207,51 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi if (!ValidTexture2DDestinationTarget(context, target)) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } if (level < 0 || xoffset < 0 || yoffset < 0 || zoffset < 0 || width < 0 || height < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } if (std::numeric_limits<GLsizei>::max() - xoffset < width || std::numeric_limits<GLsizei>::max() - yoffset < height) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } if (border != 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } if (!ValidMipLevel(context, target, level)) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE) { - return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false); + context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION)); + return false; } if (context->getState().getReadFramebuffer()->id() != 0 && framebuffer->getSamples() != 0) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } const gl::Caps &caps = context->getCaps(); gl::Texture *texture = NULL; GLenum textureInternalFormat = GL_NONE; - bool textureCompressed = false; - bool textureIsDepth = false; GLint textureLevelWidth = 0; GLint textureLevelHeight = 0; GLint textureLevelDepth = 0; @@ -1176,8 +1265,6 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi if (texture2d) { textureInternalFormat = texture2d->getInternalFormat(level); - textureCompressed = texture2d->isCompressed(level); - textureIsDepth = texture2d->isDepth(level); textureLevelWidth = texture2d->getWidth(level); textureLevelHeight = texture2d->getHeight(level); textureLevelDepth = 1; @@ -1198,8 +1285,6 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi if (textureCube) { textureInternalFormat = textureCube->getInternalFormat(target, level); - textureCompressed = textureCube->isCompressed(target, level); - textureIsDepth = false; textureLevelWidth = textureCube->getWidth(target, level); textureLevelHeight = textureCube->getHeight(target, level); textureLevelDepth = 1; @@ -1215,8 +1300,6 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi if (texture2dArray) { textureInternalFormat = texture2dArray->getInternalFormat(level); - textureCompressed = texture2dArray->isCompressed(level); - textureIsDepth = texture2dArray->isDepth(level); textureLevelWidth = texture2dArray->getWidth(level); textureLevelHeight = texture2dArray->getHeight(level); textureLevelDepth = texture2dArray->getLayers(level); @@ -1232,8 +1315,6 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi if (texture3d) { textureInternalFormat = texture3d->getInternalFormat(level); - textureCompressed = texture3d->isCompressed(level); - textureIsDepth = texture3d->isDepth(level); textureLevelWidth = texture3d->getWidth(level); textureLevelHeight = texture3d->getHeight(level); textureLevelDepth = texture3d->getDepth(level); @@ -1244,33 +1325,37 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi break; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } if (!texture) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (texture->isImmutable() && !isSubImage) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - if (textureIsDepth) + const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat); + + if (formatInfo.depthBits > 0) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - if (textureCompressed) + if (formatInfo.compressed) { - GLint blockWidth = GetCompressedBlockWidth(textureInternalFormat); - GLint blockHeight = GetCompressedBlockHeight(textureInternalFormat); - - if (((width % blockWidth) != 0 && width != textureLevelWidth) || - ((height % blockHeight) != 0 && height != textureLevelHeight)) + if (((width % formatInfo.compressedBlockWidth) != 0 && width != textureLevelWidth) || + ((height % formatInfo.compressedBlockHeight) != 0 && height != textureLevelHeight)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } @@ -1280,25 +1365,29 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi yoffset + height > textureLevelHeight || zoffset >= textureLevelDepth) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } } else { if (IsCubemapTextureTarget(target) && width != height) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } - if (!IsValidInternalFormat(internalformat, context->getExtensions(), context->getClientVersion())) + if (!formatInfo.textureSupport(context->getClientVersion(), context->getExtensions())) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } int maxLevelDimension = (maxDimension >> level); if (static_cast<int>(width) > maxLevelDimension || static_cast<int>(height) > maxLevelDimension) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } } @@ -1306,7 +1395,7 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi return true; } -static bool ValidateDrawBase(const gl::Context *context, GLenum mode, GLsizei count) +static bool ValidateDrawBase(Context *context, GLenum mode, GLsizei count, GLsizei maxVertex, GLsizei primcount) { switch (mode) { @@ -1319,71 +1408,128 @@ static bool ValidateDrawBase(const gl::Context *context, GLenum mode, GLsizei co case GL_TRIANGLE_FAN: break; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } if (count < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } + const State &state = context->getState(); + // Check for mapped buffers - if (context->hasMappedBuffer(GL_ARRAY_BUFFER)) + if (state.hasMappedBuffer(GL_ARRAY_BUFFER)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - const gl::DepthStencilState &depthStencilState = context->getState().getDepthStencilState(); + const gl::DepthStencilState &depthStencilState = state.getDepthStencilState(); if (depthStencilState.stencilWritemask != depthStencilState.stencilBackWritemask || - context->getState().getStencilRef() != context->getState().getStencilBackRef() || + state.getStencilRef() != state.getStencilBackRef() || depthStencilState.stencilMask != depthStencilState.stencilBackMask) { // Note: these separate values are not supported in WebGL, due to D3D's limitations. // See Section 6.10 of the WebGL 1.0 spec ERR("This ANGLE implementation does not support separate front/back stencil " "writemasks, reference values, or stencil mask values."); - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - const gl::Framebuffer *fbo = context->getState().getDrawFramebuffer(); + const gl::Framebuffer *fbo = state.getDrawFramebuffer(); if (!fbo || fbo->completeness() != GL_FRAMEBUFFER_COMPLETE) { - return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false); + context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION)); + return false; + } + + if (state.getCurrentProgramId() == 0) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - if (context->getState().getCurrentProgramId() == 0) + gl::ProgramBinary *programBinary = state.getCurrentProgramBinary(); + if (!programBinary->validateSamplers(NULL, context->getCaps())) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary(); - if (!programBinary->validateSamplers(NULL)) + // Buffer validations + const VertexArray *vao = state.getVertexArray(); + for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++) { - return gl::error(GL_INVALID_OPERATION, false); + const VertexAttribute &attrib = vao->getVertexAttribute(attributeIndex); + bool attribActive = (programBinary->getSemanticIndex(attributeIndex) != -1); + if (attribActive && attrib.enabled) + { + gl::Buffer *buffer = attrib.buffer.get(); + + if (buffer) + { + GLint64 attribStride = static_cast<GLint64>(ComputeVertexAttributeStride(attrib)); + GLint64 maxVertexElement = 0; + + if (attrib.divisor > 0) + { + maxVertexElement = static_cast<GLint64>(primcount) / static_cast<GLint64>(attrib.divisor); + } + else + { + maxVertexElement = static_cast<GLint64>(maxVertex); + } + + GLint64 attribDataSize = maxVertexElement * attribStride; + + // [OpenGL ES 3.0.2] section 2.9.4 page 40: + // We can return INVALID_OPERATION if our vertex attribute does not have + // enough backing data. + if (attribDataSize > buffer->getSize()) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + } + else if (attrib.pointer == NULL) + { + // This is an application error that would normally result in a crash, + // but we catch it and return an error + context->recordError(Error(GL_INVALID_OPERATION, "An enabled vertex array has no buffer and no pointer.")); + return false; + } + } } // No-op if zero count return (count > 0); } -bool ValidateDrawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count) +bool ValidateDrawArrays(Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount) { if (first < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } - gl::TransformFeedback *curTransformFeedback = context->getState().getCurrentTransformFeedback(); + const State &state = context->getState(); + gl::TransformFeedback *curTransformFeedback = state.getCurrentTransformFeedback(); if (curTransformFeedback && curTransformFeedback->isStarted() && !curTransformFeedback->isPaused() && curTransformFeedback->getDrawMode() != mode) { // It is an invalid operation to call DrawArrays or DrawArraysInstanced with a draw mode // that does not match the current transform feedback object's draw mode (if transform feedback // is active), (3.0.2, section 2.14, pg 86) - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - if (!ValidateDrawBase(context, mode, count)) + if (!ValidateDrawBase(context, mode, count, count, primcount)) { return false; } @@ -1391,14 +1537,15 @@ bool ValidateDrawArrays(const gl::Context *context, GLenum mode, GLint first, GL return true; } -bool ValidateDrawArraysInstanced(const gl::Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount) +bool ValidateDrawArraysInstanced(Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount) { if (primcount < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } - if (!ValidateDrawArrays(context, mode, first, count)) + if (!ValidateDrawArrays(context, mode, first, count, primcount)) { return false; } @@ -1407,7 +1554,41 @@ bool ValidateDrawArraysInstanced(const gl::Context *context, GLenum mode, GLint return (primcount > 0); } -bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) +static bool ValidateDrawInstancedANGLE(Context *context) +{ + // Verify there is at least one active attribute with a divisor of zero + const gl::State& state = context->getState(); + + gl::ProgramBinary *programBinary = state.getCurrentProgramBinary(); + + const VertexArray *vao = state.getVertexArray(); + for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++) + { + const VertexAttribute &attrib = vao->getVertexAttribute(attributeIndex); + bool active = (programBinary->getSemanticIndex(attributeIndex) != -1); + if (active && attrib.divisor == 0) + { + return true; + } + } + + context->recordError(Error(GL_INVALID_OPERATION, "ANGLE_instanced_arrays requires that at least one active attribute" + "has a divisor of zero.")); + return false; +} + +bool ValidateDrawArraysInstancedANGLE(Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount) +{ + if (!ValidateDrawInstancedANGLE(context)) + { + return false; + } + + return ValidateDrawArraysInstanced(context, mode, first, count, primcount); +} + +bool ValidateDrawElements(Context *context, GLenum mode, GLsizei count, GLenum type, + const GLvoid* indices, GLsizei primcount, rx::RangeUI *indexRangeOut) { switch (type) { @@ -1417,34 +1598,89 @@ bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count case GL_UNSIGNED_INT: if (!context->getExtensions().elementIndexUint) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } break; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } - gl::TransformFeedback *curTransformFeedback = context->getState().getCurrentTransformFeedback(); + const State &state = context->getState(); + + gl::TransformFeedback *curTransformFeedback = state.getCurrentTransformFeedback(); if (curTransformFeedback && curTransformFeedback->isStarted() && !curTransformFeedback->isPaused()) { // It is an invalid operation to call DrawElements, DrawRangeElements or DrawElementsInstanced // while transform feedback is active, (3.0.2, section 2.14, pg 86) - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } // Check for mapped buffers - if (context->hasMappedBuffer(GL_ELEMENT_ARRAY_BUFFER)) + if (state.hasMappedBuffer(GL_ELEMENT_ARRAY_BUFFER)) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + const gl::VertexArray *vao = state.getVertexArray(); + const gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer(); + if (!indices && !elementArrayBuffer) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + if (elementArrayBuffer) { - return gl::error(GL_INVALID_OPERATION, false); + const gl::Type &typeInfo = gl::GetTypeInfo(type); + + GLint64 offset = reinterpret_cast<GLint64>(indices); + GLint64 byteCount = static_cast<GLint64>(typeInfo.bytes) * static_cast<GLint64>(count)+offset; + + // check for integer overflows + if (static_cast<GLuint>(count) > (std::numeric_limits<GLuint>::max() / typeInfo.bytes) || + byteCount > static_cast<GLint64>(std::numeric_limits<GLuint>::max())) + { + context->recordError(Error(GL_OUT_OF_MEMORY)); + return false; + } + + // Check for reading past the end of the bound buffer object + if (byteCount > elementArrayBuffer->getSize()) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + } + else if (!indices) + { + // Catch this programming error here + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } - gl::VertexArray *vao = context->getState().getVertexArray(); - if (!indices && !vao->getElementArrayBuffer()) + // Use max index to validate if our vertex buffers are large enough for the pull. + // TODO: offer fast path, with disabled index validation. + // TODO: also disable index checking on back-ends that are robust to out-of-range accesses. + if (elementArrayBuffer) + { + GLint64 offset = reinterpret_cast<GLint64>(indices); + if (!elementArrayBuffer->getIndexRangeCache()->findRange(type, offset, count, indexRangeOut, NULL)) + { + const void *dataPointer = elementArrayBuffer->getImplementation()->getData(); + const uint8_t *offsetPointer = static_cast<const uint8_t *>(dataPointer) + offset; + *indexRangeOut = rx::IndexRangeCache::ComputeRange(type, offsetPointer, count); + } + } + else { - return gl::error(GL_INVALID_OPERATION, false); + *indexRangeOut = rx::IndexRangeCache::ComputeRange(type, indices, count); } - if (!ValidateDrawBase(context, mode, count)) + if (!ValidateDrawBase(context, mode, count, static_cast<GLsizei>(indexRangeOut->end), primcount)) { return false; } @@ -1452,15 +1688,18 @@ bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count return true; } -bool ValidateDrawElementsInstanced(const gl::Context *context, GLenum mode, GLsizei count, GLenum type, - const GLvoid *indices, GLsizei primcount) +bool ValidateDrawElementsInstanced(Context *context, + GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices, GLsizei primcount, + rx::RangeUI *indexRangeOut) { if (primcount < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } - if (!ValidateDrawElements(context, mode, count, type, indices)) + if (!ValidateDrawElements(context, mode, count, type, indices, primcount, indexRangeOut)) { return false; } @@ -1469,12 +1708,24 @@ bool ValidateDrawElementsInstanced(const gl::Context *context, GLenum mode, GLsi return (primcount > 0); } -bool ValidateFramebufferTextureBase(const gl::Context *context, GLenum target, GLenum attachment, +bool ValidateDrawElementsInstancedANGLE(Context *context, GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices, GLsizei primcount, rx::RangeUI *indexRangeOut) +{ + if (!ValidateDrawInstancedANGLE(context)) + { + return false; + } + + return ValidateDrawElementsInstanced(context, mode, count, type, indices, primcount, indexRangeOut); +} + +bool ValidateFramebufferTextureBase(Context *context, GLenum target, GLenum attachment, GLuint texture, GLint level) { if (!ValidFramebufferTarget(target)) { - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } if (!ValidateAttachmentTarget(context, attachment)) @@ -1488,12 +1739,14 @@ bool ValidateFramebufferTextureBase(const gl::Context *context, GLenum target, G if (tex == NULL) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } if (level < 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } } @@ -1502,19 +1755,21 @@ bool ValidateFramebufferTextureBase(const gl::Context *context, GLenum target, G if (framebufferHandle == 0 || !framebuffer) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } return true; } -bool ValidateFramebufferTexture2D(const gl::Context *context, GLenum target, GLenum attachment, +bool ValidateFramebufferTexture2D(Context *context, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { // Attachments are required to be bound to level 0 in ES2 if (context->getClientVersion() < 3 && level != 0) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level)) @@ -1535,16 +1790,19 @@ bool ValidateFramebufferTexture2D(const gl::Context *context, GLenum target, GLe { if (level > gl::log2(caps.max2DTextureSize)) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } if (tex->getTarget() != GL_TEXTURE_2D) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } gl::Texture2D *tex2d = static_cast<gl::Texture2D *>(tex); if (tex2d->isCompressed(level)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } break; @@ -1558,26 +1816,105 @@ bool ValidateFramebufferTexture2D(const gl::Context *context, GLenum target, GLe { if (level > gl::log2(caps.maxCubeMapTextureSize)) { - return gl::error(GL_INVALID_VALUE, false); + context->recordError(Error(GL_INVALID_VALUE)); + return false; } if (tex->getTarget() != GL_TEXTURE_CUBE_MAP) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } gl::TextureCubeMap *texcube = static_cast<gl::TextureCubeMap *>(tex); if (texcube->isCompressed(textarget, level)) { - return gl::error(GL_INVALID_OPERATION, false); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; } } break; default: - return gl::error(GL_INVALID_ENUM, false); + context->recordError(Error(GL_INVALID_ENUM)); + return false; } } return true; } +bool ValidateGetUniformBase(Context *context, GLuint program, GLint location) +{ + if (program == 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + + gl::Program *programObject = context->getProgram(program); + + if (!programObject || !programObject->isLinked()) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + gl::ProgramBinary *programBinary = programObject->getProgramBinary(); + if (!programBinary) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + if (!programBinary->isValidUniformLocation(location)) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return true; +} + +bool ValidateGetUniformfv(Context *context, GLuint program, GLint location, GLfloat* params) +{ + return ValidateGetUniformBase(context, program, location); +} + +bool ValidateGetUniformiv(Context *context, GLuint program, GLint location, GLint* params) +{ + return ValidateGetUniformBase(context, program, location); +} + +static bool ValidateSizedGetUniform(Context *context, GLuint program, GLint location, GLsizei bufSize) +{ + if (!ValidateGetUniformBase(context, program, location)) + { + return false; + } + + gl::Program *programObject = context->getProgram(program); + ASSERT(programObject); + gl::ProgramBinary *programBinary = programObject->getProgramBinary(); + + // sized queries -- ensure the provided buffer is large enough + LinkedUniform *uniform = programBinary->getUniformByLocation(location); + size_t requiredBytes = VariableExternalSize(uniform->type); + if (static_cast<size_t>(bufSize) < requiredBytes) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return true; +} + +bool ValidateGetnUniformfvEXT(Context *context, GLuint program, GLint location, GLsizei bufSize, GLfloat* params) +{ + return ValidateSizedGetUniform(context, program, location, bufSize); +} + +bool ValidateGetnUniformivEXT(Context *context, GLuint program, GLint location, GLsizei bufSize, GLint* params) +{ + return ValidateSizedGetUniform(context, program, location, bufSize); +} + } |