Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: content/common/gpu/image_transport_surface_overlay_mac.mm

Issue 1273563002: Mac Overlays: Add GPU back-pressure (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Use default fences Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 <OpenGL/gl.h> 7 #include <IOSurface/IOSurface.h>
8 #include <OpenGL/GL.h>
8 9
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"
9 #include "content/common/gpu/gpu_messages.h" 18 #include "content/common/gpu/gpu_messages.h"
10 #include "ui/accelerated_widget_mac/surface_handle_types.h" 19 #include "ui/accelerated_widget_mac/surface_handle_types.h"
11 #include "ui/base/cocoa/animation_utils.h" 20 #include "ui/base/cocoa/animation_utils.h"
12 #include "ui/base/cocoa/remote_layer_api.h" 21 #include "ui/base/cocoa/remote_layer_api.h"
13 #include "ui/gfx/geometry/dip_util.h" 22 #include "ui/gfx/geometry/dip_util.h"
23 #include "ui/gl/gl_fence.h"
14 #include "ui/gl/gl_image_io_surface.h" 24 #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
15 55
16 namespace content { 56 namespace content {
17 57
58 class ImageTransportSurfaceOverlayMac::PendingSwap {
59 public:
60 PendingSwap() : scale_factor(1) {}
61 ~PendingSwap() { DCHECK(!gl_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::GLFence> gl_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
18 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac( 83 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac(
19 GpuChannelManager* manager, 84 GpuChannelManager* manager,
20 GpuCommandBufferStub* stub, 85 GpuCommandBufferStub* stub,
21 gfx::PluginWindowHandle handle) 86 gfx::PluginWindowHandle handle)
22 : scale_factor_(1), pending_overlay_image_(nullptr) { 87 : scale_factor_(1), pending_overlay_image_(nullptr),
88 has_pending_callback_(false), weak_factory_(this) {
23 helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); 89 helper_.reset(new ImageTransportHelper(this, manager, stub, handle));
24 } 90 }
25 91
26 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() { 92 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() {
27 gfx::GLImageIOSurface::SetLayerForWidget(widget_, nil);
28 } 93 }
29 94
30 bool ImageTransportSurfaceOverlayMac::Initialize() { 95 bool ImageTransportSurfaceOverlayMac::Initialize() {
31 if (!helper_->Initialize()) 96 if (!helper_->Initialize())
32 return false; 97 return false;
33 98
34 // Create the CAContext to send this to the GPU process, and the layer for 99 // Create the CAContext to send this to the GPU process, and the layer for
35 // the context. 100 // the context.
36 CGSConnectionID connection_id = CGSMainConnectionID(); 101 CGSConnectionID connection_id = CGSMainConnectionID();
37 ca_context_.reset( 102 ca_context_.reset(
38 [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]); 103 [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]);
39 layer_.reset([[CALayer alloc] init]); 104 layer_.reset([[CALayer alloc] init]);
105 [layer_ setGeometryFlipped:YES];
106 [layer_ setOpaque:YES];
40 [ca_context_ setLayer:layer_]; 107 [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
48 return true; 108 return true;
49 } 109 }
50 110
51 void ImageTransportSurfaceOverlayMac::Destroy() {} 111 void ImageTransportSurfaceOverlayMac::Destroy() {
112 FinishAllPendingSwaps();
113 }
52 114
53 bool ImageTransportSurfaceOverlayMac::IsOffscreen() { 115 bool ImageTransportSurfaceOverlayMac::IsOffscreen() {
54 return false; 116 return false;
55 } 117 }
56 118
57 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { 119 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal(
58 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffers"); 120 const gfx::Rect& pixel_damage_rect) {
59 121 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffersInternal");
60 // A flush is required to ensure that all content appears in the layer. 122
61 { 123 // Use the same concept of 'now' for the entire function. The duration of
62 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::glFlush"); 124 // this function only affect the result if this function lasts across a vsync
63 glFlush(); 125 // boundary, in which case smooth animation is out the window anyway.
64 } 126 const base::TimeTicks now = base::TimeTicks::Now();
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 }
135
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_);
65 142
66 // There should exist only one overlay image, and it should cover the whole 143 // There should exist only one overlay image, and it should cover the whole
67 // surface. 144 // surface.
68 DCHECK(pending_overlay_image_); 145 DCHECK(pending_overlay_image_);
69 if (pending_overlay_image_) { 146 if (pending_overlay_image_) {
70 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::setContents"); 147 gfx::GLImageIOSurface* pending_overlay_image_io_surface =
148 static_cast<gfx::GLImageIOSurface*>(pending_overlay_image_);
reveman 2015/08/18 08:44:11 this static cast seems scary. how do we know that
149 new_swap->io_surface = pending_overlay_image_io_surface->io_surface();
150 pending_overlay_image_ = nullptr;
151 }
152
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 if (gfx::GLFence::IsSupported())
162 new_swap->gl_fence.reset(gfx::GLFence::Create());
163 else
164 glFlush();
165 CheckGLErrors("while flushing frame");
166 }
167
168 // Compute the deadlines for drawing this frame.
169 if (display_link_mac_) {
170 new_swap->earliest_allowed_draw_time =
171 display_link_mac_->GetNextVSyncTimeAfter(
172 now, kVSyncIntervalFractionForEarliestDisplay);
173 new_swap->latest_allowed_draw_time = now +
174 base::TimeDelta::FromSecondsD(kMaximumDelayWaitingForFenceInSeconds);
175 } else {
176 // If we have no display link (because vsync is disabled or because we have
177 // not received display parameters yet), immediately attempt to display the
178 // surface.
179 new_swap->earliest_allowed_draw_time = now;
180 new_swap->latest_allowed_draw_time = now;
181 }
182
183 pending_swaps_.push_back(new_swap);
184 PostCheckPendingSwapsCallbackIfNeeded(now);
185 return gfx::SwapResult::SWAP_ACK;
186 }
187
188 bool ImageTransportSurfaceOverlayMac::IsFirstPendingSwapReadyToDisplay(
189 const base::TimeTicks& now) {
190 DCHECK(!pending_swaps_.empty());
191 linked_ptr<PendingSwap> swap = pending_swaps_.front();
192
193 // If more that a certain amount of time has passed since the swap,
194 // unconditionally continue.
195 if (now > swap->latest_allowed_draw_time)
196 return true;
197
198 // Frames are disallowed from drawing until the vsync interval after their
199 // swap is issued.
200 if (now < swap->earliest_allowed_draw_time)
201 return false;
202
203 // If there is no fence then this is either for immediate display, or the
204 // fence was aready successfully checked and deleted.
205 if (!swap->gl_fence)
206 return true;
207
208 // Check if the pending work has finished (and early-out if it is not, and
209 // this is not forced).
210 bool has_completed = true;
211 if (swap->gl_fence) {
212 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
213 gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context);
214
215 CheckGLErrors("before testing fence");
216 has_completed= swap->gl_fence->HasCompleted();
217 CheckGLErrors("after testing fence");
218 if (has_completed) {
219 swap->gl_fence.reset();
220 CheckGLErrors("while deleting fence");
221 }
222 }
223 return has_completed;
224 }
225
226 void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() {
227 TRACE_EVENT0("gpu",
228 "ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately");
229 DCHECK(!pending_swaps_.empty());
230 linked_ptr<PendingSwap> swap = pending_swaps_.front();
231
232 // If there is a fence for this object, delete it.
233 if (swap->gl_fence) {
234 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
235 gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context);
236
237 CheckGLErrors("before deleting active fence");
238 swap->gl_fence.reset();
239 CheckGLErrors("while deleting active fence");
240 }
241
242 // Update the CALayer hierarchy.
243 {
244 TRACE_EVENT1("gpu", "ImageTransportSurfaceOverlayMac::setContents",
245 "surface", swap->io_surface.get());
71 ScopedCAActionDisabler disabler; 246 ScopedCAActionDisabler disabler;
72 gfx::Rect dip_bounds = gfx::ConvertRectToDIP( 247
73 scale_factor_, gfx::Rect(pixel_size_)); 248 id new_contents = static_cast<id>(swap->io_surface.get());
74 gfx::RectF crop_rect(0, 0, 1, 1); 249 [layer_ setContents:new_contents];
75 pending_overlay_image_->ScheduleOverlayPlane( 250
76 widget_, 0, gfx::OVERLAY_TRANSFORM_NONE, dip_bounds, crop_rect); 251 CGRect new_frame = gfx::ConvertRectToDIP(
77 pending_overlay_image_ = nullptr; 252 swap->scale_factor, gfx::Rect(swap->pixel_size)).ToCGRect();
78 } 253 if (!CGRectEqualToRect([layer_ frame], new_frame))
79 254 [layer_ setFrame:new_frame];
255 }
256
257 // Send acknowledgement to the browser.
80 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; 258 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
81 params.surface_handle = 259 params.surface_handle =
82 ui::SurfaceHandleFromCAContextID([ca_context_ contextId]); 260 ui::SurfaceHandleFromCAContextID([ca_context_ contextId]);
83 params.size = pixel_size_; 261 params.size = pixel_size_;
84 params.scale_factor = scale_factor_; 262 params.scale_factor = scale_factor_;
85 params.latency_info.swap(latency_info_); 263 params.latency_info.swap(swap->latency_info);
86 helper_->SendAcceleratedSurfaceBuffersSwapped(params); 264 helper_->SendAcceleratedSurfaceBuffersSwapped(params);
87 return gfx::SwapResult::SWAP_ACK; 265
266 // Remove this from the queue, and reset any callback timers.
267 pending_swaps_.pop_front();
268 }
269
270 void ImageTransportSurfaceOverlayMac::FinishAllPendingSwaps() {
271 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::FinishAllPendingSwaps");
272 while (!pending_swaps_.empty())
273 DisplayFirstPendingSwapImmediately();
274 }
275
276 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() {
277 TRACE_EVENT0("gpu",
278 "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback");
279
280 DCHECK(has_pending_callback_);
281 has_pending_callback_ = false;
282
283 if (pending_swaps_.empty())
284 return;
285
286 const base::TimeTicks now = base::TimeTicks::Now();
287 if (IsFirstPendingSwapReadyToDisplay(now))
288 DisplayFirstPendingSwapImmediately();
289 PostCheckPendingSwapsCallbackIfNeeded(now);
290 }
291
292 void ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded(
293 const base::TimeTicks& now) {
294 TRACE_EVENT0("gpu",
295 "ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded");
296
297 if (has_pending_callback_)
298 return;
299 if (pending_swaps_.empty())
300 return;
301
302 base::TimeTicks target;
303 if (display_link_mac_) {
304 target = display_link_mac_->GetNextVSyncTimeAfter(
305 now, kVSyncIntervalFractionForDisplayCallback);
306 } else {
307 target = now;
308 }
309 base::TimeDelta delay = target - now;
310
311 base::MessageLoop::current()->PostDelayedTask(
312 FROM_HERE,
313 base::Bind(&ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback,
314 weak_factory_.GetWeakPtr()), delay);
315 has_pending_callback_ = true;
316 }
317
318 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() {
319 return SwapBuffersInternal(gfx::Rect(pixel_size_));
88 } 320 }
89 321
90 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x, 322 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x,
91 int y, 323 int y,
92 int width, 324 int width,
93 int height) { 325 int height) {
94 return SwapBuffers(); 326 return SwapBuffersInternal(gfx::Rect(x, y, width, height));
95 } 327 }
96 328
97 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() { 329 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() {
98 return true; 330 return true;
99 } 331 }
100 332
101 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() { 333 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() {
102 return gfx::Size(); 334 return gfx::Size();
103 } 335 }
104 336
(...skipping 17 matching lines...) Expand all
122 DCHECK(!pending_overlay_image_); 354 DCHECK(!pending_overlay_image_);
123 pending_overlay_image_ = image; 355 pending_overlay_image_ = image;
124 return true; 356 return true;
125 } 357 }
126 358
127 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const { 359 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const {
128 return true; 360 return true;
129 } 361 }
130 362
131 void ImageTransportSurfaceOverlayMac::OnBufferPresented( 363 void ImageTransportSurfaceOverlayMac::OnBufferPresented(
132 const AcceleratedSurfaceMsg_BufferPresented_Params& params) {} 364 const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
365 if (display_link_mac_ &&
366 display_link_mac_->display_id() == params.display_id_for_vsync)
367 return;
368 display_link_mac_ = ui::DisplayLinkMac::GetForDisplay(
369 params.display_id_for_vsync);
370 }
133 371
134 void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size, 372 void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size,
135 float scale_factor) { 373 float scale_factor) {
374 // Flush through any pending frames.
375 FinishAllPendingSwaps();
136 pixel_size_ = pixel_size; 376 pixel_size_ = pixel_size;
137 scale_factor_ = scale_factor; 377 scale_factor_ = scale_factor;
138 } 378 }
139 379
140 void ImageTransportSurfaceOverlayMac::SetLatencyInfo( 380 void ImageTransportSurfaceOverlayMac::SetLatencyInfo(
141 const std::vector<ui::LatencyInfo>& latency_info) { 381 const std::vector<ui::LatencyInfo>& latency_info) {
142 latency_info_.insert( 382 latency_info_.insert(
143 latency_info_.end(), latency_info.begin(), latency_info.end()); 383 latency_info_.end(), latency_info.begin(), latency_info.end());
144 } 384 }
145 385
146 void ImageTransportSurfaceOverlayMac::WakeUpGpu() {} 386 void ImageTransportSurfaceOverlayMac::WakeUpGpu() {}
147 387
148 } // namespace content 388 } // namespace content
OLDNEW
« no previous file with comments | « content/common/gpu/image_transport_surface_overlay_mac.h ('k') | gpu/command_buffer/service/gl_context_virtual.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698