Commit c88bf1a4 authored by ccameron@chromium.org's avatar ccameron@chromium.org

Mac ÜC: Enable GPU back-pressure from the browser to the renderer

When the browser compositor does a SwapBuffers to its output surface,
take a lock on the compositor until that swap is displayed on the screen.
This prevents the renderer from running ahead of the browser.

If the renderer sends a new frame (it can send at most one), then track
this event, and request redraw as soon as the lock is released.

BUG=314190

Review URL: https://codereview.chromium.org/316103004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@275478 0039d316-1c4b-4281-b951-d872f2087c98
parent 4b7e4db8
......@@ -15,7 +15,15 @@
#include "ui/compositor/compositor.h"
#include "ui/gfx/geometry/size.h"
@class BrowserCompositorViewMac;
namespace content {
class BrowserCompositorViewMacHelper;
class BrowserCompositorViewMacClient {
public:
virtual void BrowserCompositorDidDrawFrame() = 0;
};
} // namespace content
// Additions to the NSView interface for compositor frames.
@interface NSView (BrowserCompositorView)
......@@ -26,7 +34,7 @@
- (void)gotSoftwareFrame:(cc::SoftwareFrameData*)frame_data
withScaleFactor:(float)scale_factor
withCanvas:(SkCanvas*)canvas;
@end
@end // NSView (BrowserCompositorView)
// NSView drawn by a ui::Compositor. The superview of this view is responsible
// for changing the ui::Compositor SizeAndScale and calling layoutLayers when
......@@ -34,20 +42,28 @@
// the needs of RenderWidgetHostViewCocoa, and could change.
@interface BrowserCompositorViewMac : NSView {
scoped_ptr<ui::Compositor> compositor_;
base::scoped_nsobject<CALayer> background_layer_;
base::scoped_nsobject<CompositingIOSurfaceLayer> accelerated_layer_;
base::scoped_nsobject<SoftwareLayer> software_layer_;
content::BrowserCompositorViewMacClient* client_;
scoped_ptr<content::BrowserCompositorViewMacHelper> helper_;
}
// Initialize to render the content of a specific superview.
- (id)initWithSuperview:(NSView*)view;
- (id)initWithSuperview:(NSView*)view
withClient:(content::BrowserCompositorViewMacClient*)client;
// Re-position the layers to the correct place when this view's superview
// changes size, or when the accelerated or software content changes.
- (void)layoutLayers;
// Disallow further access to the client.
- (void)resetClient;
// Access the underlying ui::Compositor for this view.
- (ui::Compositor*)compositor;
@end
@end // BrowserCompositorViewMac
#endif // CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_VIEW_MAC_H_
......@@ -13,6 +13,36 @@
#include "ui/base/cocoa/animation_utils.h"
#include "ui/gl/scoped_cgl.h"
@interface BrowserCompositorViewMac (Private)
- (void)layerDidDrawFrame;
- (void)gotAcceleratedLayerError;
@end // BrowserCompositorViewMac (Private)
namespace content {
// The CompositingIOSurfaceLayerClient interface needs to be implemented as a
// C++ class to operate on, rather than Objective C class. This helper class
// provides a bridge between the two.
class BrowserCompositorViewMacHelper : public CompositingIOSurfaceLayerClient {
public:
BrowserCompositorViewMacHelper(BrowserCompositorViewMac* view)
: view_(view) {}
virtual ~BrowserCompositorViewMacHelper() {}
private:
// CompositingIOSurfaceLayerClient implementation:
virtual void AcceleratedLayerDidDrawFrame(bool succeeded) OVERRIDE {
[view_ layerDidDrawFrame];
if (!succeeded)
[view_ gotAcceleratedLayerError];
}
BrowserCompositorViewMac* view_;
};
} // namespace content
// The default implementation of additions to the NSView interface for browser
// compositing should never be called. Log an error if they are.
@implementation NSView (BrowserCompositorView)
......@@ -34,8 +64,12 @@
@implementation BrowserCompositorViewMac : NSView
- (id)initWithSuperview:(NSView*)view {
- (id)initWithSuperview:(NSView*)view
withClient:(content::BrowserCompositorViewMacClient*)client {
if (self = [super init]) {
client_ = client;
helper_.reset(new content::BrowserCompositorViewMacHelper(self));
// Disable the fade-in animation as the layer and view are added.
ScopedCAActionDisabler disabler;
......@@ -51,6 +85,14 @@
return self;
}
- (void)gotAcceleratedLayerError {
if (!accelerated_layer_)
return;
[accelerated_layer_ context]->PoisonContextAndSharegroup();
compositor_->ScheduleFullRedraw();
}
// This function closely mirrors RenderWidgetHostViewMac::LayoutLayers. When
// only delegated rendering is supported, only one copy of this code will
// need to exist.
......@@ -97,6 +139,11 @@
[software_layer_ setBounds:new_background_frame];
}
- (void)resetClient {
[accelerated_layer_ resetClient];
client_ = NULL;
}
- (ui::Compositor*)compositor {
return compositor_.get();
}
......@@ -125,7 +172,7 @@
accelerated_layer_.reset([[CompositingIOSurfaceLayer alloc]
initWithIOSurface:iosurface
withScaleFactor:scale_factor
withClient:NULL]);
withClient:helper_.get()]);
[[self layer] addSublayer:accelerated_layer_];
}
......@@ -184,6 +231,18 @@
[accelerated_layer_ removeFromSuperlayer];
accelerated_layer_.reset();
}
// This call can be nested insider ui::Compositor commit calls, and can also
// make additional ui::Compositor commit calls. Avoid the potential recursion
// by acknowledging the frame asynchronously.
[self performSelector:@selector(layerDidDrawFrame)
withObject:nil
afterDelay:0];
}
- (void)layerDidDrawFrame {
if (client_)
client_->BrowserCompositorDidDrawFrame();
}
@end // BrowserCompositorViewMac
......@@ -751,14 +751,15 @@ void DelegatedFrameHost::OnCompositingDidCommit(
void DelegatedFrameHost::OnCompositingStarted(
ui::Compositor* compositor, base::TimeTicks start_time) {
last_draw_ended_ = start_time;
client_->DelegatedCompositorDidSwapBuffers();
}
void DelegatedFrameHost::OnCompositingEnded(
ui::Compositor* compositor) {
}
void DelegatedFrameHost::OnCompositingAborted(
ui::Compositor* compositor) {
void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
client_->DelegatedCompositorAbortedSwapBuffers();
}
void DelegatedFrameHost::OnCompositingLockStateChanged(
......
......@@ -42,6 +42,8 @@ class CONTENT_EXPORT DelegatedFrameHostClient {
virtual ui::Layer* GetLayer() = 0;
virtual RenderWidgetHostImpl* GetHost() = 0;
virtual void SchedulePaintInRect(const gfx::Rect& damage_rect_in_dip) = 0;
virtual void DelegatedCompositorDidSwapBuffers() = 0;
virtual void DelegatedCompositorAbortedSwapBuffers() = 0;
virtual bool IsVisible() = 0;
virtual scoped_ptr<ResizeLock> CreateResizeLock(
bool defer_compositor_lock) = 0;
......
......@@ -21,7 +21,7 @@ class CompositingIOSurfaceLayerClient {
virtual void AcceleratedLayerDidDrawFrame(bool succeeded) = 0;
};
}
} // namespace content
// The CoreAnimation layer for drawing accelerated content.
@interface CompositingIOSurfaceLayer : CAOpenGLLayer {
......
......@@ -424,6 +424,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual RenderWidgetHostImpl* GetHost() OVERRIDE;
virtual void SchedulePaintInRect(
const gfx::Rect& damage_rect_in_dip) OVERRIDE;
virtual void DelegatedCompositorDidSwapBuffers() OVERRIDE {}
virtual void DelegatedCompositorAbortedSwapBuffers() OVERRIDE {}
virtual bool IsVisible() OVERRIDE;
virtual scoped_ptr<ResizeLock> CreateResizeLock(
bool defer_compositor_lock) OVERRIDE;
......
......@@ -16,6 +16,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "content/browser/compositor/browser_compositor_view_mac.h"
#include "content/browser/compositor/delegated_frame_host.h"
#include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
#include "content/browser/renderer_host/display_link_mac.h"
......@@ -213,6 +214,7 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
public DelegatedFrameHostClient,
public IPC::Sender,
public SoftwareFrameManagerClient,
public BrowserCompositorViewMacClient,
public CompositingIOSurfaceLayerClient {
public:
// The view will associate itself with the given widget. The native view must
......@@ -343,6 +345,9 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
virtual SkBitmap::Config PreferredReadbackFormat() OVERRIDE;
// BrowserCompositorViewMacHelper implementation.
virtual void BrowserCompositorDidDrawFrame() OVERRIDE;
// CompositingIOSurfaceLayerClient implementation.
virtual void AcceleratedLayerDidDrawFrame(bool succeeded) OVERRIDE;
......@@ -448,6 +453,12 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
scoped_ptr<DelegatedFrameHost> delegated_frame_host_;
scoped_ptr<ui::Layer> root_layer_;
// This lock is taken when the browser compositor produces a frame, and is
// released when that frame is displayed. It is by this mechanism that the
// browser compositor can exert GPU backpressure on the renderer compositor.
scoped_refptr<ui::CompositorLock> browser_compositor_lock_;
bool browser_compositor_damaged_during_lock_;
// This holds the current software compositing framebuffer, if any.
scoped_ptr<SoftwareFrameManager> software_frame_manager_;
......@@ -518,6 +529,8 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
virtual RenderWidgetHostImpl* GetHost() OVERRIDE;
virtual void SchedulePaintInRect(
const gfx::Rect& damage_rect_in_dip) OVERRIDE;
virtual void DelegatedCompositorDidSwapBuffers() OVERRIDE;
virtual void DelegatedCompositorAbortedSwapBuffers() OVERRIDE;
virtual bool IsVisible() OVERRIDE;
virtual scoped_ptr<ResizeLock> CreateResizeLock(
bool defer_compositor_lock) OVERRIDE;
......@@ -590,6 +603,12 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
// Send updated vsync parameters to the renderer.
void SendVSyncParametersToRenderer();
// Release the browser compositor lock, and request another frame from the
// browser compositor. Because this can be requested from inside compositor
// calbacks, post it as task instead of calling it directly.
void PostReleaseBrowserCompositorLock();
void ReleaseBrowserCompositorLock();
// The associated view. This is weak and is inserted into the view hierarchy
// to own this RenderWidgetHostViewMac object. Set to nil at the start of the
// destructor.
......
......@@ -27,7 +27,6 @@
#include "base/sys_info.h"
#import "content/browser/accessibility/browser_accessibility_cocoa.h"
#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
#include "content/browser/compositor/browser_compositor_view_mac.h"
#include "content/browser/compositor/resize_lock.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/frame_tree_node.h"
......@@ -426,7 +425,30 @@ RenderWidgetHostImpl* RenderWidgetHostViewMac::GetHost() {
void RenderWidgetHostViewMac::SchedulePaintInRect(
const gfx::Rect& damage_rect_in_dip) {
[browser_compositor_view_ compositor]->Draw();
if (browser_compositor_lock_)
browser_compositor_damaged_during_lock_ = true;
else
[browser_compositor_view_ compositor]->ScheduleFullRedraw();
}
void RenderWidgetHostViewMac::DelegatedCompositorDidSwapBuffers() {
// If this view is not visible then do not lock the compositor, because the
// wait for the surface to be drawn will time out.
NSWindow* window = [cocoa_view_ window];
if (!window)
return;
if (window && [window respondsToSelector:@selector(occlusionState)]) {
bool window_is_occluded =
!([window occlusionState] & NSWindowOcclusionStateVisible);
if (window_is_occluded)
return;
}
browser_compositor_lock_ =
[browser_compositor_view_ compositor]->GetCompositorLock();
}
void RenderWidgetHostViewMac::DelegatedCompositorAbortedSwapBuffers() {
PostReleaseBrowserCompositorLock();
}
bool RenderWidgetHostViewMac::IsVisible() {
......@@ -511,10 +533,6 @@ RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
// pointer.
cocoa_view_ = nil;
// Delete the delegated frame state.
delegated_frame_host_.reset();
root_layer_.reset();
UnlockMouse();
// Make sure that the layer doesn't reach into the now-invalid object.
......@@ -826,6 +844,23 @@ void RenderWidgetHostViewMac::UpdateDisplayLink() {
}
}
void RenderWidgetHostViewMac::PostReleaseBrowserCompositorLock() {
base::MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&RenderWidgetHostViewMac::ReleaseBrowserCompositorLock,
weak_factory_.GetWeakPtr()));
}
void RenderWidgetHostViewMac::ReleaseBrowserCompositorLock() {
if (!browser_compositor_view_)
return;
browser_compositor_lock_ = NULL;
if (browser_compositor_damaged_during_lock_) {
browser_compositor_damaged_during_lock_ = false;
[browser_compositor_view_ compositor]->ScheduleFullRedraw();
}
}
void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
if (!render_widget_host_ || !display_link_)
return;
......@@ -882,6 +917,7 @@ void RenderWidgetHostViewMac::WasHidden() {
// Any pending frames will not be displayed until this is shown again. Ack
// them now.
SendPendingSwapAck();
PostReleaseBrowserCompositorLock();
// If we have a renderer, then inform it that we are being hidden so it can
// reduce its resource utilization.
......@@ -1106,6 +1142,13 @@ void RenderWidgetHostViewMac::Destroy() {
// object needs to survive until the stack unwinds.
pepper_fullscreen_window_.autorelease();
// Delete the delegated frame state, which will reach back into
// render_widget_host_.
browser_compositor_lock_ = NULL;
[browser_compositor_view_ resetClient];
delegated_frame_host_.reset();
root_layer_.reset();
// We get this call just before |render_widget_host_| deletes
// itself. But we are owned by |cocoa_view_|, which may be retained
// by some other code. Examples are WebContentsViewMac's
......@@ -1894,8 +1937,8 @@ void RenderWidgetHostViewMac::OnSwapCompositorFrame(
if (frame->delegated_frame_data) {
if (!browser_compositor_view_) {
browser_compositor_view_.reset(
[[BrowserCompositorViewMac alloc] initWithSuperview:cocoa_view_]);
browser_compositor_view_.reset([[BrowserCompositorViewMac alloc]
initWithSuperview:cocoa_view_ withClient:this]);
root_layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
delegated_frame_host_.reset(new DelegatedFrameHost(this));
[browser_compositor_view_ compositor]->SetRootLayer(root_layer_.get());
......@@ -2470,6 +2513,13 @@ SkBitmap::Config RenderWidgetHostViewMac::PreferredReadbackFormat() {
return SkBitmap::kARGB_8888_Config;
}
////////////////////////////////////////////////////////////////////////////////
// BrowserCompositorViewMacClient, public:
void RenderWidgetHostViewMac::BrowserCompositorDidDrawFrame() {
PostReleaseBrowserCompositorLock();
}
////////////////////////////////////////////////////////////////////////////////
// CompositingIOSurfaceLayerClient, public:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment