| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/compositing_iosurface_layer_mac.h" | 5 #include "content/browser/renderer_host/compositing_iosurface_layer_mac.h" |
| 6 | 6 |
| 7 #include <CoreFoundation/CoreFoundation.h> | 7 #include <CoreFoundation/CoreFoundation.h> |
| 8 #include <OpenGL/gl.h> | 8 #include <OpenGL/gl.h> |
| 9 | 9 |
| 10 #include "base/mac/mac_util.h" | 10 #include "base/mac/mac_util.h" |
| 11 #include "base/mac/sdk_forward_declarations.h" | 11 #include "base/mac/sdk_forward_declarations.h" |
| 12 #include "content/browser/renderer_host/render_widget_host_impl.h" | 12 #include "content/browser/renderer_host/render_widget_host_impl.h" |
| 13 #include "content/browser/renderer_host/render_widget_host_view_mac.h" | 13 #include "content/browser/renderer_host/render_widget_host_view_mac.h" |
| 14 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h" | 14 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h" |
| 15 #include "content/browser/renderer_host/compositing_iosurface_mac.h" | 15 #include "content/browser/renderer_host/compositing_iosurface_mac.h" |
| 16 #include "ui/base/cocoa/animation_utils.h" | 16 #include "ui/base/cocoa/animation_utils.h" |
| 17 #include "ui/gfx/size_conversions.h" | 17 #include "ui/gfx/size_conversions.h" |
| 18 #include "ui/gl/gpu_switching_manager.h" | 18 #include "ui/gl/gpu_switching_manager.h" |
| 19 | 19 |
| 20 @implementation CompositingIOSurfaceLayer | 20 @implementation CompositingIOSurfaceLayer |
| 21 | 21 |
| 22 - (id)initWithRenderWidgetHostViewMac:(content::RenderWidgetHostViewMac*)r { | 22 - (content::CompositingIOSurfaceMac*)iosurface { |
| 23 return iosurface_.get(); |
| 24 } |
| 25 |
| 26 - (content::CompositingIOSurfaceContext*)context { |
| 27 return context_.get(); |
| 28 } |
| 29 |
| 30 - (id)initWithIOSurface:(scoped_refptr<content::CompositingIOSurfaceMac>) |
| 31 iosurface |
| 32 withClient:(content::CompositingIOSurfaceLayerClient*)client { |
| 23 if (self = [super init]) { | 33 if (self = [super init]) { |
| 24 renderWidgetHostView_ = r; | 34 iosurface_ = iosurface; |
| 35 client_ = client; |
| 36 |
| 25 context_ = content::CompositingIOSurfaceContext::Get( | 37 context_ = content::CompositingIOSurfaceContext::Get( |
| 26 content::CompositingIOSurfaceContext::kCALayerContextWindowNumber); | 38 content::CompositingIOSurfaceContext::kCALayerContextWindowNumber); |
| 27 DCHECK(context_); | 39 DCHECK(context_); |
| 28 needsDisplay_ = NO; | 40 needs_display_ = NO; |
| 41 did_not_draw_counter_ = 0; |
| 29 | 42 |
| 30 [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; | 43 [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; |
| 31 [self setAnchorPoint:CGPointMake(0, 0)]; | 44 [self setAnchorPoint:CGPointMake(0, 0)]; |
| 32 // Setting contents gravity is necessary to prevent the layer from being | 45 // Setting contents gravity is necessary to prevent the layer from being |
| 33 // scaled during dyanmic resizes (especially with devtools open). | 46 // scaled during dyanmic resizes (especially with devtools open). |
| 34 [self setContentsGravity:kCAGravityTopLeft]; | 47 [self setContentsGravity:kCAGravityTopLeft]; |
| 35 if (renderWidgetHostView_->compositing_iosurface_ && | 48 if ([self respondsToSelector:(@selector(setContentsScale:))]) { |
| 36 [self respondsToSelector:(@selector(setContentsScale:))]) { | 49 [self setContentsScale:iosurface_->scale_factor()]; |
| 37 [self setContentsScale: | |
| 38 renderWidgetHostView_->compositing_iosurface_->scale_factor()]; | |
| 39 } | 50 } |
| 40 } | 51 } |
| 41 return self; | 52 return self; |
| 42 } | 53 } |
| 43 | 54 |
| 44 - (void)disableCompositing{ | 55 - (void)resetClient { |
| 45 renderWidgetHostView_ = nil; | 56 client_ = NULL; |
| 46 } | 57 } |
| 47 | 58 |
| 48 - (void)gotNewFrame { | 59 - (void)gotNewFrame { |
| 49 if (context_ && context_->is_vsync_disabled()) { | 60 if (context_ && context_->is_vsync_disabled()) { |
| 50 // If vsync is disabled, draw immediately and don't bother trying to use | 61 // If vsync is disabled, draw immediately and don't bother trying to use |
| 51 // the isAsynchronous property to ensure smooth animation. | 62 // the isAsynchronous property to ensure smooth animation. |
| 52 [self setNeedsDisplay]; | 63 [self setNeedsDisplay]; |
| 53 [self displayIfNeeded]; | 64 [self displayIfNeeded]; |
| 54 | 65 |
| 55 // Calls to setNeedsDisplay can sometimes be ignored, especially if issued | 66 // Calls to setNeedsDisplay can sometimes be ignored, especially if issued |
| 56 // rapidly (e.g, with vsync off). This is unacceptable because the failure | 67 // rapidly (e.g, with vsync off). This is unacceptable because the failure |
| 57 // to ack a single frame will hang the renderer. Ensure that the renderer | 68 // to ack a single frame will hang the renderer. Ensure that the renderer |
| 58 // not be blocked. | 69 // not be blocked by lying and claiming that we drew the frame. |
| 59 if (needsDisplay_) | 70 if (needs_display_ && client_) |
| 60 renderWidgetHostView_->SendPendingSwapAck(); | 71 client_->AcceleratedLayerDidDrawFrame(true); |
| 61 } else { | 72 } else { |
| 62 needsDisplay_ = YES; | 73 needs_display_ = YES; |
| 63 if (![self isAsynchronous]) | 74 if (![self isAsynchronous]) |
| 64 [self setAsynchronous:YES]; | 75 [self setAsynchronous:YES]; |
| 65 } | 76 } |
| 66 } | 77 } |
| 67 | 78 |
| 68 - (void)timerSinceGotNewFrameFired { | |
| 69 if (![self isAsynchronous]) | |
| 70 return; | |
| 71 | |
| 72 [self setAsynchronous:NO]; | |
| 73 | |
| 74 // If there was a pending frame, ensure that it goes through. | |
| 75 if (needsDisplay_) { | |
| 76 [self setNeedsDisplay]; | |
| 77 [self displayIfNeeded]; | |
| 78 } | |
| 79 // If that fails then ensure that, at a minimum, the renderer is not blocked. | |
| 80 if (needsDisplay_) | |
| 81 renderWidgetHostView_->SendPendingSwapAck(); | |
| 82 } | |
| 83 | |
| 84 // The remaining methods implement the CAOpenGLLayer interface. | 79 // The remaining methods implement the CAOpenGLLayer interface. |
| 85 | 80 |
| 86 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { | 81 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { |
| 87 if (!context_) | 82 if (!context_) |
| 88 return [super copyCGLPixelFormatForDisplayMask:mask]; | 83 return [super copyCGLPixelFormatForDisplayMask:mask]; |
| 89 return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context())); | 84 return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context())); |
| 90 } | 85 } |
| 91 | 86 |
| 92 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { | 87 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { |
| 93 if (!context_) | 88 if (!context_) |
| 94 return [super copyCGLContextForPixelFormat:pixelFormat]; | 89 return [super copyCGLContextForPixelFormat:pixelFormat]; |
| 95 return CGLRetainContext(context_->cgl_context()); | 90 return CGLRetainContext(context_->cgl_context()); |
| 96 } | 91 } |
| 97 | 92 |
| 98 - (void)setNeedsDisplay { | 93 - (void)setNeedsDisplay { |
| 99 needsDisplay_ = YES; | 94 needs_display_ = YES; |
| 100 [super setNeedsDisplay]; | 95 [super setNeedsDisplay]; |
| 101 } | 96 } |
| 102 | 97 |
| 103 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext | 98 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext |
| 104 pixelFormat:(CGLPixelFormatObj)pixelFormat | 99 pixelFormat:(CGLPixelFormatObj)pixelFormat |
| 105 forLayerTime:(CFTimeInterval)timeInterval | 100 forLayerTime:(CFTimeInterval)timeInterval |
| 106 displayTime:(const CVTimeStamp*)timeStamp { | 101 displayTime:(const CVTimeStamp*)timeStamp { |
| 107 if (!renderWidgetHostView_) | |
| 108 return NO; | |
| 109 | |
| 110 // Add an instantaneous blip to the PendingSwapAck state to indicate | 102 // Add an instantaneous blip to the PendingSwapAck state to indicate |
| 111 // that CoreAnimation asked if a frame is ready. A blip up to to 3 (usually | 103 // that CoreAnimation asked if a frame is ready. A blip up to to 3 (usually |
| 112 // from 2, indicating that a swap ack is pending) indicates that we requested | 104 // from 2, indicating that a swap ack is pending) indicates that we requested |
| 113 // a draw. A blip up to 1 (usually from 0, indicating there is no pending swap | 105 // a draw. A blip up to 1 (usually from 0, indicating there is no pending swap |
| 114 // ack) indicates that we did not request a draw. This would be more natural | 106 // ack) indicates that we did not request a draw. This would be more natural |
| 115 // to do with a tracing pseudo-thread | 107 // to do with a tracing pseudo-thread |
| 116 // http://crbug.com/366300 | 108 // http://crbug.com/366300 |
| 117 TRACE_COUNTER_ID1("browser", "PendingSwapAck", renderWidgetHostView_, | 109 if (client_) { |
| 118 needsDisplay_ ? 3 : 1); | 110 TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, |
| 119 TRACE_COUNTER_ID1("browser", "PendingSwapAck", renderWidgetHostView_, | 111 needs_display_ ? 3 : 1); |
| 120 renderWidgetHostView_->HasPendingSwapAck() ? 2 : 0); | 112 TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, |
| 113 client_->AcceleratedLayerHasNotAckedPendingFrame() ? 2 : 0); |
| 114 } |
| 121 | 115 |
| 122 return needsDisplay_; | 116 // If we return NO 30 times in a row, switch to being synchronous to avoid |
| 117 // burning CPU cycles on this callback. |
| 118 if (needs_display_) { |
| 119 did_not_draw_counter_ = 0; |
| 120 } else { |
| 121 did_not_draw_counter_ += 1; |
| 122 if (did_not_draw_counter_ > 30) |
| 123 [self setAsynchronous:NO]; |
| 124 } |
| 125 |
| 126 return needs_display_; |
| 123 } | 127 } |
| 124 | 128 |
| 125 - (void)drawInCGLContext:(CGLContextObj)glContext | 129 - (void)drawInCGLContext:(CGLContextObj)glContext |
| 126 pixelFormat:(CGLPixelFormatObj)pixelFormat | 130 pixelFormat:(CGLPixelFormatObj)pixelFormat |
| 127 forLayerTime:(CFTimeInterval)timeInterval | 131 forLayerTime:(CFTimeInterval)timeInterval |
| 128 displayTime:(const CVTimeStamp*)timeStamp { | 132 displayTime:(const CVTimeStamp*)timeStamp { |
| 129 TRACE_EVENT0("browser", "CompositingIOSurfaceLayer::drawInCGLContext"); | 133 TRACE_EVENT0("browser", "CompositingIOSurfaceLayer::drawInCGLContext"); |
| 130 | 134 |
| 131 if (!context_ || | 135 if (!iosurface_->HasIOSurface() || context_->cgl_context() != glContext) { |
| 132 (context_ && context_->cgl_context() != glContext) || | |
| 133 !renderWidgetHostView_ || | |
| 134 !renderWidgetHostView_->compositing_iosurface_ || | |
| 135 !renderWidgetHostView_->compositing_iosurface_->HasIOSurface()) { | |
| 136 glClearColor(1, 1, 1, 1); | 136 glClearColor(1, 1, 1, 1); |
| 137 glClear(GL_COLOR_BUFFER_BIT); | 137 glClear(GL_COLOR_BUFFER_BIT); |
| 138 return; | 138 return; |
| 139 } | 139 } |
| 140 | 140 |
| 141 // The correct viewport to cover the layer will be set up by the caller. | 141 // The correct viewport to cover the layer will be set up by the caller. |
| 142 // Transform this into a window size for DrawIOSurface, where it will be | 142 // Transform this into a window size for DrawIOSurface, where it will be |
| 143 // transformed back into this viewport. | 143 // transformed back into this viewport. |
| 144 GLint viewport[4]; | 144 GLint viewport[4]; |
| 145 glGetIntegerv(GL_VIEWPORT, viewport); | 145 glGetIntegerv(GL_VIEWPORT, viewport); |
| 146 gfx::Rect window_rect(viewport[0], viewport[1], viewport[2], viewport[3]); | 146 gfx::Rect window_rect(viewport[0], viewport[1], viewport[2], viewport[3]); |
| 147 float window_scale_factor = 1.f; | 147 float window_scale_factor = 1.f; |
| 148 if ([self respondsToSelector:(@selector(contentsScale))]) | 148 if ([self respondsToSelector:(@selector(contentsScale))]) |
| 149 window_scale_factor = [self contentsScale]; | 149 window_scale_factor = [self contentsScale]; |
| 150 window_rect = ToNearestRect( | 150 window_rect = ToNearestRect( |
| 151 gfx::ScaleRect(window_rect, 1.f/window_scale_factor)); | 151 gfx::ScaleRect(window_rect, 1.f/window_scale_factor)); |
| 152 | 152 |
| 153 if (!renderWidgetHostView_->compositing_iosurface_->DrawIOSurface( | 153 bool draw_succeeded = iosurface_->DrawIOSurface( |
| 154 context_, | 154 context_, window_rect, window_scale_factor, false); |
| 155 window_rect, | |
| 156 window_scale_factor, | |
| 157 false)) { | |
| 158 renderWidgetHostView_->GotAcceleratedCompositingError(); | |
| 159 return; | |
| 160 } | |
| 161 | 155 |
| 162 needsDisplay_ = NO; | 156 needs_display_ = NO; |
| 163 renderWidgetHostView_->SendPendingLatencyInfoToHost(); | 157 if (client_) |
| 164 renderWidgetHostView_->SendPendingSwapAck(); | 158 client_->AcceleratedLayerDidDrawFrame(draw_succeeded); |
| 165 | 159 |
| 166 [super drawInCGLContext:glContext | 160 [super drawInCGLContext:glContext |
| 167 pixelFormat:pixelFormat | 161 pixelFormat:pixelFormat |
| 168 forLayerTime:timeInterval | 162 forLayerTime:timeInterval |
| 169 displayTime:timeStamp]; | 163 displayTime:timeStamp]; |
| 170 } | 164 } |
| 171 | 165 |
| 172 @end | 166 @end |
| OLD | NEW |