Commit 22a4f38c by Geoff Lang

Implement the egl and gl layers of EGL Image.

Add end2end tests and unittests. BUG=angleproject:970 Change-Id: I13fc501b24c3f11bfedc810c1ff80fcf1318877c Reviewed-on: https://chromium-review.googlesource.com/287343Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Tested-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 6b2a0b0c
...@@ -555,6 +555,99 @@ std::string ParseUniformName(const std::string &name, size_t *outSubscript) ...@@ -555,6 +555,99 @@ std::string ParseUniformName(const std::string &name, size_t *outSubscript)
} }
namespace egl
{
static_assert(EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 1,
"Unexpected EGL cube map enum value.");
static_assert(EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 2,
"Unexpected EGL cube map enum value.");
static_assert(EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 3,
"Unexpected EGL cube map enum value.");
static_assert(EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 4,
"Unexpected EGL cube map enum value.");
static_assert(EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 5,
"Unexpected EGL cube map enum value.");
bool IsCubeMapTextureTarget(EGLenum target)
{
return (target >= FirstCubeMapTextureTarget && target <= LastCubeMapTextureTarget);
}
size_t CubeMapTextureTargetToLayerIndex(EGLenum target)
{
ASSERT(IsCubeMapTextureTarget(target));
return target - static_cast<size_t>(FirstCubeMapTextureTarget);
}
EGLenum LayerIndexToCubeMapTextureTarget(size_t index)
{
ASSERT(index <= (LastCubeMapTextureTarget - FirstCubeMapTextureTarget));
return FirstCubeMapTextureTarget + static_cast<GLenum>(index);
}
bool IsTextureTarget(EGLenum target)
{
switch (target)
{
case EGL_GL_TEXTURE_2D_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR:
case EGL_GL_TEXTURE_3D_KHR:
return true;
default:
return false;
}
}
bool IsRenderbufferTarget(EGLenum target)
{
return target == EGL_GL_RENDERBUFFER_KHR;
}
}
namespace egl_gl
{
GLenum EGLCubeMapTargetToGLCubeMapTarget(EGLenum eglTarget)
{
ASSERT(egl::IsCubeMapTextureTarget(eglTarget));
return gl::LayerIndexToCubeMapTextureTarget(egl::CubeMapTextureTargetToLayerIndex(eglTarget));
}
GLenum EGLImageTargetToGLTextureTarget(EGLenum eglTarget)
{
switch (eglTarget)
{
case EGL_GL_TEXTURE_2D_KHR:
return GL_TEXTURE_2D;
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR:
return EGLCubeMapTargetToGLCubeMapTarget(eglTarget);
case EGL_GL_TEXTURE_3D_KHR:
return GL_TEXTURE_3D;
default:
UNREACHABLE();
return GL_NONE;
}
}
GLuint EGLClientBufferToGLObjectHandle(EGLClientBuffer buffer)
{
return static_cast<GLuint>(reinterpret_cast<uintptr_t>(buffer));
}
}
#if !defined(ANGLE_ENABLE_WINDOWS_STORE) #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
std::string getTempPath() std::string getTempPath()
{ {
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
#ifndef COMMON_UTILITIES_H_ #ifndef COMMON_UTILITIES_H_
#define COMMON_UTILITIES_H_ #define COMMON_UTILITIES_H_
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "angle_gl.h" #include "angle_gl.h"
#include <string> #include <string>
#include <math.h> #include <math.h>
...@@ -59,6 +62,24 @@ template <typename outT> outT uiround(GLfloat value) { return static_cast<outT>( ...@@ -59,6 +62,24 @@ template <typename outT> outT uiround(GLfloat value) { return static_cast<outT>(
} }
namespace egl
{
static const EGLenum FirstCubeMapTextureTarget = EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR;
static const EGLenum LastCubeMapTextureTarget = EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR;
bool IsCubeMapTextureTarget(EGLenum target);
size_t CubeMapTextureTargetToLayerIndex(EGLenum target);
EGLenum LayerIndexToCubeMapTextureTarget(size_t index);
bool IsTextureTarget(EGLenum target);
bool IsRenderbufferTarget(EGLenum target);
}
namespace egl_gl
{
GLenum EGLCubeMapTargetToGLCubeMapTarget(EGLenum eglTarget);
GLenum EGLImageTargetToGLTextureTarget(EGLenum eglTarget);
GLuint EGLClientBufferToGLObjectHandle(EGLClientBuffer buffer);
}
#if !defined(ANGLE_ENABLE_WINDOWS_STORE) #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
std::string getTempPath(); std::string getTempPath();
void writeFile(const char* path, const void* data, size_t size); void writeFile(const char* path, const void* data, size_t size);
......
...@@ -22,11 +22,14 @@ ...@@ -22,11 +22,14 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/mathutil.h" #include "common/mathutil.h"
#include "common/platform.h" #include "common/platform.h"
#include "common/utilities.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Device.h" #include "libANGLE/Device.h"
#include "libANGLE/histogram_macros.h" #include "libANGLE/histogram_macros.h"
#include "libANGLE/Image.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
#include "libANGLE/renderer/DisplayImpl.h" #include "libANGLE/renderer/DisplayImpl.h"
#include "libANGLE/renderer/ImageImpl.h"
#include "third_party/trace_event/trace_event.h" #include "third_party/trace_event/trace_event.h"
#if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11)
...@@ -303,6 +306,11 @@ void Display::terminate() ...@@ -303,6 +306,11 @@ void Display::terminate()
destroyContext(*mContextSet.begin()); destroyContext(*mContextSet.begin());
} }
while (!mImageSet.empty())
{
destroyImage(*mImageSet.begin());
}
mConfigSet.clear(); mConfigSet.clear();
mImplementation->terminate(); mImplementation->terminate();
...@@ -508,7 +516,39 @@ Error Display::createImage(gl::Context *context, ...@@ -508,7 +516,39 @@ Error Display::createImage(gl::Context *context,
} }
} }
UNIMPLEMENTED(); egl::ImageSibling *sibling = nullptr;
if (IsTextureTarget(target))
{
sibling = context->getTexture(egl_gl::EGLClientBufferToGLObjectHandle(buffer));
}
else if (IsRenderbufferTarget(target))
{
sibling = context->getRenderbuffer(egl_gl::EGLClientBufferToGLObjectHandle(buffer));
}
else
{
UNREACHABLE();
}
ASSERT(sibling != nullptr);
rx::ImageImpl *imageImpl = mImplementation->createImage(target, sibling, attribs);
ASSERT(imageImpl != nullptr);
Error error = imageImpl->initialize();
if (error.isError())
{
return error;
}
Image *image = new Image(imageImpl, target, sibling, attribs);
ASSERT(outImage != nullptr);
*outImage = image;
// Add this image to the list of all images and hold a ref to it.
image->addRef();
mImageSet.insert(image);
return Error(EGL_SUCCESS); return Error(EGL_SUCCESS);
} }
...@@ -599,7 +639,10 @@ void Display::destroySurface(Surface *surface) ...@@ -599,7 +639,10 @@ void Display::destroySurface(Surface *surface)
void Display::destroyImage(egl::Image *image) void Display::destroyImage(egl::Image *image)
{ {
UNIMPLEMENTED(); auto iter = mImageSet.find(image);
ASSERT(iter != mImageSet.end());
(*iter)->release();
mImageSet.erase(iter);
} }
void Display::destroyContext(gl::Context *context) void Display::destroyContext(gl::Context *context)
...@@ -653,6 +696,11 @@ bool Display::isValidSurface(Surface *surface) const ...@@ -653,6 +696,11 @@ bool Display::isValidSurface(Surface *surface) const
return mImplementation->getSurfaceSet().find(surface) != mImplementation->getSurfaceSet().end(); return mImplementation->getSurfaceSet().find(surface) != mImplementation->getSurfaceSet().end();
} }
bool Display::isValidImage(const Image *image) const
{
return mImageSet.find(const_cast<Image *>(image)) != mImageSet.end();
}
bool Display::hasExistingWindowSurface(EGLNativeWindowType window) bool Display::hasExistingWindowSurface(EGLNativeWindowType window)
{ {
WindowSurfaceMap *windowSurfaces = GetWindowSurfaces(); WindowSurfaceMap *windowSurfaces = GetWindowSurfaces();
......
...@@ -79,6 +79,7 @@ class Display final : angle::NonCopyable ...@@ -79,6 +79,7 @@ class Display final : angle::NonCopyable
bool isValidConfig(const Config *config) const; bool isValidConfig(const Config *config) const;
bool isValidContext(gl::Context *context) const; bool isValidContext(gl::Context *context) const;
bool isValidSurface(egl::Surface *surface) const; bool isValidSurface(egl::Surface *surface) const;
bool isValidImage(const Image *image) const;
bool isValidNativeWindow(EGLNativeWindowType window) const; bool isValidNativeWindow(EGLNativeWindowType window) const;
static bool isValidDisplay(const egl::Display *display); static bool isValidDisplay(const egl::Display *display);
...@@ -121,6 +122,9 @@ class Display final : angle::NonCopyable ...@@ -121,6 +122,9 @@ class Display final : angle::NonCopyable
typedef std::set<gl::Context*> ContextSet; typedef std::set<gl::Context*> ContextSet;
ContextSet mContextSet; ContextSet mContextSet;
typedef std::set<Image *> ImageSet;
ImageSet mImageSet;
bool mInitialized; bool mInitialized;
Caps mCaps; Caps mCaps;
......
//
// Copyright (c) 2015 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.
//
// Image.cpp: Implements the egl::Image class representing the EGLimage object.
#include "libANGLE/Image.h"
#include "common/debug.h"
#include "common/utilities.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/Texture.h"
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/renderer/ImageImpl.h"
namespace egl
{
ImageSibling::ImageSibling(GLuint id)
: gl::FramebufferAttachmentObject(id), mSourcesOf(), mTargetOf()
{
}
ImageSibling::~ImageSibling()
{
// EGL images should hold a ref to their targets and siblings, a Texture should not be deletable
// while it is attached to an EGL image.
ASSERT(mSourcesOf.empty());
orphanImages();
}
void ImageSibling::setTargetImage(egl::Image *imageTarget)
{
ASSERT(imageTarget != nullptr);
mTargetOf.set(imageTarget);
imageTarget->addTargetSibling(this);
}
gl::Error ImageSibling::orphanImages()
{
if (mTargetOf.get() != nullptr)
{
// Can't be a target and have sources.
ASSERT(mSourcesOf.empty());
gl::Error error = mTargetOf->orphanSibling(this);
if (error.isError())
{
return error;
}
mTargetOf.set(nullptr);
}
else
{
for (auto &sourceImage : mSourcesOf)
{
gl::Error error = sourceImage->orphanSibling(this);
if (error.isError())
{
return error;
}
}
mSourcesOf.clear();
}
return gl::Error(GL_NO_ERROR);
}
void ImageSibling::addImageSource(egl::Image *imageSource)
{
ASSERT(imageSource != nullptr);
mSourcesOf.insert(imageSource);
}
void ImageSibling::removeImageSource(egl::Image *imageSource)
{
ASSERT(mSourcesOf.find(imageSource) != mSourcesOf.end());
mSourcesOf.erase(imageSource);
}
Image::Image(rx::ImageImpl *impl, EGLenum target, ImageSibling *buffer, const AttributeMap &attribs)
: RefCountObject(0),
mImplementation(impl),
mInternalFormat(GL_NONE),
mWidth(0),
mHeight(0),
mSamples(0),
mSource(),
mTargets()
{
ASSERT(mImplementation != nullptr);
ASSERT(buffer != nullptr);
mSource.set(buffer);
mSource->addImageSource(this);
if (IsTextureTarget(target))
{
gl::Texture *texture = rx::GetAs<gl::Texture>(mSource.get());
GLenum textureTarget = egl_gl::EGLImageTargetToGLTextureTarget(target);
size_t level = attribs.get(EGL_GL_TEXTURE_LEVEL_KHR, 0);
mInternalFormat = texture->getInternalFormat(textureTarget, level);
mWidth = texture->getWidth(textureTarget, level);
mHeight = texture->getHeight(textureTarget, level);
mSamples = 0;
}
else if (IsRenderbufferTarget(target))
{
gl::Renderbuffer *renderbuffer = rx::GetAs<gl::Renderbuffer>(mSource.get());
mInternalFormat = renderbuffer->getInternalFormat();
mWidth = renderbuffer->getWidth();
mHeight = renderbuffer->getHeight();
mSamples = renderbuffer->getSamples();
}
else
{
UNREACHABLE();
}
}
Image::~Image()
{
SafeDelete(mImplementation);
// All targets should hold a ref to the egl image and it should not be deleted until there are
// no siblings left.
ASSERT(mTargets.empty());
// Tell the source that it is no longer used by this image
if (mSource.get() != nullptr)
{
mSource->removeImageSource(this);
mSource.set(nullptr);
}
}
void Image::addTargetSibling(ImageSibling *sibling)
{
mTargets.insert(sibling);
}
gl::Error Image::orphanSibling(ImageSibling *sibling)
{
// notify impl
gl::Error error = mImplementation->orphan(sibling);
if (mSource.get() == sibling)
{
// If the sibling is the source, it cannot be a target.
ASSERT(mTargets.find(sibling) == mTargets.end());
mSource.set(nullptr);
}
else
{
mTargets.erase(sibling);
}
return error;
}
GLenum Image::getInternalFormat() const
{
return mInternalFormat;
}
size_t Image::getWidth() const
{
return mWidth;
}
size_t Image::getHeight() const
{
return mHeight;
}
size_t Image::getSamples() const
{
return mSamples;
}
rx::ImageImpl *Image::getImplementation()
{
return mImplementation;
}
const rx::ImageImpl *Image::getImplementation() const
{
return mImplementation;
}
}
//
// Copyright (c) 2015 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.
//
// Image.h: Defines the egl::Image class representing the EGLimage object.
#ifndef LIBANGLE_IMAGE_H_
#define LIBANGLE_IMAGE_H_
#include "common/angleutils.h"
#include "libANGLE/AttributeMap.h"
#include "libANGLE/Error.h"
#include "libANGLE/RefCountObject.h"
#include "libANGLE/FramebufferAttachment.h"
#include <set>
namespace rx
{
class ImageImpl;
}
namespace egl
{
class Image;
class ImageSibling : public gl::FramebufferAttachmentObject
{
public:
ImageSibling(GLuint id);
virtual ~ImageSibling();
protected:
// Set the image target of this sibling
void setTargetImage(egl::Image *imageTarget);
// Orphan all EGL image sources and targets
gl::Error orphanImages();
private:
friend class Image;
// Called from Image only to add a new source image
void addImageSource(egl::Image *imageSource);
// Called from Image only to remove a source image when the Image is being deleted
void removeImageSource(egl::Image *imageSource);
std::set<Image *> mSourcesOf;
BindingPointer<Image> mTargetOf;
};
class Image final : public RefCountObject
{
public:
Image(rx::ImageImpl *impl, EGLenum target, ImageSibling *buffer, const AttributeMap &attribs);
~Image();
GLenum getInternalFormat() const;
size_t getWidth() const;
size_t getHeight() const;
size_t getSamples() const;
rx::ImageImpl *getImplementation();
const rx::ImageImpl *getImplementation() const;
private:
friend class ImageSibling;
// Called from ImageSibling only notify the image that a new target sibling exists for state
// tracking.
void addTargetSibling(ImageSibling *sibling);
// Called from ImageSibling only to notify the image that a sibling (source or target) has
// been respecified and state tracking should be updated.
gl::Error orphanSibling(ImageSibling *sibling);
rx::ImageImpl *mImplementation;
GLenum mInternalFormat;
size_t mWidth;
size_t mHeight;
size_t mSamples;
BindingPointer<ImageSibling> mSource;
std::set<ImageSibling *> mTargets;
};
}
#endif // LIBANGLE_IMAGE_H_
//
// Copyright (c) 2015 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.
//
// Image_unittest.cpp : Unittets of the Image and ImageSibling classes.
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "libANGLE/Image.h"
#include "libANGLE/Texture.h"
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/renderer/ImageImpl_mock.h"
#include "libANGLE/renderer/TextureImpl_mock.h"
#include "libANGLE/renderer/RenderbufferImpl_mock.h"
using ::testing::_;
using ::testing::Return;
namespace angle
{
// Verify ref counts are maintained between images and their siblings when objects are deleted
TEST(ImageTest, RefCounting)
{
// Create a texture and an EGL image that uses the texture as its source
rx::MockTextureImpl *textureImpl = new rx::MockTextureImpl();
gl::Texture *texture = new gl::Texture(textureImpl, 1, GL_TEXTURE_2D);
texture->addRef();
rx::MockImageImpl *imageImpl = new rx::MockImageImpl();
egl::Image *image = new egl::Image(imageImpl, EGL_GL_TEXTURE_2D, texture, egl::AttributeMap());
image->addRef();
// Verify that the image added a ref to the texture and the texture has not added a ref to the
// image
EXPECT_EQ(texture->getRefCount(), 2u);
EXPECT_EQ(image->getRefCount(), 1u);
// Create a renderbuffer and set it as a target of the EGL image
rx::MockRenderbufferImpl *renderbufferImpl = new rx::MockRenderbufferImpl();
gl::Renderbuffer *renderbuffer = new gl::Renderbuffer(renderbufferImpl, 1);
renderbuffer->addRef();
EXPECT_CALL(*renderbufferImpl, setStorageEGLImageTarget(_))
.WillOnce(Return(gl::Error(GL_NO_ERROR)))
.RetiresOnSaturation();
renderbuffer->setStorageEGLImageTarget(image);
// Verify that the renderbuffer added a ref to the image and the image did not add a ref to
// the renderbuffer
EXPECT_EQ(texture->getRefCount(), 2u);
EXPECT_EQ(image->getRefCount(), 2u);
EXPECT_EQ(renderbuffer->getRefCount(), 1u);
// Simulate deletion of the texture and verify that it still exists because the image holds a
// ref
texture->release();
EXPECT_EQ(texture->getRefCount(), 1u);
EXPECT_EQ(image->getRefCount(), 2u);
EXPECT_EQ(renderbuffer->getRefCount(), 1u);
// Simulate deletion of the image and verify that it still exists because the renderbuffer holds
// a ref
image->release();
EXPECT_EQ(texture->getRefCount(), 1u);
EXPECT_EQ(image->getRefCount(), 1u);
EXPECT_EQ(renderbuffer->getRefCount(), 1u);
// Simulate deletion of the renderbuffer and verify that the deletion cascades to all objects
EXPECT_CALL(*imageImpl, destructor()).Times(1).RetiresOnSaturation();
EXPECT_CALL(*imageImpl, orphan(_))
.WillOnce(Return(gl::Error(GL_NO_ERROR)))
.RetiresOnSaturation();
EXPECT_CALL(*textureImpl, destructor()).Times(1).RetiresOnSaturation();
EXPECT_CALL(*renderbufferImpl, destructor()).Times(1).RetiresOnSaturation();
renderbuffer->release();
}
// Verify that respecifiying textures releases references to the Image.
TEST(ImageTest, RespecificationReleasesReferences)
{
// Create a texture and an EGL image that uses the texture as its source
rx::MockTextureImpl *textureImpl = new rx::MockTextureImpl();
gl::Texture *texture = new gl::Texture(textureImpl, 1, GL_TEXTURE_2D);
texture->addRef();
EXPECT_CALL(*textureImpl, setImage(_, _, _, _, _, _, _, _))
.WillOnce(Return(gl::Error(GL_NO_ERROR)))
.RetiresOnSaturation();
texture->setImage(GL_TEXTURE_2D, 0, GL_RGBA8, gl::Extents(1, 1, 1), GL_RGBA, GL_UNSIGNED_BYTE,
gl::PixelUnpackState(), nullptr);
rx::MockImageImpl *imageImpl = new rx::MockImageImpl();
egl::Image *image = new egl::Image(imageImpl, EGL_GL_TEXTURE_2D, texture, egl::AttributeMap());
image->addRef();
// Verify that the image added a ref to the texture and the texture has not added a ref to the
// image
EXPECT_EQ(texture->getRefCount(), 2u);
EXPECT_EQ(image->getRefCount(), 1u);
// Respecify the texture and verify that the image releases its reference
EXPECT_CALL(*imageImpl, orphan(_))
.WillOnce(Return(gl::Error(GL_NO_ERROR)))
.RetiresOnSaturation();
EXPECT_CALL(*textureImpl, setImage(_, _, _, _, _, _, _, _))
.WillOnce(Return(gl::Error(GL_NO_ERROR)))
.RetiresOnSaturation();
texture->setImage(GL_TEXTURE_2D, 0, GL_RGBA8, gl::Extents(1, 1, 1), GL_RGBA, GL_UNSIGNED_BYTE,
gl::PixelUnpackState(), nullptr);
EXPECT_EQ(texture->getRefCount(), 1u);
EXPECT_EQ(image->getRefCount(), 1u);
// Delete the texture and verify that the image still exists
EXPECT_CALL(*textureImpl, destructor()).Times(1).RetiresOnSaturation();
texture->release();
EXPECT_EQ(image->getRefCount(), 1u);
// Delete the image
EXPECT_CALL(*imageImpl, destructor()).Times(1).RetiresOnSaturation();
image->release();
}
}
...@@ -12,20 +12,20 @@ ...@@ -12,20 +12,20 @@
#include "common/utilities.h" #include "common/utilities.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Image.h"
#include "libANGLE/Texture.h" #include "libANGLE/Texture.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/renderer/d3d/RenderTargetD3D.h" #include "libANGLE/renderer/d3d/RenderTargetD3D.h"
namespace gl namespace gl
{ {
Renderbuffer::Renderbuffer(rx::RenderbufferImpl *impl, GLuint id) Renderbuffer::Renderbuffer(rx::RenderbufferImpl *impl, GLuint id)
: FramebufferAttachmentObject(id), : egl::ImageSibling(id),
mRenderbuffer(impl), mRenderbuffer(impl),
mWidth(0), mWidth(0),
mHeight(0), mHeight(0),
mInternalFormat(GL_RGBA4), mInternalFormat(GL_RGBA4),
mSamples(0) mSamples(0)
{ {
} }
...@@ -36,6 +36,8 @@ Renderbuffer::~Renderbuffer() ...@@ -36,6 +36,8 @@ Renderbuffer::~Renderbuffer()
Error Renderbuffer::setStorage(GLenum internalformat, size_t width, size_t height) Error Renderbuffer::setStorage(GLenum internalformat, size_t width, size_t height)
{ {
orphanImages();
Error error = mRenderbuffer->setStorage(internalformat, width, height); Error error = mRenderbuffer->setStorage(internalformat, width, height);
if (error.isError()) if (error.isError())
{ {
...@@ -52,6 +54,8 @@ Error Renderbuffer::setStorage(GLenum internalformat, size_t width, size_t heigh ...@@ -52,6 +54,8 @@ Error Renderbuffer::setStorage(GLenum internalformat, size_t width, size_t heigh
Error Renderbuffer::setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height) Error Renderbuffer::setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height)
{ {
orphanImages();
Error error = mRenderbuffer->setStorageMultisample(samples, internalformat, width, height); Error error = mRenderbuffer->setStorageMultisample(samples, internalformat, width, height);
if (error.isError()) if (error.isError())
{ {
...@@ -66,6 +70,26 @@ Error Renderbuffer::setStorageMultisample(size_t samples, GLenum internalformat, ...@@ -66,6 +70,26 @@ Error Renderbuffer::setStorageMultisample(size_t samples, GLenum internalformat,
return Error(GL_NO_ERROR); return Error(GL_NO_ERROR);
} }
Error Renderbuffer::setStorageEGLImageTarget(egl::Image *image)
{
orphanImages();
Error error = mRenderbuffer->setStorageEGLImageTarget(image);
if (error.isError())
{
return error;
}
setTargetImage(image);
mWidth = image->getWidth();
mHeight = image->getHeight();
mInternalFormat = image->getInternalFormat();
mSamples = 0;
return Error(GL_NO_ERROR);
}
rx::RenderbufferImpl *Renderbuffer::getImplementation() rx::RenderbufferImpl *Renderbuffer::getImplementation()
{ {
ASSERT(mRenderbuffer); ASSERT(mRenderbuffer);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "common/angleutils.h" #include "common/angleutils.h"
#include "libANGLE/Error.h" #include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Image.h"
#include "libANGLE/renderer/RenderbufferImpl.h" #include "libANGLE/renderer/RenderbufferImpl.h"
namespace gl namespace gl
...@@ -24,7 +25,7 @@ namespace gl ...@@ -24,7 +25,7 @@ namespace gl
// FramebufferAttachment and Framebuffer for how they are applied to an FBO via an // FramebufferAttachment and Framebuffer for how they are applied to an FBO via an
// attachment point. // attachment point.
class Renderbuffer : public FramebufferAttachmentObject class Renderbuffer : public egl::ImageSibling
{ {
public: public:
Renderbuffer(rx::RenderbufferImpl *impl, GLuint id); Renderbuffer(rx::RenderbufferImpl *impl, GLuint id);
...@@ -32,6 +33,7 @@ class Renderbuffer : public FramebufferAttachmentObject ...@@ -32,6 +33,7 @@ class Renderbuffer : public FramebufferAttachmentObject
Error setStorage(GLenum internalformat, size_t width, size_t height); Error setStorage(GLenum internalformat, size_t width, size_t height);
Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height); Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height);
Error setStorageEGLImageTarget(egl::Image *imageTarget);
rx::RenderbufferImpl *getImplementation(); rx::RenderbufferImpl *getImplementation();
const rx::RenderbufferImpl *getImplementation() const; const rx::RenderbufferImpl *getImplementation() const;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "common/utilities.h" #include "common/utilities.h"
#include "libANGLE/Config.h" #include "libANGLE/Config.h"
#include "libANGLE/Data.h" #include "libANGLE/Data.h"
#include "libANGLE/Image.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
...@@ -46,7 +47,7 @@ static size_t GetImageDescIndex(GLenum target, size_t level) ...@@ -46,7 +47,7 @@ static size_t GetImageDescIndex(GLenum target, size_t level)
} }
Texture::Texture(rx::TextureImpl *impl, GLuint id, GLenum target) Texture::Texture(rx::TextureImpl *impl, GLuint id, GLenum target)
: FramebufferAttachmentObject(id), : egl::ImageSibling(id),
mTexture(impl), mTexture(impl),
mUsage(GL_NONE), mUsage(GL_NONE),
mImmutableLevelCount(0), mImmutableLevelCount(0),
...@@ -127,6 +128,11 @@ bool Texture::isSamplerComplete(const SamplerState &samplerState, const Data &da ...@@ -127,6 +128,11 @@ bool Texture::isSamplerComplete(const SamplerState &samplerState, const Data &da
return mCompletenessCache.samplerComplete; return mCompletenessCache.samplerComplete;
} }
bool Texture::isMipmapComplete() const
{
return computeMipmapCompleteness(mSamplerState);
}
// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
bool Texture::isCubeComplete() const bool Texture::isCubeComplete() const
{ {
...@@ -152,6 +158,22 @@ bool Texture::isCubeComplete() const ...@@ -152,6 +158,22 @@ bool Texture::isCubeComplete() const
return true; return true;
} }
size_t Texture::getMipCompleteLevels() const
{
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), 0);
if (mTarget == GL_TEXTURE_3D)
{
const size_t maxDim =
std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height),
baseImageDesc.size.depth);
return log2(maxDim) + 1;
}
else
{
return log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height)) + 1;
}
}
bool Texture::isImmutable() const bool Texture::isImmutable() const
{ {
return (mImmutableLevelCount > 0); return (mImmutableLevelCount > 0);
...@@ -162,6 +184,11 @@ int Texture::immutableLevelCount() ...@@ -162,6 +184,11 @@ int Texture::immutableLevelCount()
return mImmutableLevelCount; return mImmutableLevelCount;
} }
egl::Surface *Texture::getBoundSurface() const
{
return mBoundSurface;
}
Error Texture::setImage(GLenum target, size_t level, GLenum internalFormat, const Extents &size, GLenum format, GLenum type, Error Texture::setImage(GLenum target, size_t level, GLenum internalFormat, const Extents &size, GLenum format, GLenum type,
const PixelUnpackState &unpack, const uint8_t *pixels) const PixelUnpackState &unpack, const uint8_t *pixels)
{ {
...@@ -169,6 +196,7 @@ Error Texture::setImage(GLenum target, size_t level, GLenum internalFormat, cons ...@@ -169,6 +196,7 @@ Error Texture::setImage(GLenum target, size_t level, GLenum internalFormat, cons
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal(); releaseTexImageInternal();
orphanImages();
Error error = mTexture->setImage(target, level, internalFormat, size, format, type, unpack, pixels); Error error = mTexture->setImage(target, level, internalFormat, size, format, type, unpack, pixels);
if (error.isError()) if (error.isError())
...@@ -196,6 +224,7 @@ Error Texture::setCompressedImage(GLenum target, size_t level, GLenum internalFo ...@@ -196,6 +224,7 @@ Error Texture::setCompressedImage(GLenum target, size_t level, GLenum internalFo
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal(); releaseTexImageInternal();
orphanImages();
Error error = mTexture->setCompressedImage(target, level, internalFormat, size, unpack, imageSize, pixels); Error error = mTexture->setCompressedImage(target, level, internalFormat, size, unpack, imageSize, pixels);
if (error.isError()) if (error.isError())
...@@ -223,6 +252,7 @@ Error Texture::copyImage(GLenum target, size_t level, const Rectangle &sourceAre ...@@ -223,6 +252,7 @@ Error Texture::copyImage(GLenum target, size_t level, const Rectangle &sourceAre
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal(); releaseTexImageInternal();
orphanImages();
Error error = mTexture->copyImage(target, level, sourceArea, internalFormat, source); Error error = mTexture->copyImage(target, level, sourceArea, internalFormat, source);
if (error.isError()) if (error.isError())
...@@ -250,6 +280,7 @@ Error Texture::setStorage(GLenum target, size_t levels, GLenum internalFormat, c ...@@ -250,6 +280,7 @@ Error Texture::setStorage(GLenum target, size_t levels, GLenum internalFormat, c
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal(); releaseTexImageInternal();
orphanImages();
Error error = mTexture->setStorage(target, levels, internalFormat, size); Error error = mTexture->setStorage(target, levels, internalFormat, size);
if (error.isError()) if (error.isError())
...@@ -270,6 +301,13 @@ Error Texture::generateMipmaps() ...@@ -270,6 +301,13 @@ Error Texture::generateMipmaps()
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal(); releaseTexImageInternal();
// EGL_KHR_gl_image states that images are only orphaned when generating mipmaps if the texture
// is not mip complete.
if (!isMipmapComplete())
{
orphanImages();
}
Error error = mTexture->generateMipmaps(getSamplerState()); Error error = mTexture->generateMipmaps(getSamplerState());
if (error.isError()) if (error.isError())
{ {
...@@ -388,22 +426,36 @@ void Texture::releaseTexImageInternal() ...@@ -388,22 +426,36 @@ void Texture::releaseTexImageInternal()
} }
} }
GLenum Texture::getBaseImageTarget() const Error Texture::setEGLImageTarget(GLenum target, egl::Image *imageTarget)
{ {
return mTarget == GL_TEXTURE_CUBE_MAP ? FirstCubeMapTextureTarget : mTarget; ASSERT(target == mTarget);
} ASSERT(target == GL_TEXTURE_2D);
size_t Texture::getExpectedMipLevels() const // Release from previous calls to eglBindTexImage, to avoid calling the Impl after
{ releaseTexImageInternal();
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), 0); orphanImages();
if (mTarget == GL_TEXTURE_3D)
{ Error error = mTexture->setEGLImageTarget(target, imageTarget);
return log2(std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height), baseImageDesc.size.depth)) + 1; if (error.isError())
}
else
{ {
return log2(std::max(baseImageDesc.size.width, baseImageDesc.size.height)) + 1; return error;
} }
setTargetImage(imageTarget);
Extents size(imageTarget->getWidth(), imageTarget->getHeight(), 1);
GLenum internalFormat = imageTarget->getInternalFormat();
GLenum type = GetInternalFormatInfo(internalFormat).type;
clearImageDescs();
setImageDesc(target, 0, ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
return Error(GL_NO_ERROR);
}
GLenum Texture::getBaseImageTarget() const
{
return mTarget == GL_TEXTURE_CUBE_MAP ? FirstCubeMapTextureTarget : mTarget;
} }
bool Texture::computeSamplerCompleteness(const SamplerState &samplerState, const Data &data) const bool Texture::computeSamplerCompleteness(const SamplerState &samplerState, const Data &data) const
...@@ -481,7 +533,7 @@ bool Texture::computeSamplerCompleteness(const SamplerState &samplerState, const ...@@ -481,7 +533,7 @@ bool Texture::computeSamplerCompleteness(const SamplerState &samplerState, const
bool Texture::computeMipmapCompleteness(const gl::SamplerState &samplerState) const bool Texture::computeMipmapCompleteness(const gl::SamplerState &samplerState) const
{ {
size_t expectedMipLevels = getExpectedMipLevels(); size_t expectedMipLevels = getMipCompleteLevels();
size_t maxLevel = std::min<size_t>(expectedMipLevels, samplerState.maxLevel + 1); size_t maxLevel = std::min<size_t>(expectedMipLevels, samplerState.maxLevel + 1);
...@@ -525,13 +577,13 @@ bool Texture::computeLevelCompleteness(GLenum target, size_t level, const gl::Sa ...@@ -525,13 +577,13 @@ bool Texture::computeLevelCompleteness(GLenum target, size_t level, const gl::Sa
return false; return false;
} }
// The base image level is complete if the width and height are positive const ImageDesc &levelImageDesc = getImageDesc(target, level);
if (level == 0) if (levelImageDesc.size.width == 0 || levelImageDesc.size.height == 0 ||
levelImageDesc.size.depth == 0)
{ {
return true; return false;
} }
const ImageDesc &levelImageDesc = getImageDesc(target, level);
if (levelImageDesc.internalFormat != baseImageDesc.internalFormat) if (levelImageDesc.internalFormat != baseImageDesc.internalFormat)
{ {
return false; return false;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "libANGLE/Constants.h" #include "libANGLE/Constants.h"
#include "libANGLE/Error.h" #include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Image.h"
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#include "libANGLE/renderer/TextureImpl.h" #include "libANGLE/renderer/TextureImpl.h"
...@@ -33,7 +34,7 @@ struct Data; ...@@ -33,7 +34,7 @@ struct Data;
bool IsMipmapFiltered(const gl::SamplerState &samplerState); bool IsMipmapFiltered(const gl::SamplerState &samplerState);
class Texture final : public FramebufferAttachmentObject class Texture final : public egl::ImageSibling
{ {
public: public:
Texture(rx::TextureImpl *impl, GLuint id, GLenum target); Texture(rx::TextureImpl *impl, GLuint id, GLenum target);
...@@ -54,7 +55,9 @@ class Texture final : public FramebufferAttachmentObject ...@@ -54,7 +55,9 @@ class Texture final : public FramebufferAttachmentObject
GLenum getInternalFormat(GLenum target, size_t level) const; GLenum getInternalFormat(GLenum target, size_t level) const;
bool isSamplerComplete(const SamplerState &samplerState, const Data &data) const; bool isSamplerComplete(const SamplerState &samplerState, const Data &data) const;
bool isMipmapComplete() const;
bool isCubeComplete() const; bool isCubeComplete() const;
size_t getMipCompleteLevels() const;
virtual Error setImage(GLenum target, size_t level, GLenum internalFormat, const Extents &size, GLenum format, GLenum type, virtual Error setImage(GLenum target, size_t level, GLenum internalFormat, const Extents &size, GLenum format, GLenum type,
const PixelUnpackState &unpack, const uint8_t *pixels); const PixelUnpackState &unpack, const uint8_t *pixels);
...@@ -73,11 +76,15 @@ class Texture final : public FramebufferAttachmentObject ...@@ -73,11 +76,15 @@ class Texture final : public FramebufferAttachmentObject
virtual Error setStorage(GLenum target, size_t levels, GLenum internalFormat, const Extents &size); virtual Error setStorage(GLenum target, size_t levels, GLenum internalFormat, const Extents &size);
Error setEGLImageTarget(GLenum target, egl::Image *imageTarget);
virtual Error generateMipmaps(); virtual Error generateMipmaps();
bool isImmutable() const; bool isImmutable() const;
GLsizei immutableLevelCount(); GLsizei immutableLevelCount();
egl::Surface *getBoundSurface() const;
rx::TextureImpl *getImplementation() { return mTexture; } rx::TextureImpl *getImplementation() { return mTexture; }
const rx::TextureImpl *getImplementation() const { return mTexture; } const rx::TextureImpl *getImplementation() const { return mTexture; }
...@@ -114,7 +121,6 @@ class Texture final : public FramebufferAttachmentObject ...@@ -114,7 +121,6 @@ class Texture final : public FramebufferAttachmentObject
}; };
GLenum getBaseImageTarget() const; GLenum getBaseImageTarget() const;
size_t getExpectedMipLevels() const;
bool computeSamplerCompleteness(const SamplerState &samplerState, const Data &data) const; bool computeSamplerCompleteness(const SamplerState &samplerState, const Data &data) const;
bool computeMipmapCompleteness(const gl::SamplerState &samplerState) const; bool computeMipmapCompleteness(const gl::SamplerState &samplerState) const;
......
//
// Copyright (c) 2015 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.
//
// ImageImpl.h: Defines the rx::ImageImpl class representing the EGLimage object.
#ifndef LIBANGLE_RENDERER_IMAGEIMPL_H_
#define LIBANGLE_RENDERER_IMAGEIMPL_H_
#include "common/angleutils.h"
#include "libANGLE/Error.h"
namespace egl
{
class ImageSibling;
}
namespace rx
{
class ImageImpl : angle::NonCopyable
{
public:
virtual ~ImageImpl() {}
virtual egl::Error initialize() = 0;
virtual gl::Error orphan(egl::ImageSibling *sibling) = 0;
};
}
#endif // LIBANGLE_RENDERER_IMAGEIMPL_H_
//
// Copyright (c) 2015 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.
//
// ImageImpl_mock.h: Defines a mock of the ImageImpl class.
#ifndef LIBANGLE_RENDERER_IMAGEIMPLMOCK_H_
#define LIBANGLE_RENDERER_IMAGEIMPLMOCK_H_
#include "gmock/gmock.h"
#include "libANGLE/renderer/ImageImpl.h"
namespace rx
{
class MockImageImpl : public ImageImpl
{
public:
~MockImageImpl() override { destructor(); }
MOCK_METHOD0(initialize, egl::Error(void));
MOCK_METHOD1(orphan, gl::Error(egl::ImageSibling *));
MOCK_METHOD0(destructor, void());
};
}
#endif // LIBANGLE_RENDERER_IMAGEIMPLMOCK_H_
...@@ -14,6 +14,11 @@ ...@@ -14,6 +14,11 @@
#include "libANGLE/Error.h" #include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
namespace egl
{
class Image;
}
namespace rx namespace rx
{ {
...@@ -25,6 +30,7 @@ class RenderbufferImpl : public FramebufferAttachmentObjectImpl ...@@ -25,6 +30,7 @@ class RenderbufferImpl : public FramebufferAttachmentObjectImpl
virtual gl::Error setStorage(GLenum internalformat, size_t width, size_t height) = 0; virtual gl::Error setStorage(GLenum internalformat, size_t width, size_t height) = 0;
virtual gl::Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height) = 0; virtual gl::Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height) = 0;
virtual gl::Error setStorageEGLImageTarget(egl::Image *image) = 0;
}; };
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "libANGLE/Image.h"
#include "libANGLE/renderer/RenderbufferImpl.h" #include "libANGLE/renderer/RenderbufferImpl.h"
namespace rx namespace rx
...@@ -22,6 +23,7 @@ class MockRenderbufferImpl : public RenderbufferImpl ...@@ -22,6 +23,7 @@ class MockRenderbufferImpl : public RenderbufferImpl
~MockRenderbufferImpl() override { destructor(); } ~MockRenderbufferImpl() override { destructor(); }
MOCK_METHOD3(setStorage, gl::Error(GLenum, size_t, size_t)); MOCK_METHOD3(setStorage, gl::Error(GLenum, size_t, size_t));
MOCK_METHOD4(setStorageMultisample, gl::Error(size_t, GLenum, size_t, size_t)); MOCK_METHOD4(setStorageMultisample, gl::Error(size_t, GLenum, size_t, size_t));
MOCK_METHOD1(setStorageEGLImageTarget, gl::Error(egl::Image *));
MOCK_METHOD2(getAttachmentRenderTarget, gl::Error(const gl::FramebufferAttachment::Target &, FramebufferAttachmentRenderTarget **)); MOCK_METHOD2(getAttachmentRenderTarget, gl::Error(const gl::FramebufferAttachment::Target &, FramebufferAttachmentRenderTarget **));
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
namespace egl namespace egl
{ {
class Surface; class Surface;
class Image;
} }
namespace gl namespace gl
...@@ -61,6 +62,8 @@ class TextureImpl : public FramebufferAttachmentObjectImpl ...@@ -61,6 +62,8 @@ class TextureImpl : public FramebufferAttachmentObjectImpl
virtual gl::Error setStorage(GLenum target, size_t levels, GLenum internalFormat, const gl::Extents &size) = 0; virtual gl::Error setStorage(GLenum target, size_t levels, GLenum internalFormat, const gl::Extents &size) = 0;
virtual gl::Error setEGLImageTarget(GLenum target, egl::Image *image) = 0;
virtual gl::Error generateMipmaps(const gl::SamplerState &samplerState) = 0; virtual gl::Error generateMipmaps(const gl::SamplerState &samplerState) = 0;
virtual void bindTexImage(egl::Surface *surface) = 0; virtual void bindTexImage(egl::Surface *surface) = 0;
......
...@@ -28,6 +28,7 @@ class MockTextureImpl : public TextureImpl ...@@ -28,6 +28,7 @@ class MockTextureImpl : public TextureImpl
MOCK_METHOD5(copyImage, gl::Error(GLenum, size_t, const gl::Rectangle &, GLenum, const gl::Framebuffer *)); MOCK_METHOD5(copyImage, gl::Error(GLenum, size_t, const gl::Rectangle &, GLenum, const gl::Framebuffer *));
MOCK_METHOD5(copySubImage, gl::Error(GLenum, size_t, const gl::Offset &, const gl::Rectangle &, const gl::Framebuffer *)); MOCK_METHOD5(copySubImage, gl::Error(GLenum, size_t, const gl::Offset &, const gl::Rectangle &, const gl::Framebuffer *));
MOCK_METHOD4(setStorage, gl::Error(GLenum, size_t, GLenum, const gl::Extents &)); MOCK_METHOD4(setStorage, gl::Error(GLenum, size_t, GLenum, const gl::Extents &));
MOCK_METHOD2(setEGLImageTarget, gl::Error(GLenum, egl::Image *));
MOCK_METHOD1(generateMipmaps, gl::Error(const gl::SamplerState &)); MOCK_METHOD1(generateMipmaps, gl::Error(const gl::SamplerState &));
MOCK_METHOD1(bindTexImage, void(egl::Surface *)); MOCK_METHOD1(bindTexImage, void(egl::Surface *));
MOCK_METHOD0(releaseTexImage, void(void)); MOCK_METHOD0(releaseTexImage, void(void));
......
...@@ -65,6 +65,12 @@ gl::Error RenderbufferD3D::setStorageMultisample(size_t samples, GLenum internal ...@@ -65,6 +65,12 @@ gl::Error RenderbufferD3D::setStorageMultisample(size_t samples, GLenum internal
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
gl::Error RenderbufferD3D::setStorageEGLImageTarget(egl::Image *image)
{
UNIMPLEMENTED();
return gl::Error(GL_NO_ERROR);
}
RenderTargetD3D *RenderbufferD3D::getRenderTarget() RenderTargetD3D *RenderbufferD3D::getRenderTarget()
{ {
return mRenderTarget; return mRenderTarget;
......
...@@ -26,11 +26,14 @@ class RenderbufferD3D : public RenderbufferImpl ...@@ -26,11 +26,14 @@ class RenderbufferD3D : public RenderbufferImpl
RenderbufferD3D(RendererD3D *renderer); RenderbufferD3D(RendererD3D *renderer);
virtual ~RenderbufferD3D(); virtual ~RenderbufferD3D();
virtual gl::Error setStorage(GLenum internalformat, size_t width, size_t height) override; gl::Error setStorage(GLenum internalformat, size_t width, size_t height) override;
virtual gl::Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height) override; gl::Error setStorageMultisample(size_t samples,
GLenum internalformat,
size_t width,
size_t height) override;
gl::Error setStorageEGLImageTarget(egl::Image *image) override;
RenderTargetD3D *getRenderTarget(); RenderTargetD3D *getRenderTarget();
gl::Error getAttachmentRenderTarget(const gl::FramebufferAttachment::Target &target, gl::Error getAttachmentRenderTarget(const gl::FramebufferAttachment::Target &target,
FramebufferAttachmentRenderTarget **rtOut) override; FramebufferAttachmentRenderTarget **rtOut) override;
......
...@@ -969,6 +969,12 @@ void TextureD3D_2D::releaseTexImage() ...@@ -969,6 +969,12 @@ void TextureD3D_2D::releaseTexImage()
} }
} }
gl::Error TextureD3D_2D::setEGLImageTarget(GLenum target, egl::Image *image)
{
UNIMPLEMENTED();
return gl::Error(GL_NO_ERROR);
}
void TextureD3D_2D::initMipmapsImages() void TextureD3D_2D::initMipmapsImages()
{ {
// Purge array levels 1 through q and reset them to represent the generated mipmap levels. // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
...@@ -1298,6 +1304,12 @@ bool TextureD3D_Cube::isDepth(GLint level, GLint layer) const ...@@ -1298,6 +1304,12 @@ bool TextureD3D_Cube::isDepth(GLint level, GLint layer) const
return gl::GetInternalFormatInfo(getInternalFormat(level, layer)).depthBits > 0; return gl::GetInternalFormatInfo(getInternalFormat(level, layer)).depthBits > 0;
} }
gl::Error TextureD3D_Cube::setEGLImageTarget(GLenum target, egl::Image *image)
{
UNREACHABLE();
return gl::Error(GL_INVALID_OPERATION);
}
gl::Error TextureD3D_Cube::setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type, gl::Error TextureD3D_Cube::setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type,
const gl::PixelUnpackState &unpack, const uint8_t *pixels) const gl::PixelUnpackState &unpack, const uint8_t *pixels)
{ {
...@@ -1883,6 +1895,12 @@ bool TextureD3D_3D::isDepth(GLint level) const ...@@ -1883,6 +1895,12 @@ bool TextureD3D_3D::isDepth(GLint level) const
return gl::GetInternalFormatInfo(getInternalFormat(level)).depthBits > 0; return gl::GetInternalFormatInfo(getInternalFormat(level)).depthBits > 0;
} }
gl::Error TextureD3D_3D::setEGLImageTarget(GLenum target, egl::Image *image)
{
UNREACHABLE();
return gl::Error(GL_INVALID_OPERATION);
}
gl::Error TextureD3D_3D::setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type, gl::Error TextureD3D_3D::setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type,
const gl::PixelUnpackState &unpack, const uint8_t *pixels) const gl::PixelUnpackState &unpack, const uint8_t *pixels)
{ {
...@@ -2411,6 +2429,12 @@ bool TextureD3D_2DArray::isDepth(GLint level) const ...@@ -2411,6 +2429,12 @@ bool TextureD3D_2DArray::isDepth(GLint level) const
return gl::GetInternalFormatInfo(getInternalFormat(level)).depthBits > 0; return gl::GetInternalFormatInfo(getInternalFormat(level)).depthBits > 0;
} }
gl::Error TextureD3D_2DArray::setEGLImageTarget(GLenum target, egl::Image *image)
{
UNREACHABLE();
return gl::Error(GL_INVALID_OPERATION);
}
gl::Error TextureD3D_2DArray::setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type, gl::Error TextureD3D_2DArray::setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type,
const gl::PixelUnpackState &unpack, const uint8_t *pixels) const gl::PixelUnpackState &unpack, const uint8_t *pixels)
{ {
......
...@@ -147,6 +147,8 @@ class TextureD3D_2D : public TextureD3D ...@@ -147,6 +147,8 @@ class TextureD3D_2D : public TextureD3D
virtual void bindTexImage(egl::Surface *surface); virtual void bindTexImage(egl::Surface *surface);
virtual void releaseTexImage(); virtual void releaseTexImage();
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT); virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::ImageIndexIterator imageIterator() const; virtual gl::ImageIndexIterator imageIterator() const;
...@@ -209,6 +211,8 @@ class TextureD3D_Cube : public TextureD3D ...@@ -209,6 +211,8 @@ class TextureD3D_Cube : public TextureD3D
virtual void bindTexImage(egl::Surface *surface); virtual void bindTexImage(egl::Surface *surface);
virtual void releaseTexImage(); virtual void releaseTexImage();
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT); virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::ImageIndexIterator imageIterator() const; virtual gl::ImageIndexIterator imageIterator() const;
...@@ -270,6 +274,8 @@ class TextureD3D_3D : public TextureD3D ...@@ -270,6 +274,8 @@ class TextureD3D_3D : public TextureD3D
virtual void bindTexImage(egl::Surface *surface); virtual void bindTexImage(egl::Surface *surface);
virtual void releaseTexImage(); virtual void releaseTexImage();
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT); virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::ImageIndexIterator imageIterator() const; virtual gl::ImageIndexIterator imageIterator() const;
...@@ -329,6 +335,8 @@ class TextureD3D_2DArray : public TextureD3D ...@@ -329,6 +335,8 @@ class TextureD3D_2DArray : public TextureD3D
virtual void bindTexImage(egl::Surface *surface); virtual void bindTexImage(egl::Surface *surface);
virtual void releaseTexImage(); virtual void releaseTexImage();
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT); virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::ImageIndexIterator imageIterator() const; virtual gl::ImageIndexIterator imageIterator() const;
......
...@@ -81,6 +81,12 @@ gl::Error RenderbufferGL::setStorageMultisample(size_t samples, GLenum internalf ...@@ -81,6 +81,12 @@ gl::Error RenderbufferGL::setStorageMultisample(size_t samples, GLenum internalf
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
gl::Error RenderbufferGL::setStorageEGLImageTarget(egl::Image *image)
{
UNIMPLEMENTED();
return gl::Error(GL_INVALID_OPERATION);
}
GLuint RenderbufferGL::getRenderbufferID() const GLuint RenderbufferGL::getRenderbufferID() const
{ {
return mRenderbufferID; return mRenderbufferID;
......
...@@ -34,6 +34,7 @@ class RenderbufferGL : public RenderbufferImpl ...@@ -34,6 +34,7 @@ class RenderbufferGL : public RenderbufferImpl
virtual gl::Error setStorage(GLenum internalformat, size_t width, size_t height) override; virtual gl::Error setStorage(GLenum internalformat, size_t width, size_t height) override;
virtual gl::Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height) override; virtual gl::Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height) override;
virtual gl::Error setStorageEGLImageTarget(egl::Image *image) override;
GLuint getRenderbufferID() const; GLuint getRenderbufferID() const;
......
...@@ -418,6 +418,12 @@ void TextureGL::releaseTexImage() ...@@ -418,6 +418,12 @@ void TextureGL::releaseTexImage()
} }
} }
gl::Error TextureGL::setEGLImageTarget(GLenum target, egl::Image *image)
{
UNIMPLEMENTED();
return gl::Error(GL_INVALID_OPERATION);
}
template <typename T> template <typename T>
static inline void SyncSamplerStateMember(const FunctionsGL *functions, const gl::SamplerState &newState, static inline void SyncSamplerStateMember(const FunctionsGL *functions, const gl::SamplerState &newState,
gl::SamplerState &curState, GLenum textureType, GLenum name, gl::SamplerState &curState, GLenum textureType, GLenum name,
......
...@@ -52,6 +52,8 @@ class TextureGL : public TextureImpl ...@@ -52,6 +52,8 @@ class TextureGL : public TextureImpl
void bindTexImage(egl::Surface *surface) override; void bindTexImage(egl::Surface *surface) override;
void releaseTexImage() override; void releaseTexImage() override;
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
void syncSamplerState(const gl::SamplerState &samplerState) const; void syncSamplerState(const gl::SamplerState &samplerState) const;
GLuint getTextureID() const; GLuint getTextureID() const;
......
...@@ -8,13 +8,86 @@ ...@@ -8,13 +8,86 @@
#include "libANGLE/validationEGL.h" #include "libANGLE/validationEGL.h"
#include "common/utilities.h"
#include "libANGLE/Config.h" #include "libANGLE/Config.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Display.h" #include "libANGLE/Display.h"
#include "libANGLE/Image.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
#include <EGL/eglext.h> #include <EGL/eglext.h>
namespace
{
size_t GetMaximumMipLevel(const gl::Context *context, GLenum target)
{
const gl::Caps &caps = context->getCaps();
size_t maxDimension = 0;
switch (target)
{
case GL_TEXTURE_2D:
maxDimension = caps.max2DTextureSize;
break;
case GL_TEXTURE_CUBE_MAP:
maxDimension = caps.maxCubeMapTextureSize;
break;
case GL_TEXTURE_3D:
maxDimension = caps.max3DTextureSize;
break;
case GL_TEXTURE_2D_ARRAY:
maxDimension = caps.max2DTextureSize;
break;
default:
UNREACHABLE();
}
return gl::log2(maxDimension);
}
bool TextureHasNonZeroMipLevelsSpecified(const gl::Context *context, const gl::Texture *texture)
{
size_t maxMip = GetMaximumMipLevel(context, texture->getTarget());
for (size_t level = 1; level < maxMip; level++)
{
if (texture->getTarget() == GL_TEXTURE_CUBE_MAP)
{
for (GLenum face = gl::FirstCubeMapTextureTarget; face <= gl::LastCubeMapTextureTarget;
face++)
{
if (texture->getInternalFormat(face, level) != GL_NONE)
{
return true;
}
}
}
else
{
if (texture->getInternalFormat(texture->getTarget(), level) != GL_NONE)
{
return true;
}
}
}
return false;
}
bool CubeTextureHasUnspecifiedLevel0Face(const gl::Texture *texture)
{
ASSERT(texture->getTarget() == GL_TEXTURE_CUBE_MAP);
for (GLenum face = gl::FirstCubeMapTextureTarget; face <= gl::LastCubeMapTextureTarget; face++)
{
if (texture->getInternalFormat(face, 0) == GL_NONE)
{
return true;
}
}
return false;
}
}
namespace egl namespace egl
{ {
...@@ -86,6 +159,22 @@ Error ValidateContext(const Display *display, gl::Context *context) ...@@ -86,6 +159,22 @@ Error ValidateContext(const Display *display, gl::Context *context)
return Error(EGL_SUCCESS); return Error(EGL_SUCCESS);
} }
Error ValidateImage(const Display *display, const Image *image)
{
Error error = ValidateDisplay(display);
if (error.isError())
{
return error;
}
if (!display->isValidImage(image))
{
return Error(EGL_BAD_PARAMETER, "image is not valid.");
}
return Error(EGL_SUCCESS);
}
Error ValidateCreateContext(Display *display, Config *configuration, gl::Context *shareContext, Error ValidateCreateContext(Display *display, Config *configuration, gl::Context *shareContext,
const AttributeMap& attributes) const AttributeMap& attributes)
{ {
...@@ -542,13 +631,307 @@ Error ValidateCreateImageKHR(const Display *display, ...@@ -542,13 +631,307 @@ Error ValidateCreateImageKHR(const Display *display,
EGLClientBuffer buffer, EGLClientBuffer buffer,
const AttributeMap &attributes) const AttributeMap &attributes)
{ {
UNIMPLEMENTED(); Error error = ValidateContext(display, context);
if (error.isError())
{
return error;
}
const DisplayExtensions &displayExtensions = display->getExtensions();
if (!displayExtensions.imageBase && !displayExtensions.image)
{
// It is out of spec what happens when calling an extension function when the extension is
// not available.
// EGL_BAD_DISPLAY seems like a reasonable error.
return Error(EGL_BAD_DISPLAY, "EGL_KHR_image not supported.");
}
// TODO(geofflang): Complete validation from EGL_KHR_image_base:
// If the resource specified by <dpy>, <ctx>, <target>, <buffer> and <attrib_list> is itself an
// EGLImage sibling, the error EGL_BAD_ACCESS is generated.
for (AttributeMap::const_iterator attributeIter = attributes.begin();
attributeIter != attributes.end(); attributeIter++)
{
EGLint attribute = attributeIter->first;
EGLint value = attributeIter->second;
switch (attribute)
{
case EGL_IMAGE_PRESERVED_KHR:
switch (value)
{
case EGL_TRUE:
case EGL_FALSE:
break;
default:
return Error(EGL_BAD_PARAMETER,
"EGL_IMAGE_PRESERVED_KHR must be EGL_TRUE or EGL_FALSE.");
}
break;
case EGL_GL_TEXTURE_LEVEL_KHR:
if (!displayExtensions.glTexture2DImage &&
!displayExtensions.glTextureCubemapImage && !displayExtensions.glTexture3DImage)
{
return Error(EGL_BAD_PARAMETER,
"EGL_GL_TEXTURE_LEVEL_KHR cannot be used without "
"KHR_gl_texture_*_image support.");
}
if (value < 0)
{
return Error(EGL_BAD_PARAMETER, "EGL_GL_TEXTURE_LEVEL_KHR cannot be negative.");
}
break;
case EGL_GL_TEXTURE_ZOFFSET_KHR:
if (!displayExtensions.glTexture3DImage)
{
return Error(EGL_BAD_PARAMETER,
"EGL_GL_TEXTURE_ZOFFSET_KHR cannot be used without "
"KHR_gl_texture_3D_image support.");
}
break;
default:
return Error(EGL_BAD_PARAMETER, "invalid attribute: 0x%X", attribute);
}
}
switch (target)
{
case EGL_GL_TEXTURE_2D_KHR:
{
if (!displayExtensions.glTexture2DImage)
{
return Error(EGL_BAD_PARAMETER, "KHR_gl_texture_2D_image not supported.");
}
if (buffer == 0)
{
return Error(EGL_BAD_PARAMETER,
"buffer cannot reference a 2D texture with the name 0.");
}
const gl::Texture *texture =
context->getTexture(egl_gl::EGLClientBufferToGLObjectHandle(buffer));
if (texture == nullptr || texture->getTarget() != GL_TEXTURE_2D)
{
return Error(EGL_BAD_PARAMETER, "target is not a 2D texture.");
}
if (texture->getBoundSurface() != nullptr)
{
return Error(EGL_BAD_ACCESS, "texture has a surface bound to it.");
}
EGLint level = attributes.get(EGL_GL_TEXTURE_LEVEL_KHR, 0);
if (texture->getWidth(GL_TEXTURE_2D, static_cast<size_t>(level)) == 0 ||
texture->getHeight(GL_TEXTURE_2D, static_cast<size_t>(level)) == 0)
{
return Error(EGL_BAD_PARAMETER,
"target 2D texture does not have a valid size at specified level.");
}
if (level > 0 && (!texture->isMipmapComplete() ||
static_cast<size_t>(level) >= texture->getMipCompleteLevels()))
{
return Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
{
return Error(EGL_BAD_PARAMETER,
"if level is zero and the texture is incomplete, it must have no mip "
"levels specified except zero.");
}
}
break;
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR:
case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR:
{
if (!displayExtensions.glTextureCubemapImage)
{
return Error(EGL_BAD_PARAMETER, "KHR_gl_texture_cubemap_image not supported.");
}
if (buffer == 0)
{
return Error(EGL_BAD_PARAMETER,
"buffer cannot reference a cubemap texture with the name 0.");
}
const gl::Texture *texture =
context->getTexture(egl_gl::EGLClientBufferToGLObjectHandle(buffer));
if (texture == nullptr || texture->getTarget() != GL_TEXTURE_CUBE_MAP)
{
return Error(EGL_BAD_PARAMETER, "target is not a cubemap texture.");
}
if (texture->getBoundSurface() != nullptr)
{
return Error(EGL_BAD_ACCESS, "texture has a surface bound to it.");
}
EGLint level = attributes.get(EGL_GL_TEXTURE_LEVEL_KHR, 0);
GLenum cubeMapFace = egl_gl::EGLCubeMapTargetToGLCubeMapTarget(target);
if (texture->getWidth(cubeMapFace, static_cast<size_t>(level)) == 0 ||
texture->getHeight(cubeMapFace, static_cast<size_t>(level)) == 0)
{
return Error(EGL_BAD_PARAMETER,
"target cubemap texture does not have a valid size at specified level "
"and face.");
}
if (level > 0 && (!texture->isMipmapComplete() ||
static_cast<size_t>(level) >= texture->getMipCompleteLevels()))
{
return Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
{
return Error(EGL_BAD_PARAMETER,
"if level is zero and the texture is incomplete, it must have no mip "
"levels specified except zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
CubeTextureHasUnspecifiedLevel0Face(texture))
{
return Error(EGL_BAD_PARAMETER,
"if level is zero and the texture is incomplete, it must have all of "
"its faces specified at level zero.");
}
}
break;
case EGL_GL_TEXTURE_3D_KHR:
{
if (!displayExtensions.glTexture3DImage)
{
return Error(EGL_BAD_PARAMETER, "KHR_gl_texture_3D_image not supported.");
}
if (buffer == 0)
{
return Error(EGL_BAD_PARAMETER,
"buffer cannot reference a 3D texture with the name 0.");
}
const gl::Texture *texture =
context->getTexture(egl_gl::EGLClientBufferToGLObjectHandle(buffer));
if (texture == nullptr || texture->getTarget() != GL_TEXTURE_3D)
{
return Error(EGL_BAD_PARAMETER, "target is not a 3D texture.");
}
if (texture->getBoundSurface() != nullptr)
{
return Error(EGL_BAD_ACCESS, "texture has a surface bound to it.");
}
EGLint level = attributes.get(EGL_GL_TEXTURE_LEVEL_KHR, 0);
EGLint zOffset = attributes.get(EGL_GL_TEXTURE_ZOFFSET_KHR, 0);
if (texture->getWidth(GL_TEXTURE_3D, static_cast<size_t>(level)) == 0 ||
texture->getHeight(GL_TEXTURE_3D, static_cast<size_t>(level)) == 0 ||
texture->getDepth(GL_TEXTURE_3D, static_cast<size_t>(level)) == 0)
{
return Error(EGL_BAD_PARAMETER,
"target 3D texture does not have a valid size at specified level.");
}
if (static_cast<size_t>(zOffset) >=
texture->getDepth(GL_TEXTURE_3D, static_cast<size_t>(level)))
{
return Error(EGL_BAD_PARAMETER,
"target 3D texture does not have enough layers for the specified Z "
"offset at the specified level.");
}
if (level > 0 && (!texture->isMipmapComplete() ||
static_cast<size_t>(level) >= texture->getMipCompleteLevels()))
{
return Error(EGL_BAD_PARAMETER, "texture must be complete if level is non-zero.");
}
if (level == 0 && !texture->isMipmapComplete() &&
TextureHasNonZeroMipLevelsSpecified(context, texture))
{
return Error(EGL_BAD_PARAMETER,
"if level is zero and the texture is incomplete, it must have no mip "
"levels specified except zero.");
}
}
break;
case EGL_GL_RENDERBUFFER_KHR:
{
if (!displayExtensions.glRenderbufferImage)
{
return Error(EGL_BAD_PARAMETER, "KHR_gl_renderbuffer_image not supported.");
}
if (attributes.contains(EGL_GL_TEXTURE_LEVEL_KHR))
{
return Error(EGL_BAD_PARAMETER,
"EGL_GL_TEXTURE_LEVEL_KHR cannot be used in conjunction with a "
"renderbuffer target.");
}
if (buffer == 0)
{
return Error(EGL_BAD_PARAMETER,
"buffer cannot reference a renderbuffer with the name 0.");
}
const gl::Renderbuffer *renderbuffer =
context->getRenderbuffer(egl_gl::EGLClientBufferToGLObjectHandle(buffer));
if (renderbuffer == nullptr)
{
return Error(EGL_BAD_PARAMETER, "target is not a renderbuffer.");
}
if (renderbuffer->getSamples() > 0)
{
return Error(EGL_BAD_PARAMETER, "target renderbuffer cannot be multisampled.");
}
}
break;
default:
return Error(EGL_BAD_PARAMETER, "invalid target: 0x%X", target);
}
return Error(EGL_SUCCESS); return Error(EGL_SUCCESS);
} }
Error ValidateDestroyImageKHR(const Display *display, const Image *image) Error ValidateDestroyImageKHR(const Display *display, const Image *image)
{ {
UNIMPLEMENTED(); Error error = ValidateImage(display, image);
if (error.isError())
{
return error;
}
if (!display->getExtensions().imageBase && !display->getExtensions().image)
{
// It is out of spec what happens when calling an extension function when the extension is
// not available.
// EGL_BAD_DISPLAY seems like a reasonable error.
return Error(EGL_BAD_DISPLAY);
}
return Error(EGL_SUCCESS); return Error(EGL_SUCCESS);
} }
} }
...@@ -32,6 +32,7 @@ Error ValidateDisplay(const Display *display); ...@@ -32,6 +32,7 @@ Error ValidateDisplay(const Display *display);
Error ValidateSurface(const Display *display, Surface *surface); Error ValidateSurface(const Display *display, Surface *surface);
Error ValidateConfig(const Display *display, const Config *config); Error ValidateConfig(const Display *display, const Config *config);
Error ValidateContext(const Display *display, gl::Context *context); Error ValidateContext(const Display *display, gl::Context *context);
Error ValidateImage(const Display *display, const Image *image);
// Entry point validation // Entry point validation
Error ValidateCreateContext(Display *display, Config *configuration, gl::Context *shareContext, Error ValidateCreateContext(Display *display, Config *configuration, gl::Context *shareContext,
......
...@@ -10,10 +10,12 @@ ...@@ -10,10 +10,12 @@
#include "libANGLE/validationES2.h" #include "libANGLE/validationES2.h"
#include "libANGLE/validationES3.h" #include "libANGLE/validationES3.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/Texture.h" #include "libANGLE/Texture.h"
#include "libANGLE/Framebuffer.h" #include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/Image.h"
#include "libANGLE/Query.h" #include "libANGLE/Query.h"
#include "libANGLE/Program.h" #include "libANGLE/Program.h"
#include "libANGLE/Uniform.h" #include "libANGLE/Uniform.h"
...@@ -2006,7 +2008,43 @@ bool ValidateEGLImageTargetTexture2DOES(Context *context, ...@@ -2006,7 +2008,43 @@ bool ValidateEGLImageTargetTexture2DOES(Context *context,
GLenum target, GLenum target,
egl::Image *image) egl::Image *image)
{ {
UNIMPLEMENTED(); 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; return true;
} }
...@@ -2015,7 +2053,36 @@ bool ValidateEGLImageTargetRenderbufferStorageOES(Context *context, ...@@ -2015,7 +2053,36 @@ bool ValidateEGLImageTargetRenderbufferStorageOES(Context *context,
GLenum target, GLenum target,
egl::Image *image) egl::Image *image)
{ {
UNIMPLEMENTED(); 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; return true;
} }
} }
...@@ -82,6 +82,8 @@ ...@@ -82,6 +82,8 @@
'libANGLE/FramebufferAttachment.h', 'libANGLE/FramebufferAttachment.h',
'libANGLE/HandleAllocator.cpp', 'libANGLE/HandleAllocator.cpp',
'libANGLE/HandleAllocator.h', 'libANGLE/HandleAllocator.h',
'libANGLE/Image.h',
'libANGLE/Image.cpp',
'libANGLE/ImageIndex.h', 'libANGLE/ImageIndex.h',
'libANGLE/ImageIndex.cpp', 'libANGLE/ImageIndex.cpp',
'libANGLE/IndexRangeCache.cpp', 'libANGLE/IndexRangeCache.cpp',
...@@ -136,6 +138,7 @@ ...@@ -136,6 +138,7 @@
'libANGLE/renderer/FenceNVImpl.h', 'libANGLE/renderer/FenceNVImpl.h',
'libANGLE/renderer/FenceSyncImpl.h', 'libANGLE/renderer/FenceSyncImpl.h',
'libANGLE/renderer/FramebufferImpl.h', 'libANGLE/renderer/FramebufferImpl.h',
'libANGLE/renderer/ImageImpl.h',
'libANGLE/renderer/ImplFactory.h', 'libANGLE/renderer/ImplFactory.h',
'libANGLE/renderer/ProgramImpl.cpp', 'libANGLE/renderer/ProgramImpl.cpp',
'libANGLE/renderer/ProgramImpl.h', 'libANGLE/renderer/ProgramImpl.h',
......
...@@ -1196,7 +1196,13 @@ ANGLE_EXPORT void GL_APIENTRY EGLImageTargetTexture2DOES(GLenum target, GLeglIma ...@@ -1196,7 +1196,13 @@ ANGLE_EXPORT void GL_APIENTRY EGLImageTargetTexture2DOES(GLenum target, GLeglIma
return; return;
} }
UNIMPLEMENTED(); Texture *texture = context->getTargetTexture(target);
Error error = texture->setEGLImageTarget(target, imageObject);
if (error.isError())
{
context->recordError(error);
return;
}
} }
} }
...@@ -1215,7 +1221,13 @@ ANGLE_EXPORT void GL_APIENTRY EGLImageTargetRenderbufferStorageOES(GLenum target ...@@ -1215,7 +1221,13 @@ ANGLE_EXPORT void GL_APIENTRY EGLImageTargetRenderbufferStorageOES(GLenum target
return; return;
} }
UNIMPLEMENTED(); Renderbuffer *renderbuffer = context->getState().getCurrentRenderbuffer();
Error error = renderbuffer->setStorageEGLImageTarget(imageObject);
if (error.isError())
{
context->recordError(error);
return;
}
} }
} }
} }
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
'<(angle_path)/src/tests/gl_tests/FramebufferFormatsTest.cpp', '<(angle_path)/src/tests/gl_tests/FramebufferFormatsTest.cpp',
'<(angle_path)/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp', '<(angle_path)/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp',
'<(angle_path)/src/tests/gl_tests/GLSLTest.cpp', '<(angle_path)/src/tests/gl_tests/GLSLTest.cpp',
'<(angle_path)/src/tests/gl_tests/ImageTest.cpp',
'<(angle_path)/src/tests/gl_tests/IncompleteTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/IncompleteTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/IndexBufferOffsetTest.cpp', '<(angle_path)/src/tests/gl_tests/IndexBufferOffsetTest.cpp',
'<(angle_path)/src/tests/gl_tests/IndexedPointsTest.cpp', '<(angle_path)/src/tests/gl_tests/IndexedPointsTest.cpp',
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
'<(angle_path)/src/libANGLE/Config_unittest.cpp', '<(angle_path)/src/libANGLE/Config_unittest.cpp',
'<(angle_path)/src/libANGLE/Fence_unittest.cpp', '<(angle_path)/src/libANGLE/Fence_unittest.cpp',
'<(angle_path)/src/libANGLE/HandleAllocator_unittest.cpp', '<(angle_path)/src/libANGLE/HandleAllocator_unittest.cpp',
'<(angle_path)/src/libANGLE/Image_unittest.cpp',
'<(angle_path)/src/libANGLE/ImageIndexIterator_unittest.cpp', '<(angle_path)/src/libANGLE/ImageIndexIterator_unittest.cpp',
'<(angle_path)/src/libANGLE/Program_unittest.cpp', '<(angle_path)/src/libANGLE/Program_unittest.cpp',
'<(angle_path)/src/libANGLE/ResourceManager_unittest.cpp', '<(angle_path)/src/libANGLE/ResourceManager_unittest.cpp',
...@@ -31,6 +32,7 @@ ...@@ -31,6 +32,7 @@
'<(angle_path)/src/libANGLE/TransformFeedback_unittest.cpp', '<(angle_path)/src/libANGLE/TransformFeedback_unittest.cpp',
'<(angle_path)/src/libANGLE/renderer/BufferImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/BufferImpl_mock.h',
'<(angle_path)/src/libANGLE/renderer/RenderbufferImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/RenderbufferImpl_mock.h',
'<(angle_path)/src/libANGLE/renderer/ImageImpl_mock.h',
'<(angle_path)/src/libANGLE/renderer/TextureImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/TextureImpl_mock.h',
'<(angle_path)/src/libANGLE/renderer/TransformFeedbackImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/TransformFeedbackImpl_mock.h',
'<(angle_path)/src/tests/angle_unittests_utils.h', '<(angle_path)/src/tests/angle_unittests_utils.h',
......
//
// Copyright 2015 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.
//
// ImageTest:
// Tests the correctness of eglImage.
//
#include "test_utils/ANGLETest.h"
namespace angle
{
class ImageTest : public ANGLETest
{
protected:
ImageTest()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
}
void SetUp() override
{
ANGLETest::SetUp();
const std::string vsSource =
"precision highp float;\n"
"attribute vec4 position;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
" texcoord = (position.xy * 0.5) + 0.5;\n"
" texcoord.y = 1.0 - texcoord.y;\n"
"}\n";
const std::string textureFSSource =
"precision highp float;\n"
"uniform sampler2D tex;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex, texcoord);\n"
"}\n";
mTextureProgram = CompileProgram(vsSource, textureFSSource);
if (mTextureProgram == 0)
{
FAIL() << "shader compilation failed.";
}
mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex");
ASSERT_GL_NO_ERROR();
}
void TearDown() override
{
ANGLETest::TearDown();
glDeleteProgram(mTextureProgram);
}
void createEGLImage2DTextureSource(size_t width,
size_t height,
GLenum format,
GLenum type,
void *data,
GLuint *outSourceTexture,
EGLImageKHR *outSourceImage)
{
// Create a source 2D texture
GLuint source;
glGenTextures(1, &source);
glBindTexture(GL_TEXTURE_2D, source);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
// Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Create an image from the source texture
EGLWindow *window = getEGLWindow();
EGLImageKHR image =
eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(source), nullptr);
ASSERT_EGL_SUCCESS();
*outSourceTexture = source;
*outSourceImage = image;
}
void createEGLImageCubemapTextureSource(size_t width,
size_t height,
GLenum format,
GLenum type,
uint8_t *data,
size_t dataStride,
EGLenum imageTarget,
GLuint *outSourceTexture,
EGLImageKHR *outSourceImage)
{
// Create a source cube map texture
GLuint source;
glGenTextures(1, &source);
glBindTexture(GL_TEXTURE_CUBE_MAP, source);
for (size_t faceIdx = 0; faceIdx < 6; faceIdx++)
{
glTexImage2D(faceIdx + GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format, width, height, 0,
format, type, data + (faceIdx * dataStride));
}
// Disable mipmapping
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Create an image from the source texture
EGLWindow *window = getEGLWindow();
EGLImageKHR image =
eglCreateImageKHR(window->getDisplay(), window->getContext(), imageTarget,
reinterpret_cast<EGLClientBuffer>(source), nullptr);
ASSERT_EGL_SUCCESS();
*outSourceTexture = source;
*outSourceImage = image;
}
void createEGLImage3DTextureSource(size_t width,
size_t height,
size_t depth,
GLenum format,
GLenum type,
void *data,
size_t imageLayer,
GLuint *outSourceTexture,
EGLImageKHR *outSourceImage)
{
// Create a source 3D texture
GLuint source;
glGenTextures(1, &source);
glBindTexture(GL_TEXTURE_3D, source);
glTexImage3D(GL_TEXTURE_3D, 0, format, width, height, depth, 0, format, type, data);
// Disable mipmapping
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Create an image from the source texture
EGLWindow *window = getEGLWindow();
EGLint attribs[] = {
EGL_GL_TEXTURE_ZOFFSET_KHR, static_cast<EGLint>(imageLayer), EGL_NONE,
};
EGLImageKHR image =
eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_3D_KHR,
reinterpret_cast<EGLClientBuffer>(source), attribs);
ASSERT_EGL_SUCCESS();
*outSourceTexture = source;
*outSourceImage = image;
}
void createEGLImageRenderbufferSource(size_t width,
size_t height,
GLenum internalFormat,
GLubyte data[4],
GLuint *outSourceRenderbuffer,
EGLImageKHR *outSourceImage)
{
// Create a source renderbuffer
GLuint source;
glGenRenderbuffers(1, &source);
glBindRenderbuffer(GL_RENDERBUFFER, source);
glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height);
// Create a framebuffer and clear it to set the data
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, source);
glClearColor(data[0] / 255.0f, data[1] / 255.0f, data[2] / 255.0f, data[3] / 255.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDeleteFramebuffers(1, &framebuffer);
ASSERT_GL_NO_ERROR();
// Create an image from the source renderbuffer
EGLWindow *window = getEGLWindow();
EGLImageKHR image =
eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_RENDERBUFFER_KHR,
reinterpret_cast<EGLClientBuffer>(source), nullptr);
ASSERT_EGL_SUCCESS();
*outSourceRenderbuffer = source;
*outSourceImage = image;
}
void createEGLImageTargetTexture2D(EGLImageKHR image, GLuint *outTargetTexture)
{
// Create a target texture from the image
GLuint target;
glGenTextures(1, &target);
glBindTexture(GL_TEXTURE_2D, target);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
// Disable mipmapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
*outTargetTexture = target;
}
void createEGLImageTargetRenderbuffer(EGLImageKHR image, GLuint *outTargetRenderbuffer)
{
// Create a target texture from the image
GLuint target;
glGenRenderbuffers(1, &target);
glBindRenderbuffer(GL_RENDERBUFFER, target);
glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image);
ASSERT_GL_NO_ERROR();
*outTargetRenderbuffer = target;
}
void verifyResults2D(GLuint texture, GLubyte data[4])
{
// Draw a quad with the target texture
glUseProgram(mTextureProgram);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(mTextureUniformLocation, 0);
drawQuad(mTextureProgram, "position", 0.5f);
// Expect that the rendered quad has the same color as the source texture
EXPECT_PIXEL_EQ(0, 0, data[0], data[1], data[2], data[3]);
}
void verifyResultsRenderbuffer(GLuint renderbuffer, GLubyte data[4])
{
// Bind the renderbuffer to a framebuffer
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
renderbuffer);
// Expect that the rendered quad has the same color as the source texture
EXPECT_PIXEL_EQ(0, 0, data[0], data[1], data[2], data[3]);
glDeleteFramebuffers(1, &framebuffer);
}
GLuint mTextureProgram;
GLint mTextureUniformLocation;
};
// Check validation from the EGL_KHR_image_base extension
TEST_P(ImageTest, ValidationImageBase)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
GLuint glTexture2D;
glGenTextures(1, &glTexture2D);
glBindTexture(GL_TEXTURE_2D, glTexture2D);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
EGLDisplay display = window->getDisplay();
EGLContext context = window->getContext();
EGLConfig config = window->getConfig();
EGLImageKHR image = EGL_NO_IMAGE_KHR;
EGLClientBuffer texture2D = reinterpret_cast<EGLClientBuffer>(glTexture2D);
// Test validation of eglCreateImageKHR
// If <dpy> is not the handle of a valid EGLDisplay object, the error EGL_BAD_DISPLAY is
// generated.
image = eglCreateImageKHR(reinterpret_cast<EGLDisplay>(0xBAADF00D), context,
EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
// If <ctx> is neither the handle of a valid EGLContext object on <dpy> nor EGL_NO_CONTEXT, the
// error EGL_BAD_CONTEXT is generated.
image = eglCreateImageKHR(display, reinterpret_cast<EGLContext>(0xBAADF00D),
EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_CONTEXT);
// Test EGL_NO_CONTEXT with a 2D texture target which does require a context.
image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_CONTEXT);
// If an attribute specified in <attrib_list> is not one of the attributes listed in Table bbb,
// the error EGL_BAD_PARAMETER is generated.
EGLint badAttributes[] = {
static_cast<EGLint>(0xDEADBEEF), 0, EGL_NONE,
};
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, texture2D, badAttributes);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
// If the resource specified by <dpy>, <ctx>, <target>, <buffer> and <attrib_list> has an off -
// screen buffer bound to it(e.g., by a
// previous call to eglBindTexImage), the error EGL_BAD_ACCESS is generated.
EGLint surfaceType = 0;
eglGetConfigAttrib(display, config, EGL_SURFACE_TYPE, &surfaceType);
EGLint bindToTextureRGBA = 0;
eglGetConfigAttrib(display, config, EGL_BIND_TO_TEXTURE_RGBA, &bindToTextureRGBA);
if ((surfaceType & EGL_PBUFFER_BIT) != 0 && bindToTextureRGBA == EGL_TRUE)
{
EGLint pbufferAttributes[] = {
EGL_WIDTH, 1,
EGL_HEIGHT, 1,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_NONE, EGL_NONE,
};
EGLSurface pbuffer = eglCreatePbufferSurface(display, config, pbufferAttributes);
ASSERT_NE(pbuffer, EGL_NO_SURFACE);
EXPECT_EGL_SUCCESS();
eglBindTexImage(display, pbuffer, EGL_BACK_BUFFER);
EXPECT_EGL_SUCCESS();
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_ACCESS);
eglReleaseTexImage(display, pbuffer, EGL_BACK_BUFFER);
eglDestroySurface(display, pbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
EXPECT_EGL_SUCCESS();
EXPECT_GL_NO_ERROR();
}
// If the resource specified by <dpy>, <ctx>, <target>, <buffer> and
// <attrib_list> is itself an EGLImage sibling, the error EGL_BAD_ACCESS is generated.
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr);
EXPECT_NE(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_SUCCESS();
/* TODO(geofflang): Enable this validation when it passes.
EGLImageKHR image2 = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(texture2D), nullptr);
EXPECT_EQ(image2, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_ACCESS);
*/
// Test validation of eglDestroyImageKHR
// Note: image is now a valid EGL image
EGLBoolean result = EGL_FALSE;
// If <dpy> is not the handle of a valid EGLDisplay object, the error EGL_BAD_DISPLAY is
// generated.
result = eglDestroyImageKHR(reinterpret_cast<EGLDisplay>(0xBAADF00D), image);
EXPECT_EQ(result, EGL_FALSE);
EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
// If <image> is not a valid EGLImageKHR object created with respect to <dpy>, the error
// EGL_BAD_PARAMETER is generated.
result = eglDestroyImageKHR(display, reinterpret_cast<EGLImageKHR>(0xBAADF00D));
EXPECT_EQ(result, EGL_FALSE);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
// Clean up and validate image is destroyed
result = eglDestroyImageKHR(display, image);
EXPECT_EQ(result, EGL_TRUE);
EXPECT_EGL_SUCCESS();
glDeleteTextures(1, &glTexture2D);
EXPECT_GL_NO_ERROR();
}
// Check validation from the EGL_KHR_gl_texture_2D_image extension
TEST_P(ImageTest, ValidationImagePixmap)
{
// This extension is not implemented anywhere yet. This makes sure that it is tested once it is
// added.
EGLWindow *window = getEGLWindow();
EXPECT_FALSE(eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_pixmap"));
}
// Check validation from the EGL_KHR_gl_texture_2D_image, EGL_KHR_gl_texture_cubemap_image,
// EGL_KHR_gl_texture_3D_image and EGL_KHR_gl_renderbuffer_image extensions
TEST_P(ImageTest, ValidationGLImage)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base"))
{
std::cout << "Test skipped because OES_EGL_image or EGL_KHR_image_base is not available."
<< std::endl;
return;
}
EGLDisplay display = window->getDisplay();
EGLContext context = window->getContext();
EGLImageKHR image = EGL_NO_IMAGE_KHR;
if (eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
// If <target> is EGL_GL_TEXTURE_2D_KHR, EGL_GL_TEXTURE_CUBE_MAP_*_KHR or
// EGL_GL_TEXTURE_3D_KHR and <buffer> is not the name of a texture object of type <target>,
// the error EGL_BAD_PARAMETER is generated.
GLuint textureCube;
glGenTextures(1, &textureCube);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureCube);
for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
face++)
{
glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(textureCube), nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
// If EGL_GL_TEXTURE_LEVEL_KHR is 0, <target> is EGL_GL_TEXTURE_2D_KHR,
// EGL_GL_TEXTURE_CUBE_MAP_*_KHR or EGL_GL_TEXTURE_3D_KHR, <buffer> is the name of an
// incomplete GL texture object, and any mipmap levels other than mipmap level 0 are
// specified, the error EGL_BAD_PARAMETER is generated.
GLuint incompleteTexture;
glGenTextures(1, &incompleteTexture);
glBindTexture(GL_TEXTURE_2D, incompleteTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
EGLint level0Attribute[] = {
EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_NONE,
};
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(incompleteTexture),
level0Attribute);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
// If EGL_GL_TEXTURE_LEVEL_KHR is 0, <target> is EGL_GL_TEXTURE_2D_KHR or
// EGL_GL_TEXTURE_3D_KHR, <buffer> is not the name of a complete GL texture object, and
// mipmap level 0 is not specified, the error EGL_BAD_PARAMETER is generated.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(incompleteTexture), nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
// If <target> is EGL_GL_TEXTURE_2D_KHR, EGL_GL_TEXTURE_CUBE_MAP_*_KHR,
// EGL_GL_RENDERBUFFER_KHR or EGL_GL_TEXTURE_3D_KHR and <buffer> refers to the default GL
// texture object(0) for the corresponding GL target, the error EGL_BAD_PARAMETER is
// generated.
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, 0, nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
// If <target> is EGL_GL_TEXTURE_2D_KHR, EGL_GL_TEXTURE_CUBE_MAP_*_KHR, or
// EGL_GL_TEXTURE_3D_KHR, and the value specified in <attr_list> for
// EGL_GL_TEXTURE_LEVEL_KHR is not a valid mipmap level for the specified GL texture object
// <buffer>, the error EGL_BAD_MATCH is generated.
EGLint level2Attribute[] = {
EGL_GL_TEXTURE_LEVEL_KHR, 2, EGL_NONE,
};
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(incompleteTexture),
level2Attribute);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
else
{
GLuint texture2D;
glGenTextures(1, &texture2D);
glBindTexture(GL_TEXTURE_2D, texture2D);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// From EGL_KHR_image_base:
// If <target> is not one of the values in Table aaa, the error EGL_BAD_PARAMETER is
// generated.
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(texture2D), nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
if (eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_cubemap_image"))
{
// If EGL_GL_TEXTURE_LEVEL_KHR is 0, <target> is EGL_GL_TEXTURE_CUBE_MAP_*_KHR, <buffer> is
// not the name of a complete GL texture object, and one or more faces do not have mipmap
// level 0 specified, the error EGL_BAD_PARAMETER is generated.
GLuint incompleteTextureCube;
glGenTextures(1, &incompleteTextureCube);
glBindTexture(GL_TEXTURE_CUBE_MAP, incompleteTextureCube);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
EGLint level0Attribute[] = {
EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_NONE,
};
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR,
reinterpret_cast<EGLClientBuffer>(incompleteTextureCube),
level0Attribute);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
else
{
GLuint textureCube;
glGenTextures(1, &textureCube);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureCube);
for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
face++)
{
glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
// From EGL_KHR_image_base:
// If <target> is not one of the values in Table aaa, the error EGL_BAD_PARAMETER is
// generated.
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR,
reinterpret_cast<EGLClientBuffer>(textureCube), nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
if (eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_3D_image") &&
getClientVersion() >= 3)
{
// If <target> is EGL_GL_TEXTURE_3D_KHR, and the value specified in <attr_list> for
// EGL_GL_TEXTURE_ZOFFSET_KHR exceeds the depth of the specified mipmap level - of - detail
// in <buffer>, the error EGL_BAD_PARAMETER is generated.
GLuint texture3D;
glGenTextures(1, &texture3D);
glBindTexture(GL_TEXTURE_3D, texture3D);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
EGLint zOffset3Parameter[] = {
EGL_GL_TEXTURE_ZOFFSET_KHR, 3, EGL_NONE,
};
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_3D_KHR,
reinterpret_cast<EGLClientBuffer>(texture3D), zOffset3Parameter);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
EGLint zOffsetNegative1Parameter[] = {
EGL_GL_TEXTURE_ZOFFSET_KHR, -1, EGL_NONE,
};
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_3D_KHR,
reinterpret_cast<EGLClientBuffer>(texture3D),
zOffsetNegative1Parameter);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
else
{
if (eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
GLuint texture2D;
glGenTextures(1, &texture2D);
glBindTexture(GL_TEXTURE_2D, texture2D);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Verify EGL_GL_TEXTURE_ZOFFSET_KHR is not a valid parameter
EGLint zOffset0Parameter[] = {
EGL_GL_TEXTURE_ZOFFSET_KHR, 0, EGL_NONE,
};
image =
eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(texture2D), zOffset0Parameter);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
if (getClientVersion() >= 3)
{
GLuint texture3D;
glGenTextures(1, &texture3D);
glBindTexture(GL_TEXTURE_3D, texture3D);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// From EGL_KHR_image_base:
// If <target> is not one of the values in Table aaa, the error EGL_BAD_PARAMETER is
// generated.
image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_3D_KHR,
reinterpret_cast<EGLClientBuffer>(texture3D), nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
}
if (eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_renderbuffer_image"))
{
// If <target> is EGL_GL_RENDERBUFFER_KHR and <buffer> is not the name of a renderbuffer
// object, or if <buffer> is the name of a multisampled renderbuffer object, the error
// EGL_BAD_PARAMETER is generated.
image = eglCreateImageKHR(display, context, EGL_GL_RENDERBUFFER_KHR,
reinterpret_cast<EGLClientBuffer>(0), nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
if (extensionEnabled("GL_ANGLE_framebuffer_multisample"))
{
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 1, GL_RGBA8, 1, 1);
EXPECT_GL_NO_ERROR();
image = eglCreateImageKHR(display, context, EGL_GL_RENDERBUFFER_KHR,
reinterpret_cast<EGLClientBuffer>(0), nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
}
else
{
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 1, 1);
// From EGL_KHR_image_base:
// If <target> is not one of the values in Table aaa, the error EGL_BAD_PARAMETER is
// generated.
image = eglCreateImageKHR(display, context, EGL_GL_RENDERBUFFER_KHR,
reinterpret_cast<EGLClientBuffer>(renderbuffer), nullptr);
EXPECT_EQ(image, EGL_NO_IMAGE_KHR);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
}
// Check validation from the GL_OES_EGL_image extension
TEST_P(ImageTest, ValidationGLEGLImage)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data, &source, &image);
// If <target> is not TEXTURE_2D, the error INVALID_ENUM is generated.
glEGLImageTargetTexture2DOES(GL_TEXTURE_CUBE_MAP_POSITIVE_X, image);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// If <image> does not refer to a valid eglImageOES object, the error INVALID_VALUE is
// generated.
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, reinterpret_cast<GLeglImageOES>(0xBAADF00D));
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// <target> must be RENDERBUFFER_OES, and <image> must be the handle of a valid EGLImage
// resource, cast into the type
// eglImageOES.
glEGLImageTargetRenderbufferStorageOES(GL_TEXTURE_2D, image);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// If the GL is unable to create a renderbuffer using the specified eglImageOES, the error
// INVALID_OPERATION is generated.If <image>
// does not refer to a valid eglImageOES object, the error INVALID_VALUE is generated.
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER,
reinterpret_cast<GLeglImageOES>(0xBAADF00D));
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &texture);
glDeleteRenderbuffers(1, &renderbuffer);
}
// Check validation from the GL_OES_EGL_image_external extension
TEST_P(ImageTest, ValidationGLEGLImageExternal)
{
// This extension is not implemented anywhere yet. This makes sure that it is tested once it is
// added.
EXPECT_FALSE(extensionEnabled("GL_OES_EGL_image_external"));
}
// Check validation from the GL_OES_EGL_image_external_essl3 extension
TEST_P(ImageTest, ValidationGLEGLImageExternalESSL3)
{
// This extension is not implemented anywhere yet. This makes sure that it is tested once it is
// added.
EXPECT_FALSE(extensionEnabled("GL_OES_EGL_image_external_essl3"));
}
TEST_P(ImageTest, Source2DTarget2D)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, data);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &target);
}
TEST_P(ImageTest, Source2DTargetRenderbuffer)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsRenderbuffer(target, data);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteRenderbuffers(1, &target);
}
TEST_P(ImageTest, SourceCubeTarget2D)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_cubemap_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_cubemap_image is not available."
<< std::endl;
return;
}
GLubyte data[24] = {
255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255,
0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255,
};
for (size_t faceIdx = 0; faceIdx < 6; faceIdx++)
{
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageCubemapTextureSource(
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<uint8_t *>(data), sizeof(GLubyte) * 4,
EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, &data[faceIdx * 4]);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &target);
}
}
TEST_P(ImageTest, SourceCubeTargetRenderbuffer)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_cubemap_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_cubemap_image is not available."
<< std::endl;
return;
}
GLubyte data[24] = {
255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255,
0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255,
};
for (size_t faceIdx = 0; faceIdx < 6; faceIdx++)
{
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageCubemapTextureSource(
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<uint8_t *>(data), sizeof(GLubyte) * 4,
EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResultsRenderbuffer(target, &data[faceIdx * 4]);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteRenderbuffers(1, &target);
}
}
TEST_P(ImageTest, Source3DTargetTexture)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_3D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_3D_image is not available."
<< std::endl;
return;
}
if (getClientVersion() < 3 && !extensionEnabled("GL_OES_texture_3D"))
{
std::cout << "Test skipped because 3D textures are not available." << std::endl;
return;
}
const size_t depth = 2;
GLubyte data[4 * depth] = {
255, 0, 255, 255, 255, 255, 0, 255,
};
for (size_t layer = 0; layer < depth; layer++)
{
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, data, layer, &source,
&image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResults2D(target, &data[layer * 4]);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &target);
}
}
TEST_P(ImageTest, Source3DTargetRenderbuffer)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_3D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_3D_image is not available."
<< std::endl;
return;
}
if (getClientVersion() < 3 && !extensionEnabled("GL_OES_texture_3D"))
{
std::cout << "Test skipped because 3D textures are not available." << std::endl;
return;
}
const size_t depth = 2;
GLubyte data[4 * depth] = {
255, 0, 255, 255, 255, 255, 0, 255,
};
for (size_t layer = 0; layer < depth; layer++)
{
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, data, layer, &source,
&image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsRenderbuffer(target, &data[layer * 4]);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &target);
}
}
TEST_P(ImageTest, SourceRenderbufferTargetTexture)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_renderbuffer_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_renderbuffer_image is not available."
<< std::endl;
return;
}
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, data, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, data);
// Clean up
glDeleteRenderbuffers(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &target);
}
TEST_P(ImageTest, SourceRenderbufferTargetRenderbuffer)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_renderbuffer_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_renderbuffer_image is not available."
<< std::endl;
return;
}
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, data, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsRenderbuffer(target, data);
// Clean up
glDeleteRenderbuffers(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteRenderbuffers(1, &target);
}
// Delete the source texture and EGL image. The image targets should still have the same data
// because
// they hold refs to the image.
TEST_P(ImageTest, Deletion)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
GLubyte originalData[4] = {255, 0, 255, 255};
GLubyte updateData[4] = {0, 255, 0, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, originalData, &source, &image);
// Create multiple targets
GLuint targetTexture;
createEGLImageTargetTexture2D(image, &targetTexture);
GLuint targetRenderbuffer;
createEGLImageTargetRenderbuffer(image, &targetRenderbuffer);
// Delete the source texture
glDeleteTextures(1, &source);
source = 0;
// Expect that both the targets have the original data
verifyResults2D(targetTexture, originalData);
verifyResultsRenderbuffer(targetRenderbuffer, originalData);
// Update the data of the target
glBindTexture(GL_TEXTURE_2D, targetTexture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, updateData);
// Expect that both targets have the updated data
verifyResults2D(targetTexture, updateData);
verifyResultsRenderbuffer(targetRenderbuffer, updateData);
// Delete the EGL image
eglDestroyImageKHR(window->getDisplay(), image);
image = EGL_NO_IMAGE_KHR;
// Update the data of the target back to the original data
glBindTexture(GL_TEXTURE_2D, targetTexture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, originalData);
// Expect that both targets have the original data again
verifyResults2D(targetTexture, originalData);
verifyResultsRenderbuffer(targetRenderbuffer, originalData);
// Clean up
glDeleteTextures(1, &targetTexture);
glDeleteRenderbuffers(1, &targetRenderbuffer);
}
TEST_P(ImageTest, MipLevels)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
const size_t mipLevels = 3;
const size_t textureSize = 4;
std::vector<GLuint> mip0Data(textureSize * textureSize, 0xFFFF0000);
std::vector<GLuint> mip1Data(mip0Data.size() << 1, 0xFF00FF00);
std::vector<GLuint> mip2Data(mip0Data.size() << 2, 0xFF0000FF);
GLubyte *data[mipLevels] = {
reinterpret_cast<GLubyte *>(&mip0Data[0]), reinterpret_cast<GLubyte *>(&mip1Data[0]),
reinterpret_cast<GLubyte *>(&mip2Data[0]),
};
GLuint source;
glGenTextures(1, &source);
glBindTexture(GL_TEXTURE_2D, source);
for (size_t level = 0; level < mipLevels; level++)
{
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, textureSize >> level, textureSize >> level, 0,
GL_RGBA, GL_UNSIGNED_BYTE, data[level]);
}
ASSERT_GL_NO_ERROR();
for (size_t level = 0; level < mipLevels; level++)
{
// Create the Image
EGLint attribs[] = {
EGL_GL_TEXTURE_LEVEL_KHR, static_cast<EGLint>(level), EGL_NONE,
};
EGLImageKHR image =
eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR,
reinterpret_cast<EGLClientBuffer>(source), attribs);
ASSERT_EGL_SUCCESS();
// Create a texture and renderbuffer target
GLuint textureTarget;
createEGLImageTargetTexture2D(image, &textureTarget);
// Disable mipmapping
glBindTexture(GL_TEXTURE_2D, textureTarget);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLuint renderbufferTarget;
createEGLImageTargetRenderbuffer(image, &renderbufferTarget);
// Expect that the targets have the same color as the source texture
verifyResults2D(textureTarget, data[level]);
verifyResultsRenderbuffer(renderbufferTarget, data[level]);
// Clean up
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &textureTarget);
glDeleteRenderbuffers(1, &renderbufferTarget);
}
// Clean up
glDeleteTextures(1, &source);
}
// Respecify the source texture, orphaning it. The target texture should not have updated data.
TEST_P(ImageTest, Respecification)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
GLubyte originalData[4] = {255, 0, 255, 255};
GLubyte updateData[4] = {0, 255, 0, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, originalData, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Respecify source
glBindTexture(GL_TEXTURE_2D, source);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, updateData);
// Expect that the target texture has the original data
verifyResults2D(target, originalData);
// Expect that the source texture has the updated data
verifyResults2D(source, updateData);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &target);
}
// Test that respecifying a level of the target texture orphans it and keeps a copy of the EGLimage
// data
TEST_P(ImageTest, RespecificationOfOtherLevel)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
GLubyte originalData[2 * 2 * 4] = {
255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255,
};
GLubyte updateData[2 * 2 * 4] = {
0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255,
};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(2, 2, GL_RGBA, GL_UNSIGNED_BYTE, originalData, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target and source textures have the original data
verifyResults2D(source, originalData);
verifyResults2D(target, originalData);
// Add a new mipLevel to the target, orphaning it
glBindTexture(GL_TEXTURE_2D, target);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, originalData);
EXPECT_GL_NO_ERROR();
// Expect that the target and source textures still have the original data
verifyResults2D(source, originalData);
verifyResults2D(target, originalData);
// Update the source's data
glBindTexture(GL_TEXTURE_2D, source);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, updateData);
// Expect that the target still has the original data and source has the updated data
verifyResults2D(source, updateData);
verifyResults2D(target, originalData);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &target);
}
// Update the data of the source and target textures. All image siblings should have the new data.
TEST_P(ImageTest, UpdatedData)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
GLubyte originalData[4] = {255, 0, 255, 255};
GLubyte updateData[4] = {0, 255, 0, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, originalData, &source, &image);
// Create multiple targets
GLuint targetTexture;
createEGLImageTargetTexture2D(image, &targetTexture);
GLuint targetRenderbuffer;
createEGLImageTargetRenderbuffer(image, &targetRenderbuffer);
// Expect that both the source and targets have the original data
verifyResults2D(source, originalData);
verifyResults2D(targetTexture, originalData);
verifyResultsRenderbuffer(targetRenderbuffer, originalData);
// Update the data of the source
glBindTexture(GL_TEXTURE_2D, source);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, updateData);
// Expect that both the source and targets have the updated data
verifyResults2D(source, updateData);
verifyResults2D(targetTexture, updateData);
verifyResultsRenderbuffer(targetRenderbuffer, updateData);
// Update the data of the target back to the original data
glBindTexture(GL_TEXTURE_2D, targetTexture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, originalData);
// Expect that both the source and targets have the original data again
verifyResults2D(source, originalData);
verifyResults2D(targetTexture, originalData);
verifyResultsRenderbuffer(targetRenderbuffer, originalData);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &targetTexture);
glDeleteRenderbuffers(1, &targetRenderbuffer);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(ImageTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL());
}
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