| 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..7531297ece8d113cb366b8fbc4a922d835efcde4 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,13 @@
|
| 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),
|
| + weak_factory_(this) {}
|
|
|
| CALayerStorageProvider::~CALayerStorageProvider() {
|
| }
|
| @@ -126,15 +131,12 @@
|
| // 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,25 +157,78 @@
|
| fbo_pixel_size_ = gfx::Size();
|
| }
|
|
|
| -uint64 CALayerStorageProvider::GetSurfaceHandle() const {
|
| - return SurfaceHandleFromCAContextID([context_ contextId]);
|
| -}
|
| -
|
| -void CALayerStorageProvider::WillSwapBuffers() {
|
| +void CALayerStorageProvider::SwapBuffers(
|
| + const gfx::Size& size, float scale_factor) {
|
| 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_];
|
| -
|
| - if (![layer_ isAsynchronous])
|
| - [layer_ setAsynchronous:YES];
|
| -}
|
| -
|
| -void CALayerStorageProvider::CanFreeSwappedBuffer() {
|
| + }
|
| +
|
| + // Tell CoreAnimation to draw our frame. We will send the IPC to the browser
|
| + // when CoreAnimation has drawn our frame.
|
| + if (gpu_vsync_disabled_) {
|
| + DrawWithVsyncDisabled();
|
| + } else {
|
| + if (![layer_ isAsynchronous])
|
| + [layer_ setAsynchronous:YES];
|
| + }
|
| +}
|
| +
|
| +void CALayerStorageProvider::DrawWithVsyncDisabled() {
|
| + DCHECK(has_pending_draw_);
|
| + [layer_ setNeedsDisplay];
|
| +
|
| + // Sometimes, setNeedsDisplay calls are dropped on the floor. Make this not
|
| + // hang the renderer by re-issuing the call if the draw has not yet
|
| + // happened.
|
| + if (has_pending_draw_) {
|
| + // Delay sending another draw immediately to avoid starving the run loop.
|
| + base::MessageLoop::current()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&CALayerStorageProvider::DrawWithVsyncDisabled,
|
| + weak_factory_.GetWeakPtr()),
|
| + base::TimeDelta::FromMilliseconds(5));
|
| + }
|
| +}
|
| +
|
| +void CALayerStorageProvider::WillWriteToBackbuffer() {
|
| + // TODO(ccameron): The browser may need to continue issuing swaps even when
|
| + // they do not draw. In these cases it is necessary to either double-buffer
|
| + // the resulting texture, or to drop frames.
|
| +}
|
| +
|
| +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::SwapBuffersAckedByBrowser() {
|
| }
|
|
|
| CGLContextObj CALayerStorageProvider::LayerShareGroupContext() {
|
| @@ -185,20 +240,22 @@
|
| 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 +290,25 @@
|
| glDisable(GL_TEXTURE_RECTANGLE_ARB);
|
|
|
| // Allow forward progress in the context now that the swap is complete.
|
| - transport_surface_->UnblockContextAfterPendingSwap();
|
| + DCHECK(has_pending_draw_);
|
| + SendPendingSwapToBrowserAfterFrameDrawn();
|
| +}
|
| +
|
| +void CALayerStorageProvider::LayerResetStorageProvider() {
|
| + // If we are providing back-pressure by waiting for a draw, that draw will
|
| + // now never come, so release the pressure now.
|
| + SendPendingSwapToBrowserAfterFrameDrawn();
|
| +}
|
| +
|
| +void CALayerStorageProvider::SendPendingSwapToBrowserAfterFrameDrawn() {
|
| + if (!has_pending_draw_)
|
| + return;
|
| + weak_factory_.InvalidateWeakPtrs();
|
| + has_pending_draw_ = false;
|
| + transport_surface_->SendSwapBuffers(
|
| + SurfaceHandleFromCAContextID([context_ contextId]),
|
| + fbo_pixel_size_,
|
| + fbo_scale_factor_);
|
| }
|
|
|
| } // namespace content
|
|
|