Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(188)

Unified Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/CompositorView.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/CompositorView.java
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/CompositorView.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/CompositorView.java
new file mode 100644
index 0000000000000000000000000000000000000000..78c220cc62a0ad4bbb52ba89650471ecb677f196
--- /dev/null
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/CompositorView.java
@@ -0,0 +1,457 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.compositor;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+
+import com.google.android.apps.chrome.R;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.CommandLine;
+import org.chromium.base.JNINamespace;
+import org.chromium.base.Log;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromiumApplication;
+import org.chromium.chrome.browser.compositor.layouts.Layout;
+import org.chromium.chrome.browser.compositor.layouts.Layout.SizingFlags;
+import org.chromium.chrome.browser.compositor.layouts.LayoutProvider;
+import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
+import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
+import org.chromium.chrome.browser.compositor.layouts.content.ContentOffsetProvider;
+import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.compositor.resources.StaticResourcePreloads;
+import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
+import org.chromium.chrome.browser.device.DeviceClassManager;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
+import org.chromium.chrome.browser.tabmodel.TabModelBase;
+import org.chromium.content.browser.ContentReadbackHandler;
+import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.resources.AndroidResourceType;
+import org.chromium.ui.resources.ResourceManager;
+
+/**
+ * The is the {@link View} displaying the ui compositor results; including webpages and tabswitcher.
+ */
+@JNINamespace("chrome::android")
+public class CompositorView
+ extends SurfaceView implements ContentOffsetProvider, SurfaceHolder.Callback2 {
+ private static final String TAG = "CompositorView";
+
+ // Cache objects that should not be created every frame
+ private final Rect mCacheViewport = new Rect();
+ private final Rect mCacheAppRect = new Rect();
+ private final Rect mCacheVisibleViewport = new Rect();
+ private final int[] mCacheViewPosition = new int[2];
+
+ private long mNativeCompositorView;
+ private final LayoutRenderHost mRenderHost;
+ private boolean mEnableTabletTabStack;
+ private int mPreviousWindowTop = -1;
+
+ private int mLastLayerCount;
+
+ // Resource Management
+ private ResourceManager mResourceManager;
+
+ // Lazily populated as it is needed.
+ private View mRootActivityView;
+ private WindowAndroid mWindowAndroid;
+ private LayerTitleCache mLayerTitleCache;
+ private TabContentManager mTabContentManager;
+
+ private View mRootView;
+ private int mSurfaceWidth;
+ private int mSurfaceHeight;
+ private boolean mPreloadedResources;
+
+ private ContentReadbackHandler mContentReadbackHandler;
+
+ // The current SurfaceView pixel format. Defaults to OPAQUE.
+ private int mCurrentPixelFormat = PixelFormat.OPAQUE;
+
+ /**
+ * Creates a {@link CompositorView}. This can be called only after the native library is
+ * properly loaded.
+ * @param c The Context to create this {@link CompositorView} in.
+ * @param host The renderer host owning this view.
+ */
+ public CompositorView(Context c, LayoutRenderHost host) {
+ super(c);
+ mRenderHost = host;
+ resetFlags();
+ setVisibility(View.INVISIBLE);
+ setZOrderMediaOverlay(true);
+ mContentReadbackHandler = new ContentReadbackHandler() {
+ @Override
+ protected boolean readyForReadback() {
+ return mNativeCompositorView != 0;
+ }
+ };
+ }
+
+ /**
+ * @param view The root view of the hierarchy.
+ */
+ public void setRootView(View view) {
+ mRootView = view;
+ }
+
+ /**
+ * Reset the commandline flags. This gets called after we switch over to the
+ * native command line.
+ */
+ public void resetFlags() {
+ CommandLine commandLine = CommandLine.getInstance();
+ mEnableTabletTabStack = commandLine.hasSwitch(ChromeSwitches.ENABLE_TABLET_TAB_STACK)
+ && DeviceFormFactor.isTablet(getContext());
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mRootView != null) {
+ mRootView.getWindowVisibleDisplayFrame(mCacheAppRect);
+
+ // Check whether the top position of the window has changed as we always must
+ // resize in that case to the specified height spec. On certain versions of
+ // Android when you change the top position (i.e. by leaving fullscreen) and
+ // do not shrink the SurfaceView, it will appear to be pinned to the top of
+ // the screen under the notification bar and all touch offsets will be wrong
+ // as well as a gap will appear at the bottom of the screen.
+ int windowTop = mCacheAppRect.top;
+ boolean topChanged = windowTop != mPreviousWindowTop;
+ mPreviousWindowTop = windowTop;
+
+ Activity activity = mWindowAndroid != null ? mWindowAndroid.getActivity().get() : null;
+ ChromiumApplication application =
+ (ChromiumApplication) getContext().getApplicationContext();
+ boolean isMultiWindow = application.isMultiWindow(activity);
+
+ // If the measured width is the same as the allowed width (i.e. the orientation has
+ // not changed) and multi-window mode is off, use the largest measured height seen thus
+ // far. This will prevent surface resizes as a result of showing the keyboard.
+ if (!topChanged && !isMultiWindow
+ && getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
+ && getMeasuredHeight() > MeasureSpec.getSize(heightMeasureSpec)) {
+ heightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY);
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mRenderHost.onOverdrawBottomHeightChanged(getOverdrawBottomHeight());
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPreviousWindowTop = -1;
+ }
+
+ /**
+ * @return The content readback handler.
+ */
+ public ContentReadbackHandler getContentReadbackHandler() {
+ return mContentReadbackHandler;
+ }
+
+ /**
+ * @return The ResourceManager.
+ */
+ public ResourceManager getResourceManager() {
+ return mResourceManager;
+ }
+
+ /**
+ * @return The amount the surface view is overdrawing the window bounds.
+ */
+ public int getOverdrawBottomHeight() {
+ if (mRootActivityView == null) {
+ mRootActivityView = mRootView.findViewById(android.R.id.content);
+ }
+ if (mRootActivityView != null) {
+ int compositorHeight = getHeight();
+ int rootViewHeight = mRootActivityView.getHeight();
+ return Math.max(0, compositorHeight - rootViewHeight);
+ }
+ return 0;
+ }
+
+ /**
+ * Should be called for cleanup when the CompositorView instance is no longer used.
+ */
+ public void shutDown() {
+ getHolder().removeCallback(this);
+ mContentReadbackHandler.destroy();
+ mContentReadbackHandler = null;
+ if (mNativeCompositorView != 0) nativeDestroy(mNativeCompositorView);
+ mNativeCompositorView = 0;
+ }
+
+ /**
+ * Initializes the {@link CompositorView}'s native parts (e.g. the rendering parts).
+ * @param lowMemDevice If this is a low memory device.
+ * @param windowAndroid A {@link WindowAndroid} instance.
+ * @param layerTitleCache A {@link LayerTitleCache} instance.
+ * @param tabContentManager A {@link TabContentManager} instance.
+ */
+ public void initNativeCompositor(boolean lowMemDevice, WindowAndroid windowAndroid,
+ LayerTitleCache layerTitleCache, TabContentManager tabContentManager) {
+ mWindowAndroid = windowAndroid;
+ mLayerTitleCache = layerTitleCache;
+ mTabContentManager = tabContentManager;
+
+ mNativeCompositorView =
+ nativeInit(lowMemDevice, getResources().getColor(R.color.tab_switcher_background),
+ windowAndroid.getNativePointer(), layerTitleCache, tabContentManager);
+
+ assert !getHolder().getSurface().isValid()
+ : "Surface created before native library loaded.";
+ getHolder().addCallback(this);
+
+ // Cover the black surface before it has valid content.
+ setBackgroundColor(Color.WHITE);
+ setVisibility(View.VISIBLE);
+
+ // Grab the Resource Manager
+ mResourceManager = nativeGetResourceManager(mNativeCompositorView);
+
+ mContentReadbackHandler.initNativeContentReadbackHandler();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent e) {
+ return super.onTouchEvent(e);
+ }
+
+ /**
+ * Enables/disables overlay video mode. Affects alpha blending on this view.
+ * @param enabled Whether to enter or leave overlay video mode.
+ */
+ public void setOverlayVideoMode(boolean enabled) {
+ mCurrentPixelFormat = enabled ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
+ getHolder().setFormat(mCurrentPixelFormat);
+ nativeSetOverlayVideoMode(mNativeCompositorView, enabled);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ if (mNativeCompositorView == 0) return;
+ nativeSurfaceChanged(mNativeCompositorView, format, width, height, holder.getSurface());
+ mRenderHost.onPhysicalBackingSizeChanged(width, height);
+ mSurfaceWidth = width;
+ mSurfaceHeight = height;
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ if (mNativeCompositorView == 0) return;
+ nativeSurfaceCreated(mNativeCompositorView);
+ mRenderHost.onSurfaceCreated();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (mNativeCompositorView == 0) return;
+ nativeSurfaceDestroyed(mNativeCompositorView);
+ }
+
+ @Override
+ public void surfaceRedrawNeeded(SurfaceHolder holder) {}
+
+ @CalledByNative
+ private void onCompositorLayout() {
+ mRenderHost.onCompositorLayout();
+ }
+
+ /*
+ * On JellyBean there is a known bug where a crashed producer process
+ * (i.e. GPU process) does not properly disconnect from the BufferQueue,
+ * which means we won't be able to reconnect to it ever again.
+ * This workaround forces the creation of a new Surface.
+ */
+ @CalledByNative
+ private void onJellyBeanSurfaceDisconnectWorkaround(boolean inOverlayMode) {
+ // There is a bug in JellyBean because of which we will not be able to
+ // reconnect to the existing Surface after we launch a new GPU process.
+ // We simply trick the JB Android code to allocate a new Surface.
+ // It does a strict comparison between the current format and the requested
+ // one, even if they are the same in practice. Furthermore, the format
+ // does not matter here since the producer-side EGL config overwrites it
+ // (but transparency might matter).
+ switch (mCurrentPixelFormat) {
+ case PixelFormat.OPAQUE:
+ mCurrentPixelFormat = PixelFormat.RGBA_8888;
+ break;
+ case PixelFormat.RGBA_8888:
+ mCurrentPixelFormat = inOverlayMode
+ ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
+ break;
+ case PixelFormat.TRANSLUCENT:
+ mCurrentPixelFormat = PixelFormat.RGBA_8888;
+ break;
+ default:
+ assert false;
+ Log.e(TAG, "Unknown current pixel format.");
+ }
+ getHolder().setFormat(mCurrentPixelFormat);
+ }
+
+ /**
+ * Request compositor view to render a frame.
+ */
+ public void requestRender() {
+ if (mNativeCompositorView != 0) nativeSetNeedsComposite(mNativeCompositorView);
+ }
+
+ @CalledByNative
+ private void onSwapBuffersCompleted(int pendingSwapBuffersCount) {
+ // Clear the color used to cover the uninitialized surface.
+ if (getBackground() != null) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ setBackgroundResource(0);
+ }
+ });
+ }
+
+ mRenderHost.onSwapBuffersCompleted(pendingSwapBuffersCount);
+ }
+
+ private void updateToolbarLayer(LayoutProvider provider, boolean forRotation) {
+ if (forRotation || !DeviceClassManager.enableFullscreen()) return;
+
+ ChromeFullscreenManager fullscreenManager = provider.getFullscreenManager();
+ if (fullscreenManager == null) return;
+
+ float offset = fullscreenManager.getControlOffset();
+ boolean useTexture = fullscreenManager.drawControlsAsTexture() || offset == 0;
+
+ float dpToPx = getContext().getResources().getDisplayMetrics().density;
+ float layoutOffsetDp = provider.getActiveLayout().getTopControlsOffset(offset / dpToPx);
+ boolean validLayoutOffset = !Float.isNaN(layoutOffsetDp);
+
+ if (validLayoutOffset) {
+ offset = layoutOffsetDp * dpToPx;
+ useTexture = true;
+ }
+
+ fullscreenManager.setHideTopControlsAndroidView(validLayoutOffset && layoutOffsetDp != 0.f);
+
+ int flags = provider.getActiveLayout().getSizingFlags();
+ if ((flags & SizingFlags.REQUIRE_FULLSCREEN_SIZE) != 0
+ && (flags & SizingFlags.ALLOW_TOOLBAR_HIDE) == 0
+ && (flags & SizingFlags.ALLOW_TOOLBAR_ANIMATE) == 0) {
+ useTexture = false;
+ }
+
+ nativeUpdateToolbarLayer(
+ mNativeCompositorView, R.id.control_container, R.id.progress, offset, useTexture);
+ }
+
+ /**
+ * Converts the layout into compositor layers. This is to be called on every frame the layout
+ * is changing.
+ * @param provider Provides the layout to be rendered.
+ * @param forRotation Whether or not this is a special draw during a rotation.
+ */
+ public void finalizeLayers(final LayoutProvider provider, boolean forRotation) {
+ TraceEvent.begin("CompositorView:finalizeLayers");
+ Layout layout = provider.getActiveLayout();
+ if (layout == null || mNativeCompositorView == 0) {
+ TraceEvent.end("CompositorView:finalizeLayers");
+ return;
+ }
+
+ if (!mPreloadedResources) {
+ // Attempt to prefetch any necessary resources
+ mResourceManager.preloadResources(AndroidResourceType.STATIC,
+ StaticResourcePreloads.getSynchronousResources(getContext()),
+ StaticResourcePreloads.getAsynchronousResources());
+ mPreloadedResources = true;
+ }
+
+ // IMPORTANT: Do not do anything that impacts the compositor layer tree before this line.
+ // If you do, you could inadvertently trigger follow up renders. For further information
+ // see dtrainor@, tedchoc@, or klobag@.
+
+ // TODO(jscholler): change 1.0f to dpToPx once the native part is fully supporting dp.
+ mRenderHost.getVisibleViewport(mCacheVisibleViewport);
+ provider.getViewportPixel(mCacheViewport);
+ nativeSetLayoutViewport(mNativeCompositorView, mCacheViewport.left, mCacheViewport.top,
+ mCacheViewport.width(), mCacheViewport.height(), mCacheVisibleViewport.left,
+ mCacheVisibleViewport.top, mRenderHost.getCurrentOverdrawBottomHeight(), 1.0f);
+
+ mCacheVisibleViewport.right = mCacheVisibleViewport.left + mSurfaceWidth;
+ mCacheVisibleViewport.bottom = mCacheVisibleViewport.top
+ + Math.max(mSurfaceHeight - mRenderHost.getCurrentOverdrawBottomHeight(), 0);
+
+ // TODO(changwan): move to treeprovider.
+ updateToolbarLayer(provider, forRotation);
+
+ SceneLayer sceneLayer =
+ layout.getUpdatedSceneLayer(mCacheViewport, mCacheVisibleViewport, mLayerTitleCache,
+ mTabContentManager, mResourceManager, provider.getFullscreenManager());
+
+ nativeSetSceneLayer(mNativeCompositorView, sceneLayer);
+
+ final LayoutTab[] tabs = layout.getLayoutTabsToRender();
+ final int tabsCount = tabs != null ? tabs.length : 0;
+ mLastLayerCount = tabsCount;
+ TabModelBase.flushActualTabSwitchLatencyMetric();
+ nativeFinalizeLayers(mNativeCompositorView);
+ TraceEvent.end("CompositorView:finalizeLayers");
+ }
+
+ /**
+ * @return The number of layer put the last frame.
+ */
+ @VisibleForTesting
+ public int getLastLayerCount() {
+ return mLastLayerCount;
+ }
+
+ @Override
+ public int getOverlayTranslateY() {
+ return mRenderHost.areTopControlsPermanentlyHidden()
+ ? mRenderHost.getTopControlsHeightPixels()
+ : mRenderHost.getVisibleViewport(mCacheVisibleViewport).top;
+ }
+
+ // Implemented in native
+ private native long nativeInit(boolean lowMemDevice, int emptyColor, long nativeWindowAndroid,
+ LayerTitleCache layerTitleCache, TabContentManager tabContentManager);
+ private native void nativeDestroy(long nativeCompositorView);
+ private native ResourceManager nativeGetResourceManager(long nativeCompositorView);
+ private native void nativeSurfaceCreated(long nativeCompositorView);
+ private native void nativeSurfaceDestroyed(long nativeCompositorView);
+ private native void nativeSurfaceChanged(
+ long nativeCompositorView, int format, int width, int height, Surface surface);
+ private native void nativeFinalizeLayers(long nativeCompositorView);
+ private native void nativeSetNeedsComposite(long nativeCompositorView);
+ private native void nativeSetLayoutViewport(long nativeCompositorView, float x, float y,
+ float width, float height, float visibleXOffset, float visibleYOffset,
+ float overdrawBottomHeight, float dpToPixel);
+ private native void nativeUpdateToolbarLayer(long nativeCompositorView, int resourceId,
+ int progressResourceId, float topOffset, boolean visible);
+ private native void nativeSetOverlayVideoMode(long nativeCompositorView, boolean enabled);
+ private native void nativeSetSceneLayer(long nativeCompositorView, SceneLayer sceneLayer);
+}

Powered by Google App Engine
This is Rietveld 408576698