Skip to content

Commit

Permalink
Wait before switching surfaces (flutter#20100)
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel Garcia authored Jul 31, 2020
1 parent 280bbfc commit 7f5d044
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -953,13 +953,22 @@ public void detachFromFlutterEngine() {
flutterEngine = null;
}

@VisibleForTesting
@NonNull
public FlutterImageView createImageView() {
return new FlutterImageView(
getContext(), getWidth(), getHeight(), FlutterImageView.SurfaceKind.background);
}

/**
* Converts the current render surface to a {@link FlutterImageView} if it's not one already.
* Otherwise, it resizes the {@link FlutterImageView} based on the current view size.
*/
public void convertToImageView() {
renderSurface.pause();

if (flutterImageView == null) {
flutterImageView =
new FlutterImageView(
getContext(), getWidth(), getHeight(), FlutterImageView.SurfaceKind.background);
flutterImageView = createImageView();
addView(flutterImageView);
} else {
flutterImageView.resizeIfNeeded(getWidth(), getHeight());
Expand All @@ -973,10 +982,13 @@ public void convertToImageView() {
}

/**
* If the surface is rendered by a {@code FlutterImageView}. Then, calling this method will stop
* rendering to a {@code FlutterImageView}, and use the previous surface instead.
* If the surface is rendered by a {@link FlutterImageView}, then calling this method will stop
* rendering to a {@link FlutterImageView}, and render on the previous surface instead.
*
* @param onDone a callback called when Flutter UI is rendered on the previous surface. Use this
* callback to perform cleanups. For example, destroy overlay surfaces.
*/
public void revertImageView() {
public void revertImageView(@NonNull Runnable onDone) {
if (flutterImageView == null) {
Log.v(TAG, "Tried to revert the image view, but no image view is used.");
return;
Expand All @@ -985,12 +997,39 @@ public void revertImageView() {
Log.v(TAG, "Tried to revert the image view, but no previous surface was used.");
return;
}
flutterImageView.detachFromRenderer();
renderSurface = previousRenderSurface;
previousRenderSurface = null;
if (flutterEngine != null) {
renderSurface.attachToRenderer(flutterEngine.getRenderer());
if (flutterEngine == null) {
flutterImageView.detachFromRenderer();
onDone.run();
return;
}
final FlutterRenderer renderer = flutterEngine.getRenderer();
if (renderer == null) {
flutterImageView.detachFromRenderer();
onDone.run();
return;
}
// Start rendering on the previous surface.
// This surface is typically `FlutterSurfaceView` or `FlutterTextureView`.
renderSurface.attachToRenderer(renderer);

// Install a Flutter UI listener to wait until the first frame is rendered
// in the new surface to call the `onDone` callback.
renderer.addIsDisplayingFlutterUiListener(
new FlutterUiDisplayListener() {
@Override
public void onFlutterUiDisplayed() {
renderer.removeIsDisplayingFlutterUiListener(this);
onDone.run();
flutterImageView.detachFromRenderer();
}

@Override
public void onFlutterUiNoLongerDisplayed() {
// no-op
}
});
}

public void attachOverlaySurfaceToRender(FlutterImageView view) {
Expand Down
19 changes: 14 additions & 5 deletions shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
*
* <p>To invoke a native method that is not associated with a platform view, invoke it statically:
*
* <p>{@code bool enabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled(); }
* <p>{@code bool enabled = FlutterJNI.getIsSoftwareRenderingEnabled(); }
*/
@Keep
public class FlutterJNI {
Expand Down Expand Up @@ -120,9 +120,13 @@ public static native void nativeInit(
*/
public static native void nativePrefetchDefaultFontManager();

// TODO(mattcarroll): add javadocs
private native boolean nativeGetIsSoftwareRenderingEnabled();

@UiThread
public native boolean nativeGetIsSoftwareRenderingEnabled();
// TODO(mattcarroll): add javadocs
public boolean getIsSoftwareRenderingEnabled() {
return nativeGetIsSoftwareRenderingEnabled();
}

@Nullable
// TODO(mattcarroll): add javadocs
Expand Down Expand Up @@ -212,7 +216,12 @@ public boolean isAttached() {
public void attachToNative(boolean isBackgroundView) {
ensureRunningOnMainThread();
ensureNotAttachedToNative();
nativePlatformViewId = nativeAttach(this, isBackgroundView);
nativePlatformViewId = performNativeAttach(this, isBackgroundView);
}

@VisibleForTesting
public long performNativeAttach(@NonNull FlutterJNI flutterJNI, boolean isBackgroundView) {
return nativeAttach(flutterJNI, isBackgroundView);
}

private native long nativeAttach(@NonNull FlutterJNI flutterJNI, boolean isBackgroundView);
Expand Down Expand Up @@ -279,7 +288,7 @@ public void removeIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListene
@SuppressWarnings("unused")
@VisibleForTesting
@UiThread
void onFirstFrame() {
public void onFirstFrame() {
ensureRunningOnMainThread();

for (FlutterUiDisplayListener listener : flutterUiDisplayListeners) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ private void unregisterTexture(long textureId) {

// TODO(mattcarroll): describe the native behavior that this invokes
public boolean isSoftwareRenderingEnabled() {
return flutterJNI.nativeGetIsSoftwareRenderingEnabled();
return flutterJNI.getIsSoftwareRenderingEnabled();
}

// TODO(mattcarroll): describe the native behavior that this invokes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ public void onDisplayPlatformView(
int width,
int height,
int viewWidth,
int ViewHeight,
int viewHeight,
FlutterMutatorsStack mutatorsStack) {
initializeRootImageViewIfNeeded();
initializePlatformViewIfNeeded(viewId);
Expand All @@ -723,7 +723,7 @@ public void onDisplayPlatformView(
mutatorView.setVisibility(View.VISIBLE);
mutatorView.bringToFront();

FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(viewWidth, ViewHeight);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(viewWidth, viewHeight);
View platformView = platformViews.get(viewId);
platformView.setLayoutParams(layoutParams);
platformView.bringToFront();
Expand Down Expand Up @@ -753,6 +753,20 @@ public void onBeginFrame() {
}

public void onEndFrame() {
final FlutterView view = (FlutterView) flutterView;
// If there are no platform views in the current frame,
// then revert the image view surface and use the previous surface.
//
// Otherwise, acquire the latest image.
if (flutterViewConvertedToImageView && currentFrameUsedPlatformViewIds.isEmpty()) {
flutterViewConvertedToImageView = false;
view.revertImageView(
() -> {
// Destroy overlay surfaces once the surface reversion is completed.
finishFrame(false);
});
return;
}
// Whether the current frame was rendered using ImageReaders.
//
// Since the image readers may not have images available at this point,
Expand All @@ -762,22 +776,12 @@ public void onEndFrame() {
// If one of the surfaces doesn't have an image, the frame may be incomplete and must be
// dropped.
// For example, a toolbar widget painted by Flutter may not be rendered.
boolean isFrameRenderedUsingImageReaders = false;

if (flutterViewConvertedToImageView) {
FlutterView view = (FlutterView) flutterView;
// If there are no platform views in the current frame,
// then revert the image view surface and use the previous surface.
//
// Otherwise, acquire the latest image.
if (currentFrameUsedPlatformViewIds.isEmpty()) {
view.revertImageView();
flutterViewConvertedToImageView = false;
} else {
isFrameRenderedUsingImageReaders = view.acquireLatestImageViewFrame();
}
}
boolean isFrameRenderedUsingImageReaders =
flutterViewConvertedToImageView && view.acquireLatestImageViewFrame();
finishFrame(isFrameRenderedUsingImageReaders);
}

private void finishFrame(boolean isFrameRenderedUsingImageReaders) {
for (int i = 0; i < overlayLayerViews.size(); i++) {
int overlayId = overlayLayerViews.keyAt(i);
FlutterImageView overlayView = overlayLayerViews.valueAt(i);
Expand Down Expand Up @@ -818,6 +822,14 @@ public void onEndFrame() {
}
}

@VisibleForTesting
@TargetApi(19)
public FlutterOverlaySurface createOverlaySurface(@NonNull FlutterImageView imageView) {
final int id = nextOverlayLayerId++;
overlayLayerViews.put(id, imageView);
return new FlutterOverlaySurface(id, imageView.getSurface());
}

@TargetApi(19)
public FlutterOverlaySurface createOverlaySurface() {
// Overlay surfaces have the same size as the background surface.
Expand All @@ -826,17 +838,12 @@ public FlutterOverlaySurface createOverlaySurface() {
// if the drawings they contain have a different tight bound.
//
// The final view size is determined when its frame is set.
FlutterImageView imageView =
return createOverlaySurface(
new FlutterImageView(
flutterView.getContext(),
flutterView.getWidth(),
flutterView.getHeight(),
FlutterImageView.SurfaceKind.overlay);

int id = nextOverlayLayerId++;
overlayLayerViews.put(id, imageView);

return new FlutterOverlaySurface(id, imageView.getSurface());
FlutterImageView.SurfaceKind.overlay));
}

public void destroyOverlaySurfaces() {
Expand Down
2 changes: 1 addition & 1 deletion shell/platform/android/io/flutter/view/FlutterView.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public FlutterView(Context context, AttributeSet attrs, FlutterNativeView native

dartExecutor = mNativeView.getDartExecutor();
flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI());
mIsSoftwareRenderingEnabled = mNativeView.getFlutterJNI().nativeGetIsSoftwareRenderingEnabled();
mIsSoftwareRenderingEnabled = mNativeView.getFlutterJNI().getIsSoftwareRenderingEnabled();
mMetrics = new ViewportMetrics();
mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
setFocusable(true);
Expand Down
Loading

0 comments on commit 7f5d044

Please sign in to comment.