| 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);
|
| +}
|
|
|