Bug 1415994 - 3. Add LayerSession; r=snorp draft
authorJim Chen <nchen@mozilla.com>
Tue, 14 Nov 2017 18:18:34 -0500
changeset 697877 eaf42122db1238c99b0da1e689bc365180a2835f
parent 697876 282b27a1f7c11cf4e559b1f25946c3f4574e900a
child 697878 040442539fd7cc1af8e13e4be1d9ebfb1625f778
push id89133
push userbmo:nchen@mozilla.com
push dateTue, 14 Nov 2017 23:19:10 +0000
reviewerssnorp
bugs1415994
milestone59.0a1
Bug 1415994 - 3. Add LayerSession; r=snorp Add a LayerSession class that's split off LayerView. Currently, LayerSession takes over Surface management and the Compositor class from LayerView. Eventually, all of LayerView will migrate to LayerSession. MozReview-Commit-ID: F1ozOfZGY2g
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -398,16 +398,17 @@ gvjar.sources += [geckoview_source_dir +
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
     'gfx/GeckoDisplay.java',
     'gfx/GeckoLayerClient.java',
     'gfx/GeckoSurface.java',
     'gfx/GeckoSurfaceTexture.java',
     'gfx/ImmutableViewportMetrics.java',
     'gfx/IntSize.java',
+    'gfx/LayerSession.java',
     'gfx/LayerView.java',
     'gfx/NativePanZoomController.java',
     'gfx/Overscroll.java',
     'gfx/OverscrollEdgeEffect.java',
     'gfx/PanningPerfAPI.java',
     'gfx/PanZoomController.java',
     'gfx/PointUtils.java',
     'gfx/RenderTask.java',
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
@@ -0,0 +1,274 @@
+/* -*- 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 org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.mozglue.JNIObject;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.Surface;
+
+public class LayerSession {
+    private static final String LOGTAG = "GeckoLayerSession";
+    private static final boolean DEBUG = false;
+
+    // Special message sent from UiCompositorControllerChild once it is open.
+    /* package */ final static int COMPOSITOR_CONTROLLER_OPEN = 20;
+    // Special message sent from controller to query if the compositor controller is open.
+    /* package */ final static int IS_COMPOSITOR_CONTROLLER_OPEN = 21;
+
+    protected class Compositor extends JNIObject {
+        public LayerView layerView;
+
+        public boolean isReady() {
+            return LayerSession.this.isCompositorReady();
+        }
+
+        @WrapForJNI(calledFrom = "ui")
+        private void onCompositorAttached() {
+            LayerSession.this.onCompositorAttached();
+        }
+
+        @WrapForJNI(calledFrom = "ui")
+        private void onCompositorDetached() {
+            if (layerView != null) {
+                layerView.clearDrawListeners();
+                layerView = null;
+            }
+            // Clear out any pending calls on the UI thread.
+            LayerSession.this.onCompositorDetached();
+            disposeNative();
+        }
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
+        @Override protected native void disposeNative();
+
+        @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
+        public native void attachToJava(GeckoLayerClient layerClient,
+                                        NativePanZoomController npzc);
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
+        public native void onSizeChanged(int windowWidth, int windowHeight);
+
+        // Gecko thread creates compositor; blocks UI thread.
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "proxy")
+        public native void createCompositor(int width, int height, Object surface);
+
+        // Gecko thread pauses compositor; blocks UI thread.
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void syncPauseCompositor();
+
+        // UI thread resumes compositor and notifies Gecko thread; does not block UI thread.
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void syncResumeResizeCompositor(int width, int height, Object surface);
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void setMaxToolbarHeight(int height);
+
+        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
+        public native void setPinned(boolean pinned, int reason);
+
+        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
+        public native void sendToolbarAnimatorMessage(int message);
+
+        @WrapForJNI(calledFrom = "ui")
+        private void recvToolbarAnimatorMessage(int message) {
+            if (message == COMPOSITOR_CONTROLLER_OPEN) {
+                if (LayerSession.this.isCompositorReady()) {
+                    return;
+                }
+
+                // Delay calling onCompositorReady to avoid deadlock due
+                // to synchronous call to the compositor.
+                ThreadUtils.postToUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        LayerSession.this.onCompositorReady();
+                    }
+                });
+            }
+
+            if (layerView != null) {
+                layerView.handleToolbarAnimatorMessage(message);
+            }
+        }
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void setDefaultClearColor(int color);
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void requestScreenPixels();
+
+        @WrapForJNI(calledFrom = "ui")
+        private void recvScreenPixels(int width, int height, int[] pixels) {
+            if (layerView != null) {
+                layerView.recvScreenPixels(width, height, pixels);
+            }
+        }
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void enableLayerUpdateNotifications(boolean enable);
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void sendToolbarPixelsToCompositor(final int width, final int height,
+                                                         final int[] pixels);
+    }
+
+    protected final Compositor mCompositor = new Compositor();
+
+    // Following fields are accessed on UI thread.
+    private GeckoDisplay mDisplay;
+    private boolean mAttachedCompositor;
+    private boolean mCalledCreateCompositor;
+    private boolean mCompositorReady;
+    private Surface mSurface;
+    private int mWidth;
+    private int mHeight;
+
+    /* package */ GeckoDisplay getDisplay() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+        return mDisplay;
+    }
+
+    /* package */ void onCompositorAttached() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        mAttachedCompositor = true;
+
+        if (mSurface != null) {
+            // If we have a valid surface, create the compositor now that we're attached.
+            // Leave mSurface alone because we'll need it later for onCompositorReady.
+            onSurfaceChanged(mSurface, mWidth, mHeight);
+        }
+    }
+
+    /* package */ void onCompositorDetached() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        mAttachedCompositor = false;
+        mCalledCreateCompositor = false;
+        mCompositorReady = false;
+    }
+
+    /* package */ boolean isCompositorReady() {
+        return mCompositorReady;
+    }
+
+    /* package */ void onCompositorReady() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        mCompositorReady = true;
+
+        if (mSurface != null) {
+            // If we have a valid surface, resume the
+            // compositor now that the compositor is ready.
+            onSurfaceChanged(mSurface, mWidth, mHeight);
+            mSurface = null;
+        }
+    }
+
+    private void onWindowResize() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        if (mAttachedCompositor) {
+            final int viewportHeight;
+            if (mCompositor.layerView != null) {
+                viewportHeight = mHeight - mCompositor.layerView.getCurrentToolbarHeight();
+            } else {
+                viewportHeight = mHeight;
+            }
+            mCompositor.onSizeChanged(mWidth, viewportHeight);
+
+            if (mCompositor.layerView != null) {
+                mCompositor.layerView.onSizeChanged(mWidth, mHeight);
+            }
+        }
+    }
+
+    /* package */ void onSurfaceChanged(final Surface surface, final int width,
+                                        final int height) {
+        ThreadUtils.assertOnUiThread();
+
+        mWidth = width;
+        mHeight = height;
+        onWindowResize();
+
+        if (mCompositorReady) {
+            mCompositor.syncResumeResizeCompositor(width, height, surface);
+            return;
+        }
+
+        if (mAttachedCompositor && !mCalledCreateCompositor) {
+            mCompositor.createCompositor(width, height, surface);
+            mCompositor.sendToolbarAnimatorMessage(IS_COMPOSITOR_CONTROLLER_OPEN);
+            mCalledCreateCompositor = true;
+        }
+
+        // We have a valid surface but we're not attached or the compositor
+        // is not ready; save the surface for later when we're ready.
+        mSurface = surface;
+    }
+
+    /* package */ void onSurfaceDestroyed() {
+        ThreadUtils.assertOnUiThread();
+
+        if (mCompositorReady) {
+            mCompositor.syncPauseCompositor();
+            return;
+        }
+
+        // While the surface was valid, we never became attached or the
+        // compositor never became ready; clear the saved surface.
+        mSurface = null;
+    }
+
+    public void addDisplay(final GeckoDisplay display) {
+        ThreadUtils.assertOnUiThread();
+
+        if (display.getListener() != null) {
+            throw new IllegalArgumentException("Display already attached");
+        } else if (mDisplay != null) {
+            throw new IllegalArgumentException("Only one display supported");
+        }
+
+        mDisplay = display;
+        display.setListener(new GeckoDisplay.Listener() {
+            @Override
+            public void surfaceChanged(final Surface surface, final int width,
+                                       final int height) {
+                onSurfaceChanged(surface, width, height);
+            }
+
+            @Override
+            public void surfaceDestroyed() {
+                onSurfaceDestroyed();
+            }
+        });
+    }
+
+    public void removeDisplay(final GeckoDisplay display) {
+        ThreadUtils.assertOnUiThread();
+
+        if (mDisplay != display) {
+            throw new IllegalArgumentException("Display not attached");
+        }
+
+        display.setListener(null);
+        mDisplay = null;
+    }
+}