1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 package org.mozilla.gecko.gfx; 7 8 import android.graphics.SurfaceTexture; 9 import android.os.Build; 10 import android.support.annotation.RequiresApi; 11 import android.util.Log; 12 13 import java.util.concurrent.atomic.AtomicInteger; 14 import java.util.HashMap; 15 import java.util.LinkedList; 16 17 import org.mozilla.gecko.annotation.WrapForJNI; 18 import org.mozilla.gecko.mozglue.JNIObject; 19 20 /* package */ final class GeckoSurfaceTexture extends SurfaceTexture { 21 private static final String LOGTAG = "GeckoSurfaceTexture"; 22 private static final int MAX_SURFACE_TEXTURES = 200; 23 private static volatile int sNextHandle = 1; 24 private static final HashMap<Integer, GeckoSurfaceTexture> sSurfaceTextures = new HashMap<Integer, GeckoSurfaceTexture>(); 25 26 27 private static HashMap<Long, LinkedList<GeckoSurfaceTexture>> sUnusedTextures = 28 new HashMap<Long, LinkedList<GeckoSurfaceTexture>>(); 29 30 private int mHandle; 31 private boolean mIsSingleBuffer; 32 33 private long mAttachedContext; 34 private int mTexName; 35 36 private GeckoSurfaceTexture.Callbacks mListener; 37 private AtomicInteger mUseCount; 38 private boolean mFinalized; 39 40 private int mUpstream; 41 private NativeGLBlitHelper mBlitter; 42 GeckoSurfaceTexture(final int handle)43 private GeckoSurfaceTexture(final int handle) { 44 super(0); 45 init(handle, false); 46 } 47 48 @RequiresApi(api = Build.VERSION_CODES.KITKAT) GeckoSurfaceTexture(final int handle, final boolean singleBufferMode)49 private GeckoSurfaceTexture(final int handle, final boolean singleBufferMode) { 50 super(0, singleBufferMode); 51 init(handle, singleBufferMode); 52 } 53 54 @Override finalize()55 protected void finalize() throws Throwable { 56 // We only want finalize() to be called once 57 if (mFinalized) { 58 return; 59 } 60 61 mFinalized = true; 62 super.finalize(); 63 } 64 init(final int handle, final boolean singleBufferMode)65 private void init(final int handle, final boolean singleBufferMode) { 66 mHandle = handle; 67 mIsSingleBuffer = singleBufferMode; 68 mUseCount = new AtomicInteger(1); 69 70 // Start off detached 71 detachFromGLContext(); 72 } 73 74 @WrapForJNI getHandle()75 public int getHandle() { 76 return mHandle; 77 } 78 79 @WrapForJNI getTexName()80 public int getTexName() { 81 return mTexName; 82 } 83 84 @WrapForJNI(exceptionMode = "nsresult") attachToGLContext(final long context, final int texName)85 public synchronized void attachToGLContext(final long context, final int texName) { 86 if (context == mAttachedContext && texName == mTexName) { 87 return; 88 } 89 90 attachToGLContext(texName); 91 92 mAttachedContext = context; 93 mTexName = texName; 94 } 95 96 @Override 97 @WrapForJNI(exceptionMode = "nsresult") detachFromGLContext()98 public synchronized void detachFromGLContext() { 99 super.detachFromGLContext(); 100 101 mAttachedContext = mTexName = 0; 102 } 103 104 @WrapForJNI isAttachedToGLContext(final long context)105 public synchronized boolean isAttachedToGLContext(final long context) { 106 return mAttachedContext == context; 107 } 108 109 @WrapForJNI isSingleBuffer()110 public boolean isSingleBuffer() { 111 return mIsSingleBuffer; 112 } 113 114 @Override 115 @WrapForJNI updateTexImage()116 public synchronized void updateTexImage() { 117 try { 118 if (mUpstream != 0) { 119 SurfaceAllocator.sync(mUpstream); 120 } 121 super.updateTexImage(); 122 if (mListener != null) { 123 mListener.onUpdateTexImage(); 124 } 125 } catch (Exception e) { 126 Log.w(LOGTAG, "updateTexImage() failed", e); 127 } 128 } 129 130 @Override release()131 public synchronized void release() { 132 mUpstream = 0; 133 if (mBlitter != null) { 134 mBlitter.disposeNative(); 135 } 136 try { 137 super.release(); 138 synchronized (sSurfaceTextures) { 139 sSurfaceTextures.remove(mHandle); 140 } 141 } catch (Exception e) { 142 Log.w(LOGTAG, "release() failed", e); 143 } 144 } 145 146 @Override 147 @WrapForJNI releaseTexImage()148 public synchronized void releaseTexImage() { 149 if (!mIsSingleBuffer) { 150 return; 151 } 152 153 try { 154 super.releaseTexImage(); 155 if (mListener != null) { 156 mListener.onReleaseTexImage(); 157 } 158 } catch (Exception e) { 159 Log.w(LOGTAG, "releaseTexImage() failed", e); 160 } 161 } 162 setListener(final GeckoSurfaceTexture.Callbacks listener)163 public synchronized void setListener(final GeckoSurfaceTexture.Callbacks listener) { 164 mListener = listener; 165 } 166 167 @WrapForJNI isSingleBufferSupported()168 public static boolean isSingleBufferSupported() { 169 return Build.VERSION.SDK_INT >= 19; 170 } 171 172 @WrapForJNI incrementUse()173 public synchronized void incrementUse() { 174 mUseCount.incrementAndGet(); 175 } 176 177 @WrapForJNI decrementUse()178 public synchronized void decrementUse() { 179 int useCount = mUseCount.decrementAndGet(); 180 181 if (useCount == 0) { 182 setListener(null); 183 184 if (mAttachedContext == 0) { 185 release(); 186 synchronized (sUnusedTextures) { 187 sSurfaceTextures.remove(mHandle); 188 } 189 return; 190 } 191 192 synchronized (sUnusedTextures) { 193 LinkedList<GeckoSurfaceTexture> list = sUnusedTextures.get(mAttachedContext); 194 if (list == null) { 195 list = new LinkedList<GeckoSurfaceTexture>(); 196 sUnusedTextures.put(mAttachedContext, list); 197 } 198 list.addFirst(this); 199 } 200 } 201 } 202 203 @WrapForJNI destroyUnused(final long context)204 public static void destroyUnused(final long context) { 205 LinkedList<GeckoSurfaceTexture> list; 206 synchronized (sUnusedTextures) { 207 list = sUnusedTextures.remove(context); 208 } 209 210 if (list == null) { 211 return; 212 } 213 214 for (GeckoSurfaceTexture tex : list) { 215 try { 216 if (tex.isSingleBuffer()) { 217 tex.releaseTexImage(); 218 } 219 220 tex.detachFromGLContext(); 221 tex.release(); 222 223 // We need to manually call finalize here, otherwise we can run out 224 // of file descriptors if the GC doesn't kick in soon enough. Bug 1416015. 225 try { 226 tex.finalize(); 227 } catch (Throwable t) { 228 Log.e(LOGTAG, "Failed to finalize SurfaceTexture", t); 229 } 230 } catch (Exception e) { 231 Log.e(LOGTAG, "Failed to destroy SurfaceTexture", e); 232 } 233 } 234 } 235 acquire(final boolean singleBufferMode, final int handle)236 public static GeckoSurfaceTexture acquire(final boolean singleBufferMode, final int handle) { 237 if (singleBufferMode && !isSingleBufferSupported()) { 238 throw new IllegalArgumentException("single buffer mode not supported on API version < 19"); 239 } 240 241 synchronized (sSurfaceTextures) { 242 // We want to limit the maximum number of SurfaceTextures at any one time. 243 // This is because they use a large number of fds, and once the process' limit 244 // is reached bad things happen. See bug 1421586. 245 if (sSurfaceTextures.size() >= MAX_SURFACE_TEXTURES) { 246 return null; 247 } 248 249 int resolvedHandle = handle; 250 if (resolvedHandle == 0) { 251 // Generate new handle value when none specified. 252 resolvedHandle = sNextHandle++; 253 } 254 255 final GeckoSurfaceTexture gst; 256 if (isSingleBufferSupported()) { 257 gst = new GeckoSurfaceTexture(resolvedHandle, singleBufferMode); 258 } else { 259 gst = new GeckoSurfaceTexture(resolvedHandle); 260 } 261 262 if (sSurfaceTextures.containsKey(resolvedHandle)) { 263 gst.release(); 264 throw new IllegalArgumentException("Already have a GeckoSurfaceTexture with that handle"); 265 } 266 267 sSurfaceTextures.put(resolvedHandle, gst); 268 return gst; 269 } 270 } 271 272 @WrapForJNI lookup(final int handle)273 public static GeckoSurfaceTexture lookup(final int handle) { 274 synchronized (sSurfaceTextures) { 275 return sSurfaceTextures.get(handle); 276 } 277 } 278 track(final int upstream)279 /* package */ synchronized void track(final int upstream) { 280 mUpstream = upstream; 281 } 282 configureSnapshot(final GeckoSurface target, final int width, final int height)283 /* package */ synchronized void configureSnapshot(final GeckoSurface target, 284 final int width, final int height) { 285 mBlitter = NativeGLBlitHelper.create(mHandle, target, width, height); 286 } 287 takeSnapshot()288 /* package */ synchronized void takeSnapshot() { 289 mBlitter.blit(); 290 } 291 292 public interface Callbacks { onUpdateTexImage()293 void onUpdateTexImage(); onReleaseTexImage()294 void onReleaseTexImage(); 295 } 296 297 @WrapForJNI 298 public static final class NativeGLBlitHelper extends JNIObject { create(int textureHandle, GeckoSurface targetSurface, int width, int height)299 public native static NativeGLBlitHelper create(int textureHandle, 300 GeckoSurface targetSurface, 301 int width, 302 int height); blit()303 public native void blit(); 304 305 @Override disposeNative()306 protected native void disposeNative(); 307 } 308 } 309