Edit

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

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2020-08-14 22:48:15
    Hash : 185d9d08
    Message : Re-land "Feedback Loop Redesign 2/3: Track bound FBOs in Texture." Re-land fixes the crash when drawing with no bound Program executable. Currently we track feedback loops by counting the times a Texture is bound as a sampler or image in a particular context. This is a bit tricky because Texture bindings change frequently. Relative to the number of times we need to check for a feedback loop this causes excess overhead. Usually Framebuffers have a low number of Textures bound (in many cases just 1). And Textures aren't usually bound to many different FBOs. So instead of counting the number of times a Texture is bound as a sampler or image we will track the Framebuffers that the Texture is bound to. This CL adds a small vector class to gl::Texture which tracks all the Framebufer Serials of its bound Framebuffers. We can use this set to quickly check if there's any potential feedback loop between the a FBO and this Texture. We also update the feedback loop check to use this new method. We will be able to remove the old counting method when we switch the Vulkan feedback loop handling to use the new tracking in this CL. Bug: angleproject:4500 Bug: angleproject:4959 Change-Id: If2bd25b08298a99f5e64b4055137f9154b0f0860 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2365595 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@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);
    }
    
    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);
    }
    
    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),
          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;
    }
    
    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))
        {
            ANGLE_TRY(rx::GetAs<ExternalImageSibling>(mState.source)->initialize(display));
        }
    
        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