blob: 6107f3383cc9892c04760e5aac299a1216a6acff [file] [log] [blame]
//
// 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.
//
// 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/Context.h"
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/Texture.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/EGLImplFactory.h"
#include "libANGLE/renderer/ImageImpl.h"
namespace egl
{
namespace
{
gl::ImageIndex GetImageIndex(EGLenum eglTarget, const egl::AttributeMap &attribs)
{
if (!IsTextureTarget(eglTarget))
{
return gl::ImageIndex();
}
gl::TextureTarget target = egl_gl::EGLImageTargetToTextureTarget(eglTarget);
GLint mip = static_cast<GLint>(attribs.get(EGL_GL_TEXTURE_LEVEL_KHR, 0));
GLint layer = static_cast<GLint>(attribs.get(EGL_GL_TEXTURE_ZOFFSET_KHR, 0));
if (target == gl::TextureTarget::_3D)
{
return gl::ImageIndex::Make3D(mip, layer);
}
else
{
ASSERT(layer == 0);
return gl::ImageIndex::MakeFromTarget(target, mip, 1);
}
}
const Display *DisplayFromContext(const gl::Context *context)
{
return (context ? context->getDisplay() : nullptr);
}
angle::SubjectIndex kExternalImageImplSubjectIndex = 0;
} // anonymous namespace
ImageSibling::ImageSibling() : FramebufferAttachmentObject(), 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.
// Child class should orphan images before destruction.
ASSERT(mSourcesOf.empty());
ASSERT(mTargetOf.get() == nullptr);
}
void ImageSibling::setTargetImage(const gl::Context *context, egl::Image *imageTarget)
{
ASSERT(imageTarget != nullptr);
mTargetOf.set(DisplayFromContext(context), imageTarget);
imageTarget->addTargetSibling(this);
}
angle::Result ImageSibling::orphanImages(const gl::Context *context,
RefCountObjectReleaser<Image> *outReleaseImage)
{
ASSERT(outReleaseImage != nullptr);
if (mTargetOf.get() != nullptr)
{
// Can't be a target and have sources.
ASSERT(mSourcesOf.empty());
ANGLE_TRY(mTargetOf->orphanSibling(context, this));
*outReleaseImage = mTargetOf.set(DisplayFromContext(context), nullptr);
}
else
{
for (Image *sourceImage : mSourcesOf)
{
ANGLE_TRY(sourceImage->orphanSibling(context, this));
}
mSourcesOf.clear();
}
return angle::Result::Continue;
}
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);
}
bool ImageSibling::isEGLImageTarget() const
{
return (mTargetOf.get() != nullptr);
}
gl::InitState ImageSibling::sourceEGLImageInitState() const
{
ASSERT(isEGLImageTarget());
return mTargetOf->sourceInitState();
}
void ImageSibling::setSourceEGLImageInitState(gl::InitState initState) const
{
ASSERT(isEGLImageTarget());
mTargetOf->setInitState(initState);
}
bool ImageSibling::isRenderable(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex) const
{
ASSERT(isEGLImageTarget());
return mTargetOf->isRenderable(context);
}
bool ImageSibling::isYUV() const
{
return mTargetOf.get() && mTargetOf->isYUV();
}
bool ImageSibling::hasProtectedContent() const
{
return mTargetOf.get() && mTargetOf->hasProtectedContent();
}
void ImageSibling::notifySiblings(angle::SubjectMessage message)
{
if (mTargetOf.get())
{
mTargetOf->notifySiblings(this, message);
}
for (Image *source : mSourcesOf)
{
source->notifySiblings(this, message);
}
}
ExternalImageSibling::ExternalImageSibling(rx::EGLImplFactory *factory,
const gl::Context *context,
EGLenum target,
EGLClientBuffer buffer,
const AttributeMap &attribs)
: mImplementation(factory->createExternalImageSibling(context, target, buffer, attribs)),
mImplObserverBinding(this, kExternalImageImplSubjectIndex)
{
mImplObserverBinding.bind(mImplementation.get());
}
ExternalImageSibling::~ExternalImageSibling() = default;
void ExternalImageSibling::onDestroy(const egl::Display *display)
{
mImplementation->onDestroy(display);
}
Error ExternalImageSibling::initialize(const egl::Display *display)
{
return mImplementation->initialize(display);
}
gl::Extents ExternalImageSibling::getAttachmentSize(const gl::ImageIndex &imageIndex) const
{
return mImplementation->getSize();
}
gl::Format ExternalImageSibling::getAttachmentFormat(GLenum binding,
const gl::ImageIndex &imageIndex) const
{
return mImplementation->getFormat();
}
GLsizei ExternalImageSibling::getAttachmentSamples(const gl::ImageIndex &imageIndex) const
{
return static_cast<GLsizei>(mImplementation->getSamples());
}
GLuint ExternalImageSibling::getLevelCount() const
{
return static_cast<GLuint>(mImplementation->getLevelCount());
}
bool ExternalImageSibling::isRenderable(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex) const
{
return mImplementation->isRenderable(context);
}
bool ExternalImageSibling::isTextureable(const gl::Context *context) const
{
return mImplementation->isTexturable(context);
}
bool ExternalImageSibling::isYUV() const
{
return mImplementation->isYUV();
}
bool ExternalImageSibling::isCubeMap() const
{
return mImplementation->isCubeMap();
}
bool ExternalImageSibling::hasProtectedContent() const
{
return mImplementation->hasProtectedContent();
}
void ExternalImageSibling::onAttach(const gl::Context *context, rx::Serial framebufferSerial) {}
void ExternalImageSibling::onDetach(const gl::Context *context, rx::Serial framebufferSerial) {}
GLuint ExternalImageSibling::getId() const
{
UNREACHABLE();
return 0;
}
gl::InitState ExternalImageSibling::initState(GLenum binding,
const gl::ImageIndex &imageIndex) const
{
return gl::InitState::Initialized;
}
void ExternalImageSibling::setInitState(GLenum binding,
const gl::ImageIndex &imageIndex,
gl::InitState initState)
{}
rx::ExternalImageSiblingImpl *ExternalImageSibling::getImplementation() const
{
return mImplementation.get();
}
void ExternalImageSibling::onSubjectStateChange(angle::SubjectIndex index,
angle::SubjectMessage message)
{
onStateChange(message);
}
rx::FramebufferAttachmentObjectImpl *ExternalImageSibling::getAttachmentImpl() const
{
return mImplementation.get();
}
ImageState::ImageState(EGLenum target, ImageSibling *buffer, const AttributeMap &attribs)
: label(nullptr),
target(target),
imageIndex(GetImageIndex(target, attribs)),
source(buffer),
targets(),
format(GL_NONE),
yuv(false),
cubeMap(false),
size(),
samples(),
levelCount(1),
sourceType(target),
colorspace(
static_cast<EGLenum>(attribs.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_DEFAULT_EXT))),
hasProtectedContent(static_cast<bool>(attribs.get(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE)))
{}
ImageState::~ImageState() {}
Image::Image(rx::EGLImplFactory *factory,
const gl::Context *context,
EGLenum target,
ImageSibling *buffer,
const AttributeMap &attribs)
: mState(target, buffer, attribs),
mImplementation(factory->createImage(mState, context, target, attribs)),
mOrphanedAndNeedsInit(false)
{
ASSERT(mImplementation != nullptr);
ASSERT(buffer != nullptr);
mState.source->addImageSource(this);
}
void Image::onDestroy(const Display *display)
{
// All targets should hold a ref to the egl image and it should not be deleted until there are
// no siblings left.
ASSERT(mState.targets.empty());
// Make sure the implementation gets a chance to clean up before we delete the source.
mImplementation->onDestroy(display);
// Tell the source that it is no longer used by this image
if (mState.source != nullptr)
{
mState.source->removeImageSource(this);
// If the source is an external object, delete it
if (IsExternalImageTarget(mState.sourceType))
{
ExternalImageSibling *externalSibling = rx::GetAs<ExternalImageSibling>(mState.source);
externalSibling->onDestroy(display);
delete externalSibling;
}
mState.source = nullptr;
}
}
Image::~Image()
{
SafeDelete(mImplementation);
}
void Image::setLabel(EGLLabelKHR label)
{
mState.label = label;
}
EGLLabelKHR Image::getLabel() const
{
return mState.label;
}
void Image::addTargetSibling(ImageSibling *sibling)
{
mState.targets.insert(sibling);
}
angle::Result Image::orphanSibling(const gl::Context *context, ImageSibling *sibling)
{
ASSERT(sibling != nullptr);
// notify impl
ANGLE_TRY(mImplementation->orphan(context, sibling));
if (mState.source == sibling)
{
// The external source of an image cannot be redefined so it cannot be orphaned.
ASSERT(!IsExternalImageTarget(mState.sourceType));
// If the sibling is the source, it cannot be a target.
ASSERT(mState.targets.find(sibling) == mState.targets.end());
mState.source = nullptr;
mOrphanedAndNeedsInit =
(sibling->initState(GL_NONE, mState.imageIndex) == gl::InitState::MayNeedInit);
}
else
{
mState.targets.erase(sibling);
}
return angle::Result::Continue;
}
const gl::Format &Image::getFormat() const
{
return mState.format;
}
bool Image::isRenderable(const gl::Context *context) const
{
if (IsTextureTarget(mState.sourceType))
{
return mState.format.info->textureAttachmentSupport(context->getClientVersion(),
context->getExtensions());
}
else if (IsRenderbufferTarget(mState.sourceType))
{
return mState.format.info->renderbufferSupport(context->getClientVersion(),
context->getExtensions());
}
else if (IsExternalImageTarget(mState.sourceType))
{
ASSERT(mState.source != nullptr);
return mState.source->isRenderable(context, GL_NONE, gl::ImageIndex());
}
UNREACHABLE();
return false;
}
bool Image::isTexturable(const gl::Context *context) const
{
if (IsTextureTarget(mState.sourceType))
{
return mState.format.info->textureSupport(context->getClientVersion(),
context->getExtensions());
}
else if (IsRenderbufferTarget(mState.sourceType))
{
return true;
}
else if (IsExternalImageTarget(mState.sourceType))
{
ASSERT(mState.source != nullptr);
return rx::GetAs<ExternalImageSibling>(mState.source)->isTextureable(context);
}
UNREACHABLE();
return false;
}
bool Image::isYUV() const
{
return mState.yuv;
}
bool Image::isCubeMap() const
{
return mState.cubeMap;
}
size_t Image::getWidth() const
{
return mState.size.width;
}
size_t Image::getHeight() const
{
return mState.size.height;
}
const gl::Extents &Image::getExtents() const
{
return mState.size;
}
bool Image::isLayered() const
{
return mState.imageIndex.isLayered();
}
size_t Image::getSamples() const
{
return mState.samples;
}
GLuint Image::getLevelCount() const
{
return mState.levelCount;
}
bool Image::hasProtectedContent() const
{
return mState.hasProtectedContent;
}
rx::ImageImpl *Image::getImplementation() const
{
return mImplementation;
}
Error Image::initialize(const Display *display)
{
if (IsExternalImageTarget(mState.sourceType))
{
ExternalImageSibling *externalSibling = rx::GetAs<ExternalImageSibling>(mState.source);
ANGLE_TRY(externalSibling->initialize(display));
mState.hasProtectedContent = externalSibling->hasProtectedContent();
mState.levelCount = externalSibling->getLevelCount();
mState.cubeMap = externalSibling->isCubeMap();
// External siblings can be YUV
mState.yuv = externalSibling->isYUV();
}
mState.format = mState.source->getAttachmentFormat(GL_NONE, mState.imageIndex);
if (mState.colorspace != EGL_GL_COLORSPACE_DEFAULT_EXT)
{
GLenum nonLinearFormat = mState.format.info->sizedInternalFormat;
if (!gl::ColorspaceFormatOverride(mState.colorspace, &nonLinearFormat))
{
// the colorspace format is not supported
return egl::EglBadMatch();
}
mState.format = gl::Format(nonLinearFormat);
}
if (!IsExternalImageTarget(mState.sourceType))
{
// Account for the fact that GL_ANGLE_yuv_internal_format extension maybe enabled,
// in which case the internal format itself could be YUV.
mState.yuv = gl::IsYuvFormat(mState.format.info->sizedInternalFormat);
}
mState.size = mState.source->getAttachmentSize(mState.imageIndex);
mState.samples = mState.source->getAttachmentSamples(mState.imageIndex);
if (IsTextureTarget(mState.sourceType))
{
mState.size.depth = 1;
}
return mImplementation->initialize(display);
}
bool Image::orphaned() const
{
return (mState.source == nullptr);
}
gl::InitState Image::sourceInitState() const
{
if (orphaned())
{
return mOrphanedAndNeedsInit ? gl::InitState::MayNeedInit : gl::InitState::Initialized;
}
return mState.source->initState(GL_NONE, mState.imageIndex);
}
void Image::setInitState(gl::InitState initState)
{
if (orphaned())
{
mOrphanedAndNeedsInit = false;
}
return mState.source->setInitState(GL_NONE, mState.imageIndex, initState);
}
Error Image::exportVkImage(void *vkImage, void *vkImageCreateInfo)
{
return mImplementation->exportVkImage(vkImage, vkImageCreateInfo);
}
void Image::notifySiblings(const ImageSibling *notifier, angle::SubjectMessage message)
{
if (mState.source && mState.source != notifier)
{
mState.source->onSubjectStateChange(rx::kTextureImageSiblingMessageIndex, message);
}
for (ImageSibling *target : mState.targets)
{
if (target != notifier)
{
target->onSubjectStateChange(rx::kTextureImageSiblingMessageIndex, message);
}
}
}
} // namespace egl