summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java330
1 files changed, 330 insertions, 0 deletions
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
new file mode 100644
index 0000000000..2d045edb44
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
@@ -0,0 +1,330 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko.gfx;
+
+import android.graphics.SurfaceTexture;
+import android.os.Build;
+import android.util.Log;
+import android.util.LongSparseArray;
+import androidx.annotation.RequiresApi;
+import java.util.LinkedList;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.mozglue.JNIObject;
+
+/* package */ final class GeckoSurfaceTexture extends SurfaceTexture {
+ private static final String LOGTAG = "GeckoSurfaceTexture";
+ private static final int MAX_SURFACE_TEXTURES = 200;
+ private static final LongSparseArray<GeckoSurfaceTexture> sSurfaceTextures =
+ new LongSparseArray<GeckoSurfaceTexture>();
+
+ private static LongSparseArray<LinkedList<GeckoSurfaceTexture>> sUnusedTextures =
+ new LongSparseArray<LinkedList<GeckoSurfaceTexture>>();
+
+ private long mHandle;
+ private boolean mIsSingleBuffer;
+
+ private long mAttachedContext;
+ private int mTexName;
+
+ private GeckoSurfaceTexture.Callbacks mListener;
+ private AtomicInteger mUseCount;
+ private boolean mFinalized;
+
+ private long mUpstream;
+ private NativeGLBlitHelper mBlitter;
+
+ private GeckoSurfaceTexture(final long handle) {
+ super(0);
+ init(handle, false);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ private GeckoSurfaceTexture(final long handle, final boolean singleBufferMode) {
+ super(0, singleBufferMode);
+ init(handle, singleBufferMode);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ // We only want finalize() to be called once
+ if (mFinalized) {
+ return;
+ }
+
+ mFinalized = true;
+ super.finalize();
+ }
+
+ private void init(final long handle, final boolean singleBufferMode) {
+ mHandle = handle;
+ mIsSingleBuffer = singleBufferMode;
+ mUseCount = new AtomicInteger(1);
+
+ // Start off detached
+ detachFromGLContext();
+ }
+
+ @WrapForJNI
+ public long getHandle() {
+ return mHandle;
+ }
+
+ @WrapForJNI
+ public int getTexName() {
+ return mTexName;
+ }
+
+ @WrapForJNI(exceptionMode = "nsresult")
+ public synchronized void attachToGLContext(final long context, final int texName) {
+ if (context == mAttachedContext && texName == mTexName) {
+ return;
+ }
+
+ attachToGLContext(texName);
+
+ mAttachedContext = context;
+ mTexName = texName;
+ }
+
+ @Override
+ @WrapForJNI(exceptionMode = "nsresult")
+ public synchronized void detachFromGLContext() {
+ super.detachFromGLContext();
+
+ mAttachedContext = mTexName = 0;
+ }
+
+ @WrapForJNI
+ public synchronized boolean isAttachedToGLContext(final long context) {
+ return mAttachedContext == context;
+ }
+
+ @WrapForJNI
+ public boolean isSingleBuffer() {
+ return mIsSingleBuffer;
+ }
+
+ @Override
+ @WrapForJNI
+ public synchronized void updateTexImage() {
+ try {
+ if (mUpstream != 0) {
+ SurfaceAllocator.sync(mUpstream);
+ }
+ super.updateTexImage();
+ if (mListener != null) {
+ mListener.onUpdateTexImage();
+ }
+ } catch (final Exception e) {
+ Log.w(LOGTAG, "updateTexImage() failed", e);
+ }
+ }
+
+ @Override
+ public synchronized void release() {
+ mUpstream = 0;
+ if (mBlitter != null) {
+ mBlitter.close();
+ }
+ try {
+ super.release();
+ synchronized (sSurfaceTextures) {
+ sSurfaceTextures.remove(mHandle);
+ }
+ } catch (final Exception e) {
+ Log.w(LOGTAG, "release() failed", e);
+ }
+ }
+
+ @Override
+ @WrapForJNI
+ public synchronized void releaseTexImage() {
+ if (!mIsSingleBuffer) {
+ return;
+ }
+
+ try {
+ super.releaseTexImage();
+ if (mListener != null) {
+ mListener.onReleaseTexImage();
+ }
+ } catch (final Exception e) {
+ Log.w(LOGTAG, "releaseTexImage() failed", e);
+ }
+ }
+
+ public synchronized void setListener(final GeckoSurfaceTexture.Callbacks listener) {
+ mListener = listener;
+ }
+
+ @WrapForJNI
+ public static boolean isSingleBufferSupported() {
+ return Build.VERSION.SDK_INT >= 19;
+ }
+
+ @WrapForJNI
+ public synchronized void incrementUse() {
+ mUseCount.incrementAndGet();
+ }
+
+ @WrapForJNI
+ public synchronized void decrementUse() {
+ final int useCount = mUseCount.decrementAndGet();
+
+ if (useCount == 0) {
+ setListener(null);
+
+ if (mAttachedContext == 0) {
+ release();
+ synchronized (sUnusedTextures) {
+ sSurfaceTextures.remove(mHandle);
+ }
+ return;
+ }
+
+ synchronized (sUnusedTextures) {
+ LinkedList<GeckoSurfaceTexture> list = sUnusedTextures.get(mAttachedContext);
+ if (list == null) {
+ list = new LinkedList<GeckoSurfaceTexture>();
+ sUnusedTextures.put(mAttachedContext, list);
+ }
+ list.addFirst(this);
+ }
+ }
+ }
+
+ @WrapForJNI
+ public static void destroyUnused(final long context) {
+ final LinkedList<GeckoSurfaceTexture> list;
+ synchronized (sUnusedTextures) {
+ list = sUnusedTextures.get(context);
+ sUnusedTextures.delete(context);
+ }
+
+ if (list == null) {
+ return;
+ }
+
+ for (final GeckoSurfaceTexture tex : list) {
+ try {
+ if (tex.isSingleBuffer()) {
+ tex.releaseTexImage();
+ }
+
+ tex.detachFromGLContext();
+ tex.release();
+
+ // We need to manually call finalize here, otherwise we can run out
+ // of file descriptors if the GC doesn't kick in soon enough. Bug 1416015.
+ try {
+ tex.finalize();
+ } catch (final Throwable t) {
+ Log.e(LOGTAG, "Failed to finalize SurfaceTexture", t);
+ }
+ } catch (final Exception e) {
+ Log.e(LOGTAG, "Failed to destroy SurfaceTexture", e);
+ }
+ }
+ }
+
+ public static GeckoSurfaceTexture acquire(final boolean singleBufferMode, final long handle) {
+ if (singleBufferMode && !isSingleBufferSupported()) {
+ throw new IllegalArgumentException("single buffer mode not supported on API version < 19");
+ }
+
+ // Attempting to create a SurfaceTexture from an isolated process on Android versions prior to
+ // 8.0 results in an indefinite hang. See bug 1706656.
+ if (GeckoAppShell.isIsolatedProcess() && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ return null;
+ }
+
+ synchronized (sSurfaceTextures) {
+ // We want to limit the maximum number of SurfaceTextures at any one time.
+ // This is because they use a large number of fds, and once the process' limit
+ // is reached bad things happen. See bug 1421586.
+ if (sSurfaceTextures.size() >= MAX_SURFACE_TEXTURES) {
+ return null;
+ }
+
+ if (sSurfaceTextures.indexOfKey(handle) >= 0) {
+ throw new IllegalArgumentException("Already have a GeckoSurfaceTexture with that handle");
+ }
+
+ final GeckoSurfaceTexture gst;
+ if (isSingleBufferSupported()) {
+ gst = new GeckoSurfaceTexture(handle, singleBufferMode);
+ } else {
+ gst = new GeckoSurfaceTexture(handle);
+ }
+
+ sSurfaceTextures.put(handle, gst);
+
+ return gst;
+ }
+ }
+
+ @WrapForJNI
+ public static GeckoSurfaceTexture lookup(final long handle) {
+ synchronized (sSurfaceTextures) {
+ return sSurfaceTextures.get(handle);
+ }
+ }
+
+ /* package */ synchronized void track(final long upstream) {
+ mUpstream = upstream;
+ }
+
+ /* package */ synchronized void configureSnapshot(
+ final GeckoSurface target, final int width, final 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 final class NativeGLBlitHelper extends JNIObject {
+ public static NativeGLBlitHelper create(
+ final long textureHandle,
+ final GeckoSurface targetSurface,
+ final int width,
+ final int height) {
+ final NativeGLBlitHelper helper = nativeCreate(textureHandle, targetSurface, width, height);
+ helper.mTargetSurface = targetSurface; // Take ownership of surface.
+ return helper;
+ }
+
+ public static native NativeGLBlitHelper nativeCreate(
+ final long textureHandle,
+ final GeckoSurface targetSurface,
+ final int width,
+ final int height);
+
+ public native void blit();
+
+ public void close() {
+ disposeNative();
+ if (mTargetSurface != null) {
+ mTargetSurface.release();
+ mTargetSurface = null;
+ }
+ }
+
+ @Override
+ protected native void disposeNative();
+
+ private GeckoSurface mTargetSurface;
+ }
+}