Chromium Code Reviews| Index: content/common/gpu/image_transport_surface_calayer_mac.mm |
| diff --git a/content/common/gpu/image_transport_surface_calayer_mac.mm b/content/common/gpu/image_transport_surface_calayer_mac.mm |
| index ed8d3535cb9b26b90db5ac9d441c9a77c6a1e124..4d2555db43d4530a2495d023f4d79c245e4d5a9c 100644 |
| --- a/content/common/gpu/image_transport_surface_calayer_mac.mm |
| +++ b/content/common/gpu/image_transport_surface_calayer_mac.mm |
| @@ -4,10 +4,13 @@ |
| #include "content/common/gpu/image_transport_surface_calayer_mac.h" |
| +#include "base/command_line.h" |
| #include "base/mac/sdk_forward_declarations.h" |
| #include "content/common/gpu/surface_handle_types_mac.h" |
| #include "ui/base/cocoa/animation_utils.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| +#include "ui/gl/gl_gl_api_implementation.h" |
| +#include "ui/gl/gl_switches.h" |
| @interface ImageTransportLayer : CAOpenGLLayer { |
| content::CALayerStorageProvider* storageProvider_; |
| @@ -26,6 +29,8 @@ |
| } |
| - (void)resetStorageProvider { |
| + if (storageProvider_) |
| + storageProvider_->LayerResetStorageProvider(); |
| storageProvider_ = NULL; |
| } |
| @@ -60,6 +65,10 @@ |
| pixelFormat:(CGLPixelFormatObj)pixelFormat |
| forLayerTime:(CFTimeInterval)timeInterval |
| displayTime:(const CVTimeStamp*)timeStamp { |
| + // While in this callback, CoreAnimation has set |glContext| to be current. |
| + // Ensure that the GL calls that we make are made against the native GL API. |
| + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| + |
| if (storageProvider_) { |
| storageProvider_->LayerDoDraw(); |
| } else { |
| @@ -79,17 +88,17 @@ namespace content { |
| CALayerStorageProvider::CALayerStorageProvider( |
| ImageTransportSurfaceFBO* transport_surface) |
| : transport_surface_(transport_surface), |
| + gpu_vsync_disabled_(CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kDisableGpuVsync)), |
| has_pending_draw_(false), |
| can_draw_returned_false_count_(0), |
| - fbo_texture_(0) { |
| - // Allocate a CAContext to use to transport the CALayer to the browser |
| - // process. |
| - base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]); |
| - CGSConnectionID connection_id = CGSMainConnectionID(); |
| - context_.reset([CAContext contextWithCGSConnection:connection_id |
| - options:dict]); |
| - [context_ retain]; |
| -} |
| + fbo_texture_(0), |
| + fbo_scale_factor_(1), |
| + display_timeout_timer_( |
| + FROM_HERE, |
| + base::TimeDelta::FromSeconds(1) / 6, |
| + this, |
| + &CALayerStorageProvider::DisplayTimeoutFired) {} |
| CALayerStorageProvider::~CALayerStorageProvider() { |
| } |
| @@ -126,15 +135,12 @@ bool CALayerStorageProvider::AllocateColorBufferStorage( |
| // Disable the fade-in animation as the layer is changed. |
| ScopedCAActionDisabler disabler; |
| - // Allocate a CALayer to draw texture into. |
| + // Set the parameters that will be used to allocate the CALayer to draw the |
| + // texture into. |
| share_group_context_.reset(CGLRetainContext(context)); |
| fbo_texture_ = texture; |
| fbo_pixel_size_ = pixel_size; |
| - layer_.reset([[ImageTransportLayer alloc] initWithStorageProvider:this]); |
| - gfx::Size dip_size(gfx::ToFlooredSize(gfx::ScaleSize( |
| - fbo_pixel_size_, 1.0f / scale_factor))); |
| - [layer_ setContentsScale:scale_factor]; |
| - [layer_ setFrame:CGRectMake(0, 0, dip_size.width(), dip_size.height())]; |
| + fbo_scale_factor_ = scale_factor; |
| return true; |
| } |
| @@ -155,22 +161,61 @@ void CALayerStorageProvider::FreeColorBufferStorage() { |
| fbo_pixel_size_ = gfx::Size(); |
| } |
| -uint64 CALayerStorageProvider::GetSurfaceHandle() const { |
| - return SurfaceHandleFromCAContextID([context_ contextId]); |
| +void CALayerStorageProvider::DiscardBackbuffer() { |
| + // If this surface's backbuffer is discarded, it is because this surface has |
| + // been made non-visible. Ensure that the previous contents are not briefly |
| + // flashed when this is made visible by creating a new CALayer and CAContext |
| + // at the next swap. |
| + [layer_ resetStorageProvider]; |
| + layer_.reset(); |
| + context_.reset(); |
| } |
| -void CALayerStorageProvider::WillSwapBuffers() { |
| +uint64 CALayerStorageProvider::SwapBuffersAndGetSurfaceHandle() { |
|
piman
2014/08/26 01:44:38
nit: only 1 space beween uint64 and CALayerStorage
ccameron
2014/08/26 02:00:55
Done.
|
| DCHECK(!has_pending_draw_); |
| has_pending_draw_ = true; |
| - // Don't add the layer to the CAContext until a SwapBuffers is going to be |
| - // called, because the texture does not have any content until the |
| - // SwapBuffers call is about to be made. |
| - if ([context_ layer] != layer_.get()) |
| + // Allocate a CAContext to use to transport the CALayer to the browser |
| + // process. |
| + if (!context_) { |
| + base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]); |
| + CGSConnectionID connection_id = CGSMainConnectionID(); |
| + context_.reset([CAContext contextWithCGSConnection:connection_id |
| + options:dict]); |
| + [context_ retain]; |
| + } |
| + |
| + // Allocate a CALayer to use to draw the content. |
| + if (!layer_) { |
| + layer_.reset([[ImageTransportLayer alloc] initWithStorageProvider:this]); |
| + gfx::Size dip_size(gfx::ToFlooredSize(gfx::ScaleSize( |
| + fbo_pixel_size_, 1.0f / fbo_scale_factor_))); |
| + [layer_ setContentsScale:fbo_scale_factor_]; |
| + [layer_ setFrame:CGRectMake(0, 0, dip_size.width(), dip_size.height())]; |
| + |
| + // Make the CALayer current to the CAContext and display its contents |
| + // immediately. |
| [context_ setLayer:layer_]; |
| + } |
| + |
| + // Tell CoreAnimation to draw our frame. |
| + if (gpu_vsync_disabled_) { |
| + [layer_ setNeedsDisplay]; |
| + [layer_ displayIfNeeded]; |
| + // Rapid setNeedsDisplay+displayIfNeeded calls are sometimes dropped, |
| + // ensure forward progress by re-scheduling the command buffer immediately. |
| + UnblockIfNeededAfterPendingFrameDrawn(); |
| + } else { |
| + if (![layer_ isAsynchronous]) |
| + [layer_ setAsynchronous:YES]; |
| + } |
| - if (![layer_ isAsynchronous]) |
| - [layer_ setAsynchronous:YES]; |
| + // And, in case CoreAnimation drops the frame, or is lazy in actually drawing |
| + // the frame, reset the timer to un-block this command buffer in the event |
| + // of a timeout. |
| + display_timeout_timer_.Reset(); |
| + |
| + return SurfaceHandleFromCAContextID([context_ contextId]); |
| } |
| void CALayerStorageProvider::CanFreeSwappedBuffer() { |
| @@ -185,20 +230,22 @@ bool CALayerStorageProvider::LayerCanDraw() { |
| can_draw_returned_false_count_ = 0; |
| return true; |
| } else { |
| - if (can_draw_returned_false_count_ == 30) { |
| - if ([layer_ isAsynchronous]) |
| + if ([layer_ isAsynchronous]) { |
| + DCHECK(!gpu_vsync_disabled_); |
| + // If we are in asynchronous mode, we will be getting callbacks at every |
| + // vsync, asking us if we have anything to draw. If we get 30 of these in |
| + // a row, ask that we stop getting these callback for now, so that we |
| + // don't waste CPU cycles. |
| + if (can_draw_returned_false_count_ == 30) |
| [layer_ setAsynchronous:NO]; |
| - } else { |
| - can_draw_returned_false_count_ += 1; |
| + else |
| + can_draw_returned_false_count_ += 1; |
| } |
| return false; |
| } |
| } |
| void CALayerStorageProvider::LayerDoDraw() { |
| - DCHECK(has_pending_draw_); |
| - has_pending_draw_ = false; |
| - |
| GLint viewport[4] = {0, 0, 0, 0}; |
| glGetIntegerv(GL_VIEWPORT, viewport); |
| gfx::Size viewport_size(viewport[2], viewport[3]); |
| @@ -233,7 +280,24 @@ void CALayerStorageProvider::LayerDoDraw() { |
| glDisable(GL_TEXTURE_RECTANGLE_ARB); |
| // Allow forward progress in the context now that the swap is complete. |
| - transport_surface_->UnblockContextAfterPendingSwap(); |
| + UnblockIfNeededAfterPendingFrameDrawn(); |
| +} |
| + |
| +void CALayerStorageProvider::LayerResetStorageProvider() { |
| + // If the command buffer has been unscheduled waiting for the draw, that draw |
| + // will never come, so re-schedule the command buffer now. |
| + UnblockIfNeededAfterPendingFrameDrawn(); |
| +} |
| + |
| +void CALayerStorageProvider::DisplayTimeoutFired() { |
| + UnblockIfNeededAfterPendingFrameDrawn(); |
| +} |
| + |
| +void CALayerStorageProvider::UnblockIfNeededAfterPendingFrameDrawn() { |
| + if (has_pending_draw_) { |
| + transport_surface_->UnblockContextAfterPendingSwap(); |
| + has_pending_draw_ = false; |
| + } |
| } |
| } // namespace content |