diff options
author | ccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-14 15:10:56 +0000 |
---|---|---|
committer | ccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-14 15:10:56 +0000 |
commit | e5166792f54692e35afeeecde5842d912b09da67 (patch) | |
tree | 51b7b145ac75deba2ad365a3f92e51fece4d7304 | |
parent | c93ad77ea87701d70d6db1fe044e86b9a1c5e540 (diff) | |
download | chromium_src-e5166792f54692e35afeeecde5842d912b09da67.zip chromium_src-e5166792f54692e35afeeecde5842d912b09da67.tar.gz chromium_src-e5166792f54692e35afeeecde5842d912b09da67.tar.bz2 |
Dynamically set CAOpenGLLAyer to be asynchronous
The supported non-blocking path for drawing content that updates on the
order of 60fps is to create a CAOpenGLLayer and set its asynchronous
property to YES. In practice, just calling setNeedsDisplay about
about 60fps results in noticeable jank.
The downside to setting the CAOpenGLLayer to be asynchronous is that it
will be asked, every vsync, if it has anything new to draw (via the
canDrawInOpenGLContext callback), resulting in high CPU usage, even
when idle.
The solution to this is to dynamically set the isAsynchronous property
to YES when new frames are seen, and leave it at NO when it has been a
while (defined as a quarter second arbitrarily) since a new frame has
been generated.
Note that when a new frame is generated, the gotNewFrame call is made,
while when a re-display is required (say, because the window became
visible), only setNeedsDisplay is called. This is to avoid going in to
asynchronous mode unnecessarily.
Also note that the DelayTimer object is hung off the RWHVMac class
instead of the CompositingIOSurfaceLayer object. This is because
the DelayTimer class requires a C++ class to hang off of, instead of an
Objective C interface.
BUG=340133
Review URL: https://codereview.chromium.org/165623002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@251323 0039d316-1c4b-4281-b951-d872f2087c98
4 files changed, 62 insertions, 1 deletions
diff --git a/content/browser/renderer_host/compositing_iosurface_layer_mac.h b/content/browser/renderer_host/compositing_iosurface_layer_mac.h index a03ab1c..af5d5b3 100644 --- a/content/browser/renderer_host/compositing_iosurface_layer_mac.h +++ b/content/browser/renderer_host/compositing_iosurface_layer_mac.h @@ -20,6 +20,11 @@ class RenderWidgetHostViewMac; @private content::RenderWidgetHostViewMac* renderWidgetHostView_; scoped_refptr<content::CompositingIOSurfaceContext> context_; + + // Used to track when canDrawInCGLContext should return YES. This can be + // in response to receiving a new compositor frame, or from any of the events + // that cause setNeedsDisplay to be called on the layer. + BOOL needsDisplay_; } @property(nonatomic, readonly) @@ -35,6 +40,13 @@ class RenderWidgetHostViewMac; // |renderWidgetHostView_| is no longer valid and may no longer be dereferenced. - (void)disableCompositing; +// Called when a new frame is received. +- (void)gotNewFrame; + +// Called when it has been a while since a new frame has been received, and the +// layer should become not-asynchronous. +- (void)timerSinceGotNewFrameFired; + @end #endif // CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_LAYER_MAC_H_ diff --git a/content/browser/renderer_host/compositing_iosurface_layer_mac.mm b/content/browser/renderer_host/compositing_iosurface_layer_mac.mm index 38752b4..63f9ca3 100644 --- a/content/browser/renderer_host/compositing_iosurface_layer_mac.mm +++ b/content/browser/renderer_host/compositing_iosurface_layer_mac.mm @@ -26,6 +26,7 @@ context_ = content::CompositingIOSurfaceContext::Get( content::CompositingIOSurfaceContext::kOffscreenContextWindowNumber); DCHECK(context_); + needsDisplay_ = NO; ScopedCAActionDisabler disabler; [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; @@ -64,6 +65,24 @@ renderWidgetHostView_ = nil; } +- (void)gotNewFrame { + if (![self isAsynchronous]) { + [self setNeedsDisplay]; + [self setAsynchronous:YES]; + } else { + needsDisplay_ = YES; + } +} + +- (void)timerSinceGotNewFrameFired { + if (![self isAsynchronous]) + return; + + [self setAsynchronous:NO]; + if (needsDisplay_) + [self setNeedsDisplay]; +} + // The remaining methods implement the CAOpenGLLayer interface. - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { @@ -78,6 +97,18 @@ return CGLRetainContext(context_->cgl_context()); } +- (void)setNeedsDisplay { + needsDisplay_ = YES; + [super setNeedsDisplay]; +} + +- (BOOL)canDrawInCGLContext:(CGLContextObj)glContext + pixelFormat:(CGLPixelFormatObj)pixelFormat + forLayerTime:(CFTimeInterval)timeInterval + displayTime:(const CVTimeStamp*)timeStamp { + return needsDisplay_; +} + - (void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval @@ -143,6 +174,7 @@ return; } + needsDisplay_ = NO; renderWidgetHostView_->SendPendingLatencyInfoToHost(); } diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h index 2c3d44f..e141633 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h @@ -435,6 +435,11 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, scoped_ptr<CompositingIOSurfaceMac> compositing_iosurface_; scoped_refptr<CompositingIOSurfaceContext> compositing_iosurface_context_; + // Timer used to dynamically transition the compositing layer in and out of + // asynchronous mode. + base::DelayTimer<RenderWidgetHostViewMac> + compositing_iosurface_layer_async_timer_; + // This holds the current software compositing framebuffer, if any. scoped_ptr<SoftwareFrameManager> software_frame_manager_; @@ -512,6 +517,10 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, // Called when a software DIB is received. void GotSoftwareFrame(); + // Called if it has been a quarter-second since a GPU SwapBuffers has been + // received. In this case, switch from polling for frames to pushing them. + void TimerSinceGotAcceleratedFrameFired(); + void OnPluginFocusChanged(bool focused, int plugin_id); void OnStartPluginIme(); diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index 510dec1..a2d991d 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -408,6 +408,9 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) last_frame_was_accelerated_(false), text_input_type_(ui::TEXT_INPUT_TYPE_NONE), can_compose_inline_(true), + compositing_iosurface_layer_async_timer_( + FROM_HERE, base::TimeDelta::FromMilliseconds(250), + this, &RenderWidgetHostViewMac::TimerSinceGotAcceleratedFrameFired), allow_overlapping_views_(false), use_core_animation_(false), pending_latency_info_delay_(0), @@ -1405,7 +1408,8 @@ void RenderWidgetHostViewMac::CompositorSwapBuffers( if (!about_to_validate_and_paint_) { if (use_core_animation_) { DCHECK(compositing_iosurface_layer_); - [compositing_iosurface_layer_ setNeedsDisplay]; + compositing_iosurface_layer_async_timer_.Reset(); + [compositing_iosurface_layer_ gotNewFrame]; } else { if (!DrawIOSurfaceWithoutCoreAnimation()) { [cocoa_view_ setNeedsDisplay:YES]; @@ -1903,6 +1907,10 @@ void RenderWidgetHostViewMac::GotSoftwareFrame() { } } +void RenderWidgetHostViewMac::TimerSinceGotAcceleratedFrameFired() { + [compositing_iosurface_layer_ timerSinceGotNewFrameFired]; +} + void RenderWidgetHostViewMac::SetActive(bool active) { if (render_widget_host_) { render_widget_host_->SetActive(active); |