gfx/gl/SharedSurfaceEGL.cpp
author John Lin <jolin@mozilla.com>
Mon, 26 Nov 2018 19:41:31 +0000
changeset 504530 9c2834ca8823472c042a7c9bf10e06fadc5ee692
parent 489525 1b09c0419d137d79d316597078f7b106e21f3caa
child 504911 4a3f88e3629875813584f135f9e250b82eb4aa48
child 504917 b4662b6db1b34414494d070e33481193625403d1
permissions -rw-r--r--
Bug 1486659 - p3: copy texture contents for remote allocated Surface. r=snorp Child processes cannot access textures allocated in the parent process, which is needed by the compositor to render video elements efficiently. Unfortunately, Android doesn't expose Sufrace buffers (sharable across processes) in the SDK/NDK as other platforms, so we need to generate extra texture/surface in the child process and update texture images through the surface, which is passed to the parent process for the remote texture to copy its contents into. Differential Revision: https://phabricator.services.mozilla.com/D11939

/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "SharedSurfaceEGL.h"

#include "GLBlitHelper.h"
#include "GLContextEGL.h"
#include "GLContextProvider.h"
#include "GLLibraryEGL.h"
#include "GLReadTexImageHelper.h"
#include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
#include "SharedSurface.h"

namespace mozilla {
namespace gl {

/*static*/ UniquePtr<SharedSurface_EGLImage>
SharedSurface_EGLImage::Create(GLContext* prodGL,
                               const GLFormats& formats,
                               const gfx::IntSize& size,
                               bool hasAlpha,
                               EGLContext context)
{
    auto* egl = gl::GLLibraryEGL::Get();
    MOZ_ASSERT(egl);
    MOZ_ASSERT(context);

    UniquePtr<SharedSurface_EGLImage> ret;

    if (!HasExtensions(egl, prodGL)) {
        return ret;
    }

    MOZ_ALWAYS_TRUE(prodGL->MakeCurrent());
    GLuint prodTex = CreateTextureForOffscreen(prodGL, formats, size);
    if (!prodTex) {
        return ret;
    }

    EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(uintptr_t(prodTex));
    EGLImage image = egl->fCreateImage(egl->Display(), context,
                                       LOCAL_EGL_GL_TEXTURE_2D, buffer,
                                       nullptr);
    if (!image) {
        prodGL->fDeleteTextures(1, &prodTex);
        return ret;
    }

    ret.reset( new SharedSurface_EGLImage(prodGL, egl, size, hasAlpha,
                                          formats, prodTex, image) );
    return ret;
}

bool
SharedSurface_EGLImage::HasExtensions(GLLibraryEGL* egl, GLContext* gl)
{
    return egl->HasKHRImageBase() &&
           egl->IsExtensionSupported(GLLibraryEGL::KHR_gl_texture_2D_image) &&
           (gl->IsExtensionSupported(GLContext::OES_EGL_image_external) ||
            gl->IsExtensionSupported(GLContext::OES_EGL_image));
}

SharedSurface_EGLImage::SharedSurface_EGLImage(GLContext* gl,
                                               GLLibraryEGL* egl,
                                               const gfx::IntSize& size,
                                               bool hasAlpha,
                                               const GLFormats& formats,
                                               GLuint prodTex,
                                               EGLImage image)
    : SharedSurface(SharedSurfaceType::EGLImageShare,
                    AttachmentType::GLTexture,
                    gl,
                    size,
                    hasAlpha,
                    false) // Can't recycle, as mSync changes never update TextureHost.
    , mMutex("SharedSurface_EGLImage mutex")
    , mEGL(egl)
    , mFormats(formats)
    , mProdTex(prodTex)
    , mImage(image)
    , mSync(0)
{}

SharedSurface_EGLImage::~SharedSurface_EGLImage()
{
    mEGL->fDestroyImage(Display(), mImage);

    if (mSync) {
        // We can't call this unless we have the ext, but we will always have
        // the ext if we have something to destroy.
        mEGL->fDestroySync(Display(), mSync);
        mSync = 0;
    }

    if (!mGL || !mGL->MakeCurrent())
        return;

    mGL->fDeleteTextures(1, &mProdTex);
    mProdTex = 0;
}

void
SharedSurface_EGLImage::ProducerReleaseImpl()
{
    MutexAutoLock lock(mMutex);
    mGL->MakeCurrent();

    if (mEGL->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) &&
        mGL->IsExtensionSupported(GLContext::OES_EGL_sync))
    {
        if (mSync) {
            MOZ_RELEASE_ASSERT(false, "GFX: Non-recycleable should not Fence twice.");
            MOZ_ALWAYS_TRUE( mEGL->fDestroySync(Display(), mSync) );
            mSync = 0;
        }

        mSync = mEGL->fCreateSync(Display(),
                                  LOCAL_EGL_SYNC_FENCE,
                                  nullptr);
        if (mSync) {
            mGL->fFlush();
            return;
        }
    }

    MOZ_ASSERT(!mSync);
    mGL->fFinish();
}

void
SharedSurface_EGLImage::ProducerReadAcquireImpl()
{
    // Wait on the fence, because presumably we're going to want to read this surface
    if (mSync) {
        mEGL->fClientWaitSync(Display(), mSync, 0, LOCAL_EGL_FOREVER);
    }
}

EGLDisplay
SharedSurface_EGLImage::Display() const
{
    return mEGL->Display();
}

bool
SharedSurface_EGLImage::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
{
    *out_descriptor = layers::EGLImageDescriptor((uintptr_t)mImage, (uintptr_t)mSync,
                                                 mSize, mHasAlpha);
    return true;
}

bool
SharedSurface_EGLImage::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
{
    MOZ_ASSERT(out_surface);
    MOZ_ASSERT(NS_IsMainThread());
    auto* egl = gl::GLLibraryEGL::Get();
    return egl->ReadbackEGLImage(mImage, out_surface);
}

////////////////////////////////////////////////////////////////////////

/*static*/ UniquePtr<SurfaceFactory_EGLImage>
SurfaceFactory_EGLImage::Create(GLContext* prodGL, const SurfaceCaps& caps,
                                const RefPtr<layers::LayersIPCChannel>& allocator,
                                const layers::TextureFlags& flags)
{
    EGLContext context = GLContextEGL::Cast(prodGL)->mContext;

    typedef SurfaceFactory_EGLImage ptrT;
    UniquePtr<ptrT> ret;

    auto* egl = gl::GLLibraryEGL::Get();
    if (SharedSurface_EGLImage::HasExtensions(egl, prodGL)) {
        ret.reset( new ptrT(prodGL, caps, allocator, flags, context) );
    }

    return ret;
}

////////////////////////////////////////////////////////////////////////

#ifdef MOZ_WIDGET_ANDROID

/*static*/ UniquePtr<SharedSurface_SurfaceTexture>
SharedSurface_SurfaceTexture::Create(GLContext* prodGL,
                                     const GLFormats& formats,
                                     const gfx::IntSize& size,
                                     bool hasAlpha,
                                     java::GeckoSurface::Param surface)
{
    MOZ_ASSERT(surface);

    UniquePtr<SharedSurface_SurfaceTexture> ret;

    AndroidNativeWindow window(surface);
    GLContextEGL* egl = GLContextEGL::Cast(prodGL);
    MOZ_ASSERT(egl);
    EGLSurface eglSurface = egl->CreateCompatibleSurface(window.NativeWindow());
    if (!eglSurface) {
        return ret;
    }

    ret.reset(new SharedSurface_SurfaceTexture(prodGL, size, hasAlpha,
                                               formats, surface, eglSurface));
    return ret;
}

SharedSurface_SurfaceTexture::SharedSurface_SurfaceTexture(GLContext* gl,
                                                           const gfx::IntSize& size,
                                                           bool hasAlpha,
                                                           const GLFormats& formats,
                                                           java::GeckoSurface::Param surface,
                                                           EGLSurface eglSurface)
    : SharedSurface(SharedSurfaceType::AndroidSurfaceTexture,
                    AttachmentType::Screen,
                    gl,
                    size,
                    hasAlpha,
                    true)
    , mSurface(surface)
    , mEglSurface(eglSurface)
{
}

SharedSurface_SurfaceTexture::~SharedSurface_SurfaceTexture()
{
    GLContextProviderEGL::DestroyEGLSurface(mEglSurface);
    java::SurfaceAllocator::DisposeSurface(mSurface);
}

void
SharedSurface_SurfaceTexture::LockProdImpl()
{
    MOZ_RELEASE_ASSERT(mSurface->GetAvailable());

    GLContextEGL *gl = GLContextEGL::Cast(mGL);
    mOrigEglSurface = gl->GetEGLSurfaceOverride();
    gl->SetEGLSurfaceOverride(mEglSurface);
}

void
SharedSurface_SurfaceTexture::UnlockProdImpl()
{
    MOZ_RELEASE_ASSERT(mSurface->GetAvailable());

    GLContextEGL *gl = GLContextEGL::Cast(mGL);
    MOZ_ASSERT(gl->GetEGLSurfaceOverride() == mEglSurface);

    gl->SetEGLSurfaceOverride(mOrigEglSurface);
    mOrigEglSurface = nullptr;
}

void
SharedSurface_SurfaceTexture::Commit()
{
    MOZ_RELEASE_ASSERT(mSurface->GetAvailable());

    LockProdImpl();
    mGL->SwapBuffers();
    UnlockProdImpl();
    mSurface->SetAvailable(false);
}

void
SharedSurface_SurfaceTexture::WaitForBufferOwnership()
{
    mSurface->SetAvailable(true);
}

bool
SharedSurface_SurfaceTexture::IsBufferAvailable() const {
    return mSurface->GetAvailable();
}

bool
SharedSurface_SurfaceTexture::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
{
    *out_descriptor =
        layers::SurfaceTextureDescriptor(mSurface->GetImageHandle(),
                                         mSize,
                                         gfx::SurfaceFormat::R8G8B8A8,
                                         false /* NOT continuous */,
                                         false /* Do not ignore transform */);
    return true;
}

////////////////////////////////////////////////////////////////////////

/*static*/ UniquePtr<SurfaceFactory_SurfaceTexture>
SurfaceFactory_SurfaceTexture::Create(GLContext* prodGL, const SurfaceCaps& caps,
                                      const RefPtr<layers::LayersIPCChannel>& allocator,
                                      const layers::TextureFlags& flags)
{
    UniquePtr<SurfaceFactory_SurfaceTexture> ret(
        new SurfaceFactory_SurfaceTexture(prodGL, caps, allocator, flags));
    return ret;
}

UniquePtr<SharedSurface>
SurfaceFactory_SurfaceTexture::CreateShared(const gfx::IntSize& size)
{
    bool hasAlpha = mReadCaps.alpha;

    jni::Object::LocalRef surface = java::SurfaceAllocator::AcquireSurface(size.width, size.height, true);
    if (!surface) {
        // Try multi-buffer mode
        surface = java::SurfaceAllocator::AcquireSurface(size.width, size.height, false);
        if (!surface) {
            // Give up
            NS_WARNING("Failed to allocate SurfaceTexture!");
            return nullptr;
        }
    }

    return SharedSurface_SurfaceTexture::Create(mGL, mFormats, size, hasAlpha,
                                                java::GeckoSurface::Ref::From(surface));
}

#endif // MOZ_WIDGET_ANDROID

} // namespace gl

} /* namespace mozilla */