Commit dfc20daf by Jamie Madill Committed by Commit Bot

Plumb more logic for ANGLE_get_image.

Also implements and tests validation / negative API. Bug: angleproject:3944 Change-Id: I3385a4255f4fab6a12eee2abfa5ffcce2107359a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1879961 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com>
parent 2f71802b
......@@ -896,6 +896,7 @@ const ExtensionInfoMap &GetExtensionInfoMap()
map["GL_CHROMIUM_lose_context"] = enableableExtension(&Extensions::loseContextCHROMIUM);
map["GL_ANGLE_texture_external_update"] = enableableExtension(&Extensions::textureExternalUpdateANGLE);
map["GL_ANGLE_base_vertex_base_instance"] = enableableExtension(&Extensions::baseVertexBaseInstance);
map["GL_ANGLE_get_image"] = enableableExtension(&Extensions::getImageANGLE);
// GLES1 extensinos
map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArray);
map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMap);
......
......@@ -541,6 +541,9 @@ struct Extensions
// GL_ANGLE_base_vertex_base_instance
bool baseVertexBaseInstance = false;
// GL_ANGLE_get_image
bool getImageANGLE = false;
};
// Pointer to a boolean memeber of the Extensions struct
......
......@@ -2108,7 +2108,7 @@ void Context::texBufferRange(GLenum target,
void Context::getTexParameterfv(TextureType target, GLenum pname, GLfloat *params)
{
const Texture *const texture = getTextureByType(target);
QueryTexParameterfv(texture, pname, params);
QueryTexParameterfv(this, texture, pname, params);
}
void Context::getTexParameterfvRobust(TextureType target,
......@@ -2123,19 +2123,19 @@ void Context::getTexParameterfvRobust(TextureType target,
void Context::getTexParameteriv(TextureType target, GLenum pname, GLint *params)
{
const Texture *const texture = getTextureByType(target);
QueryTexParameteriv(texture, pname, params);
QueryTexParameteriv(this, texture, pname, params);
}
void Context::getTexParameterIiv(TextureType target, GLenum pname, GLint *params)
{
const Texture *const texture = getTextureByType(target);
QueryTexParameterIiv(texture, pname, params);
QueryTexParameterIiv(this, texture, pname, params);
}
void Context::getTexParameterIuiv(TextureType target, GLenum pname, GLuint *params)
{
const Texture *const texture = getTextureByType(target);
QueryTexParameterIuiv(texture, pname, params);
QueryTexParameterIuiv(this, texture, pname, params);
}
void Context::getTexParameterivRobust(TextureType target,
......@@ -9204,12 +9204,18 @@ void Context::getTexImage(TextureTarget target,
GLenum type,
void *pixels)
{
UNIMPLEMENTED();
Texture *texture = getTextureByTarget(target);
Buffer *packBuffer = mState.getTargetBuffer(BufferBinding::PixelPack);
ANGLE_CONTEXT_TRY(texture->getTexImage(this, mState.getPackState(), packBuffer, target, level,
format, type, pixels));
}
void Context::getRenderbufferImage(GLenum target, GLenum format, GLenum type, void *pixels)
{
UNIMPLEMENTED();
Renderbuffer *renderbuffer = mState.getCurrentRenderbuffer();
Buffer *packBuffer = mState.getTargetBuffer(BufferBinding::PixelPack);
ANGLE_CONTEXT_TRY(renderbuffer->getRenderbufferImage(this, mState.getPackState(), packBuffer,
format, type, pixels));
}
// ErrorSet implementation.
......
......@@ -137,6 +137,7 @@ MSG kFramebufferTextureLayerIncorrectTextureType = "Texture is not a three-dimen
MSG kGenerateMipmapNotAllowed = "Texture format does not support mipmap generation.";
MSG kGenerateMipmapZeroSize = "Cannot generate mipmaps for a zero-size texture in a WebGL context.";
MSG kGeometryShaderExtensionNotEnabled = "GL_EXT_geometry_shader extension not enabled.";
MSG kGetImageExtensionNotEnabled = "GL_ANGLE_get_image extension not enabled.";
MSG kGLES1Only = "GLES1-only function.";
MSG kImageSizeMustBeZero = "imageSize must be 0 if no texture data is provided.";
MSG kImageSizeTooSmall = "imageSize is too small.";
......
......@@ -280,4 +280,24 @@ rx::FramebufferAttachmentObjectImpl *Renderbuffer::getAttachmentImpl() const
return mImplementation.get();
}
GLenum Renderbuffer::getImplementationColorReadFormat(const Context *context) const
{
return mImplementation->getColorReadFormat(context);
}
GLenum Renderbuffer::getImplementationColorReadType(const Context *context) const
{
return mImplementation->getColorReadType(context);
}
angle::Result Renderbuffer::getRenderbufferImage(const Context *context,
const PixelPackState &packState,
Buffer *packBuffer,
GLenum format,
GLenum type,
void *pixels) const
{
return mImplementation->getRenderbufferImage(context, packState, packBuffer, format, type,
pixels);
}
} // namespace gl
......@@ -115,6 +115,17 @@ class Renderbuffer final : public RefCountObject<RenderbufferID>,
InitState initState(const ImageIndex &imageIndex) const override;
void setInitState(const ImageIndex &imageIndex, InitState initState) override;
GLenum getImplementationColorReadFormat(const Context *context) const;
GLenum getImplementationColorReadType(const Context *context) const;
// We pass the pack buffer and state explicitly so they can be overridden during capture.
angle::Result getRenderbufferImage(const Context *context,
const PixelPackState &packState,
Buffer *packBuffer,
GLenum format,
GLenum type,
void *pixels) const;
private:
rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const override;
......
......@@ -1886,4 +1886,26 @@ void Texture::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMess
}
}
GLenum Texture::getImplementationColorReadFormat(const Context *context) const
{
return mTexture->getColorReadFormat(context);
}
GLenum Texture::getImplementationColorReadType(const Context *context) const
{
return mTexture->getColorReadType(context);
}
angle::Result Texture::getTexImage(const Context *context,
const PixelPackState &packState,
Buffer *packBuffer,
TextureTarget target,
GLint level,
GLenum format,
GLenum type,
void *pixels) const
{
return mTexture->getTexImage(context, packState, packBuffer, target, level, format, type,
pixels);
}
} // namespace gl
......@@ -423,6 +423,19 @@ class Texture final : public RefCountObject<TextureID>,
bool isSamplerComplete(const Context *context, const Sampler *optionalSampler);
GLenum getImplementationColorReadFormat(const Context *context) const;
GLenum getImplementationColorReadType(const Context *context) const;
// We pass the pack buffer and state explicitly so they can be overridden during capture.
angle::Result getTexImage(const Context *context,
const PixelPackState &packState,
Buffer *packBuffer,
TextureTarget target,
GLint level,
GLenum format,
GLenum type,
void *pixels) const;
rx::TextureImpl *getImplementation() const { return mTexture; }
// FramebufferAttachmentObject implementation
......
......@@ -218,7 +218,10 @@ void QueryTexLevelParameterBase(const Texture *texture,
}
template <bool isPureInteger, typename ParamType>
void QueryTexParameterBase(const Texture *texture, GLenum pname, ParamType *params)
void QueryTexParameterBase(const Context *context,
const Texture *texture,
GLenum pname,
ParamType *params)
{
ASSERT(texture != nullptr);
......@@ -309,6 +312,14 @@ void QueryTexParameterBase(const Texture *texture, GLenum pname, ParamType *para
case GL_TEXTURE_NATIVE_ID_ANGLE:
*params = CastFromStateValue<ParamType>(pname, texture->getNativeID());
break;
case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
*params = CastFromGLintStateValue<ParamType>(
pname, texture->getImplementationColorReadFormat(context));
break;
case GL_IMPLEMENTATION_COLOR_READ_TYPE:
*params = CastFromGLintStateValue<ParamType>(
pname, texture->getImplementationColorReadType(context));
break;
default:
UNREACHABLE();
break;
......@@ -997,7 +1008,7 @@ bool IsTextureEnvEnumParameter(TextureEnvParameter pname)
}
}
} // anonymous namespace
} // namespace
void QueryFramebufferAttachmentParameteriv(const Context *context,
const Framebuffer *framebuffer,
......@@ -1303,6 +1314,12 @@ void QueryRenderbufferiv(const Context *context,
case GL_MEMORY_SIZE_ANGLE:
*params = renderbuffer->getMemorySize();
break;
case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
*params = static_cast<GLint>(renderbuffer->getImplementationColorReadFormat(context));
break;
case GL_IMPLEMENTATION_COLOR_READ_TYPE:
*params = static_cast<GLint>(renderbuffer->getImplementationColorReadType(context));
break;
default:
UNREACHABLE();
break;
......@@ -1367,24 +1384,36 @@ void QueryTexLevelParameteriv(const Texture *texture,
QueryTexLevelParameterBase(texture, target, level, pname, params);
}
void QueryTexParameterfv(const Texture *texture, GLenum pname, GLfloat *params)
void QueryTexParameterfv(const Context *context,
const Texture *texture,
GLenum pname,
GLfloat *params)
{
QueryTexParameterBase<false>(texture, pname, params);
QueryTexParameterBase<false>(context, texture, pname, params);
}
void QueryTexParameteriv(const Texture *texture, GLenum pname, GLint *params)
void QueryTexParameteriv(const Context *context,
const Texture *texture,
GLenum pname,
GLint *params)
{
QueryTexParameterBase<false>(texture, pname, params);
QueryTexParameterBase<false>(context, texture, pname, params);
}
void QueryTexParameterIiv(const Texture *texture, GLenum pname, GLint *params)
void QueryTexParameterIiv(const Context *context,
const Texture *texture,
GLenum pname,
GLint *params)
{
QueryTexParameterBase<true>(texture, pname, params);
QueryTexParameterBase<true>(context, texture, pname, params);
}
void QueryTexParameterIuiv(const Texture *texture, GLenum pname, GLuint *params)
void QueryTexParameterIuiv(const Context *context,
const Texture *texture,
GLenum pname,
GLuint *params)
{
QueryTexParameterBase<true>(texture, pname, params);
QueryTexParameterBase<true>(context, texture, pname, params);
}
void QuerySamplerParameterfv(const Sampler *sampler, GLenum pname, GLfloat *params)
......
......@@ -58,10 +58,22 @@ void QueryTexLevelParameteriv(const Texture *texture,
GLint level,
GLenum pname,
GLint *params);
void QueryTexParameterfv(const Texture *texture, GLenum pname, GLfloat *params);
void QueryTexParameteriv(const Texture *texture, GLenum pname, GLint *params);
void QueryTexParameterIiv(const Texture *texture, GLenum pname, GLint *params);
void QueryTexParameterIuiv(const Texture *texture, GLenum pname, GLuint *params);
void QueryTexParameterfv(const Context *context,
const Texture *texture,
GLenum pname,
GLfloat *params);
void QueryTexParameteriv(const Context *context,
const Texture *texture,
GLenum pname,
GLint *params);
void QueryTexParameterIiv(const Context *context,
const Texture *texture,
GLenum pname,
GLint *params);
void QueryTexParameterIuiv(const Context *context,
const Texture *texture,
GLenum pname,
GLuint *params);
void QuerySamplerParameterfv(const Sampler *sampler, GLenum pname, GLfloat *params);
void QuerySamplerParameteriv(const Sampler *sampler, GLenum pname, GLint *params);
void QuerySamplerParameterIiv(const Sampler *sampler, GLenum pname, GLint *params);
......
......@@ -16,6 +16,7 @@
namespace gl
{
struct PixelPackState;
class RenderbufferState;
} // namespace gl
......@@ -46,6 +47,16 @@ class RenderbufferImpl : public FramebufferAttachmentObjectImpl
virtual angle::Result setStorageEGLImageTarget(const gl::Context *context,
egl::Image *image) = 0;
virtual GLenum getColorReadFormat(const gl::Context *context);
virtual GLenum getColorReadType(const gl::Context *context);
virtual angle::Result getRenderbufferImage(const gl::Context *context,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
GLenum format,
GLenum type,
void *pixels);
// Override if accurate native memory size information is available
virtual GLint getMemorySize() const;
......@@ -58,6 +69,28 @@ inline GLint RenderbufferImpl::getMemorySize() const
return 0;
}
inline GLenum RenderbufferImpl::getColorReadFormat(const gl::Context *context)
{
UNREACHABLE();
return GL_NONE;
}
inline GLenum RenderbufferImpl::getColorReadType(const gl::Context *context)
{
UNREACHABLE();
return GL_NONE;
}
inline angle::Result RenderbufferImpl::getRenderbufferImage(const gl::Context *context,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
GLenum format,
GLenum type,
void *pixels)
{
UNREACHABLE();
return angle::Result::Stop;
}
} // namespace rx
#endif // LIBANGLE_RENDERER_RENDERBUFFERIMPL_H_
......@@ -108,4 +108,28 @@ GLint TextureImpl::getNativeID() const
return 0;
}
GLenum TextureImpl::getColorReadFormat(const gl::Context *context)
{
UNREACHABLE();
return GL_NONE;
}
GLenum TextureImpl::getColorReadType(const gl::Context *context)
{
UNREACHABLE();
return GL_NONE;
}
angle::Result TextureImpl::getTexImage(const gl::Context *context,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
gl::TextureTarget target,
GLint level,
GLenum format,
GLenum type,
void *pixels)
{
UNREACHABLE();
return angle::Result::Stop;
}
} // namespace rx
......@@ -188,6 +188,18 @@ class TextureImpl : public FramebufferAttachmentObjectImpl, public angle::Subjec
virtual angle::Result syncState(const gl::Context *context,
const gl::Texture::DirtyBits &dirtyBits) = 0;
virtual GLenum getColorReadFormat(const gl::Context *context);
virtual GLenum getColorReadType(const gl::Context *context);
virtual angle::Result getTexImage(const gl::Context *context,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
gl::TextureTarget target,
GLint level,
GLenum format,
GLenum type,
void *pixels);
protected:
const gl::TextureState &mState;
};
......
......@@ -225,4 +225,26 @@ void RenderbufferVk::releaseImage(ContextVk *contextVk)
mImageViews.release(renderer);
}
GLenum RenderbufferVk::getColorReadFormat(const gl::Context *context)
{
UNIMPLEMENTED();
return GL_NONE;
}
GLenum RenderbufferVk::getColorReadType(const gl::Context *context)
{
UNIMPLEMENTED();
return GL_NONE;
}
angle::Result RenderbufferVk::getRenderbufferImage(const gl::Context *context,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
GLenum format,
GLenum type,
void *pixels)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
} // namespace rx
......@@ -48,6 +48,16 @@ class RenderbufferVk : public RenderbufferImpl
vk::ImageHelper *getImage() const { return mImage; }
void releaseOwnershipOfImage(const gl::Context *context);
GLenum getColorReadFormat(const gl::Context *context) override;
GLenum getColorReadType(const gl::Context *context) override;
angle::Result getRenderbufferImage(const gl::Context *context,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
GLenum format,
GLenum type,
void *pixels) override;
private:
void releaseAndDeleteImage(ContextVk *contextVk);
void releaseImage(ContextVk *contextVk);
......
......@@ -1269,7 +1269,6 @@ angle::Result TextureVk::ensureImageInitialized(ContextVk *contextVk, ImageMipLe
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
const gl::Extents &baseLevelExtents = baseLevelDesc.size;
const uint32_t levelCount = getMipLevelCount(mipLevels);
const vk::Format &format =
contextVk->getRenderer()->getFormat(baseLevelDesc.format.info->sizedInternalFormat);
......@@ -1704,4 +1703,29 @@ angle::Result TextureVk::generateMipmapLevelsWithCPU(ContextVk *contextVk,
return angle::Result::Continue;
}
GLenum TextureVk::getColorReadFormat(const gl::Context *context)
{
UNIMPLEMENTED();
return GL_NONE;
}
GLenum TextureVk::getColorReadType(const gl::Context *context)
{
UNIMPLEMENTED();
return GL_NONE;
}
angle::Result TextureVk::getTexImage(const gl::Context *context,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
gl::TextureTarget target,
GLint level,
GLenum format,
GLenum type,
void *pixels)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
} // namespace rx
......@@ -198,6 +198,18 @@ class TextureVk : public TextureImpl
mStagingBufferInitialSize = initialSizeForTesting;
}
GLenum getColorReadFormat(const gl::Context *context) override;
GLenum getColorReadType(const gl::Context *context) override;
angle::Result getTexImage(const gl::Context *context,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
gl::TextureTarget target,
GLint level,
GLenum format,
GLenum type,
void *pixels) override;
private:
// Transform an image index from the frontend into one that can be used on the backing
// ImageHelper, taking into account mipmap or cube face offsets
......
......@@ -69,8 +69,8 @@ void RendererVk::ensureCapsInitialized() const
// Enable EXT_blend_minmax
mNativeExtensions.blendMinMax = true;
mNativeExtensions.eglImage = true;
mNativeExtensions.eglImageExternal = true;
mNativeExtensions.eglImage = true;
mNativeExtensions.eglImageExternal = true;
mNativeExtensions.eglImageExternalEssl3 = true;
mNativeExtensions.memoryObject = true;
......@@ -126,6 +126,9 @@ void RendererVk::ensureCapsInitialized() const
mNativeExtensions.fboRenderMipmap = true;
// We support getting image data for Textures and Renderbuffers.
mNativeExtensions.getImageANGLE = true;
// https://vulkan.lunarg.com/doc/view/1.0.30.0/linux/vkspec.chunked/ch31s02.html
mNativeCaps.maxElementIndex = std::numeric_limits<GLuint>::max() - 1;
mNativeCaps.max3DTextureSize = limitsVk.maxImageDimension3D;
......@@ -136,8 +139,8 @@ void RendererVk::ensureCapsInitialized() const
mNativeCaps.maxRenderbufferSize =
std::min({limitsVk.maxImageDimension2D, limitsVk.maxFramebufferWidth,
limitsVk.maxFramebufferHeight});
mNativeCaps.minAliasedPointSize = std::max(1.0f, limitsVk.pointSizeRange[0]);
mNativeCaps.maxAliasedPointSize = limitsVk.pointSizeRange[1];
mNativeCaps.minAliasedPointSize = std::max(1.0f, limitsVk.pointSizeRange[0]);
mNativeCaps.maxAliasedPointSize = limitsVk.pointSizeRange[1];
mNativeCaps.minAliasedLineWidth = 1.0f;
mNativeCaps.maxAliasedLineWidth = 1.0f;
......@@ -211,14 +214,14 @@ void RendererVk::ensureCapsInitialized() const
// Uniforms are implemented using a uniform buffer, so the max number of uniforms we can
// support is the max buffer range divided by the size of a single uniform (4X float).
mNativeCaps.maxVertexUniformVectors = maxUniformVectors;
mNativeCaps.maxFragmentUniformVectors = maxUniformVectors;
mNativeCaps.maxFragmentInputComponents = maxUniformComponents;
mNativeCaps.maxVertexUniformVectors = maxUniformVectors;
mNativeCaps.maxFragmentUniformVectors = maxUniformVectors;
mNativeCaps.maxFragmentInputComponents = maxUniformComponents;
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mNativeCaps.maxShaderUniformComponents[shaderType] = maxUniformComponents;
}
mNativeCaps.maxUniformLocations = maxUniformVectors;
mNativeCaps.maxUniformLocations = maxUniformVectors;
// Every stage has 1 reserved uniform buffer for the default uniforms, and 1 for the driver
// uniforms.
......@@ -235,7 +238,7 @@ void RendererVk::ensureCapsInitialized() const
{
mNativeCaps.maxShaderUniformBlocks[shaderType] = maxPerStageUniformBuffers;
}
mNativeCaps.maxCombinedUniformBlocks = maxCombinedUniformBuffers;
mNativeCaps.maxCombinedUniformBlocks = maxCombinedUniformBuffers;
mNativeCaps.maxUniformBufferBindings = maxCombinedUniformBuffers;
mNativeCaps.maxUniformBlockSize = maxUniformBlockSize;
......@@ -252,7 +255,7 @@ void RendererVk::ensureCapsInitialized() const
{
mNativeCaps.maxShaderTextureImageUnits[shaderType] = maxPerStageTextures;
}
mNativeCaps.maxCombinedTextureImageUnits = maxCombinedTextures;
mNativeCaps.maxCombinedTextureImageUnits = maxCombinedTextures;
uint32_t maxPerStageStorageBuffers = limitsVk.maxPerStageDescriptorStorageBuffers;
uint32_t maxVertexStageStorageBuffers = maxPerStageStorageBuffers;
......
......@@ -5101,6 +5101,15 @@ bool ValidateGetRenderbufferParameterivBase(Context *context,
}
break;
case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
case GL_IMPLEMENTATION_COLOR_READ_TYPE:
if (!context->getExtensions().getImageANGLE)
{
context->validationError(GL_INVALID_ENUM, kGetImageExtensionNotEnabled);
return false;
}
break;
default:
context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
return false;
......@@ -5311,6 +5320,15 @@ bool ValidateGetTexParameterBase(Context *context,
}
break;
case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
case GL_IMPLEMENTATION_COLOR_READ_TYPE:
if (!context->getExtensions().getImageANGLE)
{
context->validationError(GL_INVALID_ENUM, kGetImageExtensionNotEnabled);
return false;
}
break;
default:
context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
return false;
......@@ -5418,6 +5436,82 @@ bool ValidateGetVertexAttribBase(Context *context,
return true;
}
bool ValidatePixelPack(Context *context,
GLenum format,
GLenum type,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLsizei bufSize,
GLsizei *length,
void *pixels)
{
// Check for pixel pack buffer related API errors
Buffer *pixelPackBuffer = context->getState().getTargetBuffer(BufferBinding::PixelPack);
if (pixelPackBuffer != nullptr && pixelPackBuffer->isMapped())
{
// ...the buffer object's data store is currently mapped.
context->validationError(GL_INVALID_OPERATION, kBufferMapped);
return false;
}
if (context->getExtensions().webglCompatibility && pixelPackBuffer != nullptr &&
pixelPackBuffer->isBoundForTransformFeedbackAndOtherUse())
{
context->validationError(GL_INVALID_OPERATION, kPixelPackBufferBoundForTransformFeedback);
return false;
}
// .. the data would be packed to the buffer object such that the memory writes required
// would exceed the data store size.
const InternalFormat &formatInfo = GetInternalFormatInfo(format, type);
const Extents size(width, height, 1);
const auto &pack = context->getState().getPackState();
GLuint endByte = 0;
if (!formatInfo.computePackUnpackEndByte(type, size, pack, false, &endByte))
{
context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
return false;
}
if (bufSize >= 0)
{
if (pixelPackBuffer == nullptr && static_cast<size_t>(bufSize) < endByte)
{
context->validationError(GL_INVALID_OPERATION, kInsufficientBufferSize);
return false;
}
}
if (pixelPackBuffer != nullptr)
{
CheckedNumeric<size_t> checkedEndByte(endByte);
CheckedNumeric<size_t> checkedOffset(reinterpret_cast<size_t>(pixels));
checkedEndByte += checkedOffset;
if (checkedEndByte.ValueOrDie() > static_cast<size_t>(pixelPackBuffer->getSize()))
{
// Overflow past the end of the buffer
context->validationError(GL_INVALID_OPERATION, kParamOverflow);
return false;
}
}
if (pixelPackBuffer == nullptr && length != nullptr)
{
if (endByte > static_cast<size_t>(std::numeric_limits<GLsizei>::max()))
{
context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
return false;
}
*length = static_cast<GLsizei>(endByte);
}
return true;
}
bool ValidateReadPixelsBase(Context *context,
GLint x,
GLint y,
......@@ -5532,67 +5626,10 @@ bool ValidateReadPixelsBase(Context *context,
return false;
}
// Check for pixel pack buffer related API errors
Buffer *pixelPackBuffer = context->getState().getTargetBuffer(BufferBinding::PixelPack);
if (pixelPackBuffer != nullptr && pixelPackBuffer->isMapped())
if (!ValidatePixelPack(context, format, type, x, y, width, height, bufSize, length, pixels))
{
// ...the buffer object's data store is currently mapped.
context->validationError(GL_INVALID_OPERATION, kBufferMapped);
return false;
}
if (context->getExtensions().webglCompatibility && pixelPackBuffer != nullptr &&
pixelPackBuffer->isBoundForTransformFeedbackAndOtherUse())
{
context->validationError(GL_INVALID_OPERATION, kPixelPackBufferBoundForTransformFeedback);
return false;
}
// .. the data would be packed to the buffer object such that the memory writes required
// would exceed the data store size.
const InternalFormat &formatInfo = GetInternalFormatInfo(format, type);
const Extents size(width, height, 1);
const auto &pack = context->getState().getPackState();
GLuint endByte = 0;
if (!formatInfo.computePackUnpackEndByte(type, size, pack, false, &endByte))
{
context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
return false;
}
if (bufSize >= 0)
{
if (pixelPackBuffer == nullptr && static_cast<size_t>(bufSize) < endByte)
{
context->validationError(GL_INVALID_OPERATION, kInsufficientBufferSize);
return false;
}
}
if (pixelPackBuffer != nullptr)
{
CheckedNumeric<size_t> checkedEndByte(endByte);
CheckedNumeric<size_t> checkedOffset(reinterpret_cast<size_t>(pixels));
checkedEndByte += checkedOffset;
if (checkedEndByte.ValueOrDie() > static_cast<size_t>(pixelPackBuffer->getSize()))
{
// Overflow past the end of the buffer
context->validationError(GL_INVALID_OPERATION, kParamOverflow);
return false;
}
}
if (pixelPackBuffer == nullptr && length != nullptr)
{
if (endByte > static_cast<size_t>(std::numeric_limits<GLsizei>::max()))
{
context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
return false;
}
*length = static_cast<GLsizei>(endByte);
}
auto getClippedExtent = [](GLint start, GLsizei length, int bufferSize, GLsizei *outExtent) {
angle::CheckedNumeric<int> clippedExtent(length);
......@@ -5719,6 +5756,7 @@ bool ValidateTexParameterBase(Context *context,
return false;
}
break;
default:
break;
}
......
......@@ -126,6 +126,17 @@ bool ValidateBlitFramebufferParameters(Context *context,
GLbitfield mask,
GLenum filter);
bool ValidatePixelPack(Context *context,
GLenum format,
GLenum type,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLsizei bufSize,
GLsizei *length,
void *pixels);
bool ValidateReadPixelsBase(Context *context,
GLint x,
GLint y,
......
......@@ -8,9 +8,39 @@
#include "libANGLE/validationESEXT_autogen.h"
#include "libANGLE/Context.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/validationES.h"
namespace gl
{
using namespace err;
namespace
{
template <typename ObjectT>
bool ValidateGetImageFormatAndType(Context *context, ObjectT *obj, GLenum format, GLenum type)
{
GLenum implFormat = obj->getImplementationColorReadFormat(context);
if (!ValidES3Format(format) && (format != implFormat || format == GL_NONE))
{
context->validationError(GL_INVALID_ENUM, kInvalidFormat);
return false;
}
GLenum implType = obj->getImplementationColorReadType(context);
if (!ValidES3Type(type) && (type != implType || type == GL_NONE))
{
context->validationError(GL_INVALID_ENUM, kInvalidType);
return false;
}
// Format/type combinations are not yet validated.
return true;
}
} // namespace
bool ValidateGetTexImageANGLE(Context *context,
TextureTarget target,
GLint level,
......@@ -18,14 +48,81 @@ bool ValidateGetTexImageANGLE(Context *context,
GLenum type,
void *pixels)
{
return false;
if (!context->getExtensions().getImageANGLE)
{
context->validationError(GL_INVALID_OPERATION, kGetImageExtensionNotEnabled);
return false;
}
if (!ValidTexture2DDestinationTarget(context, target) &&
!ValidTexture3DDestinationTarget(context, target))
{
context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
return false;
}
if (level < 0)
{
context->validationError(GL_INVALID_VALUE, kNegativeLevel);
return false;
}
TextureType textureType = TextureTargetToType(target);
if (!ValidMipLevel(context, textureType, level))
{
context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
return false;
}
Texture *texture = context->getTextureByTarget(target);
if (!ValidateGetImageFormatAndType(context, texture, format, type))
{
return false;
}
GLsizei width = static_cast<GLsizei>(texture->getWidth(target, level));
GLsizei height = static_cast<GLsizei>(texture->getHeight(target, level));
if (!ValidatePixelPack(context, format, type, 0, 0, width, height, -1, nullptr, pixels))
{
return false;
}
return true;
}
bool ValidateGetRenderbufferImageANGLE(Context *context,
GLenum target,
GLenum format,
GLenum type,
void *pixels)
{
return false;
if (!context->getExtensions().getImageANGLE)
{
context->validationError(GL_INVALID_OPERATION, kGetImageExtensionNotEnabled);
return false;
}
if (target != GL_RENDERBUFFER)
{
context->validationError(GL_INVALID_ENUM, kInvalidRenderbufferTarget);
return false;
}
Renderbuffer *renderbuffer = context->getState().getCurrentRenderbuffer();
if (!ValidateGetImageFormatAndType(context, renderbuffer, format, type))
{
return false;
}
GLsizei width = renderbuffer->getWidth();
GLsizei height = renderbuffer->getHeight();
if (!ValidatePixelPack(context, format, type, 0, 0, width, height, -1, nullptr, pixels))
{
return false;
}
return true;
}
} // namespace gl
......@@ -43,6 +43,7 @@ angle_end2end_tests_sources = [
"gl_tests/FramebufferRenderMipmapTest.cpp",
"gl_tests/FramebufferTest.cpp",
"gl_tests/GeometryShaderTest.cpp",
"gl_tests/GetImageTest.cpp",
"gl_tests/gles1/AlphaFuncTest.cpp",
"gl_tests/gles1/BasicDrawTest.cpp",
"gl_tests/gles1/ClientActiveTextureTest.cpp",
......
//
// Copyright 2019 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.
//
// GetImageTest:
// Tests for the ANGLE_get_image extension.
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
namespace
{
constexpr uint32_t kSize = 32;
constexpr char kExtensionName[] = "GL_ANGLE_get_image";
class GetImageTest : public ANGLETest
{
public:
GetImageTest()
{
setWindowWidth(kSize);
setWindowHeight(kSize);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
class GetImageTestNoExtensions : public ANGLETest
{
public:
GetImageTestNoExtensions() { setExtensionsEnabled(false); }
};
GLTexture InitSimpleTexture()
{
std::vector<GLColor> pixelData(kSize * kSize, GLColor::red);
// Create a simple texture.
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
pixelData.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return tex;
}
GLRenderbuffer InitSimpleRenderbuffer()
{
// Create a simple renderbuffer.
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, kSize, kSize);
return renderbuf;
}
// Test validation for the extension functions.
TEST_P(GetImageTest, NegativeAPI)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
// Draw once with simple texture.
GLTexture tex = InitSimpleTexture();
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetTexImage can work with correct parameters.
std::vector<GLColor> buffer(kSize * kSize);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_NO_ERROR();
// Test invalid texture target.
glGetTexImageANGLE(GL_TEXTURE_CUBE_MAP, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Test invalid texture level.
glGetTexImageANGLE(GL_TEXTURE_2D, -1, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glGetTexImageANGLE(GL_TEXTURE_2D, 2000, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Test invalid format and type.
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_NONE, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_NONE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Create a simple renderbuffer.
GLRenderbuffer renderbuf = InitSimpleRenderbuffer();
ASSERT_GL_NO_ERROR();
// Verify GetRenderbufferImage can work with correct parameters.
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_NO_ERROR();
// Test invalid renderbuffer target.
glGetRenderbufferImageANGLE(GL_TEXTURE_2D, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Test invalid renderbuffer format/type.
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_NONE, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_NONE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Pack buffer tests. Requires ES 3+ or extension.
if (getClientMajorVersion() >= 3 || IsGLExtensionEnabled("GL_NV_pixel_buffer_object"))
{
// Test valid pack buffer.
GLBuffer packBuffer;
glBindBuffer(GL_PIXEL_PACK_BUFFER, packBuffer);
glBufferData(GL_PIXEL_PACK_BUFFER, kSize * kSize * sizeof(GLColor), nullptr,
GL_STATIC_DRAW);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Test too small pack buffer.
glBufferData(GL_PIXEL_PACK_BUFFER, kSize, nullptr, GL_STATIC_DRAW);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
}
// Verifies that the extension enums and entry points are invalid when the extension is disabled.
TEST_P(GetImageTestNoExtensions, EntryPointsInactive)
{
// Verify the extension is not enabled.
ASSERT_FALSE(IsGLExtensionEnabled(kExtensionName));
// Draw once with simple texture.
GLTexture tex = InitSimpleTexture();
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Query implementation format and type. Should give invalid enum.
GLint param;
glGetTexParameteriv(GL_TEXTURE_2D, GL_IMPLEMENTATION_COLOR_READ_FORMAT, &param);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glGetTexParameteriv(GL_TEXTURE_2D, GL_IMPLEMENTATION_COLOR_READ_TYPE, &param);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Verify calling GetTexImage produces an error.
std::vector<GLColor> buffer(kSize * kSize, 0);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Create a simple renderbuffer.
GLRenderbuffer renderbuf = InitSimpleRenderbuffer();
ASSERT_GL_NO_ERROR();
// Query implementation format and type. Should give invalid enum.
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_IMPLEMENTATION_COLOR_READ_FORMAT, &param);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_IMPLEMENTATION_COLOR_READ_TYPE, &param);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Verify calling GetRenderbufferImage produces an error.
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
ANGLE_INSTANTIATE_TEST(GetImageTest, ES2_VULKAN(), ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(GetImageTestNoExtensions, ES2_VULKAN(), ES3_VULKAN());
} // namespace
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment