diff --git a/gfx/gl/AndroidSurfaceTexture.cpp b/gfx/gl/AndroidSurfaceTexture.cpp index fddf9bfac09a..dd7a0854d726 100644 --- a/gfx/gl/AndroidSurfaceTexture.cpp +++ b/gfx/gl/AndroidSurfaceTexture.cpp @@ -8,6 +8,13 @@ #include "AndroidSurfaceTexture.h" +#include "GeneratedJNINatives.h" + +#include "AndroidNativeWindow.h" +#include "GLContextEGL.h" +#include "GLBlitHelper.h" +#include "GLImages.h" + using namespace mozilla; namespace mozilla { @@ -28,6 +35,149 @@ void AndroidSurfaceTexture::GetTransformMatrix( env->ReleaseFloatArrayElements(jarray.Get(), array, 0); } +class SharedGL { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedGL); + + SharedGL(AndroidNativeWindow& window) { + MutexAutoLock lock(sMutex); + + if (!sContext) { + MOZ_ASSERT(sInstanceCount == 0); + sContext = CreateContext(); + if (!sContext) { + return; + } + } + + InitSurface(window); + ++sInstanceCount; + } + + void Blit(const AndroidSurfaceTextureHandle& sourceTextureHandle, + const gfx::IntSize& imageSize) { + MutexAutoLock lock(sMutex); + MOZ_ASSERT(sContext); + + // Setting overide also makes conext and surface current. + sContext->SetEGLSurfaceOverride(mTargetSurface); + RefPtr img = new layers::SurfaceTextureImage( + sourceTextureHandle, imageSize, false, OriginPos::TopLeft); + sContext->BlitHelper()->BlitImage(img, imageSize, OriginPos::BottomLeft); + sContext->SwapBuffers(); + // This method is called through binder IPC and could run on any thread in + // the pool. Release the context and surface from this thread after use so + // they can be bound to another thread later. + UnmakeCurrent(sContext); + } + + private: + ~SharedGL() { + MutexAutoLock lock(sMutex); + + if (mTargetSurface != EGL_NO_SURFACE) { + GLLibraryEGL::Get()->fDestroySurface(EGL_DISPLAY(), mTargetSurface); + } + + // Destroy shared GL context when no one uses it. + if (--sInstanceCount == 0) { + sContext = nullptr; + } + } + + static already_AddRefed CreateContext() { + sMutex.AssertCurrentThreadOwns(); + MOZ_ASSERT(!sContext); + + auto* egl = gl::GLLibraryEGL::Get(); + EGLDisplay eglDisplay = egl->fGetDisplay(EGL_DEFAULT_DISPLAY); + EGLConfig eglConfig; + CreateConfig(&eglConfig, /* bpp */ 24, /* depth buffer? */ false); + EGLint attributes[] = {LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2, LOCAL_EGL_NONE}; + EGLContext eglContext = + egl->fCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attributes); + RefPtr gl = new GLContextEGL( + CreateContextFlags::NONE, SurfaceCaps::Any(), + /* offscreen? */ false, eglConfig, EGL_NO_SURFACE, eglContext); + if (!gl->Init()) { + NS_WARNING("Fail to create GL context for native blitter."); + return nullptr; + } + + // Yield the current state made in constructor. + UnmakeCurrent(gl); + return gl.forget(); + } + + void InitSurface(AndroidNativeWindow& window) { + sMutex.AssertCurrentThreadOwns(); + MOZ_ASSERT(sContext); + + mTargetSurface = gl::GLLibraryEGL::Get()->fCreateWindowSurface( + sContext->GetEGLDisplay(), sContext->mConfig, window.NativeWindow(), 0); + } + + static bool UnmakeCurrent(RefPtr& gl) { + sMutex.AssertCurrentThreadOwns(); + MOZ_ASSERT(gl); + + if (!gl->IsCurrent()) { + return true; + } + + return gl::GLLibraryEGL::Get()->fMakeCurrent( + EGL_DISPLAY(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + + static Mutex sMutex; + static RefPtr sContext; + static size_t sInstanceCount; + + EGLSurface mTargetSurface; +}; + +Mutex SharedGL::sMutex("SharedGLContext::sMutex"); +RefPtr SharedGL::sContext(nullptr); +size_t SharedGL::sInstanceCount = 0; + +class GLBlitterSupport final + : public java::GeckoSurfaceTexture::NativeGLBlitHelper::Natives< + GLBlitterSupport> { + public: + using Base = + java::GeckoSurfaceTexture::NativeGLBlitHelper::Natives; + using Base::AttachNative; + using Base::DisposeNative; + using Base::GetNative; + + static java::GeckoSurfaceTexture::NativeGLBlitHelper::LocalRef Create( + jint sourceTextureHandle, jni::Object::Param targetSurface, jint width, + jint height) { + AndroidNativeWindow win(java::GeckoSurface::Ref::From(targetSurface)); + auto helper = java::GeckoSurfaceTexture::NativeGLBlitHelper::New(); + RefPtr gl = new SharedGL(win); + GLBlitterSupport::AttachNative( + helper, MakeUnique(std::move(gl), sourceTextureHandle, + width, height)); + return helper; + } + + GLBlitterSupport(RefPtr&& gl, jint sourceTextureHandle, jint width, + jint height) + : mGl(gl), + mSourceTextureHandle(sourceTextureHandle), + mSize(width, height) {} + + void Blit() { mGl->Blit(mSourceTextureHandle, mSize); } + + private: + const RefPtr mGl; + const AndroidSurfaceTextureHandle mSourceTextureHandle; + const gfx::IntSize mSize; +}; + +void AndroidSurfaceTexture::Init() { GLBlitterSupport::Init(); } + } // namespace gl } // namespace mozilla #endif // MOZ_WIDGET_ANDROID diff --git a/gfx/gl/AndroidSurfaceTexture.h b/gfx/gl/AndroidSurfaceTexture.h index 9f937e818237..97ebf3f05f52 100644 --- a/gfx/gl/AndroidSurfaceTexture.h +++ b/gfx/gl/AndroidSurfaceTexture.h @@ -18,6 +18,7 @@ namespace gl { class AndroidSurfaceTexture { public: + static void Init(); static void GetTransformMatrix( java::sdk::SurfaceTexture::Param surfaceTexture, mozilla::gfx::Matrix4x4* outMatrix); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java index e3361772402d..a841e79597ec 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java @@ -15,6 +15,7 @@ import java.util.HashMap; import java.util.LinkedList; import org.mozilla.gecko.annotation.WrapForJNI; +import org.mozilla.gecko.mozglue.JNIObject; /* package */ final class GeckoSurfaceTexture extends SurfaceTexture { private static final String LOGTAG = "GeckoSurfaceTexture"; @@ -36,6 +37,8 @@ import org.mozilla.gecko.annotation.WrapForJNI; private AtomicInteger mUseCount; private boolean mFinalized; + private NativeGLBlitHelper mBlitter; + private GeckoSurfaceTexture(int handle) { super(0); init(handle, false); @@ -122,6 +125,9 @@ import org.mozilla.gecko.annotation.WrapForJNI; @Override public synchronized void release() { + if (mBlitter != null) { + mBlitter.disposeNative(); + } try { super.release(); synchronized (sSurfaceTextures) { @@ -276,8 +282,28 @@ import org.mozilla.gecko.annotation.WrapForJNI; } } + /* package */ synchronized void configureSnapshot(GeckoSurface target, int width, int height) { + mBlitter = NativeGLBlitHelper.create(mHandle, target, width, height); + } + + /* package */ synchronized void takeSnapshot() { + mBlitter.blit(); + } + public interface Callbacks { void onUpdateTexImage(); void onReleaseTexImage(); } + + @WrapForJNI + public static class NativeGLBlitHelper extends JNIObject { + public native static NativeGLBlitHelper create(int textureHandle, + GeckoSurface targetSurface, + int width, + int height); + public native void blit(); + + @Override + protected native void disposeNative(); + } } diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 343d5a104773..f1a48571b49a 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -40,6 +40,7 @@ #include "AndroidBridge.h" #include "AndroidBridgeUtilities.h" +#include "AndroidSurfaceTexture.h" #include "GeneratedJNINatives.h" #include #include @@ -411,6 +412,7 @@ nsAppShell::nsAppShell() mozilla::widget::Telemetry::Init(); mozilla::widget::WebExecutorSupport::Init(); nsWindow::InitNatives(); + mozilla::gl::AndroidSurfaceTexture::Init(); if (jni::IsFennec()) { BrowserLocaleManagerSupport::Init();