mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 00:35:44 +00:00
e43dca7804
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 --HG-- rename : mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/gfx/ISurfaceAllocator.aidl => mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/gfx/SyncConfig.aidl extra : moz-landing-system : lando
328 lines
10 KiB
C++
328 lines
10 KiB
C++
/* -*- 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 */
|