From e1b0d32bdece1926e3180859262f9d32c560863a Mon Sep 17 00:00:00 2001 From: Dmitry Petukhov Date: Tue, 18 Oct 2016 12:17:14 -0700 Subject: [PATCH] Two ReactART TODOs implemented on Android Summary: Implemented 2 TODOs from ReactART for Android: - TODO(7255985): Use TextureView and pass Surface from the view to draw on it asynchronously instead of passing the bitmap (which is inefficient especially in terms of memory usage) - TODO(6352067): Support dashes in ARTShape We use ReactNativeART in our Android project. 1. Our app crashes sometimes on large screen smartphones with OutOfMemoryError. Crashes happen in ARTSurfaceShadowNode where TODO(7255985) was suggested in a comment in order to use memory more efficiently. 2. We needed dashes for drawing on ARTSurface. **Test plan (required)** I attach a screenshot of our app which shows dashed-lines and two ARTSurfaces on top of each other rendering exactly the same as in the pervious implementation of ARTSurface. ![screenshot_2016-08-19-16-45-43](https://cloud.githubusercontent.com/assets/18415611/17811741/cafc35c4-662c-11e6-8a63-7c35ef1c5ba9.png) Closes https://github.com/facebook/react-native/pull/9486 Differential Revision: D4021303 Pulled By: foghina --- .../react/flat/FlatARTSurfaceViewManager.java | 4 +- .../flat/FlatARTSurfaceViewShadowNode.java | 84 +++++++++++++++---- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewManager.java index 2c1eec28ad9..0ce91a60391 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewManager.java @@ -9,8 +9,6 @@ package com.facebook.react.flat; -import android.graphics.Bitmap; - import com.facebook.csslayout.CSSMeasureMode; import com.facebook.csslayout.CSSNodeAPI; import com.facebook.csslayout.MeasureOutput; @@ -60,6 +58,6 @@ import com.facebook.react.views.art.ARTSurfaceView; @Override public void updateExtraData(ARTSurfaceView root, Object extraData) { - root.setBitmap((Bitmap) extraData); + root.setSurfaceTextureListener((FlatARTSurfaceViewShadowNode) extraData); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewShadowNode.java index 4b139f76b3f..8ca4357ce2e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewShadowNode.java @@ -9,15 +9,26 @@ package com.facebook.react.flat; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; +import javax.annotation.Nullable; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.SurfaceTexture; +import android.util.Log; +import android.view.Surface; +import android.view.TextureView; + +import com.facebook.react.common.ReactConstants; +import com.facebook.react.uimanager.ReactShadowNode; import com.facebook.react.uimanager.UIViewOperationQueue; import com.facebook.react.views.art.ARTVirtualNode; -/* package */ class FlatARTSurfaceViewShadowNode extends FlatShadowNode implements AndroidView { +/* package */ class FlatARTSurfaceViewShadowNode extends FlatShadowNode + implements AndroidView, TextureView.SurfaceTextureListener { private boolean mPaddingChanged = false; + private @Nullable Surface mSurface; /* package */ FlatARTSurfaceViewShadowNode() { forceMountToView(); @@ -37,24 +48,42 @@ import com.facebook.react.views.art.ARTVirtualNode; @Override public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) { super.onCollectExtraUpdates(uiUpdater); - uiUpdater.enqueueUpdateExtraData(getReactTag(), drawOutput()); + uiUpdater.enqueueUpdateExtraData(getReactTag(), this); } - private Object drawOutput() { - // TODO(7255985): Use TextureView and pass Surface from the view to draw on it asynchronously - // instead of passing the bitmap (which is inefficient especially in terms of memory usage) - Bitmap bitmap = Bitmap.createBitmap( - (int) getLayoutWidth(), - (int) getLayoutHeight(), - Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - Paint paint = new Paint(); - for (int i = 0; i < getChildCount(); i++) { - ARTVirtualNode child = (ARTVirtualNode) getChildAt(i); - child.draw(canvas, paint, 1f); + private void drawOutput() { + if (mSurface == null || !mSurface.isValid()) { + markChildrenUpdatesSeen(this); + return; + } + + try { + Canvas canvas = mSurface.lockCanvas(null); + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + + Paint paint = new Paint(); + for (int i = 0; i < getChildCount(); i++) { + ARTVirtualNode child = (ARTVirtualNode) getChildAt(i); + child.draw(canvas, paint, 1f); + child.markUpdateSeen(); + } + + if (mSurface == null) { + return; + } + + mSurface.unlockCanvasAndPost(canvas); + } catch (IllegalArgumentException | IllegalStateException e) { + Log.e(ReactConstants.TAG, e.getClass().getSimpleName() + " in Surface.unlockCanvasAndPost"); + } + } + + private void markChildrenUpdatesSeen(ReactShadowNode shadowNode) { + for (int i = 0; i < shadowNode.getChildCount(); i++) { + ReactShadowNode child = shadowNode.getChildAt(i); child.markUpdateSeen(); + markChildrenUpdatesSeen(child); } - return bitmap; } @Override @@ -79,4 +108,23 @@ import com.facebook.react.views.art.ARTVirtualNode; dirty(); } } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + mSurface = new Surface(surface); + drawOutput(); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + surface.release(); + mSurface = null; + return true; + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) {} }