// // 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(attribs.get(EGL_GL_TEXTURE_LEVEL_KHR, 0)); GLint layer = static_cast(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 *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::isCreatedWithAHB() const { return mTargetOf.get() && mTargetOf->isCreatedWithAHB(); } 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(mImplementation->getSamples()); } GLuint ExternalImageSibling::getLevelCount() const { return static_cast(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), format(GL_NONE), yuv(false), cubeMap(false), size(), samples(), levelCount(1), sourceType(target), colorspace( static_cast(attribs.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_DEFAULT_EXT))), hasProtectedContent(static_cast(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([&] { std::unique_lock lock(mState.targetsLock); return 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(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) { std::unique_lock lock(mState.targetsLock); 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([&] { std::unique_lock lock(mState.targetsLock); return mState.targets.find(sibling) == mState.targets.end(); }()); mState.source = nullptr; mOrphanedAndNeedsInit = (sibling->initState(GL_NONE, mState.imageIndex) == gl::InitState::MayNeedInit); } else { std::unique_lock lock(mState.targetsLock); 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(mState.source)->isTextureable(context); } UNREACHABLE(); return false; } bool Image::isYUV() const { return mState.yuv; } bool Image::isCreatedWithAHB() const { return mState.target == EGL_NATIVE_BUFFER_ANDROID; } 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(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); } std::unique_lock lock(mState.targetsLock); for (ImageSibling *target : mState.targets) { if (target != notifier) { target->onSubjectStateChange(rx::kTextureImageSiblingMessageIndex, message); } } } } // namespace egl