Commit a840617a by Geoff Lang

Implement the egl and gl layers of EGL Image.

Add end2end tests and unittests. BUG=angleproject:970 Change-Id: Ie8306971730a793f08dfd09ead1bfd6ff3e4623d Reviewed-on: https://chromium-review.googlesource.com/291260Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Tested-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 4274f7d2
......@@ -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)
std::string getTempPath()
{
......
......@@ -9,6 +9,9 @@
#ifndef COMMON_UTILITIES_H_
#define COMMON_UTILITIES_H_
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "angle_gl.h"
#include <string>
#include <math.h>
......@@ -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)
std::string getTempPath();
void writeFile(const char* path, const void* data, size_t size);
......
......@@ -22,11 +22,14 @@
#include "common/debug.h"
#include "common/mathutil.h"
#include "common/platform.h"
#include "common/utilities.h"
#include "libANGLE/Context.h"
#include "libANGLE/Device.h"
#include "libANGLE/histogram_macros.h"
#include "libANGLE/Image.h"
#include "libANGLE/Surface.h"
#include "libANGLE/renderer/DisplayImpl.h"
#include "libANGLE/renderer/ImageImpl.h"
#include "third_party/trace_event/trace_event.h"
#if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11)
......@@ -303,6 +306,11 @@ void Display::terminate()
destroyContext(*mContextSet.begin());
}
while (!mImageSet.empty())
{
destroyImage(*mImageSet.begin());
}
mConfigSet.clear();
mImplementation->terminate();
......@@ -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);
}
......@@ -599,7 +639,10 @@ void Display::destroySurface(Surface *surface)
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)
......@@ -653,6 +696,11 @@ bool Display::isValidSurface(Surface *surface) const
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)
{
WindowSurfaceMap *windowSurfaces = GetWindowSurfaces();
......
......@@ -79,6 +79,7 @@ class Display final : angle::NonCopyable
bool isValidConfig(const Config *config) const;
bool isValidContext(gl::Context *context) const;
bool isValidSurface(egl::Surface *surface) const;
bool isValidImage(const Image *image) const;
bool isValidNativeWindow(EGLNativeWindowType window) const;
static bool isValidDisplay(const egl::Display *display);
......@@ -121,6 +122,9 @@ class Display final : angle::NonCopyable
typedef std::set<gl::Context*> ContextSet;
ContextSet mContextSet;
typedef std::set<Image *> ImageSet;
ImageSet mImageSet;
bool mInitialized;
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 @@
#include "common/utilities.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Image.h"
#include "libANGLE/Texture.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/d3d/RenderTargetD3D.h"
namespace gl
{
Renderbuffer::Renderbuffer(rx::RenderbufferImpl *impl, GLuint id)
: FramebufferAttachmentObject(id),
mRenderbuffer(impl),
mWidth(0),
mHeight(0),
mInternalFormat(GL_RGBA4),
mSamples(0)
: egl::ImageSibling(id),
mRenderbuffer(impl),
mWidth(0),
mHeight(0),
mInternalFormat(GL_RGBA4),
mSamples(0)
{
}
......@@ -36,6 +36,8 @@ Renderbuffer::~Renderbuffer()
Error Renderbuffer::setStorage(GLenum internalformat, size_t width, size_t height)
{
orphanImages();
Error error = mRenderbuffer->setStorage(internalformat, width, height);
if (error.isError())
{
......@@ -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)
{
orphanImages();
Error error = mRenderbuffer->setStorageMultisample(samples, internalformat, width, height);
if (error.isError())
{
......@@ -66,6 +70,26 @@ Error Renderbuffer::setStorageMultisample(size_t samples, GLenum internalformat,
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()
{
ASSERT(mRenderbuffer);
......
......@@ -15,6 +15,7 @@
#include "common/angleutils.h"
#include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Image.h"
#include "libANGLE/renderer/RenderbufferImpl.h"
namespace gl
......@@ -24,7 +25,7 @@ namespace gl
// FramebufferAttachment and Framebuffer for how they are applied to an FBO via an
// attachment point.
class Renderbuffer : public FramebufferAttachmentObject
class Renderbuffer : public egl::ImageSibling
{
public:
Renderbuffer(rx::RenderbufferImpl *impl, GLuint id);
......@@ -32,6 +33,7 @@ class Renderbuffer : public FramebufferAttachmentObject
Error setStorage(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();
const rx::RenderbufferImpl *getImplementation() const;
......
......@@ -12,6 +12,7 @@
#include "common/utilities.h"
#include "libANGLE/Config.h"
#include "libANGLE/Data.h"
#include "libANGLE/Image.h"
#include "libANGLE/Surface.h"
#include "libANGLE/formatutils.h"
......@@ -46,7 +47,7 @@ static size_t GetImageDescIndex(GLenum target, size_t level)
}
Texture::Texture(rx::TextureImpl *impl, GLuint id, GLenum target)
: FramebufferAttachmentObject(id),
: egl::ImageSibling(id),
mTexture(impl),
mUsage(GL_NONE),
mImmutableLevelCount(0),
......@@ -127,6 +128,11 @@ bool Texture::isSamplerComplete(const SamplerState &samplerState, const Data &da
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.
bool Texture::isCubeComplete() const
{
......@@ -152,6 +158,22 @@ bool Texture::isCubeComplete() const
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
{
return (mImmutableLevelCount > 0);
......@@ -162,6 +184,11 @@ int Texture::immutableLevelCount()
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,
const PixelUnpackState &unpack, const uint8_t *pixels)
{
......@@ -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
releaseTexImageInternal();
orphanImages();
Error error = mTexture->setImage(target, level, internalFormat, size, format, type, unpack, pixels);
if (error.isError())
......@@ -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
releaseTexImageInternal();
orphanImages();
Error error = mTexture->setCompressedImage(target, level, internalFormat, size, unpack, imageSize, pixels);
if (error.isError())
......@@ -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
releaseTexImageInternal();
orphanImages();
Error error = mTexture->copyImage(target, level, sourceArea, internalFormat, source);
if (error.isError())
......@@ -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
releaseTexImageInternal();
orphanImages();
Error error = mTexture->setStorage(target, levels, internalFormat, size);
if (error.isError())
......@@ -270,6 +301,13 @@ Error Texture::generateMipmaps()
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after
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());
if (error.isError())
{
......@@ -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
{
const ImageDesc &baseImageDesc = getImageDesc(getBaseImageTarget(), 0);
if (mTarget == GL_TEXTURE_3D)
{
return log2(std::max(std::max(baseImageDesc.size.width, baseImageDesc.size.height), baseImageDesc.size.depth)) + 1;
}
else
// Release from previous calls to eglBindTexImage, to avoid calling the Impl after
releaseTexImageInternal();
orphanImages();
Error error = mTexture->setEGLImageTarget(target, imageTarget);
if (error.isError())
{
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
......@@ -481,7 +533,7 @@ bool Texture::computeSamplerCompleteness(const 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);
......@@ -525,13 +577,13 @@ bool Texture::computeLevelCompleteness(GLenum target, size_t level, const gl::Sa
return false;
}
// The base image level is complete if the width and height are positive
if (level == 0)
const ImageDesc &levelImageDesc = getImageDesc(target, level);
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)
{
return false;
......
......@@ -18,6 +18,7 @@
#include "libANGLE/Constants.h"
#include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Image.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/TextureImpl.h"
......@@ -33,7 +34,7 @@ struct Data;
bool IsMipmapFiltered(const gl::SamplerState &samplerState);
class Texture final : public FramebufferAttachmentObject
class Texture final : public egl::ImageSibling
{
public:
Texture(rx::TextureImpl *impl, GLuint id, GLenum target);
......@@ -54,7 +55,9 @@ class Texture final : public FramebufferAttachmentObject
GLenum getInternalFormat(GLenum target, size_t level) const;
bool isSamplerComplete(const SamplerState &samplerState, const Data &data) const;
bool isMipmapComplete() 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,
const PixelUnpackState &unpack, const uint8_t *pixels);
......@@ -73,11 +76,15 @@ class Texture final : public FramebufferAttachmentObject
virtual Error setStorage(GLenum target, size_t levels, GLenum internalFormat, const Extents &size);
Error setEGLImageTarget(GLenum target, egl::Image *imageTarget);
virtual Error generateMipmaps();
bool isImmutable() const;
GLsizei immutableLevelCount();
egl::Surface *getBoundSurface() const;
rx::TextureImpl *getImplementation() { return mTexture; }
const rx::TextureImpl *getImplementation() const { return mTexture; }
......@@ -114,7 +121,6 @@ class Texture final : public FramebufferAttachmentObject
};
GLenum getBaseImageTarget() const;
size_t getExpectedMipLevels() const;
bool computeSamplerCompleteness(const SamplerState &samplerState, const Data &data) 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 @@
#include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h"
namespace egl
{
class Image;
}
namespace rx
{
......@@ -25,6 +30,7 @@ class RenderbufferImpl : public FramebufferAttachmentObjectImpl
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 setStorageEGLImageTarget(egl::Image *image) = 0;
};
}
......
......@@ -11,6 +11,7 @@
#include "gmock/gmock.h"
#include "libANGLE/Image.h"
#include "libANGLE/renderer/RenderbufferImpl.h"
namespace rx
......@@ -22,6 +23,7 @@ class MockRenderbufferImpl : public RenderbufferImpl
~MockRenderbufferImpl() override { destructor(); }
MOCK_METHOD3(setStorage, gl::Error(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 **));
......
......@@ -20,6 +20,7 @@
namespace egl
{
class Surface;
class Image;
}
namespace gl
......@@ -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 setEGLImageTarget(GLenum target, egl::Image *image) = 0;
virtual gl::Error generateMipmaps(const gl::SamplerState &samplerState) = 0;
virtual void bindTexImage(egl::Surface *surface) = 0;
......
......@@ -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(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_METHOD2(setEGLImageTarget, gl::Error(GLenum, egl::Image *));
MOCK_METHOD1(generateMipmaps, gl::Error(const gl::SamplerState &));
MOCK_METHOD1(bindTexImage, void(egl::Surface *));
MOCK_METHOD0(releaseTexImage, void(void));
......
......@@ -65,6 +65,12 @@ gl::Error RenderbufferD3D::setStorageMultisample(size_t samples, GLenum internal
return gl::Error(GL_NO_ERROR);
}
gl::Error RenderbufferD3D::setStorageEGLImageTarget(egl::Image *image)
{
UNIMPLEMENTED();
return gl::Error(GL_NO_ERROR);
}
RenderTargetD3D *RenderbufferD3D::getRenderTarget()
{
return mRenderTarget;
......
......@@ -26,11 +26,14 @@ class RenderbufferD3D : public RenderbufferImpl
RenderbufferD3D(RendererD3D *renderer);
virtual ~RenderbufferD3D();
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;
gl::Error setStorage(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();
gl::Error getAttachmentRenderTarget(const gl::FramebufferAttachment::Target &target,
FramebufferAttachmentRenderTarget **rtOut) override;
......
......@@ -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()
{
// 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
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,
const gl::PixelUnpackState &unpack, const uint8_t *pixels)
{
......@@ -1883,6 +1895,12 @@ bool TextureD3D_3D::isDepth(GLint level) const
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,
const gl::PixelUnpackState &unpack, const uint8_t *pixels)
{
......@@ -2411,6 +2429,12 @@ bool TextureD3D_2DArray::isDepth(GLint level) const
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,
const gl::PixelUnpackState &unpack, const uint8_t *pixels)
{
......
......@@ -147,6 +147,8 @@ class TextureD3D_2D : public TextureD3D
virtual void bindTexImage(egl::Surface *surface);
virtual void releaseTexImage();
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::ImageIndexIterator imageIterator() const;
......@@ -209,6 +211,8 @@ class TextureD3D_Cube : public TextureD3D
virtual void bindTexImage(egl::Surface *surface);
virtual void releaseTexImage();
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::ImageIndexIterator imageIterator() const;
......@@ -270,6 +274,8 @@ class TextureD3D_3D : public TextureD3D
virtual void bindTexImage(egl::Surface *surface);
virtual void releaseTexImage();
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::ImageIndexIterator imageIterator() const;
......@@ -329,6 +335,8 @@ class TextureD3D_2DArray : public TextureD3D
virtual void bindTexImage(egl::Surface *surface);
virtual void releaseTexImage();
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::ImageIndexIterator imageIterator() const;
......
......@@ -81,6 +81,12 @@ gl::Error RenderbufferGL::setStorageMultisample(size_t samples, GLenum internalf
return gl::Error(GL_NO_ERROR);
}
gl::Error RenderbufferGL::setStorageEGLImageTarget(egl::Image *image)
{
UNIMPLEMENTED();
return gl::Error(GL_INVALID_OPERATION);
}
GLuint RenderbufferGL::getRenderbufferID() const
{
return mRenderbufferID;
......
......@@ -34,6 +34,7 @@ class RenderbufferGL : public RenderbufferImpl
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 setStorageEGLImageTarget(egl::Image *image) override;
GLuint getRenderbufferID() const;
......
......@@ -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>
static inline void SyncSamplerStateMember(const FunctionsGL *functions, const gl::SamplerState &newState,
gl::SamplerState &curState, GLenum textureType, GLenum name,
......
......@@ -52,6 +52,8 @@ class TextureGL : public TextureImpl
void bindTexImage(egl::Surface *surface) override;
void releaseTexImage() override;
gl::Error setEGLImageTarget(GLenum target, egl::Image *image) override;
void syncSamplerState(const gl::SamplerState &samplerState) const;
GLuint getTextureID() const;
......
......@@ -32,6 +32,7 @@ Error ValidateDisplay(const Display *display);
Error ValidateSurface(const Display *display, Surface *surface);
Error ValidateConfig(const Display *display, const Config *config);
Error ValidateContext(const Display *display, gl::Context *context);
Error ValidateImage(const Display *display, const Image *image);
// Entry point validation
Error ValidateCreateContext(Display *display, Config *configuration, gl::Context *shareContext,
......
......@@ -10,10 +10,12 @@
#include "libANGLE/validationES2.h"
#include "libANGLE/validationES3.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/Texture.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/Image.h"
#include "libANGLE/Query.h"
#include "libANGLE/Program.h"
#include "libANGLE/Uniform.h"
......@@ -2006,7 +2008,43 @@ bool ValidateEGLImageTargetTexture2DOES(Context *context,
GLenum target,
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;
}
......@@ -2015,7 +2053,36 @@ bool ValidateEGLImageTargetRenderbufferStorageOES(Context *context,
GLenum target,
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;
}
}
......@@ -82,6 +82,8 @@
'libANGLE/FramebufferAttachment.h',
'libANGLE/HandleAllocator.cpp',
'libANGLE/HandleAllocator.h',
'libANGLE/Image.h',
'libANGLE/Image.cpp',
'libANGLE/ImageIndex.h',
'libANGLE/ImageIndex.cpp',
'libANGLE/IndexRangeCache.cpp',
......@@ -136,6 +138,7 @@
'libANGLE/renderer/FenceNVImpl.h',
'libANGLE/renderer/FenceSyncImpl.h',
'libANGLE/renderer/FramebufferImpl.h',
'libANGLE/renderer/ImageImpl.h',
'libANGLE/renderer/ImplFactory.h',
'libANGLE/renderer/ProgramImpl.cpp',
'libANGLE/renderer/ProgramImpl.h',
......
......@@ -1196,7 +1196,13 @@ ANGLE_EXPORT void GL_APIENTRY EGLImageTargetTexture2DOES(GLenum target, GLeglIma
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
return;
}
UNIMPLEMENTED();
Renderbuffer *renderbuffer = context->getState().getCurrentRenderbuffer();
Error error = renderbuffer->setStorageEGLImageTarget(imageObject);
if (error.isError())
{
context->recordError(error);
return;
}
}
}
}
......@@ -28,6 +28,7 @@
'<(angle_path)/src/tests/gl_tests/FramebufferFormatsTest.cpp',
'<(angle_path)/src/tests/gl_tests/FramebufferRenderMipmapTest.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/IndexBufferOffsetTest.cpp',
'<(angle_path)/src/tests/gl_tests/IndexedPointsTest.cpp',
......
......@@ -24,6 +24,7 @@
'<(angle_path)/src/libANGLE/Config_unittest.cpp',
'<(angle_path)/src/libANGLE/Fence_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/Program_unittest.cpp',
'<(angle_path)/src/libANGLE/ResourceManager_unittest.cpp',
......@@ -31,6 +32,7 @@
'<(angle_path)/src/libANGLE/TransformFeedback_unittest.cpp',
'<(angle_path)/src/libANGLE/renderer/BufferImpl_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/TransformFeedbackImpl_mock.h',
'<(angle_path)/src/tests/angle_unittests_utils.h',
......
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