Update Polymer and pull in iron-list
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_calayer_mac.mm
bloba9e5a976af9ae6a4a1bdd327c2ca3fa8ff7862bd
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 <IOSurface/IOSurface.h>
8 #include <OpenGL/CGLRenderers.h>
9 #include <OpenGL/CGLIOSurface.h>
11 #include "base/command_line.h"
12 #include "base/mac/sdk_forward_declarations.h"
13 #include "base/trace_event/trace_event.h"
14 #include "gpu/config/gpu_info_collector.h"
15 #include "ui/accelerated_widget_mac/surface_handle_types.h"
16 #include "ui/base/cocoa/animation_utils.h"
17 #include "ui/gfx/geometry/dip_util.h"
18 #include "ui/gfx/geometry/size_conversions.h"
19 #include "ui/gl/gl_switches.h"
20 #include "ui/gl/gpu_switching_manager.h"
21 #include "ui/gl/scoped_api.h"
23 namespace {
24 const size_t kFramesToKeepCAContextAfterDiscard = 2;
25 const size_t kCanDrawFalsesBeforeSwitchFromAsync = 4;
26 const base::TimeDelta kMinDeltaToSwitchToAsync =
27     base::TimeDelta::FromSecondsD(1. / 15.);
30 }  // namespace
32 @interface ImageTransportCAOpenGLLayer : CAOpenGLLayer {
33   content::CALayerStorageProvider* storageProvider_;
34   base::Closure didDrawCallback_;
36   // Used to determine if we should use setNeedsDisplay or setAsynchronous to
37   // animate. If the last swap time happened very recently, then
38   // setAsynchronous is used (which allows smooth animation, but comes with the
39   // penalty of the canDrawInCGLContext function waking up the process every
40   // vsync).
41   base::TimeTicks lastSynchronousSwapTime_;
43   // A counter that is incremented whenever LayerCanDraw returns false. If this
44   // reaches a threshold, then |layer_| is switched to synchronous drawing to
45   // save CPU work.
46   uint32 canDrawReturnedFalseCount_;
48   gfx::Size pixelSize_;
51 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider
52                     pixelSize:(gfx::Size)pixelSize
53                   scaleFactor:(float)scaleFactor;
54 - (void)requestDrawNewFrame;
55 - (void)drawPendingFrameImmediately;
56 - (void)resetStorageProvider;
57 @end
59 @implementation ImageTransportCAOpenGLLayer
61 - (id)initWithStorageProvider:
62     (content::CALayerStorageProvider*)storageProvider
63                     pixelSize:(gfx::Size)pixelSize
64                   scaleFactor:(float)scaleFactor {
65   if (self = [super init]) {
66     gfx::Size dipSize = gfx::ConvertSizeToDIP(scaleFactor, pixelSize);
67     [self setContentsScale:scaleFactor];
68     [self setFrame:CGRectMake(0, 0, dipSize.width(), dipSize.height())];
69     storageProvider_ = storageProvider;
70     pixelSize_ = pixelSize;
71   }
72   return self;
75 - (void)requestDrawNewFrame {
76   // This tracing would be more natural to do with a pseudo-thread for each
77   // layer, rather than a counter.
78   // http://crbug.com/366300
79   // A trace value of 2 indicates that there is a pending swap ack. See
80   // canDrawInCGLContext for other value meanings.
81   TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 2);
83   if (![self isAsynchronous]) {
84     // Switch to asynchronous drawing only if we get two frames in rapid
85     // succession.
86     base::TimeTicks this_swap_time = base::TimeTicks::Now();
87     base::TimeDelta delta = this_swap_time - lastSynchronousSwapTime_;
88     if (delta <= kMinDeltaToSwitchToAsync) {
89       lastSynchronousSwapTime_ = base::TimeTicks();
90       [self setAsynchronous:YES];
91     } else {
92       lastSynchronousSwapTime_ = this_swap_time;
93       [self setNeedsDisplay];
94     }
95   }
98 - (void)drawPendingFrameImmediately {
99   DCHECK(storageProvider_->LayerHasPendingDraw());
100   if ([self isAsynchronous])
101     [self setAsynchronous:NO];
102   [self setNeedsDisplay];
103   [self displayIfNeeded];
106 - (void)resetStorageProvider {
107   storageProvider_ = NULL;
110 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
111   if (!storageProvider_)
112     return NULL;
113   return CGLRetainPixelFormat(CGLGetPixelFormat(
114       storageProvider_->LayerShareGroupContext()));
117 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
118   if (!storageProvider_)
119     return NULL;
120   CGLContextObj context = NULL;
121   CGLError error = CGLCreateContext(
122       pixelFormat, storageProvider_->LayerShareGroupContext(), &context);
123   if (error != kCGLNoError)
124     LOG(ERROR) << "CGLCreateContext failed with CGL error: " << error;
125   return context;
128 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
129                 pixelFormat:(CGLPixelFormatObj)pixelFormat
130                forLayerTime:(CFTimeInterval)timeInterval
131                 displayTime:(const CVTimeStamp*)timeStamp {
132   TRACE_EVENT0("gpu", "CALayerStorageProvider::LayerCanDraw");
134   if (!storageProvider_)
135     return NO;
137   if (storageProvider_->LayerHasPendingDraw()) {
138     // If there is a draw pending then increase the signal from 2 to 3, to
139     // indicate that there is a swap pending, and CoreAnimation has asked to
140     // draw it.
141     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 3);
143     canDrawReturnedFalseCount_ = 0;
144     return YES;
145   } else {
146     // If there is not a draw pending, then give an instantaneous blip up from
147     // 0 to 1, indicating that CoreAnimation was ready to draw a frame but we
148     // were not (or didn't have new content to draw).
149     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 1);
150     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 0);
152     if ([self isAsynchronous]) {
153       // If we are in asynchronous mode, we will be getting callbacks at every
154       // vsync, asking us if we have anything to draw. If we get many of these
155       // in a row, ask that we stop getting these callback for now, so that we
156       // don't waste CPU cycles.
157       if (canDrawReturnedFalseCount_ >= kCanDrawFalsesBeforeSwitchFromAsync)
158         [self setAsynchronous:NO];
159       else
160         canDrawReturnedFalseCount_ += 1;
161     }
162     return NO;
163   }
166 - (void)drawInCGLContext:(CGLContextObj)glContext
167              pixelFormat:(CGLPixelFormatObj)pixelFormat
168             forLayerTime:(CFTimeInterval)timeInterval
169              displayTime:(const CVTimeStamp*)timeStamp {
170   // While in this callback, CoreAnimation has set |glContext| to be current.
171   // Ensure that the GL calls that we make are made against the native GL API.
172   gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
174   if (storageProvider_) {
175     storageProvider_->LayerDoDraw(gfx::Rect(pixelSize_), false);
176     storageProvider_->LayerUnblockBrowserIfNeeded();
177     // A trace value of 0 indicates that there is no longer a pending swap ack.
178     TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 0);
179   } else {
180     glClearColor(1, 1, 1, 1);
181     glClear(GL_COLOR_BUFFER_BIT);
182   }
183   [super drawInCGLContext:glContext
184               pixelFormat:pixelFormat
185              forLayerTime:timeInterval
186               displayTime:timeStamp];
189 @end
191 namespace content {
193 CALayerStorageProvider::CALayerStorageProvider(
194     ImageTransportSurfaceFBO* transport_surface)
195     : transport_surface_(transport_surface),
196       gpu_vsync_disabled_(base::CommandLine::ForCurrentProcess()->HasSwitch(
197           switches::kDisableGpuVsync)),
198       throttling_disabled_(false),
199       has_pending_ack_(false),
200       fbo_texture_(0),
201       fbo_scale_factor_(1),
202       program_(0),
203       vertex_shader_(0),
204       fragment_shader_(0),
205       position_location_(0),
206       tex_location_(0),
207       vertex_buffer_(0),
208       vertex_array_(0),
209       recreate_layer_after_gpu_switch_(false),
210       pending_draw_weak_factory_(this) {
211   ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
214 CALayerStorageProvider::~CALayerStorageProvider() {
215   ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
218 gfx::Size CALayerStorageProvider::GetRoundedSize(gfx::Size size) {
219   return size;
222 bool CALayerStorageProvider::AllocateColorBufferStorage(
223     CGLContextObj context, const base::Closure& context_dirtied_callback,
224     GLuint texture, gfx::Size pixel_size, float scale_factor) {
225   // Allocate an ordinary OpenGL texture to back the FBO.
226   GLenum error;
227   while ((error = glGetError()) != GL_NO_ERROR) {
228     LOG(ERROR) << "OpenGL error hit but ignored before allocating buffer "
229                << "storage: " << error;
230   }
232   if (gfx::GetGLImplementation() ==
233       gfx::kGLImplementationDesktopGLCoreProfile) {
234     glTexImage2D(GL_TEXTURE_2D,
235                  0,
236                  GL_RGBA,
237                  pixel_size.width(),
238                  pixel_size.height(),
239                  0,
240                  GL_RGBA,
241                  GL_UNSIGNED_BYTE,
242                  NULL);
243     glFlush();
245     if (!vertex_shader_) {
246       const char* source =
247           "#version 150\n"
248           "in vec4 position;\n"
249           "out vec2 texcoord;\n"
250           "void main() {\n"
251           "    texcoord = vec2(position.x, position.y);\n"
252           "    gl_Position = vec4(2*position.x-1, 2*position.y-1,\n"
253           "        position.z, position.w);\n"
254           "}\n";
255       vertex_shader_ = glCreateShader(GL_VERTEX_SHADER);
256       glShaderSource(vertex_shader_, 1, &source, NULL);
257       glCompileShader(vertex_shader_);
258 #if DCHECK_IS_ON()
259       GLint status = GL_FALSE;
260       glGetShaderiv(vertex_shader_, GL_COMPILE_STATUS, &status);
261       DCHECK(status == GL_TRUE);
262 #endif
263     }
264     if (!fragment_shader_) {
265       const char* source =
266           "#version 150\n"
267           "uniform sampler2D tex;\n"
268           "in vec2 texcoord;\n"
269           "out vec4 frag_color;\n"
270           "void main() {\n"
271           "    frag_color = texture(tex, texcoord);\n"
272           "}\n";
273       fragment_shader_ = glCreateShader(GL_FRAGMENT_SHADER);
274       glShaderSource(fragment_shader_, 1, &source, NULL);
275       glCompileShader(fragment_shader_);
276 #if DCHECK_IS_ON()
277       GLint status = GL_FALSE;
278       glGetShaderiv(fragment_shader_, GL_COMPILE_STATUS, &status);
279       DCHECK(status == GL_TRUE);
280 #endif
281     }
282     if (!program_) {
283       program_ = glCreateProgram();
284       glAttachShader(program_, vertex_shader_);
285       glAttachShader(program_, fragment_shader_);
286       glBindFragDataLocation(program_, 0, "frag_color");
287       glLinkProgram(program_);
288 #if DCHECK_IS_ON()
289       GLint status = GL_FALSE;
290       glGetProgramiv(program_, GL_LINK_STATUS, &status);
291       DCHECK(status == GL_TRUE);
292 #endif
293       position_location_ = glGetAttribLocation(program_, "position");
294       tex_location_ = glGetUniformLocation(program_, "tex");
295     }
296     if (!vertex_buffer_) {
297       GLfloat vertex_data[24] = {
298         0, 0, 0, 1,
299         1, 0, 0, 1,
300         1, 1, 0, 1,
301         1, 1, 0, 1,
302         0, 1, 0, 1,
303         0, 0, 0, 1,
304       };
305       glGenBuffersARB(1, &vertex_buffer_);
306       // If the allocation path used GLContext::RestoreStateIfDirtiedExternally
307       // as the draw path does, this manual state restoration would not be
308       // necessary.
309       GLint bound_buffer = 0;
310       glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &bound_buffer);
311       glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
312       glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data),
313                    vertex_data, GL_STATIC_DRAW);
314       glBindBuffer(GL_ARRAY_BUFFER, bound_buffer);
315     }
316     if (!vertex_array_) {
317       // If the allocation path used GLContext::RestoreStateIfDirtiedExternally
318       // as the draw path does, this manual state restoration would not be
319       // necessary.
320       GLint bound_vao = 0;
321       glGetIntegerv(GL_VERTEX_ARRAY_BINDING_OES, &bound_vao);
322       glGenVertexArraysOES(1, &vertex_array_);
323       glBindVertexArrayOES(vertex_array_);
324       {
325         glEnableVertexAttribArray(position_location_);
326         GLint bound_buffer = 0;
327         glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &bound_buffer);
328         glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
329         glVertexAttribPointer(position_location_, 4, GL_FLOAT, GL_FALSE, 0, 0);
330         glBindBuffer(GL_ARRAY_BUFFER, bound_buffer);
331       }
332       glBindVertexArrayOES(bound_vao);
333     }
334   } else {
335     glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
336                  0,
337                  GL_RGBA,
338                  pixel_size.width(),
339                  pixel_size.height(),
340                  0,
341                  GL_RGBA,
342                  GL_UNSIGNED_BYTE,
343                  NULL);
344     glFlush();
345   }
347   bool hit_error = false;
348   while ((error = glGetError()) != GL_NO_ERROR) {
349     LOG(ERROR) << "OpenGL error hit while trying to allocate buffer storage: "
350                << error;
351     hit_error = true;
352   }
353   if (hit_error)
354     return false;
356   // Set the parameters that will be used to allocate the CALayer to draw the
357   // texture into.
358   share_group_context_.reset(CGLRetainContext(context));
359   share_group_context_dirtied_callback_ = context_dirtied_callback;
360   fbo_texture_ = texture;
361   fbo_pixel_size_ = pixel_size;
362   fbo_scale_factor_ = scale_factor;
363   return true;
366 void CALayerStorageProvider::FreeColorBufferStorage() {
367   if (gfx::GetGLImplementation() ==
368       gfx::kGLImplementationDesktopGLCoreProfile) {
369     if (vertex_shader_)
370       glDeleteShader(vertex_shader_);
371     if (fragment_shader_)
372       glDeleteShader(fragment_shader_);
373     if (program_)
374       glDeleteProgram(program_);
375     if (vertex_buffer_)
376       glDeleteBuffersARB(1, &vertex_buffer_);
377     if (vertex_array_)
378       glDeleteVertexArraysOES(1, &vertex_array_);
379     vertex_shader_ = 0;
380     fragment_shader_ = 0;
381     program_ = 0;
382     vertex_buffer_ = 0;
383     vertex_array_ = 0;
384   }
386   // Note that |context_| still holds a reference to |layer_|, and will until
387   // a new frame is swapped in.
388   ResetLayer();
390   share_group_context_.reset();
391   share_group_context_dirtied_callback_ = base::Closure();
392   fbo_texture_ = 0;
393   fbo_pixel_size_ = gfx::Size();
396 void CALayerStorageProvider::FrameSizeChanged(const gfx::Size& pixel_size,
397                                               float scale_factor) {
398   DCHECK_EQ(fbo_pixel_size_.ToString(), pixel_size.ToString());
399   DCHECK_EQ(fbo_scale_factor_, scale_factor);
402 void CALayerStorageProvider::SwapBuffers(const gfx::Rect& dirty_rect) {
403   TRACE_EVENT0("gpu", "CALayerStorageProvider::SwapBuffers");
404   DCHECK(!has_pending_ack_);
406   // Recreate the CALayer on the new GPU if a GPU switch has occurred. Note
407   // that the CAContext will retain a reference to the old CALayer until the
408   // call to -[CAContext setLayer:] replaces the old CALayer with the new one.
409   if (recreate_layer_after_gpu_switch_) {
410     ResetLayer();
411     recreate_layer_after_gpu_switch_ = false;
412   }
414   // Set the pending draw flag only after potentially destroying the old layer
415   // (otherwise destroying it will un-set the flag).
416   has_pending_ack_ = true;
418   // Allocate a CAContext to use to transport the CALayer to the browser
419   // process, if needed.
420   if (!context_) {
421     base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]);
422     CGSConnectionID connection_id = CGSMainConnectionID();
423     context_.reset([CAContext contextWithCGSConnection:connection_id
424                                                options:dict]);
425     [context_ retain];
426   }
428   // Create the appropriate CALayer (if needed) and request that it draw.
429   bool should_draw_immediately = gpu_vsync_disabled_ || throttling_disabled_;
430   CreateLayerAndRequestDraw(should_draw_immediately, dirty_rect);
432   // CoreAnimation may not call the function to un-block the browser in a
433   // timely manner (or ever). Post a task to force the draw and un-block
434   // the browser (at the next cycle through the run-loop if drawing is to
435   // be immediate, and at a timeout of 1/6th of a second otherwise).
436   if (has_pending_ack_) {
437     base::MessageLoop::current()->PostDelayedTask(
438         FROM_HERE,
439         base::Bind(&CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser,
440                    pending_draw_weak_factory_.GetWeakPtr()),
441         should_draw_immediately ? base::TimeDelta() :
442                                   base::TimeDelta::FromSeconds(1) / 6);
443   }
446 void CALayerStorageProvider::CreateLayerAndRequestDraw(
447     bool should_draw_immediately, const gfx::Rect& dirty_rect) {
448   if (!ca_opengl_layer_) {
449     ca_opengl_layer_.reset([[ImageTransportCAOpenGLLayer alloc]
450         initWithStorageProvider:this
451                       pixelSize:fbo_pixel_size_
452                     scaleFactor:fbo_scale_factor_]);
453   }
455   // -[CAOpenGLLayer drawInCGLContext] won't get called until we're in the
456   // visible layer hierarchy, so call setLayer: immediately, to make this
457   // happen.
458   [context_ setLayer:ca_opengl_layer_];
460   // Tell CoreAnimation to draw our frame. Note that sometimes, calling
461   // -[CAContext setLayer:] will result in the layer getting an immediate
462   // draw. If that happend, we're done.
463   if (!should_draw_immediately && has_pending_ack_) {
464     [ca_opengl_layer_ requestDrawNewFrame];
465   }
468 void CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser() {
469   DCHECK(has_pending_ack_);
471   if (ca_opengl_layer_) {
472     // Beware that sometimes, the setNeedsDisplay+displayIfNeeded pairs have no
473     // effect. This can happen if the NSView that this layer is attached to
474     // isn't in the window hierarchy (e.g, tab capture of a backgrounded tab).
475     // In this case, the frame will never be seen, so drop it.
476     [ca_opengl_layer_ drawPendingFrameImmediately];
477   }
479   UnblockBrowserIfNeeded();
482 void CALayerStorageProvider::WillWriteToBackbuffer() {
483   // The browser should always throttle itself so that there are no pending
484   // draws when the output surface is written to, but in the event of things
485   // like context lost, or changing context, this will not be true. If there
486   // exists a pending draw, flush it immediately to maintain a consistent
487   // state.
488   if (has_pending_ack_)
489     DrawImmediatelyAndUnblockBrowser();
492 void CALayerStorageProvider::DiscardBackbuffer() {
493   // If this surface's backbuffer is discarded, it is because this surface has
494   // been made non-visible. Ensure that the previous contents are not briefly
495   // flashed when this is made visible by creating a new CALayer and CAContext
496   // at the next swap.
497   ResetLayer();
499   // If we remove all references to the CAContext in this process, it will be
500   // blanked-out in the browser process (even if the browser process is inside
501   // a disable screen updates block). Ensure that the context is kept around
502   // until a fixed number of frames (determined empirically) have been acked.
503   // http://crbug.com/425819
504   while (previously_discarded_contexts_.size() <
505       kFramesToKeepCAContextAfterDiscard) {
506     previously_discarded_contexts_.push_back(
507         base::scoped_nsobject<CAContext>());
508   }
509   previously_discarded_contexts_.push_back(context_);
511   context_.reset();
514 void CALayerStorageProvider::SwapBuffersAckedByBrowser(
515     bool disable_throttling) {
516   TRACE_EVENT0("gpu", "CALayerStorageProvider::SwapBuffersAckedByBrowser");
517   throttling_disabled_ = disable_throttling;
518   if (!previously_discarded_contexts_.empty())
519     previously_discarded_contexts_.pop_front();
522 CGLContextObj CALayerStorageProvider::LayerShareGroupContext() {
523   return share_group_context_;
526 base::Closure CALayerStorageProvider::LayerShareGroupContextDirtiedCallback() {
527   return share_group_context_dirtied_callback_;
530 void CALayerStorageProvider::LayerDoDraw(
531     const gfx::Rect& dirty_rect, bool flipped) {
532   TRACE_EVENT0("gpu", "CALayerStorageProvider::LayerDoDraw");
533   if (gfx::GetGLImplementation() ==
534       gfx::kGLImplementationDesktopGLCoreProfile) {
535     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
536     glClearColor(1, 0, 1, 1);
537     glClear(GL_COLOR_BUFFER_BIT);
538     glDisable(GL_BLEND);
539     glDisable(GL_CULL_FACE);
540     glDisable(GL_DEPTH_TEST);
541     glDisable(GL_STENCIL_TEST);
542     glDisable(GL_SCISSOR_TEST);
544     DCHECK(glIsProgram(program_));
545     glUseProgram(program_);
546     glBindVertexArrayOES(vertex_array_);
548     glActiveTexture(GL_TEXTURE0);
549     glBindTexture(GL_TEXTURE_2D, fbo_texture_);
550     glUniform1i(tex_location_, 0);
552     glDisable(GL_CULL_FACE);
553     glDrawArrays(GL_TRIANGLES, 0, 6);
554     glBindVertexArrayOES(0);
555     glUseProgram(0);
556   } else {
557     GLint viewport[4] = {0, 0, 0, 0};
558     glGetIntegerv(GL_VIEWPORT, viewport);
559     gfx::Size viewport_size(viewport[2], viewport[3]);
561     // Set the coordinate system to be one-to-one with pixels.
562     glMatrixMode(GL_PROJECTION);
563     glLoadIdentity();
564     if (flipped)
565       glOrtho(0, viewport_size.width(), viewport_size.height(), 0, -1, 1);
566     else
567       glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1);
568     glMatrixMode(GL_MODELVIEW);
569     glLoadIdentity();
571     // Reset drawing state and draw a fullscreen quad.
572     glUseProgram(0);
573     glDisable(GL_BLEND);
574     glDisable(GL_CULL_FACE);
575     glDisable(GL_DEPTH_TEST);
576     glDisable(GL_STENCIL_TEST);
577     glDisable(GL_SCISSOR_TEST);
578     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
579     glColor4f(1, 1, 1, 1);
580     glActiveTexture(GL_TEXTURE0);
581     glEnable(GL_TEXTURE_RECTANGLE_ARB);
582     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fbo_texture_);
583     glBegin(GL_QUADS);
584     {
585       glTexCoord2f(dirty_rect.x(), dirty_rect.y());
586       glVertex2f(dirty_rect.x(), dirty_rect.y());
588       glTexCoord2f(dirty_rect.x(), dirty_rect.bottom());
589       glVertex2f(dirty_rect.x(), dirty_rect.bottom());
591       glTexCoord2f(dirty_rect.right(), dirty_rect.bottom());
592       glVertex2f(dirty_rect.right(), dirty_rect.bottom());
594       glTexCoord2f(dirty_rect.right(), dirty_rect.y());
595       glVertex2f(dirty_rect.right(), dirty_rect.y());
596     }
597     glEnd();
598     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
599     glDisable(GL_TEXTURE_RECTANGLE_ARB);
600   }
602   GLint current_renderer_id = 0;
603   if (CGLGetParameter(CGLGetCurrentContext(),
604                       kCGLCPCurrentRendererID,
605                       &current_renderer_id) == kCGLNoError) {
606     current_renderer_id &= kCGLRendererIDMatchingMask;
607     transport_surface_->SetRendererID(current_renderer_id);
608   }
610   GLenum error;
611   while ((error = glGetError()) != GL_NO_ERROR) {
612     LOG(ERROR) << "OpenGL error hit while drawing frame: " << error;
613   }
616 void CALayerStorageProvider::LayerUnblockBrowserIfNeeded() {
617   UnblockBrowserIfNeeded();
620 bool CALayerStorageProvider::LayerHasPendingDraw() const {
621   return has_pending_ack_;
624 void CALayerStorageProvider::OnGpuSwitched() {
625   recreate_layer_after_gpu_switch_ = true;
628 void CALayerStorageProvider::UnblockBrowserIfNeeded() {
629   if (!has_pending_ack_)
630     return;
631   pending_draw_weak_factory_.InvalidateWeakPtrs();
632   has_pending_ack_ = false;
633   transport_surface_->SendSwapBuffers(
634       ui::SurfaceHandleFromCAContextID([context_ contextId]),
635       fbo_pixel_size_,
636       fbo_scale_factor_);
639 void CALayerStorageProvider::ResetLayer() {
640   if (ca_opengl_layer_) {
641     [ca_opengl_layer_ resetStorageProvider];
642     // If we are providing back-pressure by waiting for a draw, that draw will
643     // now never come, so release the pressure now.
644     UnblockBrowserIfNeeded();
645     ca_opengl_layer_.reset();
646   }
649 }  //  namespace content