summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-14 15:10:56 +0000
committerccameron@chromium.org <ccameron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-14 15:10:56 +0000
commite5166792f54692e35afeeecde5842d912b09da67 (patch)
tree51b7b145ac75deba2ad365a3f92e51fece4d7304
parentc93ad77ea87701d70d6db1fe044e86b9a1c5e540 (diff)
downloadchromium_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
-rw-r--r--content/browser/renderer_host/compositing_iosurface_layer_mac.h12
-rw-r--r--content/browser/renderer_host/compositing_iosurface_layer_mac.mm32
-rw-r--r--content/browser/renderer_host/render_widget_host_view_mac.h9
-rw-r--r--content/browser/renderer_host/render_widget_host_view_mac.mm10
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);