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"
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)
35 static bool enabled_at_command_line =
36 base::CommandLine::ForCurrentProcess()->HasSwitch(
37 switches::kEnableNSCGLSurfaceApi);
38 if (!enabled_at_command_line)
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.
47 CGError cg_error = CGGetActiveDisplayList(count, NULL, &count);
48 if (cg_error != kCGErrorSuccess) {
49 LOG(ERROR) << "Failed to query the number of displays.";
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)
62 // Leave this feature disabled until a flag for it is available.
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
75 @property(readonly) CGImageRef image;
76 @property(readonly) id layerContents;
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
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
93 uint32 canDrawReturnedFalseCount_;
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;
106 @interface ImageTransportNSCGLSurface : CALayer <ImageTransportLayer> {
107 content::CALayerStorageProvider* storageProvider_;
108 base::ScopedTypeRef<CGLContextObj> cglContext_;
109 base::scoped_nsobject<NSCGLSurface> surface_;
110 gfx::Size pixelSize_;
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;
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;
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
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];
155 lastSynchronousSwapTime_ = this_swap_time;
156 [self setNeedsDisplay];
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_)
176 return CGLRetainPixelFormat(CGLGetPixelFormat(
177 storageProvider_->LayerShareGroupContext()));
180 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
181 if (!storageProvider_)
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_)
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
200 TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 3);
202 canDrawReturnedFalseCount_ = 0;
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];
219 canDrawReturnedFalseCount_ += 1;
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);
239 glClearColor(1, 1, 1, 1);
240 glClear(GL_COLOR_BUFFER_BIT);
242 [super drawInCGLContext:glContext
243 pixelFormat:pixelFormat
244 forLayerTime:timeInterval
245 displayTime:timeStamp];
247 DCHECK(!didDrawCallback_.is_null());
248 didDrawCallback_.Run();
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
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;
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.
294 dirtyRect = gfx::Rect(pixelSize_);
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_];
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);
309 [surface_ attachToCGLContext:NULL];
310 [surface_ flushRect:dirtyRect.ToCGRect()];
312 // Update the layer contents to be the content rendered by OpenGL.
314 ScopedCAActionDisabler disabler;
315 [self setContents:[surface_ layerContents]];
319 - (void)drawPendingFrameImmediately {
320 [self drawNewFrame:gfx::Rect(pixelSize_)];
323 - (void)resetStorageProvider {
324 storageProvider_ = NULL;
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),
339 fbo_scale_factor_(1),
343 position_location_(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) {
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.
365 while ((error = glGetError()) != GL_NO_ERROR) {
366 LOG(ERROR) << "OpenGL error hit but ignored before allocating buffer "
367 << "storage: " << error;
370 if (gfx::GetGLImplementation() ==
371 gfx::kGLImplementationDesktopGLCoreProfile) {
372 glTexImage2D(GL_TEXTURE_2D,
383 if (!vertex_shader_) {
386 "in vec4 position;\n"
387 "out vec2 texcoord;\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"
393 vertex_shader_ = glCreateShader(GL_VERTEX_SHADER);
394 glShaderSource(vertex_shader_, 1, &source, NULL);
395 glCompileShader(vertex_shader_);
397 GLint status = GL_FALSE;
398 glGetShaderiv(vertex_shader_, GL_COMPILE_STATUS, &status);
399 DCHECK(status == GL_TRUE);
402 if (!fragment_shader_) {
405 "uniform sampler2D tex;\n"
406 "in vec2 texcoord;\n"
407 "out vec4 frag_color;\n"
409 " frag_color = texture(tex, texcoord);\n"
411 fragment_shader_ = glCreateShader(GL_FRAGMENT_SHADER);
412 glShaderSource(fragment_shader_, 1, &source, NULL);
413 glCompileShader(fragment_shader_);
415 GLint status = GL_FALSE;
416 glGetShaderiv(fragment_shader_, GL_COMPILE_STATUS, &status);
417 DCHECK(status == GL_TRUE);
421 program_ = glCreateProgram();
422 glAttachShader(program_, vertex_shader_);
423 glAttachShader(program_, fragment_shader_);
424 glBindFragDataLocation(program_, 0, "frag_color");
425 glLinkProgram(program_);
427 GLint status = GL_FALSE;
428 glGetProgramiv(program_, GL_LINK_STATUS, &status);
429 DCHECK(status == GL_TRUE);
431 position_location_ = glGetAttribLocation(program_, "position");
432 tex_location_ = glGetUniformLocation(program_, "tex");
434 if (!vertex_buffer_) {
435 GLfloat vertex_data[24] = {
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
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);
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
459 glGetIntegerv(GL_VERTEX_ARRAY_BINDING_OES, &bound_vao);
460 glGenVertexArraysOES(1, &vertex_array_);
461 glBindVertexArrayOES(vertex_array_);
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);
470 glBindVertexArrayOES(bound_vao);
473 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
485 bool hit_error = false;
486 while ((error = glGetError()) != GL_NO_ERROR) {
487 LOG(ERROR) << "OpenGL error hit while trying to allocate buffer storage: "
494 // Set the parameters that will be used to allocate the CALayer to draw the
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;
504 void CALayerStorageProvider::FreeColorBufferStorage() {
505 if (gfx::GetGLImplementation() ==
506 gfx::kGLImplementationDesktopGLCoreProfile) {
508 glDeleteShader(vertex_shader_);
509 if (fragment_shader_)
510 glDeleteShader(fragment_shader_);
512 glDeleteProgram(program_);
514 glDeleteBuffersARB(1, &vertex_buffer_);
516 glDeleteVertexArraysOES(1, &vertex_array_);
518 fragment_shader_ = 0;
524 // Note that |context_| still holds a reference to |layer_|, and will until
525 // a new frame is swapped in.
528 share_group_context_.reset();
529 share_group_context_dirtied_callback_ = base::Closure();
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_) {
549 recreate_layer_after_gpu_switch_ = false;
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])
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.
569 base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]);
570 CGSConnectionID connection_id = CGSMainConnectionID();
571 context_.reset([CAContext contextWithCGSConnection:connection_id
576 // Allocate a CALayer to use to draw the content and make it current to the
577 // CAContext, if needed.
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_]);
585 layer_.reset([[ImageTransportCAOpenGLLayer alloc]
586 initWithStorageProvider:this
587 pixelSize:fbo_pixel_size_
588 scaleFactor:fbo_scale_factor_]);
590 [context_ setLayer:layer_];
593 // Replacing the CAContext's CALayer will sometimes results in an immediate
595 if (!has_pending_draw_)
598 // Tell CoreAnimation to draw our frame.
599 if (gpu_vsync_disabled_ || throttling_disabled_) {
600 DrawImmediatelyAndUnblockBrowser();
602 [layer_ drawNewFrame:dirty_rect];
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(
610 base::Bind(&CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser,
611 pending_draw_weak_factory_.GetWeakPtr()),
612 base::TimeDelta::FromSeconds(1) / 6);
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
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
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>());
654 previously_discarded_contexts_.push_back(context_);
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);
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);
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);
708 glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1);
709 glMatrixMode(GL_MODELVIEW);
712 // Reset drawing state and draw a fullscreen quad.
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_);
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());
739 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
740 glDisable(GL_TEXTURE_RECTANGLE_ARB);
743 GLint current_renderer_id = 0;
744 if (CGLGetParameter(CGLGetCurrentContext(),
745 kCGLCPCurrentRendererID,
746 ¤t_renderer_id) == kCGLNoError) {
747 current_renderer_id &= kCGLRendererIDMatchingMask;
748 transport_surface_->SetRendererID(current_renderer_id);
752 while ((error = glGetError()) != GL_NO_ERROR) {
753 LOG(ERROR) << "OpenGL error hit while drawing frame: " << error;
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_)
771 pending_draw_weak_factory_.InvalidateWeakPtrs();
772 has_pending_draw_ = false;
773 transport_surface_->SendSwapBuffers(
774 ui::SurfaceHandleFromCAContextID([context_ contextId]),
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.
790 } // namespace content