diff options
Diffstat (limited to 'src/3rdparty/angle/src/libANGLE/validationES.cpp')
-rw-r--r-- | src/3rdparty/angle/src/libANGLE/validationES.cpp | 1377 |
1 files changed, 1065 insertions, 312 deletions
diff --git a/src/3rdparty/angle/src/libANGLE/validationES.cpp b/src/3rdparty/angle/src/libANGLE/validationES.cpp index d267cbf2e6..12c76120bd 100644 --- a/src/3rdparty/angle/src/libANGLE/validationES.cpp +++ b/src/3rdparty/angle/src/libANGLE/validationES.cpp @@ -10,22 +10,90 @@ #include "libANGLE/validationES2.h" #include "libANGLE/validationES3.h" #include "libANGLE/Context.h" +#include "libANGLE/Display.h" #include "libANGLE/Texture.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/formatutils.h" +#include "libANGLE/Image.h" #include "libANGLE/Query.h" #include "libANGLE/Program.h" #include "libANGLE/Uniform.h" #include "libANGLE/TransformFeedback.h" #include "libANGLE/VertexArray.h" -#include "libANGLE/renderer/BufferImpl.h" #include "common/mathutil.h" #include "common/utilities.h" namespace gl { +const char *g_ExceedsMaxElementErrorMessage = "Element value exceeds maximum element index."; + +namespace +{ +bool ValidateDrawAttribs(ValidationContext *context, GLint primcount, GLint maxVertex) +{ + const gl::State &state = context->getState(); + const gl::Program *program = state.getProgram(); + + const VertexArray *vao = state.getVertexArray(); + const auto &vertexAttribs = vao->getVertexAttributes(); + size_t maxEnabledAttrib = vao->getMaxEnabledAttribute(); + for (size_t attributeIndex = 0; attributeIndex < maxEnabledAttrib; ++attributeIndex) + { + const VertexAttribute &attrib = vertexAttribs[attributeIndex]; + if (program->isAttribLocationActive(attributeIndex) && 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); + } + + // If we're drawing zero vertices, we have enough data. + if (maxVertexElement > 0) + { + // Note: Last vertex element does not take the full stride! + GLint64 attribSize = + static_cast<GLint64>(ComputeVertexAttributeTypeSize(attrib)); + GLint64 attribDataSize = (maxVertexElement - 1) * attribStride + attribSize; + + // [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; + } + } + } + + return true; +} + +} // anonymous namespace bool ValidCap(const Context *context, GLenum cap) { @@ -41,15 +109,21 @@ bool ValidCap(const Context *context, GLenum cap) case GL_BLEND: case GL_DITHER: return true; + case GL_PRIMITIVE_RESTART_FIXED_INDEX: case GL_RASTERIZER_DISCARD: return (context->getClientVersion() >= 3); + + case GL_DEBUG_OUTPUT_SYNCHRONOUS: + case GL_DEBUG_OUTPUT: + return context->getExtensions().debug; + default: return false; } } -bool ValidTextureTarget(const Context *context, GLenum target) +bool ValidTextureTarget(const ValidationContext *context, GLenum target) { switch (target) { @@ -66,11 +140,37 @@ bool ValidTextureTarget(const Context *context, GLenum target) } } +bool ValidTexture2DTarget(const ValidationContext *context, GLenum target) +{ + switch (target) + { + case GL_TEXTURE_2D: + case GL_TEXTURE_CUBE_MAP: + return true; + + default: + return false; + } +} + +bool ValidTexture3DTarget(const ValidationContext *context, GLenum target) +{ + switch (target) + { + case GL_TEXTURE_3D: + case GL_TEXTURE_2D_ARRAY: + return (context->getClientVersion() >= 3); + + default: + return false; + } +} + // This function differs from ValidTextureTarget in that the target must be // usable as the destination of a 2D operation-- so a cube face is valid, but // GL_TEXTURE_CUBE_MAP is not. // Note: duplicate of IsInternalTextureTarget -bool ValidTexture2DDestinationTarget(const Context *context, GLenum target) +bool ValidTexture2DDestinationTarget(const ValidationContext *context, GLenum target) { switch (target) { @@ -82,9 +182,18 @@ bool ValidTexture2DDestinationTarget(const Context *context, GLenum target) case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return true; - case GL_TEXTURE_2D_ARRAY: + default: + return false; + } +} + +bool ValidTexture3DDestinationTarget(const ValidationContext *context, GLenum target) +{ + switch (target) + { case GL_TEXTURE_3D: - return (context->getClientVersion() >= 3); + case GL_TEXTURE_2D_ARRAY: + return true; default: return false; } @@ -114,7 +223,7 @@ bool ValidBufferTarget(const Context *context, GLenum target) case GL_PIXEL_PACK_BUFFER: case GL_PIXEL_UNPACK_BUFFER: - return context->getExtensions().pixelBufferObject; + return (context->getExtensions().pixelBufferObject || context->getClientVersion() >= 3); case GL_COPY_READ_BUFFER: case GL_COPY_WRITE_BUFFER: @@ -129,55 +238,79 @@ bool ValidBufferTarget(const Context *context, GLenum target) bool ValidBufferParameter(const Context *context, GLenum pname) { + const Extensions &extensions = context->getExtensions(); + switch (pname) { case GL_BUFFER_USAGE: case GL_BUFFER_SIZE: return true; + case GL_BUFFER_ACCESS_OES: + return extensions.mapBuffer; + + case GL_BUFFER_MAPPED: + static_assert(GL_BUFFER_MAPPED == GL_BUFFER_MAPPED_OES, "GL enums should be equal."); + return (context->getClientVersion() >= 3) || extensions.mapBuffer || extensions.mapBufferRange; + // GL_BUFFER_MAP_POINTER is a special case, and may only be // queried with GetBufferPointerv case GL_BUFFER_ACCESS_FLAGS: - case GL_BUFFER_MAPPED: case GL_BUFFER_MAP_OFFSET: case GL_BUFFER_MAP_LENGTH: - return (context->getClientVersion() >= 3); + return (context->getClientVersion() >= 3) || extensions.mapBufferRange; default: return false; } } -bool ValidMipLevel(const Context *context, GLenum target, GLint level) +bool ValidMipLevel(const ValidationContext *context, GLenum target, GLint level) { + const auto &caps = context->getCaps(); size_t maxDimension = 0; switch (target) { - case GL_TEXTURE_2D: maxDimension = context->getCaps().max2DTextureSize; break; + case GL_TEXTURE_2D: + maxDimension = caps.max2DTextureSize; + break; case GL_TEXTURE_CUBE_MAP: 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: maxDimension = context->getCaps().maxCubeMapTextureSize; break; - case GL_TEXTURE_3D: maxDimension = context->getCaps().max3DTextureSize; break; - case GL_TEXTURE_2D_ARRAY: maxDimension = context->getCaps().max2DTextureSize; break; + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + maxDimension = caps.maxCubeMapTextureSize; + break; + case GL_TEXTURE_3D: + maxDimension = caps.max3DTextureSize; + break; + case GL_TEXTURE_2D_ARRAY: + maxDimension = caps.max2DTextureSize; + break; default: UNREACHABLE(); } - return level <= gl::log2(maxDimension); + return level <= gl::log2(static_cast<int>(maxDimension)); } -bool ValidImageSize(const Context *context, GLenum target, GLint level, - GLsizei width, GLsizei height, GLsizei depth) +bool ValidImageSizeParameters(const Context *context, + GLenum target, + GLint level, + GLsizei width, + GLsizei height, + GLsizei depth, + bool isSubImage) { if (level < 0 || width < 0 || height < 0 || depth < 0) { return false; } - if (!context->getExtensions().textureNPOT && + // TexSubImage parameters can be NPOT without textureNPOT extension, + // as long as the destination texture is POT. + if (!isSubImage && !context->getExtensions().textureNPOT && (level != 0 && (!gl::isPow2(width) || !gl::isPow2(height) || !gl::isPow2(depth)))) { return false; @@ -191,7 +324,28 @@ bool ValidImageSize(const Context *context, GLenum target, GLint level, return true; } -bool ValidCompressedImageSize(const Context *context, GLenum internalFormat, GLsizei width, GLsizei height) +bool CompressedTextureFormatRequiresExactSize(GLenum internalFormat) +{ + // List of compressed format that require that the texture size is smaller than or a multiple of + // the compressed block size. + switch (internalFormat) + { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: + case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: + case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE: + return true; + + default: + return false; + } +} + +bool ValidCompressedImageSize(const ValidationContext *context, + GLenum internalFormat, + GLsizei width, + GLsizei height) { const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); if (!formatInfo.compressed) @@ -199,12 +353,22 @@ bool ValidCompressedImageSize(const Context *context, GLenum internalFormat, GLs return false; } - 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)) + if (width < 0 || height < 0) { return false; } + if (CompressedTextureFormatRequiresExactSize(internalFormat)) + { + if ((static_cast<GLuint>(width) > formatInfo.compressedBlockWidth && + width % formatInfo.compressedBlockWidth != 0) || + (static_cast<GLuint>(height) > formatInfo.compressedBlockHeight && + height % formatInfo.compressedBlockHeight != 0)) + { + return false; + } + } + return true; } @@ -220,33 +384,57 @@ bool ValidQueryType(const Context *context, GLenum queryType) return true; case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: return (context->getClientVersion() >= 3); + case GL_TIME_ELAPSED_EXT: + return context->getExtensions().disjointTimerQuery; default: return false; } } -bool ValidProgram(Context *context, GLuint id) +Program *GetValidProgram(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 // INVALID_OPERATION if the provided name identifies an object that is not the expected type." - if (context->getProgram(id) != NULL) - { - return true; - } - else if (context->getShader(id) != NULL) + Program *validProgram = context->getProgram(id); + + if (!validProgram) { - // ID is the wrong type - context->recordError(Error(GL_INVALID_OPERATION)); - return false; + if (context->getShader(id)) + { + context->recordError( + Error(GL_INVALID_OPERATION, "Expected a program name, but found a shader name")); + } + else + { + context->recordError(Error(GL_INVALID_VALUE, "Program name is not valid")); + } } - else + + return validProgram; +} + +Shader *GetValidShader(Context *context, GLuint id) +{ + // See ValidProgram for spec details. + + Shader *validShader = context->getShader(id); + + if (!validShader) { - // No shader/program object has this ID - context->recordError(Error(GL_INVALID_VALUE)); - return false; + if (context->getProgram(id)) + { + context->recordError( + Error(GL_INVALID_OPERATION, "Expected a shader name, but found a program name")); + } + else + { + context->recordError(Error(GL_INVALID_VALUE, "Shader name is invalid")); + } } + + return validShader; } bool ValidateAttachmentTarget(gl::Context *context, GLenum attachment) @@ -343,9 +531,9 @@ bool ValidateRenderbufferStorageParametersANGLE(gl::Context *context, GLenum tar ASSERT(samples == 0 || context->getExtensions().framebufferMultisample); // ANGLE_framebuffer_multisample states that the value of samples must be less than or equal - // to MAX_SAMPLES_ANGLE (Context::getExtensions().maxSamples) otherwise GL_INVALID_VALUE is + // to MAX_SAMPLES_ANGLE (Context::getCaps().maxSamples) otherwise GL_INVALID_VALUE is // generated. - if (static_cast<GLuint>(samples) > context->getExtensions().maxSamples) + if (static_cast<GLuint>(samples) > context->getCaps().maxSamples) { context->recordError(Error(GL_INVALID_VALUE)); return false; @@ -354,11 +542,15 @@ bool ValidateRenderbufferStorageParametersANGLE(gl::Context *context, GLenum tar // ANGLE_framebuffer_multisample states GL_OUT_OF_MEMORY is generated on a failure to create // the specified storage. This is different than ES 3.0 in which a sample number higher // than the maximum sample number supported by this format generates a GL_INVALID_VALUE. - const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat); - if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples()) + // The TextureCaps::getMaxSamples method is only guarenteed to be valid when the context is ES3. + if (context->getClientVersion() >= 3) { - context->recordError(Error(GL_OUT_OF_MEMORY)); - return false; + const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat); + if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples()) + { + context->recordError(Error(GL_OUT_OF_MEMORY)); + return false; + } } return ValidateRenderbufferStorageParametersBase(context, target, samples, internalformat, width, height); @@ -374,11 +566,11 @@ bool ValidateFramebufferRenderbufferParameters(gl::Context *context, GLenum targ } gl::Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); - GLuint framebufferHandle = context->getState().getTargetFramebuffer(target)->id(); - if (!framebuffer || (framebufferHandle == 0 && renderbuffer != 0)) + ASSERT(framebuffer); + if (framebuffer->id() == 0) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Cannot change default FBO's attachments")); return false; } @@ -403,44 +595,23 @@ bool ValidateFramebufferRenderbufferParameters(gl::Context *context, GLenum targ return true; } -static bool IsPartialBlit(gl::Context *context, gl::FramebufferAttachment *readBuffer, gl::FramebufferAttachment *writeBuffer, - GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1) -{ - if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 || - dstX1 != writeBuffer->getWidth() || dstY1 != writeBuffer->getHeight() || - srcX1 != readBuffer->getWidth() || srcY1 != readBuffer->getHeight()) - { - return true; - } - else if (context->getState().isScissorTestEnabled()) - { - const Rectangle &scissor = context->getState().getScissor(); - - return scissor.x > 0 || scissor.y > 0 || - scissor.width < writeBuffer->getWidth() || - scissor.height < writeBuffer->getHeight(); - } - else - { - return false; - } -} - -bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, - GLenum filter, bool fromAngleExtension) +bool ValidateBlitFramebufferParameters(gl::Context *context, + GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) { switch (filter) { case GL_NEAREST: break; case GL_LINEAR: - if (fromAngleExtension) - { - context->recordError(Error(GL_INVALID_ENUM)); - return false; - } break; default: context->recordError(Error(GL_INVALID_ENUM)); @@ -460,13 +631,6 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint return false; } - if (fromAngleExtension && (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0)) - { - ERR("Scaling and flipping in BlitFramebufferANGLE not supported by this implementation."); - 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) @@ -477,17 +641,12 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint if (context->getState().getReadFramebuffer()->id() == context->getState().getDrawFramebuffer()->id()) { - if (fromAngleExtension) - { - ERR("Blits with the same source and destination framebuffer are not supported by this " - "implementation."); - } context->recordError(Error(GL_INVALID_OPERATION)); return false; } - gl::Framebuffer *readFramebuffer = context->getState().getReadFramebuffer(); - gl::Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer(); + const gl::Framebuffer *readFramebuffer = context->getState().getReadFramebuffer(); + const gl::Framebuffer *drawFramebuffer = context->getState().getDrawFramebuffer(); if (!readFramebuffer || !drawFramebuffer) { @@ -517,39 +676,68 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint if (mask & GL_COLOR_BUFFER_BIT) { - gl::FramebufferAttachment *readColorBuffer = readFramebuffer->getReadColorbuffer(); - gl::FramebufferAttachment *drawColorBuffer = drawFramebuffer->getFirstColorbuffer(); + const gl::FramebufferAttachment *readColorBuffer = readFramebuffer->getReadColorbuffer(); + const gl::FramebufferAttachment *drawColorBuffer = drawFramebuffer->getFirstColorbuffer(); + const Extensions &extensions = context->getExtensions(); if (readColorBuffer && drawColorBuffer) { GLenum readInternalFormat = readColorBuffer->getInternalFormat(); const InternalFormat &readFormatInfo = GetInternalFormatInfo(readInternalFormat); - for (GLuint i = 0; i < context->getCaps().maxColorAttachments; i++) + for (size_t drawbufferIdx = 0; + drawbufferIdx < drawFramebuffer->getDrawbufferStateCount(); ++drawbufferIdx) { - if (drawFramebuffer->isEnabledColorAttachment(i)) + const FramebufferAttachment *attachment = + drawFramebuffer->getDrawBuffer(drawbufferIdx); + if (attachment) { - GLenum drawInternalFormat = drawFramebuffer->getColorbuffer(i)->getInternalFormat(); + GLenum drawInternalFormat = attachment->getInternalFormat(); 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 ( (readFormatInfo.componentType == GL_UNSIGNED_NORMALIZED || readFormatInfo.componentType == GL_SIGNED_NORMALIZED) && - !(drawFormatInfo.componentType == GL_UNSIGNED_NORMALIZED || drawFormatInfo.componentType == GL_SIGNED_NORMALIZED)) + // Changes with EXT_color_buffer_float: + // Case 1) is changed to fixed point OR floating point + GLenum readComponentType = readFormatInfo.componentType; + GLenum drawComponentType = drawFormatInfo.componentType; + bool readFixedPoint = (readComponentType == GL_UNSIGNED_NORMALIZED || + readComponentType == GL_SIGNED_NORMALIZED); + bool drawFixedPoint = (drawComponentType == GL_UNSIGNED_NORMALIZED || + drawComponentType == GL_SIGNED_NORMALIZED); + + if (extensions.colorBufferFloat) { - context->recordError(Error(GL_INVALID_OPERATION)); + bool readFixedOrFloat = (readFixedPoint || readComponentType == GL_FLOAT); + bool drawFixedOrFloat = (drawFixedPoint || drawComponentType == GL_FLOAT); + + if (readFixedOrFloat != drawFixedOrFloat) + { + context->recordError(Error(GL_INVALID_OPERATION, + "If the read buffer contains fixed-point or " + "floating-point values, the draw buffer " + "must as well.")); + return false; + } + } + else if (readFixedPoint != drawFixedPoint) + { + context->recordError(Error(GL_INVALID_OPERATION, + "If the read buffer contains fixed-point " + "values, the draw buffer must as well.")); return false; } - if (readFormatInfo.componentType == GL_UNSIGNED_INT && drawFormatInfo.componentType != GL_UNSIGNED_INT) + if (readComponentType == GL_UNSIGNED_INT && + drawComponentType != GL_UNSIGNED_INT) { context->recordError(Error(GL_INVALID_OPERATION)); return false; } - if (readFormatInfo.componentType == GL_INT && drawFormatInfo.componentType != GL_INT) + if (readComponentType == GL_INT && drawComponentType != GL_INT) { context->recordError(Error(GL_INVALID_OPERATION)); return false; @@ -568,53 +756,6 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint context->recordError(Error(GL_INVALID_OPERATION)); return false; } - - if (fromAngleExtension) - { - FramebufferAttachment *readColorAttachment = readFramebuffer->getReadColorbuffer(); - if (!readColorAttachment || - (!(readColorAttachment->type() == GL_TEXTURE && readColorAttachment->getTextureImageIndex()->type == GL_TEXTURE_2D) && - readColorAttachment->type() != GL_RENDERBUFFER && - readColorAttachment->type() != GL_FRAMEBUFFER_DEFAULT)) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - - for (GLuint colorAttachment = 0; colorAttachment < context->getCaps().maxColorAttachments; ++colorAttachment) - { - if (drawFramebuffer->isEnabledColorAttachment(colorAttachment)) - { - FramebufferAttachment *attachment = drawFramebuffer->getColorbuffer(colorAttachment); - ASSERT(attachment); - - if (!(attachment->type() == GL_TEXTURE && attachment->getTextureImageIndex()->type == GL_TEXTURE_2D) && - attachment->type() != GL_RENDERBUFFER && - attachment->type() != GL_FRAMEBUFFER_DEFAULT) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - - // Return an error if the destination formats do not match - if (attachment->getInternalFormat() != readColorBuffer->getInternalFormat()) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - } - } - - int readSamples = readFramebuffer->getSamples(context->getData()); - - if (readSamples != 0 && IsPartialBlit(context, readColorBuffer, drawColorBuffer, - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1)) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - } } } @@ -624,8 +765,8 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint { if (mask & masks[i]) { - gl::FramebufferAttachment *readBuffer = readFramebuffer->getAttachment(attachments[i]); - gl::FramebufferAttachment *drawBuffer = drawFramebuffer->getAttachment(attachments[i]); + const gl::FramebufferAttachment *readBuffer = readFramebuffer->getAttachment(attachments[i]); + const gl::FramebufferAttachment *drawBuffer = drawFramebuffer->getAttachment(attachments[i]); if (readBuffer && drawBuffer) { @@ -640,23 +781,6 @@ bool ValidateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint context->recordError(Error(GL_INVALID_OPERATION)); return false; } - - if (fromAngleExtension) - { - if (IsPartialBlit(context, readBuffer, drawBuffer, - srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1)) - { - ERR("Only whole-buffer depth and stencil blits are supported by this implementation."); - context->recordError(Error(GL_INVALID_OPERATION)); // only whole-buffer copies are permitted - return false; - } - - if (readBuffer->getSamples() != 0 || drawBuffer->getSamples() != 0) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } - } } } } @@ -886,10 +1010,22 @@ bool ValidateSamplerObjectParameter(gl::Context *context, GLenum pname) } } -bool ValidateReadPixelsParameters(gl::Context *context, GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, GLsizei *bufSize, GLvoid *pixels) +bool ValidateReadPixels(Context *context, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLvoid *pixels) { - gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); + if (width < 0 || height < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "width and height must be positive")); + return false; + } + + Framebuffer *framebuffer = context->getState().getReadFramebuffer(); ASSERT(framebuffer); if (framebuffer->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE) @@ -926,35 +1062,99 @@ bool ValidateReadPixelsParameters(gl::Context *context, GLint x, GLint y, GLsize return false; } + return true; +} + +bool ValidateReadnPixelsEXT(Context *context, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLsizei bufSize, + GLvoid *pixels) +{ + if (bufSize < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "bufSize must be a positive number")); + return false; + } + GLenum sizedInternalFormat = GetSizedInternalFormat(format, type); const InternalFormat &sizedFormatInfo = GetInternalFormatInfo(sizedInternalFormat); - GLsizei outputPitch = sizedFormatInfo.computeRowPitch(type, width, context->getState().getPackAlignment(), 0); + GLsizei outputPitch = + sizedFormatInfo.computeRowPitch(type, width, context->getState().getPackAlignment(), + context->getState().getPackRowLength()); // sized query sanity check - if (bufSize) + int requiredSize = outputPitch * height; + if (requiredSize > bufSize) { - int requiredSize = outputPitch * height; - if (requiredSize > *bufSize) - { - context->recordError(Error(GL_INVALID_OPERATION)); - return false; - } + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateReadPixels(context, x, y, width, height, format, type, pixels); +} + +bool ValidateGenQueriesBase(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (n < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "Query count < 0")); + return false; } return true; } -bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) +bool ValidateGenQueriesEXT(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateGenQueriesBase(context, n, ids); +} + +bool ValidateDeleteQueriesBase(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (n < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "Query count < 0")); + return false; + } + + return true; +} + +bool ValidateDeleteQueriesEXT(gl::Context *context, GLsizei n, const GLuint *ids) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateDeleteQueriesBase(context, n, ids); +} + +bool ValidateBeginQueryBase(gl::Context *context, GLenum target, GLuint id) { if (!ValidQueryType(context, target)) { - context->recordError(Error(GL_INVALID_ENUM)); + context->recordError(Error(GL_INVALID_ENUM, "Invalid query target")); return false; } if (id == 0) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Query id is 0")); return false; } @@ -973,9 +1173,12 @@ bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) // b) There are no active queries for the requested target (and in the case // of GL_ANY_SAMPLES_PASSED_EXT and GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, // no query may be active for either if glBeginQuery targets either. + + // TODO(ewell): I think this needs to be changed for timer and occlusion queries to work at the + // same time if (context->getState().isQueryActive()) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Other query is active")); return false; } @@ -984,41 +1187,215 @@ bool ValidateBeginQuery(gl::Context *context, GLenum target, GLuint id) // check that name was obtained with glGenQueries if (!queryObject) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Invalid query id")); return false; } // check for type mismatch if (queryObject->getType() != target) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Query type does not match target")); return false; } return true; } -bool ValidateEndQuery(gl::Context *context, GLenum target) +bool ValidateBeginQueryEXT(gl::Context *context, GLenum target, GLuint id) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateBeginQueryBase(context, target, id); +} + +bool ValidateEndQueryBase(gl::Context *context, GLenum target) { if (!ValidQueryType(context, target)) { - context->recordError(Error(GL_INVALID_ENUM)); + context->recordError(Error(GL_INVALID_ENUM, "Invalid query target")); return false; } const Query *queryObject = context->getState().getActiveQuery(target); - if (queryObject == NULL) + if (queryObject == nullptr) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Query target not active")); return false; } return true; } -static bool ValidateUniformCommonBase(gl::Context *context, GLenum targetUniformType, - GLint location, GLsizei count, LinkedUniform **uniformOut) +bool ValidateEndQueryEXT(gl::Context *context, GLenum target) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateEndQueryBase(context, target); +} + +bool ValidateQueryCounterEXT(Context *context, GLuint id, GLenum target) +{ + if (!context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Disjoint timer query not enabled")); + return false; + } + + if (target != GL_TIMESTAMP_EXT) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid query target")); + return false; + } + + Query *queryObject = context->getQuery(id, true, target); + if (queryObject == nullptr) + { + context->recordError(Error(GL_INVALID_OPERATION, "Invalid query id")); + return false; + } + + if (context->getState().isQueryActive(queryObject)) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query is active")); + return false; + } + + return true; +} + +bool ValidateGetQueryivBase(Context *context, GLenum target, GLenum pname) +{ + if (!ValidQueryType(context, target) && target != GL_TIMESTAMP_EXT) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid query type")); + return false; + } + + switch (pname) + { + case GL_CURRENT_QUERY_EXT: + if (target == GL_TIMESTAMP_EXT) + { + context->recordError( + Error(GL_INVALID_ENUM, "Cannot use current query for timestamp")); + return false; + } + break; + case GL_QUERY_COUNTER_BITS_EXT: + if (!context->getExtensions().disjointTimerQuery || + (target != GL_TIMESTAMP_EXT && target != GL_TIME_ELAPSED_EXT)) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid pname")); + return false; + } + break; + default: + context->recordError(Error(GL_INVALID_ENUM, "Invalid pname")); + return false; + } + + return true; +} + +bool ValidateGetQueryivEXT(Context *context, GLenum target, GLenum pname, GLint *params) +{ + if (!context->getExtensions().occlusionQueryBoolean && + !context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + + return ValidateGetQueryivBase(context, target, pname); +} + +bool ValidateGetQueryObjectValueBase(Context *context, GLuint id, GLenum pname) +{ + Query *queryObject = context->getQuery(id, false, GL_NONE); + + if (!queryObject) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query does not exist")); + return false; + } + + if (context->getState().isQueryActive(queryObject)) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query currently active")); + return false; + } + + switch (pname) + { + case GL_QUERY_RESULT_EXT: + case GL_QUERY_RESULT_AVAILABLE_EXT: + break; + + default: + context->recordError(Error(GL_INVALID_ENUM, "Invalid pname enum")); + return false; + } + + return true; +} + +bool ValidateGetQueryObjectivEXT(Context *context, GLuint id, GLenum pname, GLint *params) +{ + if (!context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Timer query extension not enabled")); + return false; + } + return ValidateGetQueryObjectValueBase(context, id, pname); +} + +bool ValidateGetQueryObjectuivEXT(Context *context, GLuint id, GLenum pname, GLuint *params) +{ + if (!context->getExtensions().disjointTimerQuery && + !context->getExtensions().occlusionQueryBoolean) + { + context->recordError(Error(GL_INVALID_OPERATION, "Query extension not enabled")); + return false; + } + return ValidateGetQueryObjectValueBase(context, id, pname); +} + +bool ValidateGetQueryObjecti64vEXT(Context *context, GLuint id, GLenum pname, GLint64 *params) +{ + if (!context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Timer query extension not enabled")); + return false; + } + return ValidateGetQueryObjectValueBase(context, id, pname); +} + +bool ValidateGetQueryObjectui64vEXT(Context *context, GLuint id, GLenum pname, GLuint64 *params) +{ + if (!context->getExtensions().disjointTimerQuery) + { + context->recordError(Error(GL_INVALID_OPERATION, "Timer query extension not enabled")); + return false; + } + return ValidateGetQueryObjectValueBase(context, id, pname); +} + +static bool ValidateUniformCommonBase(gl::Context *context, + GLenum targetUniformType, + GLint location, + GLsizei count, + const LinkedUniform **uniformOut) { if (count < 0) { @@ -1045,16 +1422,16 @@ static bool ValidateUniformCommonBase(gl::Context *context, GLenum targetUniform return false; } - LinkedUniform *uniform = program->getUniformByLocation(location); + const LinkedUniform &uniform = program->getUniformByLocation(location); // attempting to write an array to a non-array uniform is an INVALID_OPERATION - if (uniform->elementCount() == 1 && count > 1) + if (!uniform.isArray() && count > 1) { context->recordError(Error(GL_INVALID_OPERATION)); return false; } - *uniformOut = uniform; + *uniformOut = &uniform; return true; } @@ -1067,7 +1444,7 @@ bool ValidateUniform(gl::Context *context, GLenum uniformType, GLint location, G return false; } - LinkedUniform *uniform = NULL; + const LinkedUniform *uniform = nullptr; if (!ValidateUniformCommonBase(context, uniformType, location, count, &uniform)) { return false; @@ -1102,7 +1479,7 @@ bool ValidateUniformMatrix(gl::Context *context, GLenum matrixType, GLint locati return false; } - LinkedUniform *uniform = NULL; + const LinkedUniform *uniform = nullptr; if (!ValidateUniformCommonBase(context, matrixType, location, count, &uniform)) { return false; @@ -1162,7 +1539,7 @@ bool ValidateStateQuery(gl::Context *context, GLenum pname, GLenum *nativeType, return false; } - FramebufferAttachment *attachment = framebuffer->getReadColorbuffer(); + const FramebufferAttachment *attachment = framebuffer->getReadColorbuffer(); if (!attachment) { context->recordError(Error(GL_INVALID_OPERATION)); @@ -1184,17 +1561,21 @@ bool ValidateStateQuery(gl::Context *context, GLenum pname, GLenum *nativeType, return true; } -bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLint level, GLenum internalformat, bool isSubImage, - GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, - GLint border, GLenum *textureFormatOut) +bool ValidateCopyTexImageParametersBase(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + bool isSubImage, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border, + GLenum *textureFormatOut) { - - if (!ValidTexture2DDestinationTarget(context, target)) - { - context->recordError(Error(GL_INVALID_ENUM)); - return false; - } - if (level < 0 || xoffset < 0 || yoffset < 0 || zoffset < 0 || width < 0 || height < 0) { context->recordError(Error(GL_INVALID_VALUE)); @@ -1219,14 +1600,15 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi return false; } - gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); + const gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer(); if (framebuffer->checkStatus(context->getData()) != GL_FRAMEBUFFER_COMPLETE) { context->recordError(Error(GL_INVALID_FRAMEBUFFER_OPERATION)); return false; } - if (context->getState().getReadFramebuffer()->id() != 0 && framebuffer->getSamples(context->getData()) != 0) + const auto &state = context->getState(); + if (state.getReadFramebuffer()->id() != 0 && framebuffer->getSamples(context->getData()) != 0) { context->recordError(Error(GL_INVALID_OPERATION)); return false; @@ -1263,14 +1645,15 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi return false; } - gl::Texture *texture = context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target); + gl::Texture *texture = + state.getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target); if (!texture) { context->recordError(Error(GL_INVALID_OPERATION)); return false; } - if (texture->isImmutable() && !isSubImage) + if (texture->getImmutableFormat() && !isSubImage) { context->recordError(Error(GL_INVALID_OPERATION)); return false; @@ -1326,7 +1709,10 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi return true; } -static bool ValidateDrawBase(Context *context, GLenum mode, GLsizei count, GLsizei maxVertex, GLsizei primcount) +static bool ValidateDrawBase(ValidationContext *context, + GLenum mode, + GLsizei count, + GLsizei primcount) { switch (mode) { @@ -1358,17 +1744,27 @@ static bool ValidateDrawBase(Context *context, GLenum mode, GLsizei count, GLsiz return false; } - const gl::DepthStencilState &depthStencilState = state.getDepthStencilState(); - if (depthStencilState.stencilWritemask != depthStencilState.stencilBackWritemask || - state.getStencilRef() != state.getStencilBackRef() || - depthStencilState.stencilMask != depthStencilState.stencilBackMask) + if (context->getLimitations().noSeparateStencilRefsAndMasks) { - // 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."); - context->recordError(Error(GL_INVALID_OPERATION)); - return false; + const Framebuffer *framebuffer = context->getState().getDrawFramebuffer(); + const FramebufferAttachment *stencilBuffer = framebuffer->getStencilbuffer(); + GLuint stencilBits = stencilBuffer ? stencilBuffer->getStencilSize() : 0; + GLuint minimumRequiredStencilMask = (1 << stencilBits) - 1; + const DepthStencilState &depthStencilState = state.getDepthStencilState(); + if ((depthStencilState.stencilWritemask & minimumRequiredStencilMask) != + (depthStencilState.stencilBackWritemask & minimumRequiredStencilMask) || + state.getStencilRef() != state.getStencilBackRef() || + (depthStencilState.stencilMask & minimumRequiredStencilMask) != + (depthStencilState.stencilBackMask & minimumRequiredStencilMask)) + { + // 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."); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } } const gl::Framebuffer *fbo = state.getDrawFramebuffer(); @@ -1391,74 +1787,29 @@ static bool ValidateDrawBase(Context *context, GLenum mode, GLsizei count, GLsiz return false; } - // Buffer validations - const VertexArray *vao = state.getVertexArray(); - for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++) - { - const VertexAttribute &attrib = vao->getVertexAttribute(attributeIndex); - bool attribActive = (program->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; - } - } - } - // Uniform buffer validation for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++) { - const gl::UniformBlock *uniformBlock = program->getUniformBlockByIndex(uniformBlockIndex); + const gl::UniformBlock &uniformBlock = program->getUniformBlockByIndex(uniformBlockIndex); GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex); - const gl::Buffer *uniformBuffer = state.getIndexedUniformBuffer(blockBinding); + const OffsetBindingPointer<Buffer> &uniformBuffer = + state.getIndexedUniformBuffer(blockBinding); - if (!uniformBuffer) + if (uniformBuffer.get() == nullptr) { // undefined behaviour context->recordError(Error(GL_INVALID_OPERATION, "It is undefined behaviour to have a used but unbound uniform buffer.")); return false; } - size_t uniformBufferSize = state.getIndexedUniformBufferSize(blockBinding); - + size_t uniformBufferSize = uniformBuffer.getSize(); if (uniformBufferSize == 0) { // Bind the whole buffer. - uniformBufferSize = uniformBuffer->getSize(); + uniformBufferSize = static_cast<size_t>(uniformBuffer->getSize()); } - if (uniformBufferSize < uniformBlock->dataSize) + if (uniformBufferSize < uniformBlock.dataSize) { // undefined behaviour context->recordError(Error(GL_INVALID_OPERATION, "It is undefined behaviour to use a uniform buffer that is too small.")); @@ -1480,8 +1831,8 @@ bool ValidateDrawArrays(Context *context, GLenum mode, GLint first, GLsizei coun const State &state = context->getState(); gl::TransformFeedback *curTransformFeedback = state.getCurrentTransformFeedback(); - if (curTransformFeedback && curTransformFeedback->isStarted() && !curTransformFeedback->isPaused() && - curTransformFeedback->getDrawMode() != mode) + if (curTransformFeedback && curTransformFeedback->isActive() && !curTransformFeedback->isPaused() && + curTransformFeedback->getPrimitiveMode() != 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 @@ -1490,7 +1841,12 @@ bool ValidateDrawArrays(Context *context, GLenum mode, GLint first, GLsizei coun return false; } - if (!ValidateDrawBase(context, mode, count, count, primcount)) + if (!ValidateDrawBase(context, mode, count, primcount)) + { + return false; + } + + if (!ValidateDrawAttribs(context, primcount, count)) { return false; } @@ -1523,11 +1879,10 @@ static bool ValidateDrawInstancedANGLE(Context *context) gl::Program *program = state.getProgram(); const VertexArray *vao = state.getVertexArray(); - for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++) + for (size_t attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++) { const VertexAttribute &attrib = vao->getVertexAttribute(attributeIndex); - bool active = (program->getSemanticIndex(attributeIndex) != -1); - if (active && attrib.divisor == 0) + if (program->isAttribLocationActive(attributeIndex) && attrib.divisor == 0) { return true; } @@ -1548,8 +1903,13 @@ bool ValidateDrawArraysInstancedANGLE(Context *context, GLenum mode, GLint first 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) +bool ValidateDrawElements(ValidationContext *context, + GLenum mode, + GLsizei count, + GLenum type, + const GLvoid *indices, + GLsizei primcount, + IndexRange *indexRangeOut) { switch (type) { @@ -1557,7 +1917,7 @@ bool ValidateDrawElements(Context *context, GLenum mode, GLsizei count, GLenum t case GL_UNSIGNED_SHORT: break; case GL_UNSIGNED_INT: - if (!context->getExtensions().elementIndexUint) + if (context->getClientVersion() < 3 && !context->getExtensions().elementIndexUint) { context->recordError(Error(GL_INVALID_ENUM)); return false; @@ -1571,7 +1931,7 @@ bool ValidateDrawElements(Context *context, GLenum mode, GLsizei count, GLenum t const State &state = context->getState(); gl::TransformFeedback *curTransformFeedback = state.getCurrentTransformFeedback(); - if (curTransformFeedback && curTransformFeedback->isStarted() && !curTransformFeedback->isPaused()) + if (curTransformFeedback && curTransformFeedback->isActive() && !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) @@ -1587,7 +1947,7 @@ bool ValidateDrawElements(Context *context, GLenum mode, GLsizei count, GLenum t } const gl::VertexArray *vao = state.getVertexArray(); - gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer(); + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); if (!indices && !elementArrayBuffer) { context->recordError(Error(GL_INVALID_OPERATION)); @@ -1623,45 +1983,56 @@ bool ValidateDrawElements(Context *context, GLenum mode, GLsizei count, GLenum t return false; } + if (!ValidateDrawBase(context, mode, count, primcount)) + { + return false; + } + // 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) { uintptr_t offset = reinterpret_cast<uintptr_t>(indices); - if (!elementArrayBuffer->getIndexRangeCache()->findRange(type, offset, count, indexRangeOut)) + Error error = + elementArrayBuffer->getIndexRange(type, static_cast<size_t>(offset), count, + state.isPrimitiveRestartEnabled(), indexRangeOut); + if (error.isError()) { - rx::BufferImpl *bufferImpl = elementArrayBuffer->getImplementation(); - const uint8_t *dataPointer = NULL; - Error error = bufferImpl->getData(&dataPointer); - if (error.isError()) - { - context->recordError(error); - return false; - } - - const uint8_t *offsetPointer = dataPointer + offset; - *indexRangeOut = rx::IndexRangeCache::ComputeRange(type, offsetPointer, count); - elementArrayBuffer->getIndexRangeCache()->addRange(type, offset, count, *indexRangeOut); + context->recordError(error); + return false; } } else { - *indexRangeOut = rx::IndexRangeCache::ComputeRange(type, indices, count); + *indexRangeOut = ComputeIndexRange(type, indices, count, state.isPrimitiveRestartEnabled()); } - if (!ValidateDrawBase(context, mode, count, static_cast<GLsizei>(indexRangeOut->end), primcount)) + // If we use an index greater than our maximum supported index range, return an error. + // The ES3 spec does not specify behaviour here, it is undefined, but ANGLE should always + // return an error if possible here. + if (static_cast<GLuint64>(indexRangeOut->end) >= context->getCaps().maxElementIndex) { + context->recordError(Error(GL_INVALID_OPERATION, g_ExceedsMaxElementErrorMessage)); return false; } - return true; + if (!ValidateDrawAttribs(context, primcount, static_cast<GLsizei>(indexRangeOut->end))) + { + return false; + } + + // No op if there are no real indices in the index data (all are primitive restart). + return (indexRangeOut->vertexIndexCount > 0); } bool ValidateDrawElementsInstanced(Context *context, - GLenum mode, GLsizei count, GLenum type, - const GLvoid *indices, GLsizei primcount, - rx::RangeUI *indexRangeOut) + GLenum mode, + GLsizei count, + GLenum type, + const GLvoid *indices, + GLsizei primcount, + IndexRange *indexRangeOut) { if (primcount < 0) { @@ -1678,8 +2049,13 @@ bool ValidateDrawElementsInstanced(Context *context, return (primcount > 0); } -bool ValidateDrawElementsInstancedANGLE(Context *context, GLenum mode, GLsizei count, GLenum type, - const GLvoid *indices, GLsizei primcount, rx::RangeUI *indexRangeOut) +bool ValidateDrawElementsInstancedANGLE(Context *context, + GLenum mode, + GLsizei count, + GLenum type, + const GLvoid *indices, + GLsizei primcount, + IndexRange *indexRangeOut) { if (!ValidateDrawInstancedANGLE(context)) { @@ -1721,11 +2097,11 @@ bool ValidateFramebufferTextureBase(Context *context, GLenum target, GLenum atta } const gl::Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); - GLuint framebufferHandle = context->getState().getTargetFramebuffer(target)->id(); + ASSERT(framebuffer); - if (framebufferHandle == 0 || !framebuffer) + if (framebuffer->id() == 0) { - context->recordError(Error(GL_INVALID_OPERATION)); + context->recordError(Error(GL_INVALID_OPERATION, "Cannot change default FBO's attachments")); return false; } @@ -1735,8 +2111,8 @@ bool ValidateFramebufferTextureBase(Context *context, GLenum target, GLenum atta 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) + // Attachments are required to be bound to level 0 without ES3 or the GL_OES_fbo_render_mipmap extension + if (context->getClientVersion() < 3 && !context->getExtensions().fboRenderMipmap && level != 0) { context->recordError(Error(GL_INVALID_VALUE)); return false; @@ -1815,13 +2191,12 @@ bool ValidateGetUniformBase(Context *context, GLuint program, GLint location) return false; } - if (!ValidProgram(context, program)) + gl::Program *programObject = GetValidProgram(context, program); + if (!programObject) { return false; } - gl::Program *programObject = context->getProgram(program); - if (!programObject || !programObject->isLinked()) { context->recordError(Error(GL_INVALID_OPERATION)); @@ -1858,8 +2233,8 @@ static bool ValidateSizedGetUniform(Context *context, GLuint program, GLint loca ASSERT(programObject); // sized queries -- ensure the provided buffer is large enough - LinkedUniform *uniform = programObject->getUniformByLocation(location); - size_t requiredBytes = VariableExternalSize(uniform->type); + const LinkedUniform &uniform = programObject->getUniformByLocation(location); + size_t requiredBytes = VariableExternalSize(uniform.type); if (static_cast<size_t>(bufSize) < requiredBytes) { context->recordError(Error(GL_INVALID_OPERATION)); @@ -1879,4 +2254,382 @@ bool ValidateGetnUniformivEXT(Context *context, GLuint program, GLint location, return ValidateSizedGetUniform(context, program, location, bufSize); } +bool ValidateDiscardFramebufferBase(Context *context, GLenum target, GLsizei numAttachments, + const GLenum *attachments, bool defaultFramebuffer) +{ + if (numAttachments < 0) + { + context->recordError(Error(GL_INVALID_VALUE, "numAttachments must not be less than zero")); + return false; + } + + for (GLsizei i = 0; i < numAttachments; ++i) + { + if (attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15) + { + if (defaultFramebuffer) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid attachment when the default framebuffer is bound")); + return false; + } + + if (attachments[i] >= GL_COLOR_ATTACHMENT0 + context->getCaps().maxColorAttachments) + { + context->recordError(Error(GL_INVALID_OPERATION, + "Requested color attachment is greater than the maximum supported color attachments")); + return false; + } + } + else + { + switch (attachments[i]) + { + case GL_DEPTH_ATTACHMENT: + case GL_STENCIL_ATTACHMENT: + case GL_DEPTH_STENCIL_ATTACHMENT: + if (defaultFramebuffer) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid attachment when the default framebuffer is bound")); + return false; + } + break; + case GL_COLOR: + case GL_DEPTH: + case GL_STENCIL: + if (!defaultFramebuffer) + { + context->recordError(Error(GL_INVALID_ENUM, "Invalid attachment when the default framebuffer is not bound")); + return false; + } + break; + default: + context->recordError(Error(GL_INVALID_ENUM, "Invalid attachment")); + return false; + } + } + } + + return true; } + +bool ValidateInsertEventMarkerEXT(Context *context, GLsizei length, const char *marker) +{ + // Note that debug marker calls must not set error state + + if (length < 0) + { + return false; + } + + if (marker == nullptr) + { + return false; + } + + return true; +} + +bool ValidatePushGroupMarkerEXT(Context *context, GLsizei length, const char *marker) +{ + // Note that debug marker calls must not set error state + + if (length < 0) + { + return false; + } + + if (length > 0 && marker == nullptr) + { + return false; + } + + return true; +} + +bool ValidateEGLImageTargetTexture2DOES(Context *context, + egl::Display *display, + GLenum target, + egl::Image *image) +{ + if (!context->getExtensions().eglImage && !context->getExtensions().eglImageExternal) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + switch (target) + { + case GL_TEXTURE_2D: + break; + + default: + context->recordError(Error(GL_INVALID_ENUM, "invalid texture target.")); + return false; + } + + if (!display->isValidImage(image)) + { + context->recordError(Error(GL_INVALID_VALUE, "EGL image is not valid.")); + return false; + } + + if (image->getSamples() > 0) + { + context->recordError(Error(GL_INVALID_OPERATION, + "cannot create a 2D texture from a multisampled EGL image.")); + return false; + } + + const TextureCaps &textureCaps = context->getTextureCaps().get(image->getInternalFormat()); + if (!textureCaps.texturable) + { + context->recordError(Error(GL_INVALID_OPERATION, + "EGL image internal format is not supported as a texture.")); + return false; + } + + return true; +} + +bool ValidateEGLImageTargetRenderbufferStorageOES(Context *context, + egl::Display *display, + GLenum target, + egl::Image *image) +{ + if (!context->getExtensions().eglImage) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + switch (target) + { + case GL_RENDERBUFFER: + break; + + default: + context->recordError(Error(GL_INVALID_ENUM, "invalid renderbuffer target.")); + return false; + } + + if (!display->isValidImage(image)) + { + context->recordError(Error(GL_INVALID_VALUE, "EGL image is not valid.")); + return false; + } + + const TextureCaps &textureCaps = context->getTextureCaps().get(image->getInternalFormat()); + if (!textureCaps.renderable) + { + context->recordError(Error( + GL_INVALID_OPERATION, "EGL image internal format is not supported as a renderbuffer.")); + return false; + } + + return true; +} + +bool ValidateBindVertexArrayBase(Context *context, GLuint array) +{ + if (!context->isVertexArrayGenerated(array)) + { + // The default VAO should always exist + ASSERT(array != 0); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return true; +} + +bool ValidateDeleteVertexArraysBase(Context *context, GLsizei n) +{ + if (n < 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + + return true; +} + +bool ValidateGenVertexArraysBase(Context *context, GLsizei n) +{ + if (n < 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + + return true; +} + +bool ValidateProgramBinaryBase(Context *context, + GLuint program, + GLenum binaryFormat, + const void *binary, + GLint length) +{ + Program *programObject = GetValidProgram(context, program); + if (programObject == nullptr) + { + return false; + } + + const std::vector<GLenum> &programBinaryFormats = context->getCaps().programBinaryFormats; + if (std::find(programBinaryFormats.begin(), programBinaryFormats.end(), binaryFormat) == + programBinaryFormats.end()) + { + context->recordError(Error(GL_INVALID_ENUM, "Program binary format is not valid.")); + return false; + } + + return true; +} + +bool ValidateGetProgramBinaryBase(Context *context, + GLuint program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + void *binary) +{ + Program *programObject = GetValidProgram(context, program); + if (programObject == nullptr) + { + return false; + } + + if (!programObject->isLinked()) + { + context->recordError(Error(GL_INVALID_OPERATION, "Program is not linked.")); + return false; + } + + return true; +} + +bool ValidateCopyTexImage2D(ValidationContext *context, + GLenum target, + GLint level, + GLenum internalformat, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border) +{ + if (context->getClientVersion() < 3) + { + return ValidateES2CopyTexImageParameters(context, target, level, internalformat, false, 0, + 0, x, y, width, height, border); + } + + ASSERT(context->getClientVersion() == 3); + return ValidateES3CopyTexImage2DParameters(context, target, level, internalformat, false, 0, 0, + 0, x, y, width, height, border); +} + +bool ValidateFramebufferRenderbuffer(Context *context, + GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ + if (!ValidFramebufferTarget(target) || + (renderbuffertarget != GL_RENDERBUFFER && renderbuffer != 0)) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + + return ValidateFramebufferRenderbufferParameters(context, target, attachment, + renderbuffertarget, renderbuffer); +} + +bool ValidateDrawBuffersBase(ValidationContext *context, GLsizei n, const GLenum *bufs) +{ + // INVALID_VALUE is generated if n is negative or greater than value of MAX_DRAW_BUFFERS + if (n < 0 || static_cast<GLuint>(n) > context->getCaps().maxDrawBuffers) + { + context->recordError( + Error(GL_INVALID_VALUE, "n must be non-negative and no greater than MAX_DRAW_BUFFERS")); + return false; + } + + ASSERT(context->getState().getDrawFramebuffer()); + GLuint frameBufferId = context->getState().getDrawFramebuffer()->id(); + GLuint maxColorAttachment = GL_COLOR_ATTACHMENT0_EXT + context->getCaps().maxColorAttachments; + + // This should come first before the check for the default frame buffer + // because when we switch to ES3.1+, invalid enums will return INVALID_ENUM + // rather than INVALID_OPERATION + for (int colorAttachment = 0; colorAttachment < n; colorAttachment++) + { + const GLenum attachment = GL_COLOR_ATTACHMENT0_EXT + colorAttachment; + + if (bufs[colorAttachment] != GL_NONE && bufs[colorAttachment] != GL_BACK && + (bufs[colorAttachment] < GL_COLOR_ATTACHMENT0_EXT || + bufs[colorAttachment] >= maxColorAttachment)) + { + // Value in bufs is not NONE, BACK, or GL_COLOR_ATTACHMENTi + // In the 3.0 specs, the error should return GL_INVALID_OPERATION. + // When we move to 3.1 specs, we should change the error to be GL_INVALID_ENUM + context->recordError(Error(GL_INVALID_OPERATION, "Invalid buffer value")); + return false; + } + else if (bufs[colorAttachment] != GL_NONE && bufs[colorAttachment] != attachment && + frameBufferId != 0) + { + // INVALID_OPERATION-GL is bound to buffer and ith argument + // is not COLOR_ATTACHMENTi or NONE + context->recordError( + Error(GL_INVALID_OPERATION, "Ith value does not match COLOR_ATTACHMENTi or NONE")); + return false; + } + } + + // INVALID_OPERATION is generated if GL is bound to the default framebuffer + // and n is not 1 or bufs is bound to value other than BACK and NONE + if (frameBufferId == 0) + { + if (n != 1) + { + context->recordError(Error(GL_INVALID_OPERATION, + "n must be 1 when GL is bound to the default framebuffer")); + return false; + } + + if (bufs[0] != GL_NONE && bufs[0] != GL_BACK) + { + context->recordError(Error( + GL_INVALID_OPERATION, + "Only NONE or BACK are valid values when drawing to the default framebuffer")); + return false; + } + } + + return true; +} + +bool ValidateCopyTexSubImage2D(Context *context, + GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + if (context->getClientVersion() < 3) + { + return ValidateES2CopyTexImageParameters(context, target, level, GL_NONE, true, xoffset, + yoffset, x, y, width, height, 0); + } + + return ValidateES3CopyTexImage2DParameters(context, target, level, GL_NONE, true, xoffset, + yoffset, 0, x, y, width, height, 0); +} + +} // namespace gl |