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 4ad8f92b4031c117f6377dce499e753738ea4993..ed8d3535cb9b26b90db5ac9d441c9a77c6a1e124 100644 |
--- a/content/common/gpu/image_transport_surface_calayer_mac.mm |
+++ b/content/common/gpu/image_transport_surface_calayer_mac.mm |
@@ -9,36 +9,39 @@ |
#include "ui/base/cocoa/animation_utils.h" |
#include "ui/gfx/geometry/size_conversions.h" |
-@interface ImageTransportLayer (Private) { |
+@interface ImageTransportLayer : CAOpenGLLayer { |
+ content::CALayerStorageProvider* storageProvider_; |
} |
+- (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider; |
+- (void)resetStorageProvider; |
@end |
@implementation ImageTransportLayer |
-- (id)initWithContext:(CGLContextObj)context |
- withTexture:(GLuint)texture |
- withPixelSize:(gfx::Size)pixelSize |
- withScaleFactor:(float)scaleFactor { |
- if (self = [super init]) { |
- shareContext_.reset(CGLRetainContext(context)); |
- texture_ = texture; |
- pixelSize_ = pixelSize; |
- |
- gfx::Size dipSize(gfx::ToFlooredSize(gfx::ScaleSize( |
- pixelSize_, 1.0f / scaleFactor))); |
- [self setContentsScale:scaleFactor]; |
- [self setFrame:CGRectMake(0, 0, dipSize.width(), dipSize.height())]; |
- } |
+- (id)initWithStorageProvider: |
+ (content::CALayerStorageProvider*)storageProvider { |
+ if (self = [super init]) |
+ storageProvider_ = storageProvider; |
return self; |
} |
+- (void)resetStorageProvider { |
+ storageProvider_ = NULL; |
+} |
+ |
- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { |
- return CGLRetainPixelFormat(CGLGetPixelFormat(shareContext_)); |
+ if (!storageProvider_) |
+ return NULL; |
+ return CGLRetainPixelFormat(CGLGetPixelFormat( |
+ storageProvider_->LayerShareGroupContext())); |
} |
- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { |
+ if (!storageProvider_) |
+ return NULL; |
CGLContextObj context = NULL; |
- CGLError error = CGLCreateContext(pixelFormat, shareContext_, &context); |
+ CGLError error = CGLCreateContext( |
+ pixelFormat, storageProvider_->LayerShareGroupContext(), &context); |
if (error != kCGLNoError) |
DLOG(ERROR) << "CGLCreateContext failed with CGL error: " << error; |
return context; |
@@ -48,49 +51,21 @@ |
pixelFormat:(CGLPixelFormatObj)pixelFormat |
forLayerTime:(CFTimeInterval)timeInterval |
displayTime:(const CVTimeStamp*)timeStamp { |
- return YES; |
+ if (!storageProvider_) |
+ return NO; |
+ return storageProvider_->LayerCanDraw(); |
} |
- (void)drawInCGLContext:(CGLContextObj)glContext |
pixelFormat:(CGLPixelFormatObj)pixelFormat |
forLayerTime:(CFTimeInterval)timeInterval |
displayTime:(const CVTimeStamp*)timeStamp { |
- glClearColor(1, 0, 1, 1); |
- glClear(GL_COLOR_BUFFER_BIT); |
- |
- GLint viewport[4] = {0, 0, 0, 0}; |
- glGetIntegerv(GL_VIEWPORT, viewport); |
- gfx::Size viewportSize(viewport[2], viewport[3]); |
- |
- // Set the coordinate system to be one-to-one with pixels. |
- glMatrixMode(GL_PROJECTION); |
- glLoadIdentity(); |
- glOrtho(0, viewportSize.width(), 0, viewportSize.height(), -1, 1); |
- glMatrixMode(GL_MODELVIEW); |
- glLoadIdentity(); |
- |
- // Draw a fullscreen quad. |
- glColor4f(1, 1, 1, 1); |
- glEnable(GL_TEXTURE_RECTANGLE_ARB); |
- glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_); |
- glBegin(GL_QUADS); |
- { |
- glTexCoord2f(0, 0); |
- glVertex2f(0, 0); |
- |
- glTexCoord2f(0, pixelSize_.height()); |
- glVertex2f(0, pixelSize_.height()); |
- |
- glTexCoord2f(pixelSize_.width(), pixelSize_.height()); |
- glVertex2f(pixelSize_.width(), pixelSize_.height()); |
- |
- glTexCoord2f(pixelSize_.width(), 0); |
- glVertex2f(pixelSize_.width(), 0); |
+ if (storageProvider_) { |
+ storageProvider_->LayerDoDraw(); |
+ } else { |
+ glClearColor(1, 1, 1, 1); |
+ glClear(GL_COLOR_BUFFER_BIT); |
} |
- glEnd(); |
- glBindTexture(0, texture_); |
- glDisable(GL_TEXTURE_RECTANGLE_ARB); |
- |
[super drawInCGLContext:glContext |
pixelFormat:pixelFormat |
forLayerTime:timeInterval |
@@ -101,7 +76,14 @@ |
namespace content { |
-CALayerStorageProvider::CALayerStorageProvider() { |
+CALayerStorageProvider::CALayerStorageProvider( |
+ ImageTransportSurfaceFBO* transport_surface) |
+ : transport_surface_(transport_surface), |
+ 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 |
@@ -144,18 +126,33 @@ bool CALayerStorageProvider::AllocateColorBufferStorage( |
// Disable the fade-in animation as the layer is changed. |
ScopedCAActionDisabler disabler; |
- // Resize the CAOpenGLLayer to match the size needed, and change it to be the |
- // hosted layer. |
- layer_.reset([[ImageTransportLayer alloc] initWithContext:context |
- withTexture:texture |
- withPixelSize:pixel_size |
- withScaleFactor:scale_factor]); |
+ // Allocate a CALayer to draw 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())]; |
return true; |
} |
void CALayerStorageProvider::FreeColorBufferStorage() { |
- [context_ setLayer:nil]; |
+ // We shouldn't be asked to free a texture when we still have yet to draw it. |
+ DCHECK(!has_pending_draw_); |
+ has_pending_draw_ = false; |
+ can_draw_returned_false_count_ = 0; |
+ |
+ // Note that |context_| still holds a reference to |layer_|, and will until |
+ // a new frame is swapped in. |
+ [layer_ displayIfNeeded]; |
+ [layer_ resetStorageProvider]; |
layer_.reset(); |
+ |
+ share_group_context_.reset(); |
+ fbo_texture_ = 0; |
+ fbo_pixel_size_ = gfx::Size(); |
} |
uint64 CALayerStorageProvider::GetSurfaceHandle() const { |
@@ -163,15 +160,80 @@ uint64 CALayerStorageProvider::GetSurfaceHandle() const { |
} |
void CALayerStorageProvider::WillSwapBuffers() { |
+ 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()) |
[context_ setLayer:layer_]; |
- // TODO(ccameron): Use the isAsynchronous property to ensure smooth |
- // animation. |
- [layer_ setNeedsDisplay]; |
+ if (![layer_ isAsynchronous]) |
+ [layer_ setAsynchronous:YES]; |
+} |
+ |
+void CALayerStorageProvider::CanFreeSwappedBuffer() { |
+} |
+ |
+CGLContextObj CALayerStorageProvider::LayerShareGroupContext() { |
+ return share_group_context_; |
+} |
+ |
+bool CALayerStorageProvider::LayerCanDraw() { |
+ if (has_pending_draw_) { |
+ can_draw_returned_false_count_ = 0; |
+ return true; |
+ } else { |
+ if (can_draw_returned_false_count_ == 30) { |
+ if ([layer_ isAsynchronous]) |
+ [layer_ setAsynchronous:NO]; |
+ } 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]); |
+ |
+ // Set the coordinate system to be one-to-one with pixels. |
+ glMatrixMode(GL_PROJECTION); |
+ glLoadIdentity(); |
+ glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1); |
+ glMatrixMode(GL_MODELVIEW); |
+ glLoadIdentity(); |
+ |
+ // Draw a fullscreen quad. |
+ glColor4f(1, 1, 1, 1); |
+ glEnable(GL_TEXTURE_RECTANGLE_ARB); |
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fbo_texture_); |
+ glBegin(GL_QUADS); |
+ { |
+ glTexCoord2f(0, 0); |
+ glVertex2f(0, 0); |
+ |
+ glTexCoord2f(0, fbo_pixel_size_.height()); |
+ glVertex2f(0, fbo_pixel_size_.height()); |
+ |
+ glTexCoord2f(fbo_pixel_size_.width(), fbo_pixel_size_.height()); |
+ glVertex2f(fbo_pixel_size_.width(), fbo_pixel_size_.height()); |
+ |
+ glTexCoord2f(fbo_pixel_size_.width(), 0); |
+ glVertex2f(fbo_pixel_size_.width(), 0); |
+ } |
+ glEnd(); |
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |
+ glDisable(GL_TEXTURE_RECTANGLE_ARB); |
+ |
+ // Allow forward progress in the context now that the swap is complete. |
+ transport_surface_->UnblockContextAfterPendingSwap(); |
} |
} // namespace content |