OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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_overlay_mac.h" | 5 #include "content/common/gpu/image_transport_surface_overlay_mac.h" |
6 | 6 |
7 #include <IOSurface/IOSurface.h> | 7 #include <OpenGL/gl.h> |
8 #include <OpenGL/GL.h> | |
9 | 8 |
10 // This type consistently causes problem on Mac, and needs to be dealt with | |
11 // in a systemic way. | |
12 // http://crbug.com/517208 | |
13 #ifndef GL_OES_EGL_image | |
14 typedef void* GLeglImageOES; | |
15 #endif | |
16 | |
17 #include "base/mac/scoped_cftyperef.h" | |
18 #include "content/common/gpu/gpu_messages.h" | 9 #include "content/common/gpu/gpu_messages.h" |
19 #include "ui/accelerated_widget_mac/surface_handle_types.h" | 10 #include "ui/accelerated_widget_mac/surface_handle_types.h" |
20 #include "ui/base/cocoa/animation_utils.h" | 11 #include "ui/base/cocoa/animation_utils.h" |
21 #include "ui/base/cocoa/remote_layer_api.h" | 12 #include "ui/base/cocoa/remote_layer_api.h" |
22 #include "ui/gfx/geometry/dip_util.h" | 13 #include "ui/gfx/geometry/dip_util.h" |
23 #include "ui/gl/gl_fence_apple.h" | |
24 #include "ui/gl/gl_image_io_surface.h" | 14 #include "ui/gl/gl_image_io_surface.h" |
25 #include "ui/gl/scoped_api.h" | |
26 #include "ui/gl/scoped_cgl.h" | |
27 | |
28 namespace { | |
29 | |
30 // Don't let a frame draw until 5% of the way through the next vsync interval | |
31 // after the call to SwapBuffers. This slight offset is to ensure that skew | |
32 // doesn't result in the frame being presented to the previous vsync interval. | |
33 | |
34 const double kVSyncIntervalFractionForEarliestDisplay = 0.05; | |
35 // Target 50% of the way through the next vsync interval. Empirically, it has | |
36 // been determined to be a good target for smooth animation. | |
37 const double kVSyncIntervalFractionForDisplayCallback = 0.5; | |
38 | |
39 // If a frame takes more than 1/4th of a second for its fence to finish, just | |
40 // pretend that the frame is ready to draw. | |
41 const double kMaximumDelayWaitingForFenceInSeconds = 0.25; | |
42 | |
43 void CheckGLErrors(const char* msg) { | |
44 GLenum gl_error; | |
45 while ((gl_error = glGetError()) != GL_NO_ERROR) { | |
46 LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error; | |
47 } | |
48 } | |
49 | |
50 } // namespace | |
51 | |
52 @interface CALayer(Private) | |
53 -(void)setContentsChanged; | |
54 @end | |
55 | 15 |
56 namespace content { | 16 namespace content { |
57 | 17 |
58 class ImageTransportSurfaceOverlayMac::PendingSwap { | |
59 public: | |
60 PendingSwap() : scale_factor(1) {} | |
61 ~PendingSwap() { DCHECK(!fence); } | |
62 | |
63 gfx::Size pixel_size; | |
64 float scale_factor; | |
65 std::vector<ui::LatencyInfo> latency_info; | |
66 | |
67 // The IOSurface with new content for this swap. | |
68 base::ScopedCFTypeRef<IOSurfaceRef> io_surface; | |
69 | |
70 // A fence object, and the CGL context it was issued in. | |
71 base::ScopedTypeRef<CGLContextObj> cgl_context; | |
72 scoped_ptr<gfx::GLFenceAPPLE> fence; | |
73 | |
74 // The earliest time that this frame may be drawn. A frame is not allowed | |
75 // to draw until a fraction of the way through the vsync interval after its | |
76 // This extra latency is to allow wiggle-room for smoothness. | |
77 base::TimeTicks earliest_allowed_draw_time; | |
78 // After this time, always draw the frame, no matter what its fence says. This | |
79 // is to prevent GL bugs from locking the compositor forever. | |
80 base::TimeTicks latest_allowed_draw_time; | |
81 }; | |
82 | |
83 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac( | 18 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac( |
84 GpuChannelManager* manager, | 19 GpuChannelManager* manager, |
85 GpuCommandBufferStub* stub, | 20 GpuCommandBufferStub* stub, |
86 gfx::PluginWindowHandle handle) | 21 gfx::PluginWindowHandle handle) |
87 : scale_factor_(1), pending_overlay_image_(nullptr), | 22 : scale_factor_(1), pending_overlay_image_(nullptr) { |
88 has_pending_callback_(false), weak_factory_(this) { | |
89 helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); | 23 helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); |
90 } | 24 } |
91 | 25 |
92 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() { | 26 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() { |
| 27 gfx::GLImageIOSurface::SetLayerForWidget(widget_, nil); |
93 } | 28 } |
94 | 29 |
95 bool ImageTransportSurfaceOverlayMac::Initialize() { | 30 bool ImageTransportSurfaceOverlayMac::Initialize() { |
96 if (!helper_->Initialize()) | 31 if (!helper_->Initialize()) |
97 return false; | 32 return false; |
98 | 33 |
99 // Create the CAContext to send this to the GPU process, and the layer for | 34 // Create the CAContext to send this to the GPU process, and the layer for |
100 // the context. | 35 // the context. |
101 CGSConnectionID connection_id = CGSMainConnectionID(); | 36 CGSConnectionID connection_id = CGSMainConnectionID(); |
102 ca_context_.reset( | 37 ca_context_.reset( |
103 [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]); | 38 [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]); |
104 layer_.reset([[CALayer alloc] init]); | 39 layer_.reset([[CALayer alloc] init]); |
105 [layer_ setGeometryFlipped:YES]; | |
106 [layer_ setOpaque:YES]; | |
107 [ca_context_ setLayer:layer_]; | 40 [ca_context_ setLayer:layer_]; |
| 41 |
| 42 // Register the CALayer so that it can be picked up in GLImageIOSurface. |
| 43 static intptr_t previous_widget = 0; |
| 44 previous_widget += 1; |
| 45 widget_ = reinterpret_cast<gfx::AcceleratedWidget>(previous_widget); |
| 46 gfx::GLImageIOSurface::SetLayerForWidget(widget_, layer_); |
| 47 |
108 return true; | 48 return true; |
109 } | 49 } |
110 | 50 |
111 void ImageTransportSurfaceOverlayMac::Destroy() { | 51 void ImageTransportSurfaceOverlayMac::Destroy() {} |
112 FinishAllPendingSwaps(); | |
113 } | |
114 | 52 |
115 bool ImageTransportSurfaceOverlayMac::IsOffscreen() { | 53 bool ImageTransportSurfaceOverlayMac::IsOffscreen() { |
116 return false; | 54 return false; |
117 } | 55 } |
118 | 56 |
119 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal( | 57 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { |
120 const gfx::Rect& pixel_damage_rect) { | 58 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffers"); |
121 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffersInternal"); | |
122 | 59 |
123 // Use the same concept of 'now' for the entire function. The duration of | 60 // A flush is required to ensure that all content appears in the layer. |
124 // this function only affect the result if this function lasts across a vsync | 61 { |
125 // boundary, in which case smooth animation is out the window anyway. | 62 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::glFlush"); |
126 const base::TimeTicks now = base::TimeTicks::Now(); | 63 glFlush(); |
127 | |
128 // If the previous swap is ready to display, do it before flushing the | |
129 // new swap. It is desirable to always be hitting this path when trying to | |
130 // animate smoothly with vsync. | |
131 if (!pending_swaps_.empty()) { | |
132 if (IsFirstPendingSwapReadyToDisplay(now)) | |
133 DisplayFirstPendingSwapImmediately(); | |
134 } | 64 } |
135 | 65 |
136 // The remainder of the function will populate the PendingSwap structure and | |
137 // then enqueue it. | |
138 linked_ptr<PendingSwap> new_swap(new PendingSwap); | |
139 new_swap->pixel_size = pixel_size_; | |
140 new_swap->scale_factor = scale_factor_; | |
141 new_swap->latency_info.swap(latency_info_); | |
142 | |
143 // There should exist only one overlay image, and it should cover the whole | 66 // There should exist only one overlay image, and it should cover the whole |
144 // surface. | 67 // surface. |
145 DCHECK(pending_overlay_image_); | 68 DCHECK(pending_overlay_image_); |
146 if (pending_overlay_image_) { | 69 if (pending_overlay_image_) { |
147 gfx::GLImageIOSurface* pending_overlay_image_io_surface = | 70 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::setContents"); |
148 static_cast<gfx::GLImageIOSurface*>(pending_overlay_image_); | 71 ScopedCAActionDisabler disabler; |
149 new_swap->io_surface = pending_overlay_image_io_surface->io_surface(); | 72 gfx::Rect dip_bounds = gfx::ConvertRectToDIP( |
| 73 scale_factor_, gfx::Rect(pixel_size_)); |
| 74 gfx::RectF crop_rect(0, 0, 1, 1); |
| 75 pending_overlay_image_->ScheduleOverlayPlane( |
| 76 widget_, 0, gfx::OVERLAY_TRANSFORM_NONE, dip_bounds, crop_rect); |
150 pending_overlay_image_ = nullptr; | 77 pending_overlay_image_ = nullptr; |
151 } | 78 } |
152 | 79 |
153 // A flush is required to ensure that all content appears in the layer. | |
154 { | |
155 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
156 TRACE_EVENT1("gpu", "ImageTransportSurfaceOverlayMac::glFlush", "surface", | |
157 new_swap->io_surface.get()); | |
158 CheckGLErrors("before flushing frame"); | |
159 new_swap->cgl_context.reset(CGLGetCurrentContext(), | |
160 base::scoped_policy::RETAIN); | |
161 new_swap->fence.reset(new gfx::GLFenceAPPLE); | |
162 CheckGLErrors("while flushing frame"); | |
163 } | |
164 | |
165 // Compute the deadlines for drawing this frame. | |
166 if (display_link_mac_) { | |
167 new_swap->earliest_allowed_draw_time = | |
168 display_link_mac_->GetNextVSyncTimeAfter( | |
169 now, kVSyncIntervalFractionForEarliestDisplay); | |
170 new_swap->latest_allowed_draw_time = now + | |
171 base::TimeDelta::FromSecondsD(kMaximumDelayWaitingForFenceInSeconds); | |
172 } else { | |
173 // If we have no display link (because vsync is disabled or because we have | |
174 // not received display parameters yet), immediately attempt to display the | |
175 // surface. | |
176 new_swap->earliest_allowed_draw_time = now; | |
177 new_swap->latest_allowed_draw_time = now; | |
178 } | |
179 | |
180 pending_swaps_.push_back(new_swap); | |
181 PostCheckPendingSwapsCallbackIfNeeded(now); | |
182 return gfx::SwapResult::SWAP_ACK; | |
183 } | |
184 | |
185 bool ImageTransportSurfaceOverlayMac::IsFirstPendingSwapReadyToDisplay( | |
186 const base::TimeTicks& now) { | |
187 DCHECK(!pending_swaps_.empty()); | |
188 linked_ptr<PendingSwap> swap = pending_swaps_.front(); | |
189 | |
190 // If more that a certain amount of time has passed since the swap, | |
191 // unconditionally continue. | |
192 if (now > swap->latest_allowed_draw_time) | |
193 return true; | |
194 | |
195 // Frames are disallowed from drawing until the vsync interval after their | |
196 // swap is issued. | |
197 if (now < swap->earliest_allowed_draw_time) | |
198 return false; | |
199 | |
200 // If there is no fence then this is either for immediate display, or the | |
201 // fence was aready successfully checked and deleted. | |
202 if (!swap->fence) | |
203 return true; | |
204 | |
205 // Check if the pending work has finished (and early-out if it is not, and | |
206 // this is not forced). | |
207 bool has_completed = false; | |
208 { | |
209 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
210 gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context); | |
211 | |
212 CheckGLErrors("before testing fence"); | |
213 has_completed= swap->fence->HasCompleted(); | |
214 CheckGLErrors("after testing fence"); | |
215 if (has_completed) { | |
216 swap->fence.reset(); | |
217 CheckGLErrors("while deleting fence"); | |
218 } | |
219 } | |
220 return has_completed; | |
221 } | |
222 | |
223 void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() { | |
224 TRACE_EVENT0("gpu", | |
225 "ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately"); | |
226 DCHECK(!pending_swaps_.empty()); | |
227 linked_ptr<PendingSwap> swap = pending_swaps_.front(); | |
228 | |
229 // If there is a fence for this object, delete it. | |
230 if (swap->fence) { | |
231 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
232 gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context); | |
233 | |
234 CheckGLErrors("before deleting active fence"); | |
235 swap->fence.reset(); | |
236 CheckGLErrors("while deleting active fence"); | |
237 } | |
238 | |
239 // Update the CALayer hierarchy. | |
240 { | |
241 TRACE_EVENT1("gpu", "ImageTransportSurfaceOverlayMac::setContents", | |
242 "surface", swap->io_surface.get()); | |
243 ScopedCAActionDisabler disabler; | |
244 | |
245 id new_contents = static_cast<id>(swap->io_surface.get()); | |
246 [layer_ setContents:new_contents]; | |
247 | |
248 CGRect new_frame = gfx::ConvertRectToDIP( | |
249 swap->scale_factor, gfx::Rect(swap->pixel_size)).ToCGRect(); | |
250 if (!CGRectEqualToRect([layer_ frame], new_frame)) | |
251 [layer_ setFrame:new_frame]; | |
252 } | |
253 | |
254 // Send acknowledgement to the browser. | |
255 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; | 80 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; |
256 params.surface_handle = | 81 params.surface_handle = |
257 ui::SurfaceHandleFromCAContextID([ca_context_ contextId]); | 82 ui::SurfaceHandleFromCAContextID([ca_context_ contextId]); |
258 params.size = pixel_size_; | 83 params.size = pixel_size_; |
259 params.scale_factor = scale_factor_; | 84 params.scale_factor = scale_factor_; |
260 params.latency_info.swap(swap->latency_info); | 85 params.latency_info.swap(latency_info_); |
261 helper_->SendAcceleratedSurfaceBuffersSwapped(params); | 86 helper_->SendAcceleratedSurfaceBuffersSwapped(params); |
262 | 87 return gfx::SwapResult::SWAP_ACK; |
263 // Remove this from the queue, and reset any callback timers. | |
264 pending_swaps_.pop_front(); | |
265 } | |
266 | |
267 void ImageTransportSurfaceOverlayMac::FinishAllPendingSwaps() { | |
268 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::FinishAllPendingSwaps"); | |
269 while (!pending_swaps_.empty()) | |
270 DisplayFirstPendingSwapImmediately(); | |
271 } | |
272 | |
273 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() { | |
274 TRACE_EVENT0("gpu", | |
275 "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback"); | |
276 | |
277 DCHECK(has_pending_callback_); | |
278 has_pending_callback_ = false; | |
279 | |
280 if (pending_swaps_.empty()) | |
281 return; | |
282 | |
283 const base::TimeTicks now = base::TimeTicks::Now(); | |
284 if (IsFirstPendingSwapReadyToDisplay(now)) | |
285 DisplayFirstPendingSwapImmediately(); | |
286 PostCheckPendingSwapsCallbackIfNeeded(now); | |
287 } | |
288 | |
289 void ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded( | |
290 const base::TimeTicks& now) { | |
291 TRACE_EVENT0("gpu", | |
292 "ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded"); | |
293 | |
294 if (has_pending_callback_) | |
295 return; | |
296 if (pending_swaps_.empty()) | |
297 return; | |
298 | |
299 base::TimeTicks target; | |
300 if (display_link_mac_) { | |
301 target = display_link_mac_->GetNextVSyncTimeAfter( | |
302 now, kVSyncIntervalFractionForDisplayCallback); | |
303 } else { | |
304 target = now; | |
305 } | |
306 base::TimeDelta delay = target - now; | |
307 | |
308 base::MessageLoop::current()->PostDelayedTask( | |
309 FROM_HERE, | |
310 base::Bind(&ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback, | |
311 weak_factory_.GetWeakPtr()), delay); | |
312 has_pending_callback_ = true; | |
313 } | |
314 | |
315 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { | |
316 return SwapBuffersInternal(gfx::Rect(pixel_size_)); | |
317 } | 88 } |
318 | 89 |
319 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x, | 90 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x, |
320 int y, | 91 int y, |
321 int width, | 92 int width, |
322 int height) { | 93 int height) { |
323 return SwapBuffersInternal(gfx::Rect(x, y, width, height)); | 94 return SwapBuffers(); |
324 } | 95 } |
325 | 96 |
326 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() { | 97 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() { |
327 return true; | 98 return true; |
328 } | 99 } |
329 | 100 |
330 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() { | 101 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() { |
331 return gfx::Size(); | 102 return gfx::Size(); |
332 } | 103 } |
333 | 104 |
(...skipping 17 matching lines...) Expand all Loading... |
351 DCHECK(!pending_overlay_image_); | 122 DCHECK(!pending_overlay_image_); |
352 pending_overlay_image_ = image; | 123 pending_overlay_image_ = image; |
353 return true; | 124 return true; |
354 } | 125 } |
355 | 126 |
356 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const { | 127 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const { |
357 return true; | 128 return true; |
358 } | 129 } |
359 | 130 |
360 void ImageTransportSurfaceOverlayMac::OnBufferPresented( | 131 void ImageTransportSurfaceOverlayMac::OnBufferPresented( |
361 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { | 132 const AcceleratedSurfaceMsg_BufferPresented_Params& params) {} |
362 if (display_link_mac_ && | |
363 display_link_mac_->display_id() == params.display_id_for_vsync) | |
364 return; | |
365 display_link_mac_ = ui::DisplayLinkMac::GetForDisplay( | |
366 params.display_id_for_vsync); | |
367 } | |
368 | 133 |
369 void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size, | 134 void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size, |
370 float scale_factor) { | 135 float scale_factor) { |
371 // Flush through any pending frames. | |
372 FinishAllPendingSwaps(); | |
373 pixel_size_ = pixel_size; | 136 pixel_size_ = pixel_size; |
374 scale_factor_ = scale_factor; | 137 scale_factor_ = scale_factor; |
375 } | 138 } |
376 | 139 |
377 void ImageTransportSurfaceOverlayMac::SetLatencyInfo( | 140 void ImageTransportSurfaceOverlayMac::SetLatencyInfo( |
378 const std::vector<ui::LatencyInfo>& latency_info) { | 141 const std::vector<ui::LatencyInfo>& latency_info) { |
379 latency_info_.insert( | 142 latency_info_.insert( |
380 latency_info_.end(), latency_info.begin(), latency_info.end()); | 143 latency_info_.end(), latency_info.begin(), latency_info.end()); |
381 } | 144 } |
382 | 145 |
383 void ImageTransportSurfaceOverlayMac::WakeUpGpu() {} | 146 void ImageTransportSurfaceOverlayMac::WakeUpGpu() {} |
384 | 147 |
385 } // namespace content | 148 } // namespace content |
OLD | NEW |