Index: content/browser/renderer_host/compositing_iosurface_layer_mac.mm |
diff --git a/content/browser/renderer_host/compositing_iosurface_layer_mac.mm b/content/browser/renderer_host/compositing_iosurface_layer_mac.mm |
index f96e07f925a01ee4a766dc5da238149bd36d3ec7..a55636936af7a576a12c9e6326c6b5565d2ea83a 100644 |
--- a/content/browser/renderer_host/compositing_iosurface_layer_mac.mm |
+++ b/content/browser/renderer_host/compositing_iosurface_layer_mac.mm |
@@ -17,6 +17,29 @@ |
#include "ui/gfx/size_conversions.h" |
#include "ui/gl/gpu_switching_manager.h" |
+@interface CompositingIOSurfaceLayer(Private) |
+- (void)immediatelyForceDisplayAndAck; |
+- (void)ackPendingFrame:(bool)success; |
+- (void)timerFired; |
+@end |
+ |
+namespace content { |
+ |
+// The base::DelayTimer needs a C++ class to operate on, rather than Objective C |
+// class. This helper class provides a bridge between the two. |
+class CompositingIOSurfaceLayerHelper { |
+ public: |
+ CompositingIOSurfaceLayerHelper(CompositingIOSurfaceLayer* layer) |
+ : layer_(layer) {} |
+ void TimerFired() { |
+ [layer_ timerFired]; |
+ } |
+ private: |
+ CompositingIOSurfaceLayer* layer_; |
+}; |
+ |
+} // namespace content |
+ |
@implementation CompositingIOSurfaceLayer |
- (content::CompositingIOSurfaceMac*)iosurface { |
@@ -33,11 +56,18 @@ |
if (self = [super init]) { |
iosurface_ = iosurface; |
client_ = client; |
+ helper_.reset(new content::CompositingIOSurfaceLayerHelper(self)); |
+ timer_.reset(new base::DelayTimer<content::CompositingIOSurfaceLayerHelper>( |
+ FROM_HERE, |
+ base::TimeDelta::FromSeconds(1) / 6, |
+ helper_.get(), |
+ &content::CompositingIOSurfaceLayerHelper::TimerFired)); |
context_ = content::CompositingIOSurfaceContext::Get( |
content::CompositingIOSurfaceContext::kCALayerContextWindowNumber); |
DCHECK(context_); |
needs_display_ = NO; |
+ has_pending_frame_ = NO; |
did_not_draw_counter_ = 0; |
[self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; |
@@ -57,18 +87,17 @@ |
} |
- (void)gotNewFrame { |
+ has_pending_frame_ = YES; |
+ timer_->Reset(); |
+ |
+ // A trace value of 2 indicates that there is a pending swap ack. See |
+ // canDrawInCGLContext for other value meanings. |
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, 2); |
+ |
if (context_ && context_->is_vsync_disabled()) { |
// If vsync is disabled, draw immediately and don't bother trying to use |
// the isAsynchronous property to ensure smooth animation. |
- [self setNeedsDisplay]; |
- [self displayIfNeeded]; |
- |
- // Calls to setNeedsDisplay can sometimes be ignored, especially if issued |
- // rapidly (e.g, with vsync off). This is unacceptable because the failure |
- // to ack a single frame will hang the renderer. Ensure that the renderer |
- // not be blocked by lying and claiming that we drew the frame. |
- if (needs_display_ && client_) |
- client_->AcceleratedLayerDidDrawFrame(true); |
+ [self immediatelyForceDisplayAndAck]; |
} else { |
needs_display_ = YES; |
if (![self isAsynchronous]) |
@@ -76,6 +105,34 @@ |
} |
} |
+// Private methods: |
+ |
+- (void)immediatelyForceDisplayAndAck { |
+ [self setNeedsDisplay]; |
+ [self displayIfNeeded]; |
+ |
+ // Calls to setNeedsDisplay can sometimes be ignored, especially if issued |
+ // rapidly (e.g, with vsync off). This is unacceptable because the failure |
+ // to ack a single frame will hang the renderer. Ensure that the renderer |
+ // not be blocked by lying and claiming that we drew the frame. |
+ [self ackPendingFrame:true]; |
+} |
+ |
+- (void)ackPendingFrame:(bool)success { |
+ if (!has_pending_frame_) |
+ return; |
+ |
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, 0); |
+ has_pending_frame_ = NO; |
+ if (client_) |
+ client_->AcceleratedLayerDidDrawFrame(success); |
+} |
+ |
+- (void)timerFired { |
+ if (has_pending_frame_) |
+ [self immediatelyForceDisplayAndAck]; |
+} |
+ |
// The remaining methods implement the CAOpenGLLayer interface. |
- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { |
@@ -106,12 +163,9 @@ |
// ack) indicates that we did not request a draw. This would be more natural |
// to do with a tracing pseudo-thread |
// http://crbug.com/366300 |
- if (client_) { |
- TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, |
- needs_display_ ? 3 : 1); |
- TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, |
- client_->AcceleratedLayerHasNotAckedPendingFrame() ? 2 : 0); |
- } |
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, needs_display_ ? 3 : 1); |
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, |
+ has_pending_frame_ ? 2 : 0); |
// If we return NO 30 times in a row, switch to being synchronous to avoid |
// burning CPU cycles on this callback. |
@@ -153,9 +207,8 @@ |
bool draw_succeeded = iosurface_->DrawIOSurface( |
context_, window_rect, window_scale_factor, false); |
+ [self ackPendingFrame:draw_succeeded]; |
needs_display_ = NO; |
- if (client_) |
- client_->AcceleratedLayerDidDrawFrame(draw_succeeded); |
[super drawInCGLContext:glContext |
pixelFormat:pixelFormat |