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>
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
25 CompositingIOSurfaceLayerHelper::CompositingIOSurfaceLayerHelper(
26 CompositingIOSurfaceLayerClient* client,
27 CompositingIOSurfaceLayer* layer)
30 needs_display_(false),
31 has_pending_frame_(false),
32 did_not_draw_counter_(0),
33 is_pumping_frames_(false),
36 base::TimeDelta::FromSeconds(1) / 6,
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;
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();
62 if (![layer_ isAsynchronous])
63 [layer_ setAsynchronous:YES];
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.
75 did_not_draw_counter_ = 0;
77 did_not_draw_counter_ += 1;
78 if (did_not_draw_counter_ == 30)
79 [layer_ setAsynchronous:NO];
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_)
104 has_pending_frame_ = false;
106 client_->AcceleratedLayerDidDrawFrame();
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
118 // http://crbug.com/395827
119 if ([layer_ isAsynchronous])
120 [layer_ setAsynchronous:NO];
122 [layer_ setNeedsDisplay];
123 DisplayIfNeededAndAck();
126 void CompositingIOSurfaceLayerHelper::DisplayIfNeededAndAck() {
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>)
178 withScaleFactor:(float)scale_factor
179 withClient:(content::CompositingIOSurfaceLayerClient*)client {
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);
188 LOG(ERROR) << "Failed create CompositingIOSurfaceContext";
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];
211 - (void)resetClient {
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 {
239 return [super copyCGLPixelFormatForDisplayMask:mask];
240 return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context()));
243 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
245 return [super copyCGLContextForPixelFormat:pixelFormat];
246 return CGLRetainContext(context_->cgl_context());
249 - (void)setNeedsDisplay {
251 helper_->SetNeedsDisplay();
252 [super setNeedsDisplay];
255 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
256 pixelFormat:(CGLPixelFormatObj)pixelFormat
257 forLayerTime:(CFTimeInterval)timeInterval
258 displayTime:(const CVTimeStamp*)timeStamp {
260 return helper_->CanDraw();
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);
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.
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);
292 helper_->DidDraw(draw_succeeded);
294 [super drawInCGLContext:glContext
295 pixelFormat:pixelFormat
296 forLayerTime:timeInterval
297 displayTime:timeStamp];