[Android WebView] Fix webview perf bot switchover to use org.chromium.webview_shell...
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_calayer_mac.mm
blobe2c8b43f17271794cf0b6bd01950981b6f811115
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.
5 #include "content/common/gpu/image_transport_surface_calayer_mac.h"
7 #include <OpenGL/CGLRenderers.h>
9 #include "base/command_line.h"
10 #include "base/mac/sdk_forward_declarations.h"
11 #include "base/trace_event/trace_event.h"
12 #include "gpu/config/gpu_info_collector.h"
13 #include "ui/accelerated_widget_mac/surface_handle_types.h"
14 #include "ui/base/cocoa/animation_utils.h"
15 #include "ui/base/ui_base_switches.h"
16 #include "ui/gfx/geometry/dip_util.h"
17 #include "ui/gfx/geometry/size_conversions.h"
18 #include "ui/gl/gl_gl_api_implementation.h"
19 #include "ui/gl/gl_switches.h"
20 #include "ui/gl/gpu_switching_manager.h"
22 namespace {
23 const size_t kFramesToKeepCAContextAfterDiscard = 2;
24 const size_t kCanDrawFalsesBeforeSwitchFromAsync = 4;
25 const base::TimeDelta kMinDeltaToSwitchToAsync =
26     base::TimeDelta::FromSecondsD(1. / 15.);
28 bool CanUseNSCGLSurface(const gpu::gles2::FeatureInfo* feature_info) {
29   // Respect command line flags for the API's usage.
30   static bool forced_at_command_line =
31       base::CommandLine::ForCurrentProcess()->HasSwitch(
32           switches::kForceNSCGLSurfaceApi);
33   if (forced_at_command_line)
34     return true;
35   static bool enabled_at_command_line =
36       base::CommandLine::ForCurrentProcess()->HasSwitch(
37           switches::kEnableNSCGLSurfaceApi);
38   if (!enabled_at_command_line)
39     return false;
41   // If there are multiple displays connected, then it is possible that we will
42   // end up on the slow path, where -[NSCGLSurface layerContents] will return
43   // a CGImage that is a dearly-made copy of the surface. Since we don't yet
44   // know how to avoid those sharp edges, just avoid using NSCGLSurface when
45   // multiple screens are present.
46   uint32_t count = 0;
47   CGError cg_error = CGGetActiveDisplayList(count, NULL, &count);
48   if (cg_error != kCGErrorSuccess) {
49     LOG(ERROR) << "Failed to query the number of displays.";
50     return false;
51   }
52   if (count != 1)
53     return false;
55   // Systems with multiple GPUs can exhibit problems where incorrect content
56   // will briefly flash during resize, and especially during transitions between
57   // the iGPU and the dGPU. These problems are exhibited by layer-backed
58   // NSOpenGLViews as well.
59   if (feature_info->workarounds().disable_ns_cgl_surface_api)
60     return false;
62   // Leave this feature disabled until a flag for it is available.
63   return false;
66 }  // namespace
68 // Private NSCGLSurface API.
69 @interface NSCGLSurface : NSObject
70 - (void)flushRect:(CGRect)rect;
71 - (void)attachToCGLContext:(CGLContextObj)cglContext;
72 - (id)initWithSize:(CGSize)size
73         colorSpace:(CGColorSpaceRef)colorSpace
74             atomic:(BOOL)atomic;
75 @property(readonly) CGImageRef image;
76 @property(readonly) id layerContents;
77 @end
79 @interface ImageTransportCAOpenGLLayer : CAOpenGLLayer <ImageTransportLayer> {
80   content::CALayerStorageProvider* storageProvider_;
81   base::Closure didDrawCallback_;
83   // Used to determine if we should use setNeedsDisplay or setAsynchronous to
84   // animate. If the last swap time happened very recently, then
85   // setAsynchronous is used (which allows smooth animation, but comes with the
86   // penalty of the canDrawInCGLContext function waking up the process every
87   // vsync).
88   base::TimeTicks lastSynchronousSwapTime_;
90   // A counter that is incremented whenever LayerCanDraw returns false. If this
91   // reaches a threshold, then |layer_| is switched to synchronous drawing to
92   // save CPU work.
93   uint32 canDrawReturnedFalseCount_;
95   gfx::Size pixelSize_;
98 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider
99                     pixelSize:(gfx::Size)pixelSize
100                   scaleFactor:(float)scaleFactor;
101 - (void)drawNewFrame:(gfx::Rect)dirtyRect;
102 - (void)drawPendingFrameImmediately;
103 - (void)resetStorageProvider;
104 @end
106 @interface ImageTransportNSCGLSurface : CALayer <ImageTransportLayer> {
107   content::CALayerStorageProvider* storageProvider_;
108   base::ScopedTypeRef<CGLContextObj> cglContext_;
109   base::scoped_nsobject<NSCGLSurface> surface_;
110   gfx::Size pixelSize_;
111   bool hasDrawn_;
114 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider
115                     pixelSize:(gfx::Size)pixelSize
116                   scaleFactor:(float)scaleFactor;
117 - (void)drawNewFrame:(gfx::Rect)dirtyRect;
118 - (void)drawPendingFrameImmediately;
119 - (void)resetStorageProvider;
120 @end
122 @implementation ImageTransportCAOpenGLLayer
124 - (id)initWithStorageProvider:
125     (content::CALayerStorageProvider*)storageProvider
126                     pixelSize:(gfx::Size)pixelSize
127                   scaleFactor:(float)scaleFactor {
128   if (self = [super init]) {
129     gfx::Size dipSize = gfx::ConvertSizeToDIP(scaleFactor, pixelSize);
130     [self setContentsScale:scaleFactor];
131     [self setFrame:CGRectMake(0, 0, dipSize.width(), dipSize.height())];
132     storageProvider_ = storageProvider;
133     pixelSize_ = pixelSize;
134   }
135   return self;
138 - (void)drawNewFrame:(gfx::Rect)dirtyRect {
139   // This tracing would be more natural to do with a pseudo-thread for each
140   // layer, rather than a counter.
141   // http://crbug.com/366300
142   // A trace value of 2 indicates that there is a pending swap ack. See
143   // canDrawInCGLContext for other value meanings.
144   TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 2);
146   if (![self isAsynchronous]) {
147     // Switch to asynchronous drawing only if we get two frames in rapid
148     // succession.
149     base::TimeTicks this_swap_time = base::TimeTicks::Now();
150     base::TimeDelta delta = this_swap_time - lastSynchronousSwapTime_;
151     if (delta <= kMinDeltaToSwitchToAsync) {
152       lastSynchronousSwapTime_ = base::TimeTicks();
153       [self setAsynchronous:YES];
154     } else {
155       lastSynchronousSwapTime_ = this_swap_time;
156       [self setNeedsDisplay];
157     }
158   }
161 - (void)drawPendingFrameImmediately {
162   DCHECK(storageProvider_->LayerHasPendingDraw());
163   if ([self isAsynchronous])
164     [self setAsynchronous:NO];
165   [self setNeedsDisplay];
166   [self displayIfNeeded];
169 - (void)resetStorageProvider {
170   storageProvider_ = NULL;
173 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
174   if (!storageProvider_)
175     return NULL;
176   return CGLRetainPixelFormat(CGLGetPixelFormat(
177       storageProvider_->LayerShareGroupContext()));
180 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
181   if (!storageProvider_)
182     return NULL;
183   didDrawCallback_ = storageProvider_->LayerShareGroupContextDirtiedCallback();
184   return CGLRetainContext(storageProvider_->LayerShareGroupContext());
187 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
188                 pixelFormat:(CGLPixelFormatObj)pixelFormat
189                forLayerTime:(CFTimeInterval)timeInterval
190                 displayTime:(const CVTimeStamp*)timeStamp {
191   TRACE_EVENT0("gpu", "CALayerStorageProvider::LayerCanDraw");
193   if (!storageProvider_)
194     return NO;
196   if (storageProvider_->LayerHasPendingDraw()) {
197     // If there is a draw pending then increase the signal from 2 to 3, to
198     // indicate that there is a swap pending, and CoreAnimation has asked to
199     // draw it.
200     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 3);
202     canDrawReturnedFalseCount_ = 0;
203     return YES;
204   } else {
205     // If there is not a draw pending, then give an instantaneous blip up from
206     // 0 to 1, indicating that CoreAnimation was ready to draw a frame but we
207     // were not (or didn't have new content to draw).
208     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 1);
209     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 0);
211     if ([self isAsynchronous]) {
212       // If we are in asynchronous mode, we will be getting callbacks at every
213       // vsync, asking us if we have anything to draw. If we get many of these
214       // in a row, ask that we stop getting these callback for now, so that we
215       // don't waste CPU cycles.
216       if (canDrawReturnedFalseCount_ >= kCanDrawFalsesBeforeSwitchFromAsync)
217         [self setAsynchronous:NO];
218       else
219         canDrawReturnedFalseCount_ += 1;
220     }
221     return NO;
222   }
225 - (void)drawInCGLContext:(CGLContextObj)glContext
226              pixelFormat:(CGLPixelFormatObj)pixelFormat
227             forLayerTime:(CFTimeInterval)timeInterval
228              displayTime:(const CVTimeStamp*)timeStamp {
229   // While in this callback, CoreAnimation has set |glContext| to be current.
230   // Ensure that the GL calls that we make are made against the native GL API.
231   gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
233   if (storageProvider_) {
234     storageProvider_->LayerDoDraw(gfx::Rect(pixelSize_));
236     // A trace value of 0 indicates that there is no longer a pending swap ack.
237     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 0);
238   } else {
239     glClearColor(1, 1, 1, 1);
240     glClear(GL_COLOR_BUFFER_BIT);
241   }
242   [super drawInCGLContext:glContext
243               pixelFormat:pixelFormat
244              forLayerTime:timeInterval
245               displayTime:timeStamp];
247   DCHECK(!didDrawCallback_.is_null());
248   didDrawCallback_.Run();
251 @end
253 @implementation ImageTransportNSCGLSurface
255 - (id)initWithStorageProvider:
256     (content::CALayerStorageProvider*)storageProvider
257                     pixelSize:(gfx::Size)pixelSize
258                   scaleFactor:(float)scaleFactor {
259   if (self = [super init]) {
260     ScopedCAActionDisabler disabler;
261     gfx::Size dipSize = gfx::ConvertSizeToDIP(scaleFactor, pixelSize);
262     [self setContentsScale:scaleFactor];
263     [self setFrame:CGRectMake(0, 0, dipSize.width(), dipSize.height())];
264     storageProvider_ = storageProvider;
266     // Allocate the NSCGLSurface to render into.
267     base::ScopedCFTypeRef<CGColorSpaceRef> cgColorSpace(
268         CGDisplayCopyColorSpace(CGMainDisplayID()));
269     Class NSCGLSurface_class = NSClassFromString(@"NSCGLSurface");
270     surface_.reset([[NSCGLSurface_class alloc] initWithSize:pixelSize.ToCGSize()
271                                                  colorSpace:cgColorSpace
272                                                      atomic:NO]);
274     // Create a context in the share group of the storage provider. We will
275     // draw content using this context.
276     CGLError cglError = kCGLNoError;
277     cglError = CGLCreateContext(
278         CGLGetPixelFormat(storageProvider_->LayerShareGroupContext()),
279         storageProvider_->LayerShareGroupContext(),
280         cglContext_.InitializeInto());
281     LOG_IF(ERROR, cglError != kCGLNoError) <<
282         "Failed to create CGL context for NSCGL surface.";
284     pixelSize_ = pixelSize;
285     hasDrawn_ = false;
286   }
287   return self;
290 - (void)drawNewFrame:(gfx::Rect)dirtyRect {
291   // Draw the first frame to the layer as covering the full layer. Subsequent
292   // frames may use partial damage.
293   if (!hasDrawn_) {
294     dirtyRect = gfx::Rect(pixelSize_);
295     hasDrawn_ = true;
296   }
298   // Make the context current to the thread, make the surface be the current
299   // drawable for the context, and draw.
300   [surface_ attachToCGLContext:cglContext_];
301   {
302     gfx::ScopedCGLSetCurrentContext scopedSetCurrentContext(cglContext_);
303     gfx::ScopedSetGLToRealGLApi scopedSetRealGLApi;
304     glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
305     glViewport(0, 0, pixelSize_.width(), pixelSize_.height());
306     storageProvider_->LayerDoDraw(dirtyRect);
307     glFlush();
308   }
309   [surface_ attachToCGLContext:NULL];
310   [surface_ flushRect:dirtyRect.ToCGRect()];
312   // Update the layer contents to be the content rendered by OpenGL.
313   {
314     ScopedCAActionDisabler disabler;
315     [self setContents:[surface_ layerContents]];
316   }
319 - (void)drawPendingFrameImmediately {
320   [self drawNewFrame:gfx::Rect(pixelSize_)];
323 - (void)resetStorageProvider {
324   storageProvider_ = NULL;
327 @end
329 namespace content {
331 CALayerStorageProvider::CALayerStorageProvider(
332     ImageTransportSurfaceFBO* transport_surface)
333     : transport_surface_(transport_surface),
334       gpu_vsync_disabled_(base::CommandLine::ForCurrentProcess()->HasSwitch(
335           switches::kDisableGpuVsync)),
336       throttling_disabled_(false),
337       has_pending_draw_(false),
338       fbo_texture_(0),
339       fbo_scale_factor_(1),
340       program_(0),
341       vertex_shader_(0),
342       fragment_shader_(0),
343       position_location_(0),
344       tex_location_(0),
345       vertex_buffer_(0),
346       vertex_array_(0),
347       recreate_layer_after_gpu_switch_(false),
348       pending_draw_weak_factory_(this) {
349   ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
352 CALayerStorageProvider::~CALayerStorageProvider() {
353   ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
356 gfx::Size CALayerStorageProvider::GetRoundedSize(gfx::Size size) {
357   return size;
360 bool CALayerStorageProvider::AllocateColorBufferStorage(
361     CGLContextObj context, const base::Closure& context_dirtied_callback,
362     GLuint texture, gfx::Size pixel_size, float scale_factor) {
363   // Allocate an ordinary OpenGL texture to back the FBO.
364   GLenum error;
365   while ((error = glGetError()) != GL_NO_ERROR) {
366     LOG(ERROR) << "OpenGL error hit but ignored before allocating buffer "
367                << "storage: " << error;
368   }
370   if (gfx::GetGLImplementation() ==
371       gfx::kGLImplementationDesktopGLCoreProfile) {
372     glTexImage2D(GL_TEXTURE_2D,
373                  0,
374                  GL_RGBA,
375                  pixel_size.width(),
376                  pixel_size.height(),
377                  0,
378                  GL_RGBA,
379                  GL_UNSIGNED_BYTE,
380                  NULL);
381     glFlush();
383     if (!vertex_shader_) {
384       const char* source =
385           "#version 150\n"
386           "in vec4 position;\n"
387           "out vec2 texcoord;\n"
388           "void main() {\n"
389           "    texcoord = vec2(position.x, position.y);\n"
390           "    gl_Position = vec4(2*position.x-1, 2*position.y-1,\n"
391           "        position.z, position.w);\n"
392           "}\n";
393       vertex_shader_ = glCreateShader(GL_VERTEX_SHADER);
394       glShaderSource(vertex_shader_, 1, &source, NULL);
395       glCompileShader(vertex_shader_);
396 #if DCHECK_IS_ON()
397       GLint status = GL_FALSE;
398       glGetShaderiv(vertex_shader_, GL_COMPILE_STATUS, &status);
399       DCHECK(status == GL_TRUE);
400 #endif
401     }
402     if (!fragment_shader_) {
403       const char* source =
404           "#version 150\n"
405           "uniform sampler2D tex;\n"
406           "in vec2 texcoord;\n"
407           "out vec4 frag_color;\n"
408           "void main() {\n"
409           "    frag_color = texture(tex, texcoord);\n"
410           "}\n";
411       fragment_shader_ = glCreateShader(GL_FRAGMENT_SHADER);
412       glShaderSource(fragment_shader_, 1, &source, NULL);
413       glCompileShader(fragment_shader_);
414 #if DCHECK_IS_ON()
415       GLint status = GL_FALSE;
416       glGetShaderiv(fragment_shader_, GL_COMPILE_STATUS, &status);
417       DCHECK(status == GL_TRUE);
418 #endif
419     }
420     if (!program_) {
421       program_ = glCreateProgram();
422       glAttachShader(program_, vertex_shader_);
423       glAttachShader(program_, fragment_shader_);
424       glBindFragDataLocation(program_, 0, "frag_color");
425       glLinkProgram(program_);
426 #if DCHECK_IS_ON()
427       GLint status = GL_FALSE;
428       glGetProgramiv(program_, GL_LINK_STATUS, &status);
429       DCHECK(status == GL_TRUE);
430 #endif
431       position_location_ = glGetAttribLocation(program_, "position");
432       tex_location_ = glGetUniformLocation(program_, "tex");
433     }
434     if (!vertex_buffer_) {
435       GLfloat vertex_data[24] = {
436         0, 0, 0, 1,
437         1, 0, 0, 1,
438         1, 1, 0, 1,
439         1, 1, 0, 1,
440         0, 1, 0, 1,
441         0, 0, 0, 1,
442       };
443       glGenBuffersARB(1, &vertex_buffer_);
444       // If the allocation path used GLContext::RestoreStateIfDirtiedExternally
445       // as the draw path does, this manual state restoration would not be
446       // necessary.
447       GLint bound_buffer = 0;
448       glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &bound_buffer);
449       glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
450       glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data),
451                    vertex_data, GL_STATIC_DRAW);
452       glBindBuffer(GL_ARRAY_BUFFER, bound_buffer);
453     }
454     if (!vertex_array_) {
455       // If the allocation path used GLContext::RestoreStateIfDirtiedExternally
456       // as the draw path does, this manual state restoration would not be
457       // necessary.
458       GLint bound_vao = 0;
459       glGetIntegerv(GL_VERTEX_ARRAY_BINDING_OES, &bound_vao);
460       glGenVertexArraysOES(1, &vertex_array_);
461       glBindVertexArrayOES(vertex_array_);
462       {
463         glEnableVertexAttribArray(position_location_);
464         GLint bound_buffer = 0;
465         glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &bound_buffer);
466         glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
467         glVertexAttribPointer(position_location_, 4, GL_FLOAT, GL_FALSE, 0, 0);
468         glBindBuffer(GL_ARRAY_BUFFER, bound_buffer);
469       }
470       glBindVertexArrayOES(bound_vao);
471     }
472   } else {
473     glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
474                  0,
475                  GL_RGBA,
476                  pixel_size.width(),
477                  pixel_size.height(),
478                  0,
479                  GL_RGBA,
480                  GL_UNSIGNED_BYTE,
481                  NULL);
482     glFlush();
483   }
485   bool hit_error = false;
486   while ((error = glGetError()) != GL_NO_ERROR) {
487     LOG(ERROR) << "OpenGL error hit while trying to allocate buffer storage: "
488                << error;
489     hit_error = true;
490   }
491   if (hit_error)
492     return false;
494   // Set the parameters that will be used to allocate the CALayer to draw the
495   // texture into.
496   share_group_context_.reset(CGLRetainContext(context));
497   share_group_context_dirtied_callback_ = context_dirtied_callback;
498   fbo_texture_ = texture;
499   fbo_pixel_size_ = pixel_size;
500   fbo_scale_factor_ = scale_factor;
501   return true;
504 void CALayerStorageProvider::FreeColorBufferStorage() {
505   if (gfx::GetGLImplementation() ==
506       gfx::kGLImplementationDesktopGLCoreProfile) {
507     if (vertex_shader_)
508       glDeleteShader(vertex_shader_);
509     if (fragment_shader_)
510       glDeleteShader(fragment_shader_);
511     if (program_)
512       glDeleteProgram(program_);
513     if (vertex_buffer_)
514       glDeleteBuffersARB(1, &vertex_buffer_);
515     if (vertex_array_)
516       glDeleteVertexArraysOES(1, &vertex_array_);
517     vertex_shader_ = 0;
518     fragment_shader_ = 0;
519     program_ = 0;
520     vertex_buffer_ = 0;
521     vertex_array_ = 0;
522   }
524   // Note that |context_| still holds a reference to |layer_|, and will until
525   // a new frame is swapped in.
526   ResetLayer();
528   share_group_context_.reset();
529   share_group_context_dirtied_callback_ = base::Closure();
530   fbo_texture_ = 0;
531   fbo_pixel_size_ = gfx::Size();
534 void CALayerStorageProvider::FrameSizeChanged(const gfx::Size& pixel_size,
535                                               float scale_factor) {
536   DCHECK_EQ(fbo_pixel_size_.ToString(), pixel_size.ToString());
537   DCHECK_EQ(fbo_scale_factor_, scale_factor);
540 void CALayerStorageProvider::SwapBuffers(const gfx::Rect& dirty_rect) {
541   TRACE_EVENT0("gpu", "CALayerStorageProvider::SwapBuffers");
542   DCHECK(!has_pending_draw_);
544   // Recreate the CALayer on the new GPU if a GPU switch has occurred. Note
545   // that the CAContext will retain a reference to the old CALayer until the
546   // call to -[CAContext setLayer:] replaces the old CALayer with the new one.
547   if (recreate_layer_after_gpu_switch_) {
548     ResetLayer();
549     recreate_layer_after_gpu_switch_ = false;
550   }
552   // Determine if it is safe to use an NSCGLSurface, or if we should use the
553   // CAOpenGLLayer fallback. If we're not using the preferred type of layer,
554   // then reset the layer and re-create one of the preferred type.
555   bool can_use_ns_cgl_surface =
556       CanUseNSCGLSurface(transport_surface_->GetFeatureInfo());
557   Class expected_layer_class = can_use_ns_cgl_surface ?
558       [ImageTransportNSCGLSurface class] : [ImageTransportCAOpenGLLayer class];
559   if (![layer_ isKindOfClass:expected_layer_class])
560     ResetLayer();
562   // Set the pending draw flag only after destroying the old layer (otherwise
563   // destroying it will un-set the flag).
564   has_pending_draw_ = true;
566   // Allocate a CAContext to use to transport the CALayer to the browser
567   // process, if needed.
568   if (!context_) {
569     base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]);
570     CGSConnectionID connection_id = CGSMainConnectionID();
571     context_.reset([CAContext contextWithCGSConnection:connection_id
572                                                options:dict]);
573     [context_ retain];
574   }
576   // Allocate a CALayer to use to draw the content and make it current to the
577   // CAContext, if needed.
578   if (!layer_) {
579     if (can_use_ns_cgl_surface) {
580       layer_.reset([[ImageTransportNSCGLSurface alloc]
581           initWithStorageProvider:this
582                         pixelSize:fbo_pixel_size_
583                       scaleFactor:fbo_scale_factor_]);
584     } else {
585       layer_.reset([[ImageTransportCAOpenGLLayer alloc]
586           initWithStorageProvider:this
587                         pixelSize:fbo_pixel_size_
588                       scaleFactor:fbo_scale_factor_]);
589     }
590     [context_ setLayer:layer_];
591   }
593   // Replacing the CAContext's CALayer will sometimes results in an immediate
594   // draw.
595   if (!has_pending_draw_)
596     return;
598   // Tell CoreAnimation to draw our frame.
599   if (gpu_vsync_disabled_ || throttling_disabled_) {
600     DrawImmediatelyAndUnblockBrowser();
601   } else {
602     [layer_ drawNewFrame:dirty_rect];
603   }
605   if (has_pending_draw_) {
606     // If CoreAnimation doesn't end up drawing our frame, un-block the browser
607     // after a timeout of 1/6th of a second has passed.
608     base::MessageLoop::current()->PostDelayedTask(
609         FROM_HERE,
610         base::Bind(&CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser,
611                    pending_draw_weak_factory_.GetWeakPtr()),
612         base::TimeDelta::FromSeconds(1) / 6);
613   }
616 void CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser() {
617   CHECK(has_pending_draw_);
618   [layer_ drawPendingFrameImmediately];
620   // Sometimes, the setNeedsDisplay+displayIfNeeded pairs have no effect. This
621   // can happen if the NSView that this layer is attached to isn't in the
622   // window hierarchy (e.g, tab capture of a backgrounded tab). In this case,
623   // the frame will never be seen, so drop it.
624   UnblockBrowserIfNeeded();
627 void CALayerStorageProvider::WillWriteToBackbuffer() {
628   // The browser should always throttle itself so that there are no pending
629   // draws when the output surface is written to, but in the event of things
630   // like context lost, or changing context, this will not be true. If there
631   // exists a pending draw, flush it immediately to maintain a consistent
632   // state.
633   if (has_pending_draw_)
634     DrawImmediatelyAndUnblockBrowser();
637 void CALayerStorageProvider::DiscardBackbuffer() {
638   // If this surface's backbuffer is discarded, it is because this surface has
639   // been made non-visible. Ensure that the previous contents are not briefly
640   // flashed when this is made visible by creating a new CALayer and CAContext
641   // at the next swap.
642   ResetLayer();
644   // If we remove all references to the CAContext in this process, it will be
645   // blanked-out in the browser process (even if the browser process is inside
646   // a NSDisableScreenUpdates block). Ensure that the context is kept around
647   // until a fixed number of frames (determined empirically) have been acked.
648   // http://crbug.com/425819
649   while (previously_discarded_contexts_.size() <
650       kFramesToKeepCAContextAfterDiscard) {
651     previously_discarded_contexts_.push_back(
652         base::scoped_nsobject<CAContext>());
653   }
654   previously_discarded_contexts_.push_back(context_);
656   context_.reset();
659 void CALayerStorageProvider::SwapBuffersAckedByBrowser(
660     bool disable_throttling) {
661   TRACE_EVENT0("gpu", "CALayerStorageProvider::SwapBuffersAckedByBrowser");
662   throttling_disabled_ = disable_throttling;
663   if (!previously_discarded_contexts_.empty())
664     previously_discarded_contexts_.pop_front();
667 CGLContextObj CALayerStorageProvider::LayerShareGroupContext() {
668   return share_group_context_;
671 base::Closure CALayerStorageProvider::LayerShareGroupContextDirtiedCallback() {
672   return share_group_context_dirtied_callback_;
675 void CALayerStorageProvider::LayerDoDraw(const gfx::Rect& dirty_rect) {
676   TRACE_EVENT0("gpu", "CALayerStorageProvider::LayerDoDraw");
677   if (gfx::GetGLImplementation() ==
678       gfx::kGLImplementationDesktopGLCoreProfile) {
679     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
680     glClearColor(1, 0, 1, 1);
681     glClear(GL_COLOR_BUFFER_BIT);
682     glDisable(GL_BLEND);
683     glDisable(GL_CULL_FACE);
684     glDisable(GL_DEPTH_TEST);
685     glDisable(GL_STENCIL_TEST);
686     glDisable(GL_SCISSOR_TEST);
688     DCHECK(glIsProgram(program_));
689     glUseProgram(program_);
690     glBindVertexArrayOES(vertex_array_);
692     glActiveTexture(GL_TEXTURE0);
693     glBindTexture(GL_TEXTURE_2D, fbo_texture_);
694     glUniform1i(tex_location_, 0);
696     glDisable(GL_CULL_FACE);
697     glDrawArrays(GL_TRIANGLES, 0, 6);
698     glBindVertexArrayOES(0);
699     glUseProgram(0);
700   } else {
701     GLint viewport[4] = {0, 0, 0, 0};
702     glGetIntegerv(GL_VIEWPORT, viewport);
703     gfx::Size viewport_size(viewport[2], viewport[3]);
705     // Set the coordinate system to be one-to-one with pixels.
706     glMatrixMode(GL_PROJECTION);
707     glLoadIdentity();
708     glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1);
709     glMatrixMode(GL_MODELVIEW);
710     glLoadIdentity();
712     // Reset drawing state and draw a fullscreen quad.
713     glUseProgram(0);
714     glDisable(GL_BLEND);
715     glDisable(GL_CULL_FACE);
716     glDisable(GL_DEPTH_TEST);
717     glDisable(GL_STENCIL_TEST);
718     glDisable(GL_SCISSOR_TEST);
719     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
720     glColor4f(1, 1, 1, 1);
721     glActiveTexture(GL_TEXTURE0);
722     glEnable(GL_TEXTURE_RECTANGLE_ARB);
723     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fbo_texture_);
724     glBegin(GL_QUADS);
725     {
726       glTexCoord2f(dirty_rect.x(), dirty_rect.y());
727       glVertex2f(dirty_rect.x(), dirty_rect.y());
729       glTexCoord2f(dirty_rect.x(), dirty_rect.bottom());
730       glVertex2f(dirty_rect.x(), dirty_rect.bottom());
732       glTexCoord2f(dirty_rect.right(), dirty_rect.bottom());
733       glVertex2f(dirty_rect.right(), dirty_rect.bottom());
735       glTexCoord2f(dirty_rect.right(), dirty_rect.y());
736       glVertex2f(dirty_rect.right(), dirty_rect.y());
737     }
738     glEnd();
739     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
740     glDisable(GL_TEXTURE_RECTANGLE_ARB);
741   }
743   GLint current_renderer_id = 0;
744   if (CGLGetParameter(CGLGetCurrentContext(),
745                       kCGLCPCurrentRendererID,
746                       &current_renderer_id) == kCGLNoError) {
747     current_renderer_id &= kCGLRendererIDMatchingMask;
748     transport_surface_->SetRendererID(current_renderer_id);
749   }
751   GLenum error;
752   while ((error = glGetError()) != GL_NO_ERROR) {
753     LOG(ERROR) << "OpenGL error hit while drawing frame: " << error;
754   }
756   // Allow forward progress in the context now that the swap is complete.
757   UnblockBrowserIfNeeded();
760 bool CALayerStorageProvider::LayerHasPendingDraw() const {
761   return has_pending_draw_;
764 void CALayerStorageProvider::OnGpuSwitched() {
765   recreate_layer_after_gpu_switch_ = true;
768 void CALayerStorageProvider::UnblockBrowserIfNeeded() {
769   if (!has_pending_draw_)
770     return;
771   pending_draw_weak_factory_.InvalidateWeakPtrs();
772   has_pending_draw_ = false;
773   transport_surface_->SendSwapBuffers(
774       ui::SurfaceHandleFromCAContextID([context_ contextId]),
775       fbo_pixel_size_,
776       fbo_scale_factor_);
779 void CALayerStorageProvider::ResetLayer() {
780   [layer_ resetStorageProvider];
782   // If we are providing back-pressure by waiting for a draw, that draw will
783   // now never come, so release the pressure now.
784   UnblockBrowserIfNeeded();
786   // This should only ever be called by the active layer.
787   layer_.reset();
790 }  //  namespace content