Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / renderer_host / compositing_iosurface_layer_mac.mm
blob81a3529f0b56c45374e82cf450818ca8d377929f
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <OpenGL/gl.h>
10 #include "base/mac/mac_util.h"
11 #include "base/mac/sdk_forward_declarations.h"
12 #include "content/browser/renderer_host/render_widget_host_impl.h"
13 #include "content/browser/renderer_host/render_widget_host_view_mac.h"
14 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
15 #include "content/browser/renderer_host/compositing_iosurface_mac.h"
16 #include "ui/base/cocoa/animation_utils.h"
17 #include "ui/gfx/size_conversions.h"
18 #include "ui/gl/gpu_switching_manager.h"
20 ////////////////////////////////////////////////////////////////////////////////
21 // CompositingIOSurfaceLayerHelper
23 namespace content {
25 CompositingIOSurfaceLayerHelper::CompositingIOSurfaceLayerHelper(
26     CompositingIOSurfaceLayerClient* client,
27     CompositingIOSurfaceLayer* layer)
28         : client_(client),
29           layer_(layer),
30           needs_display_(false),
31           has_pending_frame_(false),
32           did_not_draw_counter_(0),
33           is_pumping_frames_(false),
34           timer_(
35               FROM_HERE,
36               base::TimeDelta::FromSeconds(1) / 6,
37               this,
38               &CompositingIOSurfaceLayerHelper::TimerFired) {}
40 CompositingIOSurfaceLayerHelper::~CompositingIOSurfaceLayerHelper() {
41   // Any acks that were waiting on this layer to draw will not occur, so ack
42   // them now to prevent blocking the renderer.
43   AckPendingFrame(true);
46 void CompositingIOSurfaceLayerHelper::GotNewFrame() {
47   // A trace value of 2 indicates that there is a pending swap ack. See
48   // canDrawInCGLContext for other value meanings.
49   TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 2);
51   has_pending_frame_ = true;
52   needs_display_ = true;
53   timer_.Reset();
55   // If reqested, draw immediately and don't bother trying to use the
56   // isAsynchronous property to ensure smooth animation. If this is while
57   // frames are being pumped then ack and display immediately to get a
58   // correct-sized frame displayed as soon as possible.
59   if (is_pumping_frames_ || client_->AcceleratedLayerShouldAckImmediately()) {
60     SetNeedsDisplayAndDisplayAndAck();
61   } else {
62     if (![layer_ isAsynchronous])
63       [layer_ setAsynchronous:YES];
64   }
67 void CompositingIOSurfaceLayerHelper::SetNeedsDisplay() {
68   needs_display_ = true;
71 bool CompositingIOSurfaceLayerHelper::CanDraw() {
72   // If we return NO 30 times in a row, switch to being synchronous to avoid
73   // burning CPU cycles on this callback.
74   if (needs_display_) {
75     did_not_draw_counter_ = 0;
76   } else {
77     did_not_draw_counter_ += 1;
78     if (did_not_draw_counter_ == 30)
79       [layer_ setAsynchronous:NO];
80   }
82   // Add an instantaneous blip to the PendingSwapAck state to indicate
83   // that CoreAnimation asked if a frame is ready. A blip up to to 3 (usually
84   // from 2, indicating that a swap ack is pending) indicates that we
85   // requested a draw. A blip up to 1 (usually from 0, indicating there is no
86   // pending swap ack) indicates that we did not request a draw. This would
87   // be more natural to do with a tracing pseudo-thread
88   // http://crbug.com/366300
89   TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, needs_display_ ? 3 : 1);
90   TRACE_COUNTER_ID1("browser", "PendingSwapAck", this,
91                     has_pending_frame_ ? 2 : 0);
93   return needs_display_;
96 void CompositingIOSurfaceLayerHelper::DidDraw(bool success) {
97   needs_display_ = false;
98   AckPendingFrame(success);
101 void CompositingIOSurfaceLayerHelper::AckPendingFrame(bool success) {
102   if (!has_pending_frame_)
103     return;
104   has_pending_frame_ = false;
105   if (success)
106     client_->AcceleratedLayerDidDrawFrame();
107   else
108     client_->AcceleratedLayerHitError();
109   // A trace value of 0 indicates that there is no longer a pending swap ack.
110   TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 0);
113 void CompositingIOSurfaceLayerHelper::SetNeedsDisplayAndDisplayAndAck() {
114   // Drawing using setNeedsDisplay and displayIfNeeded will result in
115   // subsequent canDrawInCGLContext callbacks getting dropped, and jerky
116   // animation. Disable asynchronous drawing before issuing these calls as a
117   // workaround.
118   // http://crbug.com/395827
119   if ([layer_ isAsynchronous])
120     [layer_ setAsynchronous:NO];
122   [layer_ setNeedsDisplay];
123   DisplayIfNeededAndAck();
126 void CompositingIOSurfaceLayerHelper::DisplayIfNeededAndAck() {
127   if (!needs_display_)
128     return;
130   // As in SetNeedsDisplayAndDisplayAndAck, disable asynchronous drawing before
131   // issuing displayIfNeeded.
132   // http://crbug.com/395827
133   if ([layer_ isAsynchronous])
134     [layer_ setAsynchronous:NO];
136   // Do not bother drawing while pumping new frames -- wait until the waiting
137   // block ends to draw any of the new frames.
138   if (!is_pumping_frames_)
139     [layer_ displayIfNeeded];
141   // Calls to setNeedsDisplay can sometimes be ignored, especially if issued
142   // rapidly (e.g, with vsync off). This is unacceptable because the failure
143   // to ack a single frame will hang the renderer. Ensure that the renderer
144   // not be blocked by lying and claiming that we drew the frame.
145   AckPendingFrame(true);
148 void CompositingIOSurfaceLayerHelper::TimerFired() {
149   SetNeedsDisplayAndDisplayAndAck();
152 void CompositingIOSurfaceLayerHelper::BeginPumpingFrames() {
153   is_pumping_frames_ = true;
156 void CompositingIOSurfaceLayerHelper::EndPumpingFrames() {
157   is_pumping_frames_ = false;
158   DisplayIfNeededAndAck();
161 }  // namespace content
163 ////////////////////////////////////////////////////////////////////////////////
164 // CompositingIOSurfaceLayer
166 @implementation CompositingIOSurfaceLayer
168 - (content::CompositingIOSurfaceMac*)iosurface {
169   return iosurface_.get();
172 - (content::CompositingIOSurfaceContext*)context {
173   return context_.get();
176 - (id)initWithIOSurface:(scoped_refptr<content::CompositingIOSurfaceMac>)
177                             iosurface
178         withScaleFactor:(float)scale_factor
179              withClient:(content::CompositingIOSurfaceLayerClient*)client {
180   DCHECK(iosurface);
181   if (self = [super init]) {
182     helper_.reset(new content::CompositingIOSurfaceLayerHelper(client, self));
184     iosurface_ = iosurface;
185     context_ = content::CompositingIOSurfaceContext::Get(
186         content::CompositingIOSurfaceContext::kCALayerContextWindowNumber);
187     if (!context_) {
188       LOG(ERROR) << "Failed create CompositingIOSurfaceContext";
189       [self resetClient];
190       [self release];
191       return nil;
192     }
194     [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
195     [self setAnchorPoint:CGPointMake(0, 0)];
196     // Setting contents gravity is necessary to prevent the layer from being
197     // scaled during dyanmic resizes (especially with devtools open).
198     [self setContentsGravity:kCAGravityTopLeft];
199     if ([self respondsToSelector:(@selector(setContentsScale:))]) {
200       [self setContentsScale:scale_factor];
201     }
202   }
203   return self;
206 - (void)dealloc {
207   DCHECK(!helper_);
208   [super dealloc];
211 - (void)resetClient {
212   helper_.reset();
215 - (void)gotNewFrame {
216   helper_->GotNewFrame();
219 - (void)setNeedsDisplayAndDisplayAndAck {
220   helper_->SetNeedsDisplayAndDisplayAndAck();
223 - (void)displayIfNeededAndAck {
224   helper_->DisplayIfNeededAndAck();
227 - (void)beginPumpingFrames {
228   helper_->BeginPumpingFrames();
231 - (void)endPumpingFrames {
232   helper_->EndPumpingFrames();
235 // The remaining methods implement the CAOpenGLLayer interface.
237 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
238   if (!context_)
239     return [super copyCGLPixelFormatForDisplayMask:mask];
240   return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context()));
243 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
244   if (!context_)
245     return [super copyCGLContextForPixelFormat:pixelFormat];
246   return CGLRetainContext(context_->cgl_context());
249 - (void)setNeedsDisplay {
250   if (helper_)
251     helper_->SetNeedsDisplay();
252   [super setNeedsDisplay];
255 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
256                 pixelFormat:(CGLPixelFormatObj)pixelFormat
257                forLayerTime:(CFTimeInterval)timeInterval
258                 displayTime:(const CVTimeStamp*)timeStamp {
259   if (helper_)
260     return helper_->CanDraw();
261   return NO;
264 - (void)drawInCGLContext:(CGLContextObj)glContext
265              pixelFormat:(CGLPixelFormatObj)pixelFormat
266             forLayerTime:(CFTimeInterval)timeInterval
267              displayTime:(const CVTimeStamp*)timeStamp {
268   TRACE_EVENT0("browser", "CompositingIOSurfaceLayer::drawInCGLContext");
270   if (!iosurface_->HasIOSurface() || context_->cgl_context() != glContext) {
271     glClearColor(1, 1, 1, 1);
272     glClear(GL_COLOR_BUFFER_BIT);
273     return;
274   }
276   // The correct viewport to cover the layer will be set up by the caller.
277   // Transform this into a window size for DrawIOSurface, where it will be
278   // transformed back into this viewport.
279   GLint viewport[4];
280   glGetIntegerv(GL_VIEWPORT, viewport);
281   gfx::Rect window_rect(viewport[0], viewport[1], viewport[2], viewport[3]);
282   float window_scale_factor = 1.f;
283   if ([self respondsToSelector:(@selector(contentsScale))])
284     window_scale_factor = [self contentsScale];
285   window_rect = ToNearestRect(
286       gfx::ScaleRect(window_rect, 1.f/window_scale_factor));
288   bool draw_succeeded = iosurface_->DrawIOSurface(
289       context_, window_rect, window_scale_factor);
291   if (helper_)
292     helper_->DidDraw(draw_succeeded);
294   [super drawInCGLContext:glContext
295               pixelFormat:pixelFormat
296              forLayerTime:timeInterval
297               displayTime:timeStamp];
300 @end