Edit

kc3-lang/angle/src/libANGLE/Image.cpp

Branch :

  • Show log

    Commit

  • Author : Geoff Lang
    Date : 2020-11-03 13:33:44
    Hash : 5b419533
    Message : GL: Implement EXT_YUV_target Add test coverage of YUV format sampling as RGB or directly as YUV and rendering as YUV using layout(yuv). Initializing YUV AHardwareBuffers requires Android API 29 so ANGLE must be compiled with: android32_ndk_api_level = 29 android64_ndk_api_level = 29 The following tests can still run with Android API 26 because they don't need to initialize the buffer: ImageTestES3.ClearYUVAHB ImageTestES3.YUVValidation Bug: angleproject:4852 Bug: b/172649538 Change-Id: I4fe9afb2a68fb827dc5a5732b213b5eb60d585ac Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2517562 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>

  • src/libANGLE/Image.cpp
  • //
    // 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)
    {
        if (mTargetOf.get() != nullptr)
        {
            // Can't be a target and have sources.
            ASSERT(mSourcesOf.empty());
    
            ANGLE_TRY(mTargetOf->orphanSibling(context, this));
            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();
    }
    
    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());
    }
    
    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();
    }
    
    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(const gl::ImageIndex &imageIndex) const
    {
        return gl::InitState::Initialized;
    }
    
    void ExternalImageSibling::setInitState(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),
          size(),
          samples(),
          sourceType(target),
          colorspace(
              static_cast<EGLenum>(attribs.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_DEFAULT_EXT)))
    {}
    
    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 orpahend.
            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(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;
    }
    
    size_t Image::getWidth() const
    {
        return mState.size.width;
    }
    
    size_t Image::getHeight() const
    {
        return mState.size.height;
    }
    
    bool Image::isLayered() const
    {
        return mState.imageIndex.isLayered();
    }
    
    size_t Image::getSamples() const
    {
        return mState.samples;
    }
    
    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));
    
            // Only 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);
        }
    
        mState.size    = mState.source->getAttachmentSize(mState.imageIndex);
        mState.samples = mState.source->getAttachmentSamples(mState.imageIndex);
    
        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(mState.imageIndex);
    }
    
    void Image::setInitState(gl::InitState initState)
    {
        if (orphaned())
        {
            mOrphanedAndNeedsInit = false;
        }
    
        return mState.source->setInitState(mState.imageIndex, initState);
    }
    
    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