forked from mozilla/gecko-dev
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1767128 - Rework and re-enable SurfaceControl rendering path on A…
…ndroid. r=agi,gfx-reviewers,aosmond In bug 1762424 we introduced a rendering path on Android using the SurfaceControl API, in order to work around a bug preventing recovery from a GPU process crash. However, the initial implementation caused this bug: repeatedly sending the same SurfaceControl objects over AIDL to the GPU process resulted in them being leaked, eventually causing severe display issues. Not only were we duplicating the SurfaceControl for each widget, but each time a widget was resized too. This patch reworks our usage of the SurfaceControl API to avoid ever having to send them cross-process. Instead, we create a single child SurfaceControl object for each SurfaceControl that is attached to a widget. (Typically there will be a single one shared between all widgets, changing only when the app is paused and resumed, which is much fewer than one per widget per resize.) In the parent process we obtain the Surfaces that will be rendered in to from the child SurfaceControls, and only send those Surfaces to the GPU process. Thankfully unlike SurfaceControls, sending Surfaces cross-process does not cause leaks. When the GPU process dies we simply destroy all of the child SurfaceControls, and recreate them again on-demand. Differential Revision: https://phabricator.services.mozilla.com/D147437
- Loading branch information
1 parent
f950793
commit 2cf59fe
Showing
13 changed files
with
160 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/SurfaceControlManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.Build; | ||
import android.view.Surface; | ||
import android.view.SurfaceControl; | ||
import androidx.annotation.RequiresApi; | ||
import java.util.Iterator; | ||
import java.util.Map; | ||
import java.util.WeakHashMap; | ||
import org.mozilla.gecko.annotation.WrapForJNI; | ||
|
||
// A helper class that creates Surfaces from SurfaceControl objects, for the widget to render in to. | ||
// Unlike the Surfaces provided to the widget directly from the application, these are suitable for | ||
// use in the GPU process as well as the main process. | ||
// | ||
// The reason we must not render directly in to the Surface provided by the application from the GPU | ||
// process is because of a bug on Android versions 12 and later: when the GPU process dies the | ||
// Surface is not detached from the dead process' EGL surface, and any subsequent attempts to | ||
// attach another EGL surface to the Surface will fail. | ||
// | ||
// The application is therefore required to provide the SurfaceControl object to a GeckoDisplay | ||
// whenever rendering in to a SurfaceView. The widget will then obtain a Surface from that | ||
// SurfaceControl using getChildSurface(). Internally, this creates another SurfaceControl as a | ||
// child of the provided SurfaceControl, then creates the Surface from that child. If the GPU | ||
// process dies we are able to simply destroy and recreate the child SurfaceControl objects, thereby | ||
// avoiding the bug. | ||
public class SurfaceControlManager { | ||
private static final String LOGTAG = "SurfaceControlManager"; | ||
|
||
private static final SurfaceControlManager sInstance = new SurfaceControlManager(); | ||
|
||
private WeakHashMap<SurfaceControl, SurfaceControl> mChildSurfaceControls = new WeakHashMap<>(); | ||
|
||
@WrapForJNI | ||
public static SurfaceControlManager getInstance() { | ||
return sInstance; | ||
} | ||
|
||
// Returns a Surface of the requested size that will be composited in to the specified | ||
// SurfaceControl. | ||
@RequiresApi(api = Build.VERSION_CODES.Q) | ||
@WrapForJNI(exceptionMode = "abort") | ||
public synchronized Surface getChildSurface( | ||
final SurfaceControl parent, final int width, final int height) { | ||
SurfaceControl child = mChildSurfaceControls.get(parent); | ||
if (child == null) { | ||
// We must periodically check if any of the SurfaceControls we are managing have been | ||
// destroyed, as we are unable to directly listen to their SurfaceViews' surfaceDestroyed | ||
// callbacks, and they may not be attached to any compositor when they are destroyed meaning | ||
// we cannot perform cleanup in response to the compositor being paused. | ||
// Doing so here, when we encounter a new SurfaceControl instance, is a reasonable guess as to | ||
// when a previous instance may have been released. | ||
final Iterator<Map.Entry<SurfaceControl, SurfaceControl>> it = | ||
mChildSurfaceControls.entrySet().iterator(); | ||
while (it.hasNext()) { | ||
final Map.Entry<SurfaceControl, SurfaceControl> entry = it.next(); | ||
if (!entry.getKey().isValid()) { | ||
it.remove(); | ||
} | ||
} | ||
|
||
child = new SurfaceControl.Builder().setParent(parent).setName("GeckoSurface").build(); | ||
mChildSurfaceControls.put(parent, child); | ||
} | ||
|
||
new SurfaceControl.Transaction() | ||
.setVisibility(child, true) | ||
.setBufferSize(child, width, height) | ||
.apply(); | ||
|
||
return new Surface(child); | ||
} | ||
|
||
// Must be called whenever the GPU process has died. This destroys all the child SurfaceControls | ||
// that have been created, meaning subsequent calls to getChildSurface() will create new ones. | ||
@RequiresApi(api = Build.VERSION_CODES.Q) | ||
@WrapForJNI(exceptionMode = "abort") | ||
public synchronized void onGpuProcessLoss() { | ||
for (final SurfaceControl child : mChildSurfaceControls.values()) { | ||
// We could reparent the child SurfaceControl to null here to immediately remove it from the | ||
// tree. However, this will result in a black screen while we wait for the new compositor to | ||
// be created. It's preferable for the user to see the old content instead, so simply call | ||
// release(). | ||
child.release(); | ||
} | ||
mChildSurfaceControls.clear(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.