Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / compositing_iosurface_layer_mac.mm
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.
4
5 #include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
6
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <OpenGL/gl.h>
9
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"
19
20 ////////////////////////////////////////////////////////////////////////////////
21 // CompositingIOSurfaceLayerHelper
22
23 namespace content {
24
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) {}
39
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);
44 }
45
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);
50
51   has_pending_frame_ = true;
52   needs_display_ = true;
53   timer_.Reset();
54
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   }
65 }
66
67 void CompositingIOSurfaceLayerHelper::SetNeedsDisplay() {
68   needs_display_ = true;
69 }
70
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   }
81
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);
92
93   return needs_display_;
94 }
95
96 void CompositingIOSurfaceLayerHelper::DidDraw(bool success) {
97   needs_display_ = false;
98   AckPendingFrame(success);
99 }
100
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);
111 }
112
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];
121
122   [layer_ setNeedsDisplay];
123   DisplayIfNeededAndAck();
124 }
125
126 void CompositingIOSurfaceLayerHelper::DisplayIfNeededAndAck() {
127   if (!needs_display_)
128     return;
129
130   // As in SetNeedsDisplayAndDisplayAndAck, disable asynchronous drawing before
131   // issuing displayIfNeeded.
132   // http://crbug.com/395827
133   if ([layer_ isAsynchronous])
134     [layer_ setAsynchronous:NO];
135
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];
140
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);
146 }
147
148 void CompositingIOSurfaceLayerHelper::TimerFired() {
149   DisplayIfNeededAndAck();
150 }
151
152 void CompositingIOSurfaceLayerHelper::BeginPumpingFrames() {
153   is_pumping_frames_ = true;
154 }
155
156 void CompositingIOSurfaceLayerHelper::EndPumpingFrames() {
157   is_pumping_frames_ = false;
158   DisplayIfNeededAndAck();
159 }
160
161 }  // namespace content
162
163 ////////////////////////////////////////////////////////////////////////////////
164 // CompositingIOSurfaceLayer
165
166 @implementation CompositingIOSurfaceLayer
167
168 - (content::CompositingIOSurfaceMac*)iosurface {
169   return iosurface_.get();
170 }
171
172 - (content::CompositingIOSurfaceContext*)context {
173   return context_.get();
174 }
175
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));
183
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     }
193
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;
204 }
205
206 - (void)dealloc {
207   DCHECK(!helper_);
208   [super dealloc];
209 }
210
211 - (void)resetClient {
212   helper_.reset();
213 }
214
215 - (void)gotNewFrame {
216   helper_->GotNewFrame();
217 }
218
219 - (void)setNeedsDisplayAndDisplayAndAck {
220   helper_->SetNeedsDisplayAndDisplayAndAck();
221 }
222
223 - (void)displayIfNeededAndAck {
224   helper_->DisplayIfNeededAndAck();
225 }
226
227 - (void)beginPumpingFrames {
228   helper_->BeginPumpingFrames();
229 }
230
231 - (void)endPumpingFrames {
232   helper_->EndPumpingFrames();
233 }
234
235 // The remaining methods implement the CAOpenGLLayer interface.
236
237 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
238   if (!context_)
239     return [super copyCGLPixelFormatForDisplayMask:mask];
240   return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context()));
241 }
242
243 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
244   if (!context_)
245     return [super copyCGLContextForPixelFormat:pixelFormat];
246   return CGLRetainContext(context_->cgl_context());
247 }
248
249 - (void)setNeedsDisplay {
250   if (helper_)
251     helper_->SetNeedsDisplay();
252   [super setNeedsDisplay];
253 }
254
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;
262 }
263
264 - (void)drawInCGLContext:(CGLContextObj)glContext
265              pixelFormat:(CGLPixelFormatObj)pixelFormat
266             forLayerTime:(CFTimeInterval)timeInterval
267              displayTime:(const CVTimeStamp*)timeStamp {
268   TRACE_EVENT0("browser", "CompositingIOSurfaceLayer::drawInCGLContext");
269
270   if (!iosurface_->HasIOSurface() || context_->cgl_context() != glContext) {
271     glClearColor(1, 1, 1, 1);
272     glClear(GL_COLOR_BUFFER_BIT);
273     return;
274   }
275
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));
287
288   bool draw_succeeded = iosurface_->DrawIOSurface(
289       context_, window_rect, window_scale_factor);
290
291   if (helper_)
292     helper_->DidDraw(draw_succeeded);
293
294   [super drawInCGLContext:glContext
295               pixelFormat:pixelFormat
296              forLayerTime:timeInterval
297               displayTime:timeStamp];
298 }
299
300 @end