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 disabled_at_command_line =
36 base::CommandLine::ForCurrentProcess()->HasSwitch(
37 switches::kDisableNSCGLSurfaceApi);
38 if (disabled_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)
67 // Private NSCGLSurface API.
68 @interface NSCGLSurface : NSObject
69 - (void)flushRect:(CGRect)rect;
70 - (void)attachToCGLContext:(CGLContextObj)cglContext;
71 - (id)initWithSize:(CGSize)size
72 colorSpace:(CGColorSpaceRef)colorSpace
74 @property(readonly) CGImageRef image;
75 @property(readonly) id layerContents;
78 // Private CALayer API.
79 @interface CALayer (Private)
80 - (void)setContentsChanged;
83 @interface ImageTransportCAOpenGLLayer : CAOpenGLLayer <ImageTransportLayer> {
84 content::CALayerStorageProvider* storageProvider_;
85 base::Closure didDrawCallback_;
87 // Used to determine if we should use setNeedsDisplay or setAsynchronous to
88 // animate. If the last swap time happened very recently, then
89 // setAsynchronous is used (which allows smooth animation, but comes with the
90 // penalty of the canDrawInCGLContext function waking up the process every
92 base::TimeTicks lastSynchronousSwapTime_;
94 // A counter that is incremented whenever LayerCanDraw returns false. If this
95 // reaches a threshold, then |layer_| is switched to synchronous drawing to
97 uint32 canDrawReturnedFalseCount_;
102 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider
103 pixelSize:(gfx::Size)pixelSize
104 scaleFactor:(float)scaleFactor;
105 - (void)drawNewFrame:(gfx::Rect)dirtyRect;
106 - (void)drawPendingFrameImmediately;
107 - (void)resetStorageProvider;
110 @interface ImageTransportNSCGLSurface : CALayer <ImageTransportLayer> {
111 content::CALayerStorageProvider* storageProvider_;
112 base::ScopedTypeRef<CGLContextObj> cglContext_;
113 base::scoped_nsobject<NSCGLSurface> surface_;
114 gfx::Size pixelSize_;
117 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider
118 pixelSize:(gfx::Size)pixelSize
119 scaleFactor:(float)scaleFactor;
120 - (void)drawNewFrame:(gfx::Rect)dirtyRect;
121 - (void)drawPendingFrameImmediately;
122 - (void)resetStorageProvider;
125 @implementation ImageTransportCAOpenGLLayer
127 - (id)initWithStorageProvider:
128 (content::CALayerStorageProvider*)storageProvider
129 pixelSize:(gfx::Size)pixelSize
130 scaleFactor:(float)scaleFactor {
131 if (self = [super init]) {
132 gfx::Size dipSize = gfx::ConvertSizeToDIP(scaleFactor, pixelSize);
133 [self setContentsScale:scaleFactor];
134 [self setFrame:CGRectMake(0, 0, dipSize.width(), dipSize.height())];
135 storageProvider_ = storageProvider;
136 pixelSize_ = pixelSize;
138 // -[CAOpenGLLayer drawInCGLContext] won't get called until we're in the
139 // visible layer hierarchy, so call setLayer: immediately, to make this
141 [storageProvider_->LayerCAContext() setLayer:self];
146 - (void)drawNewFrame:(gfx::Rect)dirtyRect {
147 // This tracing would be more natural to do with a pseudo-thread for each
148 // layer, rather than a counter.
149 // http://crbug.com/366300
150 // A trace value of 2 indicates that there is a pending swap ack. See
151 // canDrawInCGLContext for other value meanings.
152 TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 2);
154 if (![self isAsynchronous]) {
155 // Switch to asynchronous drawing only if we get two frames in rapid
157 base::TimeTicks this_swap_time = base::TimeTicks::Now();
158 base::TimeDelta delta = this_swap_time - lastSynchronousSwapTime_;
159 if (delta <= kMinDeltaToSwitchToAsync) {
160 lastSynchronousSwapTime_ = base::TimeTicks();
161 [self setAsynchronous:YES];
163 lastSynchronousSwapTime_ = this_swap_time;
164 [self setNeedsDisplay];
169 - (void)drawPendingFrameImmediately {
170 DCHECK(storageProvider_->LayerHasPendingDraw());
171 if ([self isAsynchronous])
172 [self setAsynchronous:NO];
173 [self setNeedsDisplay];
174 [self displayIfNeeded];
177 - (void)resetStorageProvider {
178 storageProvider_ = NULL;
181 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
182 if (!storageProvider_)
184 return CGLRetainPixelFormat(CGLGetPixelFormat(
185 storageProvider_->LayerShareGroupContext()));
188 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
189 if (!storageProvider_)
191 didDrawCallback_ = storageProvider_->LayerShareGroupContextDirtiedCallback();
192 return CGLRetainContext(storageProvider_->LayerShareGroupContext());
195 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
196 pixelFormat:(CGLPixelFormatObj)pixelFormat
197 forLayerTime:(CFTimeInterval)timeInterval
198 displayTime:(const CVTimeStamp*)timeStamp {
199 TRACE_EVENT0("gpu", "CALayerStorageProvider::LayerCanDraw");
201 if (!storageProvider_)
204 if (storageProvider_->LayerHasPendingDraw()) {
205 // If there is a draw pending then increase the signal from 2 to 3, to
206 // indicate that there is a swap pending, and CoreAnimation has asked to
208 TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 3);
210 canDrawReturnedFalseCount_ = 0;
213 // If there is not a draw pending, then give an instantaneous blip up from
214 // 0 to 1, indicating that CoreAnimation was ready to draw a frame but we
215 // were not (or didn't have new content to draw).
216 TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 1);
217 TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 0);
219 if ([self isAsynchronous]) {
220 // If we are in asynchronous mode, we will be getting callbacks at every
221 // vsync, asking us if we have anything to draw. If we get many of these
222 // in a row, ask that we stop getting these callback for now, so that we
223 // don't waste CPU cycles.
224 if (canDrawReturnedFalseCount_ >= kCanDrawFalsesBeforeSwitchFromAsync)
225 [self setAsynchronous:NO];
227 canDrawReturnedFalseCount_ += 1;
233 - (void)drawInCGLContext:(CGLContextObj)glContext
234 pixelFormat:(CGLPixelFormatObj)pixelFormat
235 forLayerTime:(CFTimeInterval)timeInterval
236 displayTime:(const CVTimeStamp*)timeStamp {
237 // While in this callback, CoreAnimation has set |glContext| to be current.
238 // Ensure that the GL calls that we make are made against the native GL API.
239 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
241 if (storageProvider_) {
242 storageProvider_->LayerDoDraw(gfx::Rect(pixelSize_));
244 // A trace value of 0 indicates that there is no longer a pending swap ack.
245 TRACE_COUNTER_ID1("gpu", "CALayerPendingSwap", self, 0);
247 glClearColor(1, 1, 1, 1);
248 glClear(GL_COLOR_BUFFER_BIT);
250 [super drawInCGLContext:glContext
251 pixelFormat:pixelFormat
252 forLayerTime:timeInterval
253 displayTime:timeStamp];
255 DCHECK(!didDrawCallback_.is_null());
256 didDrawCallback_.Run();
261 @implementation ImageTransportNSCGLSurface
263 - (id)initWithStorageProvider:
264 (content::CALayerStorageProvider*)storageProvider
265 pixelSize:(gfx::Size)pixelSize
266 scaleFactor:(float)scaleFactor {
267 if (self = [super init]) {
268 ScopedCAActionDisabler disabler;
269 gfx::Size dipSize = gfx::ConvertSizeToDIP(scaleFactor, pixelSize);
270 [self setContentsScale:scaleFactor];
271 [self setFrame:CGRectMake(0, 0, dipSize.width(), dipSize.height())];
272 storageProvider_ = storageProvider;
274 // Allocate the NSCGLSurface to render into.
275 base::ScopedCFTypeRef<CGColorSpaceRef> cgColorSpace(
276 CGDisplayCopyColorSpace(CGMainDisplayID()));
277 Class NSCGLSurface_class = NSClassFromString(@"NSCGLSurface");
278 surface_.reset([[NSCGLSurface_class alloc] initWithSize:pixelSize.ToCGSize()
279 colorSpace:cgColorSpace
282 // Create a context in the share group of the storage provider. We will
283 // draw content using this context.
284 CGLError cglError = kCGLNoError;
285 cglError = CGLCreateContext(
286 CGLGetPixelFormat(storageProvider_->LayerShareGroupContext()),
287 storageProvider_->LayerShareGroupContext(),
288 cglContext_.InitializeInto());
289 LOG_IF(ERROR, cglError != kCGLNoError) <<
290 "Failed to create CGL context for NSCGL surface.";
292 pixelSize_ = pixelSize;
297 - (void)drawNewFrame:(gfx::Rect)dirtyRect {
298 // Draw the first frame to the layer as covering the full layer. Subsequent
299 // frames may use partial damage.
300 if (![self contents])
301 dirtyRect = gfx::Rect(pixelSize_);
303 // Make the context current to the thread, make the surface be the current
304 // drawable for the context, and draw.
305 [surface_ attachToCGLContext:cglContext_];
307 gfx::ScopedCGLSetCurrentContext scopedSetCurrentContext(cglContext_);
308 gfx::ScopedSetGLToRealGLApi scopedSetRealGLApi;
309 glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
310 glViewport(0, 0, pixelSize_.width(), pixelSize_.height());
311 storageProvider_->LayerDoDraw(dirtyRect);
314 [surface_ attachToCGLContext:NULL];
315 [surface_ flushRect:dirtyRect.ToCGRect()];
317 if (![self contents]) {
318 // The first time we draw, set the layer contents and the CAContext's layer
320 ScopedCAActionDisabler disabler;
321 [self setContents:[surface_ layerContents]];
323 [storageProvider_->LayerCAContext() setLayer:self];
325 // For subsequent draws, just indicate that the layer contents has changed.
326 // This has lower power usage than calling -[CALayer setContents:].
327 [self setContentsChanged];
331 - (void)drawPendingFrameImmediately {
332 [self drawNewFrame:gfx::Rect(pixelSize_)];
335 - (void)resetStorageProvider {
336 storageProvider_ = NULL;
343 CALayerStorageProvider::CALayerStorageProvider(
344 ImageTransportSurfaceFBO* transport_surface)
345 : transport_surface_(transport_surface),
346 gpu_vsync_disabled_(base::CommandLine::ForCurrentProcess()->HasSwitch(
347 switches::kDisableGpuVsync)),
348 throttling_disabled_(false),
349 has_pending_draw_(false),
351 fbo_scale_factor_(1),
355 position_location_(0),
359 recreate_layer_after_gpu_switch_(false),
360 pending_draw_weak_factory_(this) {
361 ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
364 CALayerStorageProvider::~CALayerStorageProvider() {
365 ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
368 gfx::Size CALayerStorageProvider::GetRoundedSize(gfx::Size size) {
372 bool CALayerStorageProvider::AllocateColorBufferStorage(
373 CGLContextObj context, const base::Closure& context_dirtied_callback,
374 GLuint texture, gfx::Size pixel_size, float scale_factor) {
375 // Allocate an ordinary OpenGL texture to back the FBO.
377 while ((error = glGetError()) != GL_NO_ERROR) {
378 LOG(ERROR) << "OpenGL error hit but ignored before allocating buffer "
379 << "storage: " << error;
382 if (gfx::GetGLImplementation() ==
383 gfx::kGLImplementationDesktopGLCoreProfile) {
384 glTexImage2D(GL_TEXTURE_2D,
395 if (!vertex_shader_) {
398 "in vec4 position;\n"
399 "out vec2 texcoord;\n"
401 " texcoord = vec2(position.x, position.y);\n"
402 " gl_Position = vec4(2*position.x-1, 2*position.y-1,\n"
403 " position.z, position.w);\n"
405 vertex_shader_ = glCreateShader(GL_VERTEX_SHADER);
406 glShaderSource(vertex_shader_, 1, &source, NULL);
407 glCompileShader(vertex_shader_);
409 GLint status = GL_FALSE;
410 glGetShaderiv(vertex_shader_, GL_COMPILE_STATUS, &status);
411 DCHECK(status == GL_TRUE);
414 if (!fragment_shader_) {
417 "uniform sampler2D tex;\n"
418 "in vec2 texcoord;\n"
419 "out vec4 frag_color;\n"
421 " frag_color = texture(tex, texcoord);\n"
423 fragment_shader_ = glCreateShader(GL_FRAGMENT_SHADER);
424 glShaderSource(fragment_shader_, 1, &source, NULL);
425 glCompileShader(fragment_shader_);
427 GLint status = GL_FALSE;
428 glGetShaderiv(fragment_shader_, GL_COMPILE_STATUS, &status);
429 DCHECK(status == GL_TRUE);
433 program_ = glCreateProgram();
434 glAttachShader(program_, vertex_shader_);
435 glAttachShader(program_, fragment_shader_);
436 glBindFragDataLocation(program_, 0, "frag_color");
437 glLinkProgram(program_);
439 GLint status = GL_FALSE;
440 glGetProgramiv(program_, GL_LINK_STATUS, &status);
441 DCHECK(status == GL_TRUE);
443 position_location_ = glGetAttribLocation(program_, "position");
444 tex_location_ = glGetUniformLocation(program_, "tex");
446 if (!vertex_buffer_) {
447 GLfloat vertex_data[24] = {
455 glGenBuffersARB(1, &vertex_buffer_);
456 // If the allocation path used GLContext::RestoreStateIfDirtiedExternally
457 // as the draw path does, this manual state restoration would not be
459 GLint bound_buffer = 0;
460 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &bound_buffer);
461 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
462 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data),
463 vertex_data, GL_STATIC_DRAW);
464 glBindBuffer(GL_ARRAY_BUFFER, bound_buffer);
466 if (!vertex_array_) {
467 // If the allocation path used GLContext::RestoreStateIfDirtiedExternally
468 // as the draw path does, this manual state restoration would not be
471 glGetIntegerv(GL_VERTEX_ARRAY_BINDING_OES, &bound_vao);
472 glGenVertexArraysOES(1, &vertex_array_);
473 glBindVertexArrayOES(vertex_array_);
475 glEnableVertexAttribArray(position_location_);
476 GLint bound_buffer = 0;
477 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &bound_buffer);
478 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
479 glVertexAttribPointer(position_location_, 4, GL_FLOAT, GL_FALSE, 0, 0);
480 glBindBuffer(GL_ARRAY_BUFFER, bound_buffer);
482 glBindVertexArrayOES(bound_vao);
485 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
497 bool hit_error = false;
498 while ((error = glGetError()) != GL_NO_ERROR) {
499 LOG(ERROR) << "OpenGL error hit while trying to allocate buffer storage: "
506 // Set the parameters that will be used to allocate the CALayer to draw the
508 share_group_context_.reset(CGLRetainContext(context));
509 share_group_context_dirtied_callback_ = context_dirtied_callback;
510 fbo_texture_ = texture;
511 fbo_pixel_size_ = pixel_size;
512 fbo_scale_factor_ = scale_factor;
516 void CALayerStorageProvider::FreeColorBufferStorage() {
517 if (gfx::GetGLImplementation() ==
518 gfx::kGLImplementationDesktopGLCoreProfile) {
520 glDeleteShader(vertex_shader_);
521 if (fragment_shader_)
522 glDeleteShader(fragment_shader_);
524 glDeleteProgram(program_);
526 glDeleteBuffersARB(1, &vertex_buffer_);
528 glDeleteVertexArraysOES(1, &vertex_array_);
530 fragment_shader_ = 0;
536 // Note that |context_| still holds a reference to |layer_|, and will until
537 // a new frame is swapped in.
540 share_group_context_.reset();
541 share_group_context_dirtied_callback_ = base::Closure();
543 fbo_pixel_size_ = gfx::Size();
546 void CALayerStorageProvider::FrameSizeChanged(const gfx::Size& pixel_size,
547 float scale_factor) {
548 DCHECK_EQ(fbo_pixel_size_.ToString(), pixel_size.ToString());
549 DCHECK_EQ(fbo_scale_factor_, scale_factor);
552 void CALayerStorageProvider::SwapBuffers(const gfx::Rect& dirty_rect) {
553 TRACE_EVENT0("gpu", "CALayerStorageProvider::SwapBuffers");
554 DCHECK(!has_pending_draw_);
556 // Recreate the CALayer on the new GPU if a GPU switch has occurred. Note
557 // that the CAContext will retain a reference to the old CALayer until the
558 // call to -[CAContext setLayer:] replaces the old CALayer with the new one.
559 if (recreate_layer_after_gpu_switch_) {
561 recreate_layer_after_gpu_switch_ = false;
564 // Determine if it is safe to use an NSCGLSurface, or if we should use the
565 // CAOpenGLLayer fallback. If we're not using the preferred type of layer,
566 // then reset the layer and re-create one of the preferred type.
567 bool can_use_ns_cgl_surface =
568 CanUseNSCGLSurface(transport_surface_->GetFeatureInfo());
569 Class expected_layer_class = can_use_ns_cgl_surface ?
570 [ImageTransportNSCGLSurface class] : [ImageTransportCAOpenGLLayer class];
571 if (![layer_ isKindOfClass:expected_layer_class])
574 // Set the pending draw flag only after destroying the old layer (otherwise
575 // destroying it will un-set the flag).
576 has_pending_draw_ = true;
578 // Allocate a CAContext to use to transport the CALayer to the browser
579 // process, if needed.
581 base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]);
582 CGSConnectionID connection_id = CGSMainConnectionID();
583 context_.reset([CAContext contextWithCGSConnection:connection_id
588 // Allocate a CALayer to use to draw the content and make it current to the
589 // CAContext, if needed.
591 if (can_use_ns_cgl_surface) {
592 layer_.reset([[ImageTransportNSCGLSurface alloc]
593 initWithStorageProvider:this
594 pixelSize:fbo_pixel_size_
595 scaleFactor:fbo_scale_factor_]);
597 layer_.reset([[ImageTransportCAOpenGLLayer alloc]
598 initWithStorageProvider:this
599 pixelSize:fbo_pixel_size_
600 scaleFactor:fbo_scale_factor_]);
604 // Replacing the CAContext's CALayer will sometimes results in an immediate
606 if (!has_pending_draw_)
609 // Tell CoreAnimation to draw our frame.
610 if (gpu_vsync_disabled_ || throttling_disabled_) {
611 DrawImmediatelyAndUnblockBrowser();
613 [layer_ drawNewFrame:dirty_rect];
616 if (has_pending_draw_) {
617 // If CoreAnimation doesn't end up drawing our frame, un-block the browser
618 // after a timeout of 1/6th of a second has passed.
619 base::MessageLoop::current()->PostDelayedTask(
621 base::Bind(&CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser,
622 pending_draw_weak_factory_.GetWeakPtr()),
623 base::TimeDelta::FromSeconds(1) / 6);
627 void CALayerStorageProvider::DrawImmediatelyAndUnblockBrowser() {
628 CHECK(has_pending_draw_);
629 [layer_ drawPendingFrameImmediately];
631 // Sometimes, the setNeedsDisplay+displayIfNeeded pairs have no effect. This
632 // can happen if the NSView that this layer is attached to isn't in the
633 // window hierarchy (e.g, tab capture of a backgrounded tab). In this case,
634 // the frame will never be seen, so drop it.
635 UnblockBrowserIfNeeded();
638 void CALayerStorageProvider::WillWriteToBackbuffer() {
639 // The browser should always throttle itself so that there are no pending
640 // draws when the output surface is written to, but in the event of things
641 // like context lost, or changing context, this will not be true. If there
642 // exists a pending draw, flush it immediately to maintain a consistent
644 if (has_pending_draw_)
645 DrawImmediatelyAndUnblockBrowser();
648 void CALayerStorageProvider::DiscardBackbuffer() {
649 // If this surface's backbuffer is discarded, it is because this surface has
650 // been made non-visible. Ensure that the previous contents are not briefly
651 // flashed when this is made visible by creating a new CALayer and CAContext
655 // If we remove all references to the CAContext in this process, it will be
656 // blanked-out in the browser process (even if the browser process is inside
657 // a NSDisableScreenUpdates block). Ensure that the context is kept around
658 // until a fixed number of frames (determined empirically) have been acked.
659 // http://crbug.com/425819
660 while (previously_discarded_contexts_.size() <
661 kFramesToKeepCAContextAfterDiscard) {
662 previously_discarded_contexts_.push_back(
663 base::scoped_nsobject<CAContext>());
665 previously_discarded_contexts_.push_back(context_);
670 void CALayerStorageProvider::SwapBuffersAckedByBrowser(
671 bool disable_throttling) {
672 TRACE_EVENT0("gpu", "CALayerStorageProvider::SwapBuffersAckedByBrowser");
673 throttling_disabled_ = disable_throttling;
674 if (!previously_discarded_contexts_.empty())
675 previously_discarded_contexts_.pop_front();
678 CGLContextObj CALayerStorageProvider::LayerShareGroupContext() {
679 return share_group_context_;
682 base::Closure CALayerStorageProvider::LayerShareGroupContextDirtiedCallback() {
683 return share_group_context_dirtied_callback_;
686 void CALayerStorageProvider::LayerDoDraw(const gfx::Rect& dirty_rect) {
687 TRACE_EVENT0("gpu", "CALayerStorageProvider::LayerDoDraw");
688 if (gfx::GetGLImplementation() ==
689 gfx::kGLImplementationDesktopGLCoreProfile) {
690 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
691 glClearColor(1, 0, 1, 1);
692 glClear(GL_COLOR_BUFFER_BIT);
694 glDisable(GL_CULL_FACE);
695 glDisable(GL_DEPTH_TEST);
696 glDisable(GL_STENCIL_TEST);
697 glDisable(GL_SCISSOR_TEST);
699 DCHECK(glIsProgram(program_));
700 glUseProgram(program_);
701 glBindVertexArrayOES(vertex_array_);
703 glActiveTexture(GL_TEXTURE0);
704 glBindTexture(GL_TEXTURE_2D, fbo_texture_);
705 glUniform1i(tex_location_, 0);
707 glDisable(GL_CULL_FACE);
708 glDrawArrays(GL_TRIANGLES, 0, 6);
709 glBindVertexArrayOES(0);
712 GLint viewport[4] = {0, 0, 0, 0};
713 glGetIntegerv(GL_VIEWPORT, viewport);
714 gfx::Size viewport_size(viewport[2], viewport[3]);
716 // Set the coordinate system to be one-to-one with pixels.
717 glMatrixMode(GL_PROJECTION);
719 glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1);
720 glMatrixMode(GL_MODELVIEW);
723 // Reset drawing state and draw a fullscreen quad.
726 glDisable(GL_CULL_FACE);
727 glDisable(GL_DEPTH_TEST);
728 glDisable(GL_STENCIL_TEST);
729 glDisable(GL_SCISSOR_TEST);
730 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
731 glColor4f(1, 1, 1, 1);
732 glActiveTexture(GL_TEXTURE0);
733 glEnable(GL_TEXTURE_RECTANGLE_ARB);
734 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fbo_texture_);
737 glTexCoord2f(dirty_rect.x(), dirty_rect.y());
738 glVertex2f(dirty_rect.x(), dirty_rect.y());
740 glTexCoord2f(dirty_rect.x(), dirty_rect.bottom());
741 glVertex2f(dirty_rect.x(), dirty_rect.bottom());
743 glTexCoord2f(dirty_rect.right(), dirty_rect.bottom());
744 glVertex2f(dirty_rect.right(), dirty_rect.bottom());
746 glTexCoord2f(dirty_rect.right(), dirty_rect.y());
747 glVertex2f(dirty_rect.right(), dirty_rect.y());
750 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
751 glDisable(GL_TEXTURE_RECTANGLE_ARB);
754 GLint current_renderer_id = 0;
755 if (CGLGetParameter(CGLGetCurrentContext(),
756 kCGLCPCurrentRendererID,
757 ¤t_renderer_id) == kCGLNoError) {
758 current_renderer_id &= kCGLRendererIDMatchingMask;
759 transport_surface_->SetRendererID(current_renderer_id);
763 while ((error = glGetError()) != GL_NO_ERROR) {
764 LOG(ERROR) << "OpenGL error hit while drawing frame: " << error;
767 // Allow forward progress in the context now that the swap is complete.
768 UnblockBrowserIfNeeded();
771 bool CALayerStorageProvider::LayerHasPendingDraw() const {
772 return has_pending_draw_;
775 void CALayerStorageProvider::OnGpuSwitched() {
776 recreate_layer_after_gpu_switch_ = true;
779 void CALayerStorageProvider::UnblockBrowserIfNeeded() {
780 if (!has_pending_draw_)
782 pending_draw_weak_factory_.InvalidateWeakPtrs();
783 has_pending_draw_ = false;
784 transport_surface_->SendSwapBuffers(
785 ui::SurfaceHandleFromCAContextID([context_ contextId]),
790 void CALayerStorageProvider::ResetLayer() {
791 [layer_ resetStorageProvider];
793 // If we are providing back-pressure by waiting for a draw, that draw will
794 // now never come, so release the pressure now.
795 UnblockBrowserIfNeeded();
797 // This should only ever be called by the active layer.
801 } // namespace content