| #include "precompiled.h" | |
| // | |
| // Copyright (c) 2013 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // validationES3.cpp: Validation functions for OpenGL ES 3.0 entry point parameters | |
| #include "libGLESv2/validationES3.h" | |
| #include "libGLESv2/validationES.h" | |
| #include "libGLESv2/Context.h" | |
| #include "libGLESv2/Texture.h" | |
| #include "libGLESv2/Framebuffer.h" | |
| #include "libGLESv2/Renderbuffer.h" | |
| #include "libGLESv2/formatutils.h" | |
| #include "libGLESv2/main.h" | |
| #include "common/mathutil.h" | |
| namespace gl | |
| { | |
| bool ValidateES3TexImageParameters(gl::Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage, | |
| GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, | |
| GLint border, GLenum format, GLenum type, const GLvoid *pixels) | |
| { | |
| // Validate image size | |
| if (!ValidImageSize(context, target, level, width, height, depth)) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| // Verify zero border | |
| if (border != 0) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| gl::Texture *texture = NULL; | |
| bool textureCompressed = false; | |
| GLenum textureInternalFormat = GL_NONE; | |
| GLint textureLevelWidth = 0; | |
| GLint textureLevelHeight = 0; | |
| GLint textureLevelDepth = 0; | |
| switch (target) | |
| { | |
| case GL_TEXTURE_2D: | |
| { | |
| if (width > (context->getMaximum2DTextureDimension() >> level) || | |
| height > (context->getMaximum2DTextureDimension() >> level)) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| gl::Texture2D *texture2d = context->getTexture2D(); | |
| if (texture2d) | |
| { | |
| textureCompressed = texture2d->isCompressed(level); | |
| textureInternalFormat = texture2d->getInternalFormat(level); | |
| textureLevelWidth = texture2d->getWidth(level); | |
| textureLevelHeight = texture2d->getHeight(level); | |
| textureLevelDepth = 1; | |
| texture = texture2d; | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_X: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: | |
| { | |
| if (!isSubImage && width != height) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (width > (context->getMaximumCubeTextureDimension() >> level)) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| gl::TextureCubeMap *textureCube = context->getTextureCubeMap(); | |
| if (textureCube) | |
| { | |
| textureCompressed = textureCube->isCompressed(target, level); | |
| textureInternalFormat = textureCube->getInternalFormat(target, level); | |
| textureLevelWidth = textureCube->getWidth(target, level); | |
| textureLevelHeight = textureCube->getHeight(target, level); | |
| textureLevelDepth = 1; | |
| texture = textureCube; | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_3D: | |
| { | |
| if (width > (context->getMaximum3DTextureDimension() >> level) || | |
| height > (context->getMaximum3DTextureDimension() >> level) || | |
| depth > (context->getMaximum3DTextureDimension() >> level)) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| gl::Texture3D *texture3d = context->getTexture3D(); | |
| if (texture3d) | |
| { | |
| textureCompressed = texture3d->isCompressed(level); | |
| textureInternalFormat = texture3d->getInternalFormat(level); | |
| textureLevelWidth = texture3d->getWidth(level); | |
| textureLevelHeight = texture3d->getHeight(level); | |
| textureLevelDepth = texture3d->getDepth(level); | |
| texture = texture3d; | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_2D_ARRAY: | |
| { | |
| if (width > (context->getMaximum2DTextureDimension() >> level) || | |
| height > (context->getMaximum2DTextureDimension() >> level) || | |
| depth > (context->getMaximum2DArrayTextureLayers() >> level)) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| gl::Texture2DArray *texture2darray = context->getTexture2DArray(); | |
| if (texture2darray) | |
| { | |
| textureCompressed = texture2darray->isCompressed(level); | |
| textureInternalFormat = texture2darray->getInternalFormat(level); | |
| textureLevelWidth = texture2darray->getWidth(level); | |
| textureLevelHeight = texture2darray->getHeight(level); | |
| textureLevelDepth = texture2darray->getLayers(level); | |
| texture = texture2darray; | |
| } | |
| } | |
| break; | |
| default: | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| if (!texture) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (texture->isImmutable() && !isSubImage) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| // Validate texture formats | |
| GLenum actualInternalFormat = isSubImage ? textureInternalFormat : internalformat; | |
| if (isCompressed) | |
| { | |
| if (!ValidCompressedImageSize(context, actualInternalFormat, width, height)) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (!gl::IsFormatCompressed(actualInternalFormat, context->getClientVersion())) | |
| { | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| if (target == GL_TEXTURE_3D) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| } | |
| else | |
| { | |
| if (!gl::IsValidInternalFormat(actualInternalFormat, context) || | |
| !gl::IsValidFormat(format, context->getClientVersion()) || | |
| !gl::IsValidType(type, context->getClientVersion())) | |
| { | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| if (!gl::IsValidFormatCombination(actualInternalFormat, format, type, context->getClientVersion())) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (target == GL_TEXTURE_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL)) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| } | |
| // Validate sub image parameters | |
| if (isSubImage) | |
| { | |
| if (isCompressed != textureCompressed) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (isCompressed) | |
| { | |
| if ((width % 4 != 0 && width != textureLevelWidth) || | |
| (height % 4 != 0 && height != textureLevelHeight)) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| } | |
| if (width == 0 || height == 0 || depth == 0) | |
| { | |
| return false; | |
| } | |
| if (xoffset < 0 || yoffset < 0 || zoffset < 0) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (std::numeric_limits<GLsizei>::max() - xoffset < width || | |
| std::numeric_limits<GLsizei>::max() - yoffset < height || | |
| std::numeric_limits<GLsizei>::max() - zoffset < depth) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (xoffset + width > textureLevelWidth || | |
| yoffset + height > textureLevelHeight || | |
| zoffset + depth > textureLevelDepth) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| } | |
| // Check for pixel unpack buffer related API errors | |
| gl::Buffer *pixelUnpackBuffer = context->getPixelUnpackBuffer(); | |
| if (pixelUnpackBuffer != NULL) | |
| { | |
| // ...the data would be unpacked from the buffer object such that the memory reads required | |
| // would exceed the data store size. | |
| size_t widthSize = static_cast<size_t>(width); | |
| size_t heightSize = static_cast<size_t>(height); | |
| size_t depthSize = static_cast<size_t>(depth); | |
| size_t pixelBytes = static_cast<size_t>(gl::GetPixelBytes(actualInternalFormat, context->getClientVersion())); | |
| if (!rx::IsUnsignedMultiplicationSafe(widthSize, heightSize) || | |
| !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize, depthSize) || | |
| !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize * depthSize, pixelBytes)) | |
| { | |
| // Overflow past the end of the buffer | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| size_t copyBytes = widthSize * heightSize * depthSize * pixelBytes; | |
| size_t offset = reinterpret_cast<size_t>(pixels); | |
| if (!rx::IsUnsignedAdditionSafe(offset, copyBytes) || ((offset + copyBytes) > static_cast<size_t>(pixelUnpackBuffer->size()))) | |
| { | |
| // Overflow past the end of the buffer | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| // ...data is not evenly divisible into the number of bytes needed to store in memory a datum | |
| // indicated by type. | |
| size_t dataBytesPerPixel = static_cast<size_t>(gl::GetTypeBytes(type)); | |
| if ((offset % dataBytesPerPixel) != 0) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| // TODO: ...the buffer object's data store is currently mapped. | |
| } | |
| return true; | |
| } | |
| bool ValidateES3CopyTexImageParameters(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) | |
| { | |
| if (level < 0 || xoffset < 0 || yoffset < 0 || zoffset < 0 || width < 0 || height < 0) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (std::numeric_limits<GLsizei>::max() - xoffset < width || std::numeric_limits<GLsizei>::max() - yoffset < height) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (border != 0) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (!ValidMipLevel(context, target, level)) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| gl::Framebuffer *framebuffer = context->getReadFramebuffer(); | |
| if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE) | |
| { | |
| return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false); | |
| } | |
| if (context->getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| gl::Renderbuffer *source = framebuffer->getReadColorbuffer(); | |
| GLenum colorbufferInternalFormat = source->getInternalFormat(); | |
| gl::Texture *texture = NULL; | |
| GLenum textureInternalFormat = GL_NONE; | |
| bool textureCompressed = false; | |
| bool textureIsDepth = false; | |
| GLint textureLevelWidth = 0; | |
| GLint textureLevelHeight = 0; | |
| GLint textureLevelDepth = 0; | |
| switch (target) | |
| { | |
| case GL_TEXTURE_2D: | |
| { | |
| gl::Texture2D *texture2d = context->getTexture2D(); | |
| if (texture2d) | |
| { | |
| textureInternalFormat = texture2d->getInternalFormat(level); | |
| textureCompressed = texture2d->isCompressed(level); | |
| textureIsDepth = texture2d->isDepth(level); | |
| textureLevelWidth = texture2d->getWidth(level); | |
| textureLevelHeight = texture2d->getHeight(level); | |
| textureLevelDepth = 1; | |
| texture = texture2d; | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_X: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: | |
| { | |
| gl::TextureCubeMap *textureCube = context->getTextureCubeMap(); | |
| 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; | |
| texture = textureCube; | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_2D_ARRAY: | |
| { | |
| gl::Texture2DArray *texture2dArray = context->getTexture2DArray(); | |
| 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); | |
| texture = texture2dArray; | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_3D: | |
| { | |
| gl::Texture3D *texture3d = context->getTexture3D(); | |
| 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); | |
| texture = texture3d; | |
| } | |
| } | |
| break; | |
| default: | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| if (!texture) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (texture->isImmutable() && !isSubImage) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (textureIsDepth) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (textureCompressed) | |
| { | |
| if ((width % 4 != 0 && width != textureLevelWidth) || | |
| (height % 4 != 0 && height != textureLevelHeight)) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| } | |
| if (isSubImage) | |
| { | |
| if (xoffset + width > textureLevelWidth || | |
| yoffset + height > textureLevelHeight || | |
| zoffset >= textureLevelDepth) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (!gl::IsValidCopyTexImageCombination(textureInternalFormat, colorbufferInternalFormat, | |
| context->getClientVersion())) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| } | |
| // If width or height is zero, it is a no-op. Return false without setting an error. | |
| return (width > 0 && height > 0); | |
| } | |
| bool ValidateES3TexStorageParameters(gl::Context *context, GLenum target, GLsizei levels, GLenum internalformat, | |
| GLsizei width, GLsizei height, GLsizei depth) | |
| { | |
| if (width < 1 || height < 1 || depth < 1 || levels < 1) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (levels > gl::log2(std::max(std::max(width, height), depth)) + 1) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| gl::Texture *texture = NULL; | |
| switch (target) | |
| { | |
| case GL_TEXTURE_2D: | |
| { | |
| texture = context->getTexture2D(); | |
| if (width > (context->getMaximum2DTextureDimension()) || | |
| height > (context->getMaximum2DTextureDimension())) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_CUBE_MAP: | |
| { | |
| texture = context->getTextureCubeMap(); | |
| if (width != height) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (width > (context->getMaximumCubeTextureDimension())) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_3D: | |
| { | |
| texture = context->getTexture3D(); | |
| if (width > (context->getMaximum3DTextureDimension()) || | |
| height > (context->getMaximum3DTextureDimension()) || | |
| depth > (context->getMaximum3DTextureDimension())) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_2D_ARRAY: | |
| { | |
| texture = context->getTexture2DArray(); | |
| if (width > (context->getMaximum2DTextureDimension()) || | |
| height > (context->getMaximum2DTextureDimension()) || | |
| depth > (context->getMaximum2DArrayTextureLayers())) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| } | |
| break; | |
| default: | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| if (!texture || texture->id() == 0) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (texture->isImmutable()) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (!gl::IsValidInternalFormat(internalformat, context)) | |
| { | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| if (!gl::IsSizedInternalFormat(internalformat, context->getClientVersion())) | |
| { | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| return true; | |
| } | |
| bool ValidateES3FramebufferTextureParameters(gl::Context *context, GLenum target, GLenum attachment, | |
| GLenum textarget, GLuint texture, GLint level, GLint layer, | |
| bool layerCall) | |
| { | |
| if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER) | |
| { | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| if (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15) | |
| { | |
| const unsigned int colorAttachment = (attachment - GL_COLOR_ATTACHMENT0); | |
| if (colorAttachment >= context->getMaximumRenderTargets()) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| } | |
| else | |
| { | |
| switch (attachment) | |
| { | |
| case GL_DEPTH_ATTACHMENT: | |
| case GL_STENCIL_ATTACHMENT: | |
| case GL_DEPTH_STENCIL_ATTACHMENT: | |
| break; | |
| default: | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| } | |
| if (texture != 0) | |
| { | |
| gl::Texture *tex = context->getTexture(texture); | |
| if (tex == NULL) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| if (level < 0) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (layer < 0) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (!layerCall) | |
| { | |
| switch (textarget) | |
| { | |
| case GL_TEXTURE_2D: | |
| { | |
| if (level > gl::log2(context->getMaximum2DTextureDimension())) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (tex->getTarget() != GL_TEXTURE_2D) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| gl::Texture2D *tex2d = static_cast<gl::Texture2D *>(tex); | |
| if (tex2d->isCompressed(level)) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| break; | |
| } | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_X: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: | |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: | |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: | |
| { | |
| if (level > gl::log2(context->getMaximumCubeTextureDimension())) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (tex->getTarget() != GL_TEXTURE_CUBE_MAP) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| gl::TextureCubeMap *texcube = static_cast<gl::TextureCubeMap *>(tex); | |
| if (texcube->isCompressed(textarget, level)) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| break; | |
| } | |
| default: | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| } | |
| else | |
| { | |
| switch (tex->getTarget()) | |
| { | |
| case GL_TEXTURE_2D_ARRAY: | |
| { | |
| if (level > gl::log2(context->getMaximum2DTextureDimension())) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (layer >= context->getMaximum2DArrayTextureLayers()) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| gl::Texture2DArray *texArray = static_cast<gl::Texture2DArray *>(tex); | |
| if (texArray->isCompressed(level)) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| break; | |
| } | |
| case GL_TEXTURE_3D: | |
| { | |
| if (level > gl::log2(context->getMaximum3DTextureDimension())) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| if (layer >= context->getMaximum3DTextureDimension()) | |
| { | |
| return gl::error(GL_INVALID_VALUE, false); | |
| } | |
| gl::Texture3D *tex3d = static_cast<gl::Texture3D *>(tex); | |
| if (tex3d->isCompressed(level)) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| break; | |
| } | |
| default: | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| } | |
| } | |
| gl::Framebuffer *framebuffer = NULL; | |
| GLuint framebufferHandle = 0; | |
| if (target == GL_READ_FRAMEBUFFER) | |
| { | |
| framebuffer = context->getReadFramebuffer(); | |
| framebufferHandle = context->getReadFramebufferHandle(); | |
| } | |
| else | |
| { | |
| framebuffer = context->getDrawFramebuffer(); | |
| framebufferHandle = context->getDrawFramebufferHandle(); | |
| } | |
| if (framebufferHandle == 0 || !framebuffer) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| return true; | |
| } | |
| bool ValidES3ReadFormatType(GLenum internalFormat, GLenum format, GLenum type) | |
| { | |
| switch (format) | |
| { | |
| case GL_RGBA: | |
| switch (type) | |
| { | |
| case GL_UNSIGNED_BYTE: | |
| break; | |
| case GL_UNSIGNED_INT_2_10_10_10_REV: | |
| if (internalFormat != GL_RGB10_A2) | |
| { | |
| return false; | |
| } | |
| break; | |
| case GL_FLOAT: | |
| if (gl::GetComponentType(internalFormat, 3) != GL_FLOAT) | |
| { | |
| return false; | |
| } | |
| break; | |
| default: | |
| return false; | |
| } | |
| break; | |
| case GL_RGBA_INTEGER: | |
| switch (type) | |
| { | |
| case GL_INT: | |
| if (gl::GetComponentType(internalFormat, 3) != GL_INT) | |
| { | |
| return false; | |
| } | |
| break; | |
| case GL_UNSIGNED_INT: | |
| if (gl::GetComponentType(internalFormat, 3) != GL_UNSIGNED_INT) | |
| { | |
| return false; | |
| } | |
| break; | |
| default: | |
| return false; | |
| } | |
| break; | |
| case GL_BGRA_EXT: | |
| switch (type) | |
| { | |
| case GL_UNSIGNED_BYTE: | |
| case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT: | |
| case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT: | |
| break; | |
| default: | |
| return false; | |
| } | |
| break; | |
| default: | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ValidateInvalidateFramebufferParameters(gl::Context *context, GLenum target, GLsizei numAttachments, | |
| const GLenum* attachments) | |
| { | |
| bool defaultFramebuffer = false; | |
| switch (target) | |
| { | |
| case GL_DRAW_FRAMEBUFFER: | |
| case GL_FRAMEBUFFER: | |
| defaultFramebuffer = context->getDrawFramebufferHandle() == 0; | |
| break; | |
| case GL_READ_FRAMEBUFFER: | |
| defaultFramebuffer = context->getReadFramebufferHandle() == 0; | |
| break; | |
| default: | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| for (int i = 0; i < numAttachments; ++i) | |
| { | |
| if (attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15) | |
| { | |
| if (defaultFramebuffer) | |
| { | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| if (attachments[i] >= GL_COLOR_ATTACHMENT0 + context->getMaximumRenderTargets()) | |
| { | |
| return gl::error(GL_INVALID_OPERATION, false); | |
| } | |
| } | |
| else | |
| { | |
| switch (attachments[i]) | |
| { | |
| case GL_DEPTH_ATTACHMENT: | |
| case GL_STENCIL_ATTACHMENT: | |
| case GL_DEPTH_STENCIL_ATTACHMENT: | |
| if (defaultFramebuffer) | |
| { | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| break; | |
| case GL_COLOR: | |
| case GL_DEPTH: | |
| case GL_STENCIL: | |
| if (!defaultFramebuffer) | |
| { | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| break; | |
| default: | |
| return gl::error(GL_INVALID_ENUM, false); | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| } |