summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/SurfaceAllocator.java
blob: f3cca81a81406f56e59a1b0e669c2908ac4d3117 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* -*- 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.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.LongSparseArray;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.process.GeckoProcessManager;
import org.mozilla.gecko.process.GeckoServiceChildProcess;

/* package */ final class SurfaceAllocator {
  private static final String LOGTAG = "SurfaceAllocator";

  private static ISurfaceAllocator sAllocator;

  // Keep a reference to all allocated Surfaces, so that we can release them if we lose the
  // connection to the allocator service.
  private static final LongSparseArray<GeckoSurface> sSurfaces =
      new LongSparseArray<GeckoSurface>();

  private static synchronized void ensureConnection() {
    if (sAllocator != null) {
      return;
    }

    try {
      if (GeckoAppShell.isParentProcess()) {
        sAllocator = GeckoProcessManager.getInstance().getSurfaceAllocator();
      } else {
        sAllocator = GeckoServiceChildProcess.getSurfaceAllocator();
      }

      if (sAllocator == null) {
        Log.w(LOGTAG, "Failed to connect to RemoteSurfaceAllocator");
        return;
      }
      sAllocator
          .asBinder()
          .linkToDeath(
              new IBinder.DeathRecipient() {
                @Override
                public void binderDied() {
                  Log.w(LOGTAG, "RemoteSurfaceAllocator died");
                  synchronized (SurfaceAllocator.class) {
                    // Our connection to the remote allocator has died, so all our surfaces are
                    // invalid.  Release them all now. When their owners attempt to render in to
                    // them they can detect they have been released and allocate new ones instead.
                    for (int i = 0; i < sSurfaces.size(); i++) {
                      sSurfaces.valueAt(i).release();
                    }
                    sSurfaces.clear();
                    sAllocator = null;
                  }
                }
              },
              0);
    } catch (final RemoteException e) {
      Log.w(LOGTAG, "Failed to connect to RemoteSurfaceAllocator", e);
      sAllocator = null;
    }
  }

  @WrapForJNI
  public static synchronized GeckoSurface acquireSurface(
      final int width, final int height, final boolean singleBufferMode) {
    try {
      ensureConnection();

      if (sAllocator == null) {
        Log.w(LOGTAG, "Failed to acquire GeckoSurface: not connected");
        return null;
      }

      final GeckoSurface surface = sAllocator.acquireSurface(width, height, singleBufferMode);
      if (surface == null) {
        Log.w(LOGTAG, "Failed to acquire GeckoSurface: RemoteSurfaceAllocator returned null");
        return null;
      }
      sSurfaces.put(surface.getHandle(), surface);

      if (!surface.inProcess()) {
        final SyncConfig config = surface.initSyncSurface(width, height);
        if (config != null) {
          sAllocator.configureSync(config);
        }
      }
      return surface;
    } catch (final RemoteException e) {
      Log.w(LOGTAG, "Failed to acquire GeckoSurface", e);
      return null;
    }
  }

  @WrapForJNI
  public static synchronized void disposeSurface(final GeckoSurface surface) {
    // If the surface has already been released (probably due to losing connection to the remote
    // allocator) then there is nothing to do here.
    if (surface.isReleased()) {
      return;
    }

    sSurfaces.remove(surface.getHandle());

    // Release our Surface
    surface.release();

    if (sAllocator == null) {
      return;
    }

    // Release the SurfaceTexture on the other side. If we have lost connection then do nothing, as
    // there is nothing on the other side to release.
    try {
      if (sAllocator != null) {
        sAllocator.releaseSurface(surface.getHandle());
      }
    } catch (final RemoteException e) {
      Log.w(LOGTAG, "Failed to release surface texture", e);
    }
  }

  public static synchronized void sync(final long upstream) {
    // Sync from the SurfaceTexture on the other side. If we have lost connection then do nothing,
    // as there is nothing on the other side to sync from.
    try {
      if (sAllocator != null) {
        sAllocator.sync(upstream);
      }
    } catch (final RemoteException e) {
      Log.w(LOGTAG, "Failed to sync texture", e);
    }
  }
}