diff options
Diffstat (limited to 'gfx/gl/SharedSurfaceEGL.cpp')
-rw-r--r-- | gfx/gl/SharedSurfaceEGL.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/gfx/gl/SharedSurfaceEGL.cpp b/gfx/gl/SharedSurfaceEGL.cpp new file mode 100644 index 0000000000..09659ba519 --- /dev/null +++ b/gfx/gl/SharedSurfaceEGL.cpp @@ -0,0 +1,268 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 4; -*- */ +/* 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 "MozFramebuffer.h" +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc +#include "SharedSurface.h" + +#if defined(MOZ_WIDGET_ANDROID) +# include "AndroidNativeWindow.h" +# include "mozilla/java/SurfaceAllocatorWrappers.h" +# include "mozilla/java/GeckoSurfaceTextureWrappers.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +namespace mozilla { +namespace gl { + +static bool HasEglImageExtensions(const GLContextEGL& gl) { + const auto& egl = *(gl.mEgl); + return egl.HasKHRImageBase() && + egl.IsExtensionSupported(EGLExtension::KHR_gl_texture_2D_image) && + (gl.IsExtensionSupported(GLContext::OES_EGL_image_external) || + gl.IsExtensionSupported(GLContext::OES_EGL_image)); +} + +/*static*/ +UniquePtr<SurfaceFactory_EGLImage> SurfaceFactory_EGLImage::Create( + GLContext& gl_) { + auto& gl = *GLContextEGL::Cast(&gl_); + if (!HasEglImageExtensions(gl)) return nullptr; + + const auto partialDesc = PartialSharedSurfaceDesc{ + &gl, SharedSurfaceType::EGLImageShare, layers::TextureType::EGLImage, + false, // Can't recycle, as mSync changes never update TextureHost. + }; + return AsUnique(new SurfaceFactory_EGLImage(partialDesc)); +} + +// - + +/*static*/ +UniquePtr<SharedSurface_EGLImage> SharedSurface_EGLImage::Create( + const SharedSurfaceDesc& desc) { + const auto& gle = GLContextEGL::Cast(desc.gl); + const auto& context = gle->mContext; + const auto& egl = *(gle->mEgl); + + auto fb = MozFramebuffer::Create(desc.gl, desc.size, 0, false); + if (!fb) return nullptr; + + const auto buffer = reinterpret_cast<EGLClientBuffer>(fb->ColorTex()); + const auto image = + egl.fCreateImage(context, LOCAL_EGL_GL_TEXTURE_2D, buffer, nullptr); + if (!image) return nullptr; + + return AsUnique(new SharedSurface_EGLImage(desc, std::move(fb), image)); +} + +SharedSurface_EGLImage::SharedSurface_EGLImage(const SharedSurfaceDesc& desc, + UniquePtr<MozFramebuffer>&& fb, + const EGLImage image) + : SharedSurface(desc, std::move(fb)), + mMutex("SharedSurface_EGLImage mutex"), + mImage(image) {} + +SharedSurface_EGLImage::~SharedSurface_EGLImage() { + const auto& gle = GLContextEGL::Cast(mDesc.gl); + const auto& egl = gle->mEgl; + egl->fDestroyImage(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. + egl->fDestroySync(mSync); + mSync = 0; + } +} + +void SharedSurface_EGLImage::ProducerReleaseImpl() { + const auto& gl = GLContextEGL::Cast(mDesc.gl); + const auto& egl = gl->mEgl; + + MutexAutoLock lock(mMutex); + gl->MakeCurrent(); + + if (egl->IsExtensionSupported(EGLExtension::KHR_fence_sync) && + gl->IsExtensionSupported(GLContext::OES_EGL_sync)) { + if (mSync) { + MOZ_RELEASE_ASSERT(false, "GFX: Non-recycleable should not Fence twice."); + MOZ_ALWAYS_TRUE(egl->fDestroySync(mSync)); + mSync = 0; + } + + mSync = egl->fCreateSync(LOCAL_EGL_SYNC_FENCE, nullptr); + if (mSync) { + gl->fFlush(); + return; + } + } + + MOZ_ASSERT(!mSync); + gl->fFinish(); +} + +void SharedSurface_EGLImage::ProducerReadAcquireImpl() { + const auto& gle = GLContextEGL::Cast(mDesc.gl); + const auto& egl = gle->mEgl; + // Wait on the fence, because presumably we're going to want to read this + // surface + if (mSync) { + egl->fClientWaitSync(mSync, 0, LOCAL_EGL_FOREVER); + } +} + +Maybe<layers::SurfaceDescriptor> SharedSurface_EGLImage::ToSurfaceDescriptor() { + return Some(layers::EGLImageDescriptor((uintptr_t)mImage, (uintptr_t)mSync, + mDesc.size, true)); +} + +//////////////////////////////////////////////////////////////////////// + +#ifdef MOZ_WIDGET_ANDROID + +/*static*/ +UniquePtr<SharedSurface_SurfaceTexture> SharedSurface_SurfaceTexture::Create( + const SharedSurfaceDesc& desc) { + const auto& size = desc.size; + + jni::Object::LocalRef surfaceObj; + const bool useSingleBuffer = + desc.gl->Renderer() != GLRenderer::AndroidEmulator; + + if (useSingleBuffer) { + surfaceObj = + java::SurfaceAllocator::AcquireSurface(size.width, size.height, true); + } + + if (!surfaceObj) { + // Try multi-buffer mode + surfaceObj = + java::SurfaceAllocator::AcquireSurface(size.width, size.height, false); + } + + if (!surfaceObj) { + // Give up + NS_WARNING("Failed to allocate SurfaceTexture!"); + return nullptr; + } + const auto surface = java::GeckoSurface::Ref::From(surfaceObj); + + AndroidNativeWindow window(surface); + const auto& gle = GLContextEGL::Cast(desc.gl); + MOZ_ASSERT(gle); + const auto eglSurface = gle->CreateCompatibleSurface(window.NativeWindow()); + if (!eglSurface) return nullptr; + + return AsUnique(new SharedSurface_SurfaceTexture(desc, surface, eglSurface)); +} + +SharedSurface_SurfaceTexture::SharedSurface_SurfaceTexture( + const SharedSurfaceDesc& desc, java::GeckoSurface::Param surface, + const EGLSurface eglSurface) + : SharedSurface(desc, nullptr), + mSurface(surface), + mEglSurface(eglSurface), + mEglDisplay(GLContextEGL::Cast(desc.gl)->mEgl) {} + +SharedSurface_SurfaceTexture::~SharedSurface_SurfaceTexture() { + if (mOrigEglSurface) { + // We are about to destroy mEglSurface. + // Make sure gl->SetEGLSurfaceOverride() doesn't keep a reference + // to the surface. + UnlockProd(); + } + + std::shared_ptr<EglDisplay> display = mEglDisplay.lock(); + if (display) { + display->fDestroySurface(mEglSurface); + } + java::SurfaceAllocator::DisposeSurface(mSurface); +} + +void SharedSurface_SurfaceTexture::LockProdImpl() { + MOZ_RELEASE_ASSERT(mSurface->GetAvailable()); + + GLContextEGL* gl = GLContextEGL::Cast(mDesc.gl); + mOrigEglSurface = gl->GetEGLSurfaceOverride(); + gl->SetEGLSurfaceOverride(mEglSurface); +} + +void SharedSurface_SurfaceTexture::UnlockProdImpl() { + MOZ_RELEASE_ASSERT(mSurface->GetAvailable()); + + GLContextEGL* gl = GLContextEGL::Cast(mDesc.gl); + MOZ_ASSERT(gl->GetEGLSurfaceOverride() == mEglSurface); + + gl->SetEGLSurfaceOverride(mOrigEglSurface); + mOrigEglSurface = nullptr; +} + +void SharedSurface_SurfaceTexture::ProducerReadReleaseImpl() { + // This GeckoSurfaceTexture is not SurfaceTexture of this class's GeckoSurface + // when current process is content process. In this case, SurfaceTexture of + // this class's GeckoSurface does not exist in this process. It exists in + // compositor's process. Then GeckoSurfaceTexture in this process is a sync + // surface that copies back the SurfaceTextrure from compositor's process. It + // was added by Bug 1486659. Then SurfaceTexture::UpdateTexImage() becomes + // very heavy weight, since it does copy back the SurfaceTextrure from + // compositor's process. + java::GeckoSurfaceTexture::LocalRef surfaceTexture = + java::GeckoSurfaceTexture::Lookup(mSurface->GetHandle()); + if (!surfaceTexture) { + NS_ERROR("Didn't find GeckoSurfaceTexture in ProducerReadReleaseImpl"); + return; + } + surfaceTexture->UpdateTexImage(); + // Non single buffer mode Surface does not need ReleaseTexImage() call. + // When SurfaceTexture is sync Surface, it might not be single buffer mode. + if (surfaceTexture->IsSingleBuffer()) { + surfaceTexture->ReleaseTexImage(); + } +} + +void SharedSurface_SurfaceTexture::Commit() { + MOZ_RELEASE_ASSERT(mSurface->GetAvailable()); + + LockProdImpl(); + mDesc.gl->SwapBuffers(); + UnlockProdImpl(); + mSurface->SetAvailable(false); +} + +void SharedSurface_SurfaceTexture::WaitForBufferOwnership() { + mSurface->SetAvailable(true); +} + +bool SharedSurface_SurfaceTexture::IsBufferAvailable() const { + return mSurface->GetAvailable(); +} + +bool SharedSurface_SurfaceTexture::IsValid() const { + return !mSurface->IsReleased(); +} + +Maybe<layers::SurfaceDescriptor> +SharedSurface_SurfaceTexture::ToSurfaceDescriptor() { + return Some(layers::SurfaceTextureDescriptor( + mSurface->GetHandle(), mDesc.size, gfx::SurfaceFormat::R8G8B8A8, + false /* NOT continuous */, Nothing() /* Do not override transform */)); +} + +SurfaceFactory_SurfaceTexture::SurfaceFactory_SurfaceTexture(GLContext& gl) + : SurfaceFactory({&gl, SharedSurfaceType::AndroidSurfaceTexture, + layers::TextureType::AndroidNativeWindow, true}) {} + +#endif // MOZ_WIDGET_ANDROID + +} // namespace gl + +} /* namespace mozilla */ |