| Index: chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java
|
| deleted file mode 100644
|
| index 0fe5e71d28af3dd1fccc9495d07f05d117e16063..0000000000000000000000000000000000000000
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java
|
| +++ /dev/null
|
| @@ -1,455 +0,0 @@
|
| -// Copyright 2017 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.content.Context;
|
| -import android.graphics.PixelFormat;
|
| -import android.graphics.drawable.Drawable;
|
| -import android.view.SurfaceHolder;
|
| -import android.view.SurfaceView;
|
| -import android.view.View;
|
| -import android.view.ViewGroup;
|
| -import android.widget.FrameLayout;
|
| -
|
| -/**
|
| - * Manage multiple SurfaceViews for the compositor, so that transitions between
|
| - * surfaces with and without an alpha channel can be visually smooth.
|
| - *
|
| - * This class allows a client to request a 'translucent' or 'opaque' surface, and we will signal via
|
| - * SurfaceHolder.Callback when it's ready. We guarantee that the client will receive surfaceCreated
|
| - * / surfaceDestroyed only for a surface that represents the most recently requested PixelFormat.
|
| - *
|
| - * Internally, we maintain two SurfaceViews, since calling setFormat() to change the PixelFormat
|
| - * results in a visual glitch as the surface is torn down. crbug.com/679902
|
| - *
|
| - * The client has the responsibility to call doneWithUnownedSurface() at some point between when we
|
| - * call back its surfaceCreated, when it is safe for us to hide the SurfaceView with the wrong
|
| - * format. It is okay if it requests multiple surfaces without calling doneWithUnownedSurface.
|
| - *
|
| - * If the client requests the same format more than once in a row, it will still receive destroyed /
|
| - * created / changed messages for it, even though we won't tear it down.
|
| - *
|
| - * The full design doc is at https://goo.gl/aAmQzR .
|
| - */
|
| -class CompositorSurfaceManager implements SurfaceHolder.Callback {
|
| - private static class SurfaceState {
|
| - public SurfaceView surfaceView;
|
| -
|
| - // Do we expect a surfaceCreated?
|
| - public boolean createPending;
|
| -
|
| - // Have we started destroying |surfaceView|, but haven't received surfaceDestroyed yet?
|
| - public boolean destroyPending;
|
| -
|
| - // Last PixelFormat that we received, or UNKNOWN if we don't know / don't want to cache it.
|
| - public int format;
|
| -
|
| - // Last known width, height for thsi surface.
|
| - public int width;
|
| - public int height;
|
| -
|
| - // Parent ViewGroup, or null.
|
| - private ViewGroup mParent;
|
| -
|
| - public SurfaceState(Context context, int format, SurfaceHolder.Callback callback) {
|
| - surfaceView = new SurfaceView(context);
|
| - surfaceView.setZOrderMediaOverlay(true);
|
| - surfaceView.setVisibility(View.VISIBLE);
|
| - surfaceHolder().setFormat(format);
|
| - surfaceHolder().addCallback(callback);
|
| -
|
| - // Set this to UNKNOWN until we get a format back.
|
| - this.format = PixelFormat.UNKNOWN;
|
| - }
|
| -
|
| - public SurfaceHolder surfaceHolder() {
|
| - return surfaceView.getHolder();
|
| - }
|
| -
|
| - public boolean isValid() {
|
| - return surfaceHolder().getSurface().isValid();
|
| - }
|
| -
|
| - // Attach to |parent|, such that isAttached() will be correct immediately. Otherwise,
|
| - // attaching and detaching can cause surfaceCreated / surfaceDestroyed callbacks without
|
| - // View.hasParent being up to date.
|
| - public void attachTo(ViewGroup parent, FrameLayout.LayoutParams lp) {
|
| - mParent = parent;
|
| - mParent.addView(surfaceView, lp);
|
| - }
|
| -
|
| - public void detachFromParent() {
|
| - final ViewGroup parent = mParent;
|
| - // Since removeView can call surfaceDestroyed before returning, be sure that isAttached
|
| - // will return false.
|
| - mParent = null;
|
| - parent.removeView(surfaceView);
|
| - }
|
| -
|
| - public boolean isAttached() {
|
| - return mParent != null;
|
| - }
|
| - }
|
| -
|
| - // SurfaceView with a translucent PixelFormat.
|
| - private final SurfaceState mTranslucent;
|
| -
|
| - // SurfaceView with an opaque PixelFormat.
|
| - private final SurfaceState mOpaque;
|
| -
|
| - // Surface that we last gave to the client with surfaceCreated. Cleared when we call
|
| - // surfaceDestroyed on |mClient|. Note that it's not necessary that Android has notified us
|
| - // the surface has been destroyed; we deliberately keep it around until the client tells us that
|
| - // it's okay to get rid of it.
|
| - private SurfaceState mOwnedByClient;
|
| -
|
| - // Surface that was most recently requested by the client.
|
| - private SurfaceState mRequestedByClient;
|
| -
|
| - // Client that we notify about surface change events.
|
| - private SurfaceHolder.Callback mClient;
|
| -
|
| - // View to which we'll attach the SurfaceView.
|
| - private final ViewGroup mParentView;
|
| -
|
| - public CompositorSurfaceManager(ViewGroup parentView, SurfaceHolder.Callback client) {
|
| - mParentView = parentView;
|
| - mClient = client;
|
| -
|
| - mTranslucent = new SurfaceState(parentView.getContext(), PixelFormat.TRANSLUCENT, this);
|
| - mOpaque = new SurfaceState(mParentView.getContext(), PixelFormat.OPAQUE, this);
|
| - }
|
| -
|
| - /**
|
| - * Turn off everything.
|
| - */
|
| - public void shutDown() {
|
| - mTranslucent.surfaceHolder().removeCallback(this);
|
| - mOpaque.surfaceHolder().removeCallback(this);
|
| - }
|
| -
|
| - /**
|
| - * Called by the client to request a surface. Once called, we guarantee that the next call to
|
| - * surfaceCreated will match the most recent value of |format|. If the surface is already
|
| - * available for use, then we'll send synthetic callbacks as though it were destroyed and
|
| - * recreated. Note that |format| must be either OPAQUE or TRANSLUCENT.
|
| - */
|
| - public void requestSurface(int format) {
|
| - mRequestedByClient = (format == PixelFormat.TRANSLUCENT) ? mTranslucent : mOpaque;
|
| -
|
| - // If destruction is pending, then we must wait for it to complete. When we're notified
|
| - // that it is destroyed, we'll re-start construction if the client still wants this surface.
|
| - // Note that we could send a surfaceDestroyed for the owned surface, if there is one, but we
|
| - // defer it until later so that the client can still use it until the new one is ready.
|
| - if (mRequestedByClient.destroyPending) return;
|
| -
|
| - // The requested surface isn't being torn down.
|
| -
|
| - // If the surface isn't attached yet, then attach it. Otherwise, we're still waiting for
|
| - // the surface to be created, or we've already received surfaceCreated for it.
|
| - if (!mRequestedByClient.isAttached()) {
|
| - attachSurfaceNow(mRequestedByClient);
|
| - assert mRequestedByClient.isAttached();
|
| - return;
|
| - }
|
| -
|
| - // Surface is not pending destroy, and is attached. See if we need to send any synthetic
|
| - // callbacks to the client. If we're expecting a callback from Android, then we'll handle
|
| - // it when it arrives instead.
|
| - if (mRequestedByClient.createPending) return;
|
| -
|
| - // Surface is attached and no create is pending. Send a synthetic create. Note that, if
|
| - // Android destroyed the surface itself, then we'd have set |createPending| at that point.
|
| - // We don't check |isValid| here, since, technically, there could be a destroy in flight
|
| - // from Android. It's okay; we'll just notify the client at that point. Either way, we
|
| - // must tell the client that it now owns the surface.
|
| -
|
| - // Send a notification about any owned surface. Note that this can be |mRequestedByClient|
|
| - // which is fine. We'll send destroy / create for it. Also note that we don't actually
|
| - // start tear-down of the owned surface; the client notifies us via doneWithUnownedSurface
|
| - // when it is safe to do that.
|
| - disownClientSurface(mOwnedByClient);
|
| -
|
| - // The client now owns |mRequestedByClient|. Notify it that it's ready.
|
| - mOwnedByClient = mRequestedByClient;
|
| - mClient.surfaceCreated(mOwnedByClient.surfaceHolder());
|
| -
|
| - // See if we're expecting a surfaceChanged. If not, then send a synthetic one.
|
| - if (mOwnedByClient.format != PixelFormat.UNKNOWN) {
|
| - mClient.surfaceChanged(mOwnedByClient.surfaceHolder(), mOwnedByClient.width,
|
| - mOwnedByClient.height, mOwnedByClient.format);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Called to notify us that the client no longer needs the surface that it doesn't own. This
|
| - * tells us that we may destroy it. Note that it's okay if it never had an unowned surface.
|
| - */
|
| - public void doneWithUnownedSurface() {
|
| - if (mOwnedByClient == null) return;
|
| -
|
| - SurfaceState unowned = (mOwnedByClient == mTranslucent) ? mOpaque : mTranslucent;
|
| -
|
| - if (mRequestedByClient == unowned) {
|
| - // Client is giving us back a surface that it's since requested but hasn't gotten yet.
|
| - // Do nothing. It will be notified when the new surface is ready, and it can call us
|
| - // again for the other surface, if it wants.
|
| - return;
|
| - }
|
| -
|
| - // Start destruction of this surface. To prevent recursive call-backs to the client, we
|
| - // post this for later.
|
| - detachSurfaceLater(unowned);
|
| - }
|
| -
|
| - /**
|
| - * Return the currently owned SurfaceHolder, if any.
|
| - */
|
| - public SurfaceHolder getHolder() {
|
| - return mOwnedByClient != null ? mOwnedByClient.surfaceHolder() : null;
|
| - }
|
| -
|
| - /**
|
| - * Destroy and re-create the surface. Useful for a JB workaround needed by CompositorView.
|
| - */
|
| - public void recreateSurfaceForJellyBean() {
|
| - // If they don't have a surface, then they'll get a new one anyway.
|
| - if (mOwnedByClient == null) return;
|
| -
|
| - // Notify the client that it no longer owns this surface, then destroy it. When destruction
|
| - // completes, we will recreate it automatically, since it will look like the client since
|
| - // re-requested it. That's why we send surfaceDestroyed here rather than letting our
|
| - // surfaceDestroyed do it when destruction completes. If we just started destruction while
|
| - // the client still owns the surface, then our surfaceDestroyed would assume that Android
|
| - // initiated the destruction, and wait for Android to recreate it.
|
| -
|
| - mParentView.post(new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - if (mOwnedByClient == null) return;
|
| - SurfaceState owned = mOwnedByClient;
|
| - mClient.surfaceDestroyed(mOwnedByClient.surfaceHolder());
|
| - mOwnedByClient = null;
|
| - detachSurfaceNow(owned);
|
| - }
|
| - });
|
| - }
|
| -
|
| - @Override
|
| - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
| - SurfaceState state = getStateForHolder(holder);
|
| - assert state != null;
|
| -
|
| - // If this is the surface that the client currently cares about, then notify the client.
|
| - // Note that surfaceChanged is guaranteed to come only after surfaceCreated. Also, if the
|
| - // client has requested a different surface but hasn't gotten it yet, then skip this.
|
| - if (state == mOwnedByClient && state == mRequestedByClient) {
|
| - state.width = width;
|
| - state.height = height;
|
| - state.format = format;
|
| - mClient.surfaceChanged(holder, format, width, height);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void surfaceCreated(SurfaceHolder holder) {
|
| - SurfaceState state = getStateForHolder(holder);
|
| - assert state != null;
|
| - // Note that |createPending| might not be set, if Android destroyed and recreated this
|
| - // surface on its own.
|
| -
|
| - if (state != mRequestedByClient) {
|
| - // Surface is created, but it's not the one that's been requested most recently. Just
|
| - // destroy it again.
|
| - detachSurfaceLater(state);
|
| - return;
|
| - }
|
| -
|
| - // No create is pending.
|
| - state.createPending = false;
|
| -
|
| - // A surfaceChanged should arrive.
|
| - state.format = PixelFormat.UNKNOWN;
|
| -
|
| - // The client requested a surface, and it's now available. If the client owns a surface,
|
| - // then notify it that it doesn't. Note that the client can't own |state| at this point,
|
| - // since we would have removed ownership when we got surfaceDestroyed. It's okay if the
|
| - // client doesn't own either surface.
|
| - assert mOwnedByClient != state;
|
| - disownClientSurface(mOwnedByClient);
|
| -
|
| - // The client now owns this surface, so notify it.
|
| - mOwnedByClient = mRequestedByClient;
|
| - mClient.surfaceCreated(mOwnedByClient.surfaceHolder());
|
| - }
|
| -
|
| - @Override
|
| - public void surfaceDestroyed(SurfaceHolder holder) {
|
| - SurfaceState state = getStateForHolder(holder);
|
| - assert state != null;
|
| -
|
| - // If no destroy is pending, then Android chose to destroy this surface and will, hopefully,
|
| - // re-create it at some point. Otherwise, a destroy is either posted or has already
|
| - // detached this SurfaceView. If it's already detached, then the destruction is complete
|
| - // and we can clear |destroyPending|. Otherwise, Android has destroyed this surface while
|
| - // our destroy was posted, and might even return it before it runs. When the post runs, it
|
| - // can sort that out based on whether the surface is valid or not.
|
| - if (!state.destroyPending) {
|
| - state.createPending = true;
|
| - } else if (!state.isAttached()) {
|
| - state.destroyPending = false;
|
| - }
|
| -
|
| - state.format = PixelFormat.UNKNOWN;
|
| -
|
| - // If the client owns this surface, then notify it synchronously that it no longer does.
|
| - // This can happen if Android destroys the surface on its own. It's also possible that
|
| - // we've detached it, if a destroy was pending. Either way, notify the client.
|
| - if (state == mOwnedByClient) {
|
| - disownClientSurface(mOwnedByClient);
|
| -
|
| - // Do not re-request the surface here. If android gives the surface back, then we'll
|
| - // re-signal the client about construction.
|
| - return;
|
| - }
|
| -
|
| - // The client doesn't own this surface, but might want it.
|
| - // If the client has requested this surface, then start construction on it. The client will
|
| - // be notified when it completes. This can happen if the client re-requests a surface after
|
| - // we start destruction on it from a previous request, for example. We post this for later,
|
| - // since we might be called while removing |state| from the view tree. In general, posting
|
| - // from here is good.
|
| - if (state == mRequestedByClient && !state.isAttached()) {
|
| - attachSurfaceLater(state);
|
| - } else if (state != mRequestedByClient && state.isAttached()) {
|
| - // This isn't the requested surface. If android destroyed it, then also unhook it so
|
| - // that it isn't recreated later.
|
| - detachSurfaceLater(state);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Update the background drawable on all surfaces.
|
| - */
|
| - public void setBackgroundDrawable(Drawable background) {
|
| - mTranslucent.surfaceView.setBackgroundDrawable(background);
|
| - mOpaque.surfaceView.setBackgroundDrawable(background);
|
| - }
|
| -
|
| - /**
|
| - * Set |willNotDraw| on all surfaces.
|
| - */
|
| - public void setWillNotDraw(boolean willNotDraw) {
|
| - mTranslucent.surfaceView.setWillNotDraw(willNotDraw);
|
| - mOpaque.surfaceView.setWillNotDraw(willNotDraw);
|
| - }
|
| -
|
| - /**
|
| - * Return the SurfaceState for |holder|, or null if it isn't either.
|
| - */
|
| - private SurfaceState getStateForHolder(SurfaceHolder holder) {
|
| - if (mTranslucent.surfaceHolder() == holder) return mTranslucent;
|
| -
|
| - if (mOpaque.surfaceHolder() == holder) return mOpaque;
|
| -
|
| - return null;
|
| - }
|
| -
|
| - /**
|
| - * Attach |state| to |mParentView| immedaitely.
|
| - */
|
| - private void attachSurfaceNow(SurfaceState state) {
|
| - if (state.isAttached()) return;
|
| -
|
| - // If there is a destroy in-flight for this surface, then do nothing.
|
| - if (state.destroyPending) return;
|
| -
|
| - state.createPending = true;
|
| - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
|
| - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
| - state.attachTo(mParentView, lp);
|
| - mParentView.bringChildToFront(state.surfaceView);
|
| - mParentView.postInvalidateOnAnimation();
|
| - }
|
| -
|
| - /**
|
| - * Post a Runnable to attach |state|. This is helpful, since one cannot directly interact with
|
| - * the View heirarchy during Surface callbacks.
|
| - */
|
| - private void attachSurfaceLater(final SurfaceState state) {
|
| - // We shouldn't try to post construction if there's an in-flight destroy.
|
| - assert !state.destroyPending;
|
| - state.createPending = true;
|
| -
|
| - mParentView.post(new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - attachSurfaceNow(state);
|
| - }
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Cause the client to disown |state| if it currently owns it. This involves notifying it that
|
| - * the surface has been destroyed (recall that ownership involves getting created). It's okay
|
| - * if |state| is null or isn't owned by the client.
|
| - */
|
| - private void disownClientSurface(SurfaceState state) {
|
| - if (mOwnedByClient != state || state == null) return;
|
| -
|
| - mClient.surfaceDestroyed(mOwnedByClient.surfaceHolder());
|
| - mOwnedByClient = null;
|
| - }
|
| -
|
| - /**
|
| - * Detach |state| from |mParentView| immediately.
|
| - */
|
| - private void detachSurfaceNow(SurfaceState state) {
|
| - // If we're called while we're not attached, then do nothing. This makes it easier for the
|
| - // client, since it doesn't have to keep track of whether the outgoing surface has been
|
| - // destroyed or not. The client will be notified (or has already) when the surface is
|
| - // destroyed, if it currently owns it.
|
| - if (state.isAttached()) {
|
| - // We are attached. If the surface is not valid, then Android has destroyed it for some
|
| - // other reason, and we should clean up. Otherwise, just wait for Android to finish.
|
| - final boolean valid = state.isValid();
|
| -
|
| - // If the surface is valid, then we expect a callback to surfaceDestroyed eventually.
|
| - state.destroyPending = valid;
|
| -
|
| - // Note that this might call back surfaceDestroyed before returning!
|
| - state.detachFromParent();
|
| -
|
| - // If the surface was valid before, then we expect a surfaceDestroyed callback, which
|
| - // might have arrived during removeView. Either way, that callback will finish cleanup
|
| - // of |state|.
|
| - if (valid) return;
|
| - }
|
| -
|
| - // The surface isn't attached, or was attached but wasn't currently valid. Either way,
|
| - // we're not going to get a destroy, so notify the client now if needed.
|
| - disownClientSurface(state);
|
| -
|
| - // If the client has since re-requested the surface, then start construction.
|
| - if (state == mRequestedByClient) attachSurfaceNow(mRequestedByClient);
|
| - }
|
| -
|
| - /**
|
| - * Post detachment of |state|. This is safe during Surface callbacks.
|
| - */
|
| - private void detachSurfaceLater(final SurfaceState state) {
|
| - // If |state| is not attached, then do nothing. There might be a destroy pending from
|
| - // Android, but in any case leave it be.
|
| - if (!state.isAttached()) return;
|
| -
|
| - state.destroyPending = true;
|
| - mParentView.post(new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - detachSurfaceNow(state);
|
| - }
|
| - });
|
| - }
|
| -}
|
|
|