Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / common / gpu / image_transport_surface_calayer_mac.mm
1 // Copyright 2014 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/common/gpu/image_transport_surface_calayer_mac.h"
6
7 #include "base/command_line.h"
8 #include "base/mac/sdk_forward_declarations.h"
9 #include "content/common/gpu/surface_handle_types_mac.h"
10 #include "ui/base/cocoa/animation_utils.h"
11 #include "ui/gfx/geometry/size_conversions.h"
12 #include "ui/gl/gl_gl_api_implementation.h"
13 #include "ui/gl/gl_switches.h"
14
15 @interface ImageTransportLayer : CAOpenGLLayer {
16   content::CALayerStorageProvider* storageProvider_;
17 }
18 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider;
19 - (void)resetStorageProvider;
20 @end
21
22 @implementation ImageTransportLayer
23
24 - (id)initWithStorageProvider:
25     (content::CALayerStorageProvider*)storageProvider {
26   if (self = [super init])
27     storageProvider_ = storageProvider;
28   return self;
29 }
30
31 - (void)resetStorageProvider {
32   if (storageProvider_)
33     storageProvider_->LayerResetStorageProvider();
34   storageProvider_ = NULL;
35 }
36
37 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
38   if (!storageProvider_)
39     return NULL;
40   return CGLRetainPixelFormat(CGLGetPixelFormat(
41       storageProvider_->LayerShareGroupContext()));
42 }
43
44 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
45   if (!storageProvider_)
46     return NULL;
47   CGLContextObj context = NULL;
48   CGLError error = CGLCreateContext(
49       pixelFormat, storageProvider_->LayerShareGroupContext(), &context);
50   if (error != kCGLNoError)
51     DLOG(ERROR) << "CGLCreateContext failed with CGL error: " << error;
52   return context;
53 }
54
55 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
56                 pixelFormat:(CGLPixelFormatObj)pixelFormat
57                forLayerTime:(CFTimeInterval)timeInterval
58                 displayTime:(const CVTimeStamp*)timeStamp {
59   if (!storageProvider_)
60     return NO;
61   return storageProvider_->LayerCanDraw();
62 }
63
64 - (void)drawInCGLContext:(CGLContextObj)glContext
65              pixelFormat:(CGLPixelFormatObj)pixelFormat
66             forLayerTime:(CFTimeInterval)timeInterval
67              displayTime:(const CVTimeStamp*)timeStamp {
68   // While in this callback, CoreAnimation has set |glContext| to be current.
69   // Ensure that the GL calls that we make are made against the native GL API.
70   gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
71
72   if (storageProvider_) {
73     storageProvider_->LayerDoDraw();
74   } else {
75     glClearColor(1, 1, 1, 1);
76     glClear(GL_COLOR_BUFFER_BIT);
77   }
78   [super drawInCGLContext:glContext
79               pixelFormat:pixelFormat
80              forLayerTime:timeInterval
81               displayTime:timeStamp];
82 }
83
84 @end
85
86 namespace content {
87
88 CALayerStorageProvider::CALayerStorageProvider(
89     ImageTransportSurfaceFBO* transport_surface)
90         : transport_surface_(transport_surface),
91           gpu_vsync_disabled_(CommandLine::ForCurrentProcess()->HasSwitch(
92               switches::kDisableGpuVsync)),
93           has_pending_draw_(false),
94           can_draw_returned_false_count_(0),
95           fbo_texture_(0),
96           fbo_scale_factor_(1),
97           weak_factory_(this) {}
98
99 CALayerStorageProvider::~CALayerStorageProvider() {
100 }
101
102 gfx::Size CALayerStorageProvider::GetRoundedSize(gfx::Size size) {
103   return size;
104 }
105
106 bool CALayerStorageProvider::AllocateColorBufferStorage(
107     CGLContextObj context, GLuint texture,
108     gfx::Size pixel_size, float scale_factor) {
109   // Allocate an ordinary OpenGL texture to back the FBO.
110   GLenum error;
111   while ((error = glGetError()) != GL_NO_ERROR) {
112     DLOG(ERROR) << "Error found (and ignored) before allocating buffer "
113                 << "storage: " << error;
114   }
115   glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
116                0,
117                GL_RGBA,
118                pixel_size.width(),
119                pixel_size.height(),
120                0,
121                GL_RGBA,
122                GL_UNSIGNED_BYTE,
123                NULL);
124   error = glGetError();
125   if (error != GL_NO_ERROR) {
126     DLOG(ERROR) << "glTexImage failed with GL error: " << error;
127     return false;
128   }
129   glFlush();
130
131   // Disable the fade-in animation as the layer is changed.
132   ScopedCAActionDisabler disabler;
133
134   // Set the parameters that will be used to allocate the CALayer to draw the
135   // texture into.
136   share_group_context_.reset(CGLRetainContext(context));
137   fbo_texture_ = texture;
138   fbo_pixel_size_ = pixel_size;
139   fbo_scale_factor_ = scale_factor;
140   return true;
141 }
142
143 void CALayerStorageProvider::FreeColorBufferStorage() {
144   // We shouldn't be asked to free a texture when we still have yet to draw it.
145   DCHECK(!has_pending_draw_);
146   has_pending_draw_ = false;
147   can_draw_returned_false_count_ = 0;
148
149   // Note that |context_| still holds a reference to |layer_|, and will until
150   // a new frame is swapped in.
151   [layer_ displayIfNeeded];
152   [layer_ resetStorageProvider];
153   layer_.reset();
154
155   share_group_context_.reset();
156   fbo_texture_ = 0;
157   fbo_pixel_size_ = gfx::Size();
158 }
159
160 void CALayerStorageProvider::SwapBuffers(
161     const gfx::Size& size, float scale_factor) {
162   DCHECK(!has_pending_draw_);
163   has_pending_draw_ = true;
164
165   // Allocate a CAContext to use to transport the CALayer to the browser
166   // process.
167   if (!context_) {
168     base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]);
169     CGSConnectionID connection_id = CGSMainConnectionID();
170     context_.reset([CAContext contextWithCGSConnection:connection_id
171                                                options:dict]);
172     [context_ retain];
173   }
174
175   // Allocate a CALayer to use to draw the content.
176   if (!layer_) {
177     layer_.reset([[ImageTransportLayer alloc] initWithStorageProvider:this]);
178     gfx::Size dip_size(gfx::ToFlooredSize(gfx::ScaleSize(
179         fbo_pixel_size_, 1.0f / fbo_scale_factor_)));
180     [layer_ setContentsScale:fbo_scale_factor_];
181     [layer_ setFrame:CGRectMake(0, 0, dip_size.width(), dip_size.height())];
182
183     // Make the CALayer current to the CAContext and display its contents
184     // immediately.
185     [context_ setLayer:layer_];
186   }
187
188   // Tell CoreAnimation to draw our frame. We will send the IPC to the browser
189   // when CoreAnimation has drawn our frame.
190   if (gpu_vsync_disabled_) {
191     DrawWithVsyncDisabled();
192   } else {
193     if (![layer_ isAsynchronous])
194       [layer_ setAsynchronous:YES];
195   }
196 }
197
198 void CALayerStorageProvider::DrawWithVsyncDisabled() {
199   DCHECK(has_pending_draw_);
200   [layer_ setNeedsDisplay];
201
202   // Sometimes, setNeedsDisplay calls are dropped on the floor. Make this not
203   // hang the renderer by re-issuing the call if the draw has not yet
204   // happened.
205   if (has_pending_draw_) {
206     // Delay sending another draw immediately to avoid starving the run loop.
207     base::MessageLoop::current()->PostDelayedTask(
208         FROM_HERE,
209         base::Bind(&CALayerStorageProvider::DrawWithVsyncDisabled,
210                    weak_factory_.GetWeakPtr()),
211         base::TimeDelta::FromMilliseconds(5));
212   }
213 }
214
215 void CALayerStorageProvider::WillWriteToBackbuffer() {
216   // TODO(ccameron): The browser may need to continue issuing swaps even when
217   // they do not draw. In these cases it is necessary to either double-buffer
218   // the resulting texture, or to drop frames.
219 }
220
221 void CALayerStorageProvider::DiscardBackbuffer() {
222   // If this surface's backbuffer is discarded, it is because this surface has
223   // been made non-visible. Ensure that the previous contents are not briefly
224   // flashed when this is made visible by creating a new CALayer and CAContext
225   // at the next swap.
226   [layer_ resetStorageProvider];
227   layer_.reset();
228   context_.reset();
229 }
230
231 void CALayerStorageProvider::SwapBuffersAckedByBrowser() {
232 }
233
234 CGLContextObj CALayerStorageProvider::LayerShareGroupContext() {
235   return share_group_context_;
236 }
237
238 bool CALayerStorageProvider::LayerCanDraw() {
239   if (has_pending_draw_) {
240     can_draw_returned_false_count_ = 0;
241     return true;
242   } else {
243     if ([layer_ isAsynchronous]) {
244       DCHECK(!gpu_vsync_disabled_);
245       // If we are in asynchronous mode, we will be getting callbacks at every
246       // vsync, asking us if we have anything to draw. If we get 30 of these in
247       // a row, ask that we stop getting these callback for now, so that we
248       // don't waste CPU cycles.
249       if (can_draw_returned_false_count_ == 30)
250         [layer_ setAsynchronous:NO];
251       else
252         can_draw_returned_false_count_ += 1;
253     }
254     return false;
255   }
256 }
257
258 void CALayerStorageProvider::LayerDoDraw() {
259   GLint viewport[4] = {0, 0, 0, 0};
260   glGetIntegerv(GL_VIEWPORT, viewport);
261   gfx::Size viewport_size(viewport[2], viewport[3]);
262
263   // Set the coordinate system to be one-to-one with pixels.
264   glMatrixMode(GL_PROJECTION);
265   glLoadIdentity();
266   glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1);
267   glMatrixMode(GL_MODELVIEW);
268   glLoadIdentity();
269
270   // Draw a fullscreen quad.
271   glColor4f(1, 1, 1, 1);
272   glEnable(GL_TEXTURE_RECTANGLE_ARB);
273   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fbo_texture_);
274   glBegin(GL_QUADS);
275   {
276     glTexCoord2f(0, 0);
277     glVertex2f(0, 0);
278
279     glTexCoord2f(0, fbo_pixel_size_.height());
280     glVertex2f(0, fbo_pixel_size_.height());
281
282     glTexCoord2f(fbo_pixel_size_.width(), fbo_pixel_size_.height());
283     glVertex2f(fbo_pixel_size_.width(), fbo_pixel_size_.height());
284
285     glTexCoord2f(fbo_pixel_size_.width(), 0);
286     glVertex2f(fbo_pixel_size_.width(), 0);
287   }
288   glEnd();
289   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
290   glDisable(GL_TEXTURE_RECTANGLE_ARB);
291
292   // Allow forward progress in the context now that the swap is complete.
293   DCHECK(has_pending_draw_);
294   SendPendingSwapToBrowserAfterFrameDrawn();
295 }
296
297 void CALayerStorageProvider::LayerResetStorageProvider() {
298   // If we are providing back-pressure by waiting for a draw, that draw will
299   // now never come, so release the pressure now.
300   SendPendingSwapToBrowserAfterFrameDrawn();
301 }
302
303 void CALayerStorageProvider::SendPendingSwapToBrowserAfterFrameDrawn() {
304   if (!has_pending_draw_)
305     return;
306   weak_factory_.InvalidateWeakPtrs();
307   has_pending_draw_ = false;
308   transport_surface_->SendSwapBuffers(
309       SurfaceHandleFromCAContextID([context_ contextId]),
310       fbo_pixel_size_,
311       fbo_scale_factor_);
312 }
313
314 }  //  namespace content