OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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/common/gpu/image_transport_surface_calayer_mac.h" | 5 #include "content/common/gpu/image_transport_surface_calayer_mac.h" |
6 | 6 |
| 7 #include "base/command_line.h" |
7 #include "base/mac/sdk_forward_declarations.h" | 8 #include "base/mac/sdk_forward_declarations.h" |
8 #include "content/common/gpu/surface_handle_types_mac.h" | 9 #include "content/common/gpu/surface_handle_types_mac.h" |
9 #include "ui/base/cocoa/animation_utils.h" | 10 #include "ui/base/cocoa/animation_utils.h" |
10 #include "ui/gfx/geometry/size_conversions.h" | 11 #include "ui/gfx/geometry/size_conversions.h" |
| 12 #include "ui/gl/gl_gl_api_implementation.h" |
| 13 #include "ui/gl/gl_switches.h" |
11 | 14 |
12 @interface ImageTransportLayer : CAOpenGLLayer { | 15 @interface ImageTransportLayer : CAOpenGLLayer { |
13 content::CALayerStorageProvider* storageProvider_; | 16 content::CALayerStorageProvider* storageProvider_; |
14 } | 17 } |
15 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider; | 18 - (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider; |
16 - (void)resetStorageProvider; | 19 - (void)resetStorageProvider; |
17 @end | 20 @end |
18 | 21 |
19 @implementation ImageTransportLayer | 22 @implementation ImageTransportLayer |
20 | 23 |
21 - (id)initWithStorageProvider: | 24 - (id)initWithStorageProvider: |
22 (content::CALayerStorageProvider*)storageProvider { | 25 (content::CALayerStorageProvider*)storageProvider { |
23 if (self = [super init]) | 26 if (self = [super init]) |
24 storageProvider_ = storageProvider; | 27 storageProvider_ = storageProvider; |
25 return self; | 28 return self; |
26 } | 29 } |
27 | 30 |
28 - (void)resetStorageProvider { | 31 - (void)resetStorageProvider { |
| 32 if (storageProvider_) |
| 33 storageProvider_->LayerResetStorageProvider(); |
29 storageProvider_ = NULL; | 34 storageProvider_ = NULL; |
30 } | 35 } |
31 | 36 |
32 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { | 37 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { |
33 if (!storageProvider_) | 38 if (!storageProvider_) |
34 return NULL; | 39 return NULL; |
35 return CGLRetainPixelFormat(CGLGetPixelFormat( | 40 return CGLRetainPixelFormat(CGLGetPixelFormat( |
36 storageProvider_->LayerShareGroupContext())); | 41 storageProvider_->LayerShareGroupContext())); |
37 } | 42 } |
38 | 43 |
(...skipping 14 matching lines...) Expand all Loading... |
53 displayTime:(const CVTimeStamp*)timeStamp { | 58 displayTime:(const CVTimeStamp*)timeStamp { |
54 if (!storageProvider_) | 59 if (!storageProvider_) |
55 return NO; | 60 return NO; |
56 return storageProvider_->LayerCanDraw(); | 61 return storageProvider_->LayerCanDraw(); |
57 } | 62 } |
58 | 63 |
59 - (void)drawInCGLContext:(CGLContextObj)glContext | 64 - (void)drawInCGLContext:(CGLContextObj)glContext |
60 pixelFormat:(CGLPixelFormatObj)pixelFormat | 65 pixelFormat:(CGLPixelFormatObj)pixelFormat |
61 forLayerTime:(CFTimeInterval)timeInterval | 66 forLayerTime:(CFTimeInterval)timeInterval |
62 displayTime:(const CVTimeStamp*)timeStamp { | 67 displayTime:(const CVTimeStamp*)timeStamp { |
| 68 // While in this callback, CoreAnimation has set |glContext| to be current. |
| 69 // Ensure that the GL calls that we make are made against the native GL API. |
| 70 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| 71 |
63 if (storageProvider_) { | 72 if (storageProvider_) { |
64 storageProvider_->LayerDoDraw(); | 73 storageProvider_->LayerDoDraw(); |
65 } else { | 74 } else { |
66 glClearColor(1, 1, 1, 1); | 75 glClearColor(1, 1, 1, 1); |
67 glClear(GL_COLOR_BUFFER_BIT); | 76 glClear(GL_COLOR_BUFFER_BIT); |
68 } | 77 } |
69 [super drawInCGLContext:glContext | 78 [super drawInCGLContext:glContext |
70 pixelFormat:pixelFormat | 79 pixelFormat:pixelFormat |
71 forLayerTime:timeInterval | 80 forLayerTime:timeInterval |
72 displayTime:timeStamp]; | 81 displayTime:timeStamp]; |
73 } | 82 } |
74 | 83 |
75 @end | 84 @end |
76 | 85 |
77 namespace content { | 86 namespace content { |
78 | 87 |
79 CALayerStorageProvider::CALayerStorageProvider( | 88 CALayerStorageProvider::CALayerStorageProvider( |
80 ImageTransportSurfaceFBO* transport_surface) | 89 ImageTransportSurfaceFBO* transport_surface) |
81 : transport_surface_(transport_surface), | 90 : transport_surface_(transport_surface), |
| 91 gpu_vsync_disabled_(CommandLine::ForCurrentProcess()->HasSwitch( |
| 92 switches::kDisableGpuVsync)), |
82 has_pending_draw_(false), | 93 has_pending_draw_(false), |
83 can_draw_returned_false_count_(0), | 94 can_draw_returned_false_count_(0), |
84 fbo_texture_(0) { | 95 fbo_texture_(0), |
85 // Allocate a CAContext to use to transport the CALayer to the browser | 96 fbo_scale_factor_(1), |
86 // process. | 97 weak_factory_(this) {} |
87 base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]); | |
88 CGSConnectionID connection_id = CGSMainConnectionID(); | |
89 context_.reset([CAContext contextWithCGSConnection:connection_id | |
90 options:dict]); | |
91 [context_ retain]; | |
92 } | |
93 | 98 |
94 CALayerStorageProvider::~CALayerStorageProvider() { | 99 CALayerStorageProvider::~CALayerStorageProvider() { |
95 } | 100 } |
96 | 101 |
97 gfx::Size CALayerStorageProvider::GetRoundedSize(gfx::Size size) { | 102 gfx::Size CALayerStorageProvider::GetRoundedSize(gfx::Size size) { |
98 return size; | 103 return size; |
99 } | 104 } |
100 | 105 |
101 bool CALayerStorageProvider::AllocateColorBufferStorage( | 106 bool CALayerStorageProvider::AllocateColorBufferStorage( |
102 CGLContextObj context, GLuint texture, | 107 CGLContextObj context, GLuint texture, |
(...skipping 16 matching lines...) Expand all Loading... |
119 error = glGetError(); | 124 error = glGetError(); |
120 if (error != GL_NO_ERROR) { | 125 if (error != GL_NO_ERROR) { |
121 DLOG(ERROR) << "glTexImage failed with GL error: " << error; | 126 DLOG(ERROR) << "glTexImage failed with GL error: " << error; |
122 return false; | 127 return false; |
123 } | 128 } |
124 glFlush(); | 129 glFlush(); |
125 | 130 |
126 // Disable the fade-in animation as the layer is changed. | 131 // Disable the fade-in animation as the layer is changed. |
127 ScopedCAActionDisabler disabler; | 132 ScopedCAActionDisabler disabler; |
128 | 133 |
129 // Allocate a CALayer to draw texture into. | 134 // Set the parameters that will be used to allocate the CALayer to draw the |
| 135 // texture into. |
130 share_group_context_.reset(CGLRetainContext(context)); | 136 share_group_context_.reset(CGLRetainContext(context)); |
131 fbo_texture_ = texture; | 137 fbo_texture_ = texture; |
132 fbo_pixel_size_ = pixel_size; | 138 fbo_pixel_size_ = pixel_size; |
133 layer_.reset([[ImageTransportLayer alloc] initWithStorageProvider:this]); | 139 fbo_scale_factor_ = scale_factor; |
134 gfx::Size dip_size(gfx::ToFlooredSize(gfx::ScaleSize( | |
135 fbo_pixel_size_, 1.0f / scale_factor))); | |
136 [layer_ setContentsScale:scale_factor]; | |
137 [layer_ setFrame:CGRectMake(0, 0, dip_size.width(), dip_size.height())]; | |
138 return true; | 140 return true; |
139 } | 141 } |
140 | 142 |
141 void CALayerStorageProvider::FreeColorBufferStorage() { | 143 void CALayerStorageProvider::FreeColorBufferStorage() { |
142 // We shouldn't be asked to free a texture when we still have yet to draw it. | 144 // We shouldn't be asked to free a texture when we still have yet to draw it. |
143 DCHECK(!has_pending_draw_); | 145 DCHECK(!has_pending_draw_); |
144 has_pending_draw_ = false; | 146 has_pending_draw_ = false; |
145 can_draw_returned_false_count_ = 0; | 147 can_draw_returned_false_count_ = 0; |
146 | 148 |
147 // Note that |context_| still holds a reference to |layer_|, and will until | 149 // Note that |context_| still holds a reference to |layer_|, and will until |
148 // a new frame is swapped in. | 150 // a new frame is swapped in. |
149 [layer_ displayIfNeeded]; | 151 [layer_ displayIfNeeded]; |
150 [layer_ resetStorageProvider]; | 152 [layer_ resetStorageProvider]; |
151 layer_.reset(); | 153 layer_.reset(); |
152 | 154 |
153 share_group_context_.reset(); | 155 share_group_context_.reset(); |
154 fbo_texture_ = 0; | 156 fbo_texture_ = 0; |
155 fbo_pixel_size_ = gfx::Size(); | 157 fbo_pixel_size_ = gfx::Size(); |
156 } | 158 } |
157 | 159 |
158 uint64 CALayerStorageProvider::GetSurfaceHandle() const { | 160 void CALayerStorageProvider::SwapBuffers( |
159 return SurfaceHandleFromCAContextID([context_ contextId]); | 161 const gfx::Size& size, float scale_factor) { |
160 } | |
161 | |
162 void CALayerStorageProvider::WillSwapBuffers() { | |
163 DCHECK(!has_pending_draw_); | 162 DCHECK(!has_pending_draw_); |
164 has_pending_draw_ = true; | 163 has_pending_draw_ = true; |
165 | 164 |
166 // Don't add the layer to the CAContext until a SwapBuffers is going to be | 165 // Allocate a CAContext to use to transport the CALayer to the browser |
167 // called, because the texture does not have any content until the | 166 // process. |
168 // SwapBuffers call is about to be made. | 167 if (!context_) { |
169 if ([context_ layer] != layer_.get()) | 168 base::scoped_nsobject<NSDictionary> dict([[NSDictionary alloc] init]); |
| 169 CGSConnectionID connection_id = CGSMainConnectionID(); |
| 170 context_.reset([CAContext contextWithCGSConnection:connection_id |
| 171 options:dict]); |
| 172 [context_ retain]; |
| 173 } |
| 174 |
| 175 // Allocate a CALayer to use to draw the content. |
| 176 if (!layer_) { |
| 177 layer_.reset([[ImageTransportLayer alloc] initWithStorageProvider:this]); |
| 178 gfx::Size dip_size(gfx::ToFlooredSize(gfx::ScaleSize( |
| 179 fbo_pixel_size_, 1.0f / fbo_scale_factor_))); |
| 180 [layer_ setContentsScale:fbo_scale_factor_]; |
| 181 [layer_ setFrame:CGRectMake(0, 0, dip_size.width(), dip_size.height())]; |
| 182 |
| 183 // Make the CALayer current to the CAContext and display its contents |
| 184 // immediately. |
170 [context_ setLayer:layer_]; | 185 [context_ setLayer:layer_]; |
| 186 } |
171 | 187 |
172 if (![layer_ isAsynchronous]) | 188 // Tell CoreAnimation to draw our frame. We will send the IPC to the browser |
173 [layer_ setAsynchronous:YES]; | 189 // when CoreAnimation has drawn our frame. |
| 190 if (gpu_vsync_disabled_) { |
| 191 DrawWithVsyncDisabled(); |
| 192 } else { |
| 193 if (![layer_ isAsynchronous]) |
| 194 [layer_ setAsynchronous:YES]; |
| 195 } |
174 } | 196 } |
175 | 197 |
176 void CALayerStorageProvider::CanFreeSwappedBuffer() { | 198 void CALayerStorageProvider::DrawWithVsyncDisabled() { |
| 199 DCHECK(has_pending_draw_); |
| 200 [layer_ setNeedsDisplay]; |
| 201 |
| 202 // Sometimes, setNeedsDisplay calls are dropped on the floor. Make this not |
| 203 // hang the renderer by re-issuing the call if the draw has not yet |
| 204 // happened. |
| 205 if (has_pending_draw_) { |
| 206 // Delay sending another draw immediately to avoid starving the run loop. |
| 207 base::MessageLoop::current()->PostDelayedTask( |
| 208 FROM_HERE, |
| 209 base::Bind(&CALayerStorageProvider::DrawWithVsyncDisabled, |
| 210 weak_factory_.GetWeakPtr()), |
| 211 base::TimeDelta::FromMilliseconds(5)); |
| 212 } |
| 213 } |
| 214 |
| 215 void CALayerStorageProvider::WillWriteToBackbuffer() { |
| 216 // TODO(ccameron): The browser may need to continue issuing swaps even when |
| 217 // they do not draw. In these cases it is necessary to either double-buffer |
| 218 // the resulting texture, or to drop frames. |
| 219 } |
| 220 |
| 221 void CALayerStorageProvider::DiscardBackbuffer() { |
| 222 // If this surface's backbuffer is discarded, it is because this surface has |
| 223 // been made non-visible. Ensure that the previous contents are not briefly |
| 224 // flashed when this is made visible by creating a new CALayer and CAContext |
| 225 // at the next swap. |
| 226 [layer_ resetStorageProvider]; |
| 227 layer_.reset(); |
| 228 context_.reset(); |
| 229 } |
| 230 |
| 231 void CALayerStorageProvider::SwapBuffersAckedByBrowser() { |
177 } | 232 } |
178 | 233 |
179 CGLContextObj CALayerStorageProvider::LayerShareGroupContext() { | 234 CGLContextObj CALayerStorageProvider::LayerShareGroupContext() { |
180 return share_group_context_; | 235 return share_group_context_; |
181 } | 236 } |
182 | 237 |
183 bool CALayerStorageProvider::LayerCanDraw() { | 238 bool CALayerStorageProvider::LayerCanDraw() { |
184 if (has_pending_draw_) { | 239 if (has_pending_draw_) { |
185 can_draw_returned_false_count_ = 0; | 240 can_draw_returned_false_count_ = 0; |
186 return true; | 241 return true; |
187 } else { | 242 } else { |
188 if (can_draw_returned_false_count_ == 30) { | 243 if ([layer_ isAsynchronous]) { |
189 if ([layer_ isAsynchronous]) | 244 DCHECK(!gpu_vsync_disabled_); |
| 245 // If we are in asynchronous mode, we will be getting callbacks at every |
| 246 // vsync, asking us if we have anything to draw. If we get 30 of these in |
| 247 // a row, ask that we stop getting these callback for now, so that we |
| 248 // don't waste CPU cycles. |
| 249 if (can_draw_returned_false_count_ == 30) |
190 [layer_ setAsynchronous:NO]; | 250 [layer_ setAsynchronous:NO]; |
191 } else { | 251 else |
192 can_draw_returned_false_count_ += 1; | 252 can_draw_returned_false_count_ += 1; |
193 } | 253 } |
194 return false; | 254 return false; |
195 } | 255 } |
196 } | 256 } |
197 | 257 |
198 void CALayerStorageProvider::LayerDoDraw() { | 258 void CALayerStorageProvider::LayerDoDraw() { |
199 DCHECK(has_pending_draw_); | |
200 has_pending_draw_ = false; | |
201 | |
202 GLint viewport[4] = {0, 0, 0, 0}; | 259 GLint viewport[4] = {0, 0, 0, 0}; |
203 glGetIntegerv(GL_VIEWPORT, viewport); | 260 glGetIntegerv(GL_VIEWPORT, viewport); |
204 gfx::Size viewport_size(viewport[2], viewport[3]); | 261 gfx::Size viewport_size(viewport[2], viewport[3]); |
205 | 262 |
206 // Set the coordinate system to be one-to-one with pixels. | 263 // Set the coordinate system to be one-to-one with pixels. |
207 glMatrixMode(GL_PROJECTION); | 264 glMatrixMode(GL_PROJECTION); |
208 glLoadIdentity(); | 265 glLoadIdentity(); |
209 glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1); | 266 glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1); |
210 glMatrixMode(GL_MODELVIEW); | 267 glMatrixMode(GL_MODELVIEW); |
211 glLoadIdentity(); | 268 glLoadIdentity(); |
(...skipping 14 matching lines...) Expand all Loading... |
226 glVertex2f(fbo_pixel_size_.width(), fbo_pixel_size_.height()); | 283 glVertex2f(fbo_pixel_size_.width(), fbo_pixel_size_.height()); |
227 | 284 |
228 glTexCoord2f(fbo_pixel_size_.width(), 0); | 285 glTexCoord2f(fbo_pixel_size_.width(), 0); |
229 glVertex2f(fbo_pixel_size_.width(), 0); | 286 glVertex2f(fbo_pixel_size_.width(), 0); |
230 } | 287 } |
231 glEnd(); | 288 glEnd(); |
232 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | 289 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |
233 glDisable(GL_TEXTURE_RECTANGLE_ARB); | 290 glDisable(GL_TEXTURE_RECTANGLE_ARB); |
234 | 291 |
235 // Allow forward progress in the context now that the swap is complete. | 292 // Allow forward progress in the context now that the swap is complete. |
236 transport_surface_->UnblockContextAfterPendingSwap(); | 293 DCHECK(has_pending_draw_); |
| 294 SendPendingSwapToBrowserAfterFrameDrawn(); |
| 295 } |
| 296 |
| 297 void CALayerStorageProvider::LayerResetStorageProvider() { |
| 298 // If we are providing back-pressure by waiting for a draw, that draw will |
| 299 // now never come, so release the pressure now. |
| 300 SendPendingSwapToBrowserAfterFrameDrawn(); |
| 301 } |
| 302 |
| 303 void CALayerStorageProvider::SendPendingSwapToBrowserAfterFrameDrawn() { |
| 304 if (!has_pending_draw_) |
| 305 return; |
| 306 weak_factory_.InvalidateWeakPtrs(); |
| 307 has_pending_draw_ = false; |
| 308 transport_surface_->SendSwapBuffers( |
| 309 SurfaceHandleFromCAContextID([context_ contextId]), |
| 310 fbo_pixel_size_, |
| 311 fbo_scale_factor_); |
237 } | 312 } |
238 | 313 |
239 } // namespace content | 314 } // namespace content |
OLD | NEW |