Update V8 to version 4.7.42.
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_overlay_mac.mm
blobc5ea719c4d2317ad9aaa6eaad4e1c8d49afb56fa
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
3 // found in the LICENSE file.
5 #include "content/common/gpu/image_transport_surface_overlay_mac.h"
7 #include <algorithm>
8 #include <IOSurface/IOSurface.h>
9 #include <OpenGL/CGLRenderers.h>
10 #include <OpenGL/CGLTypes.h>
11 #include <OpenGL/gl.h>
13 // This type consistently causes problem on Mac, and needs to be dealt with
14 // in a systemic way.
15 // http://crbug.com/517208
16 #ifndef GL_OES_EGL_image
17 typedef void* GLeglImageOES;
18 #endif
20 #include "base/command_line.h"
21 #include "base/mac/scoped_cftyperef.h"
22 #include "content/common/gpu/gpu_messages.h"
23 #include "ui/accelerated_widget_mac/io_surface_context.h"
24 #include "ui/accelerated_widget_mac/surface_handle_types.h"
25 #include "ui/base/cocoa/animation_utils.h"
26 #include "ui/base/cocoa/remote_layer_api.h"
27 #include "ui/base/ui_base_switches.h"
28 #include "ui/gfx/geometry/dip_util.h"
29 #include "ui/gl/gl_context.h"
30 #include "ui/gl/gl_fence.h"
31 #include "ui/gl/gl_image_io_surface.h"
32 #include "ui/gl/gpu_switching_manager.h"
33 #include "ui/gl/scoped_api.h"
34 #include "ui/gl/scoped_cgl.h"
36 namespace {
38 // Don't let a frame draw until 5% of the way through the next vsync interval
39 // after the call to SwapBuffers. This slight offset is to ensure that skew
40 // doesn't result in the frame being presented to the previous vsync interval.
41 const double kVSyncIntervalFractionForEarliestDisplay = 0.05;
43 // After doing a glFlush and putting in a fence in SwapBuffers, post a task to
44 // query the fence 50% of the way through the next vsync interval. If we are
45 // trying to animate smoothly, then want to query the fence at the next
46 // SwapBuffers. For this reason we schedule the callback for a long way into
47 // the next frame.
48 const double kVSyncIntervalFractionForDisplayCallback = 0.5;
50 // If swaps arrive regularly and nearly at the vsync rate, then attempt to
51 // make animation smooth (each frame is shown for one vsync interval) by sending
52 // them to the window server only when their GL work completes. If frames are
53 // not coming in with each vsync, then just throw them at the window server as
54 // they come.
55 const double kMaximumVSyncsBetweenSwapsForSmoothAnimation = 1.5;
57 void CheckGLErrors(const char* msg) {
58   GLenum gl_error;
59   while ((gl_error = glGetError()) != GL_NO_ERROR) {
60     LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error;
61   }
64 void IOSurfaceContextNoOp(scoped_refptr<ui::IOSurfaceContext>) {
67 }  // namespace
69 @interface CALayer(Private)
70 -(void)setContentsChanged;
71 @end
73 namespace content {
75 class ImageTransportSurfaceOverlayMac::OverlayPlane {
76  public:
77   enum Type {
78     ROOT = 0,
79     ROOT_PARTIAL_DAMAGE = 1,
80     OVERLAY = 2,
81   };
83   OverlayPlane(
84       Type type,
85       int z_order,
86       base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
87       gfx::Rect dip_frame_rect,
88       gfx::RectF contents_rect)
89       : type(type), z_order(z_order), io_surface(io_surface),
90         dip_frame_rect(dip_frame_rect), contents_rect(contents_rect) {}
91   ~OverlayPlane() {}
93   static bool Compare(const linked_ptr<OverlayPlane>& a,
94                       const linked_ptr<OverlayPlane>& b) {
95     // Sort by z_order first.
96     if (a->z_order < b->z_order)
97       return true;
98     if (a->z_order > b->z_order)
99       return false;
100     // Then ensure that the root partial damage is after the root.
101     if (a->type < b->type)
102       return true;
103     if (a->type > b->type)
104       return false;
105     // Then sort by x.
106     if (a->dip_frame_rect.x() < b->dip_frame_rect.x())
107       return true;
108     if (a->dip_frame_rect.x() > b->dip_frame_rect.x())
109       return false;
110     // Then sort by y.
111     if (a->dip_frame_rect.y() < b->dip_frame_rect.y())
112       return true;
113     if (a->dip_frame_rect.y() > b->dip_frame_rect.y())
114       return false;
116     return false;
117   }
119   const Type type;
120   const int z_order;
121   // The IOSurface to set the CALayer's contents to. This can be nil for the
122   // root layer, indicating that the layer's content has not been damaged since
123   // the last time it was set.
124   base::ScopedCFTypeRef<IOSurfaceRef> io_surface;
125   const gfx::Rect dip_frame_rect;
126   const gfx::RectF contents_rect;
129 class ImageTransportSurfaceOverlayMac::PendingSwap {
130  public:
131   PendingSwap() {}
132   ~PendingSwap() { DCHECK(!gl_fence); }
134   gfx::Size pixel_size;
135   float scale_factor;
137   std::vector<linked_ptr<OverlayPlane>> overlay_planes;
138   std::vector<ui::LatencyInfo> latency_info;
140   // A fence object, and the CGL context it was issued in.
141   base::ScopedTypeRef<CGLContextObj> cgl_context;
142   scoped_ptr<gfx::GLFence> gl_fence;
144   // The earliest time that this frame may be drawn. A frame is not allowed
145   // to draw until a fraction of the way through the vsync interval after its
146   // This extra latency is to allow wiggle-room for smoothness.
147   base::TimeTicks earliest_display_time_allowed;
149   // The time that this will wake up and draw, if a following swap does not
150   // cause it to draw earlier.
151   base::TimeTicks target_display_time;
154 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac(
155     GpuChannelManager* manager,
156     GpuCommandBufferStub* stub,
157     gfx::PluginWindowHandle handle)
158     : scale_factor_(1), gl_renderer_id_(0), vsync_parameters_valid_(false),
159       display_pending_swap_timer_(true, false), weak_factory_(this) {
160   helper_.reset(new ImageTransportHelper(this, manager, stub, handle));
161   ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
164 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() {
165   ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
166   Destroy();
169 bool ImageTransportSurfaceOverlayMac::Initialize() {
170   if (!helper_->Initialize())
171     return false;
173   // Create the CAContext to send this to the GPU process, and the layer for
174   // the context.
175   CGSConnectionID connection_id = CGSMainConnectionID();
176   ca_context_.reset(
177       [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]);
178   layer_.reset([[CALayer alloc] init]);
179   [layer_ setGeometryFlipped:YES];
180   [layer_ setOpaque:YES];
181   [ca_context_ setLayer:layer_];
182   return true;
185 void ImageTransportSurfaceOverlayMac::Destroy() {
186   DisplayAndClearAllPendingSwaps();
189 bool ImageTransportSurfaceOverlayMac::IsOffscreen() {
190   return false;
193 void ImageTransportSurfaceOverlayMac::ScheduleOverlayPlaneForPartialDamage(
194     const gfx::Rect& this_damage_pixel_rect) {
195   // Find the root plane. If none is present, then we're hosed.
196   linked_ptr<OverlayPlane> root_plane;
197   for (linked_ptr<OverlayPlane>& plane : pending_overlay_planes_) {
198     if (plane->type == OverlayPlane::ROOT) {
199       root_plane = plane;
200       break;
201     }
202   }
203   if (!root_plane.get())
204     return;
206   gfx::Rect this_damage_dip_rect = gfx::ConvertRectToDIP(
207       scale_factor_, this_damage_pixel_rect);
209   for (const linked_ptr<OverlayPlane>& plane : pending_overlay_planes_) {
210     if (plane->type == OverlayPlane::OVERLAY) {
211       this_damage_dip_rect.Subtract(plane->dip_frame_rect);
212       accumulated_damage_dip_rect_.Subtract(plane->dip_frame_rect);
213     }
214   }
216   if (this_damage_dip_rect.IsEmpty()) {
217     // Keep existing root layer unchanged.
218     root_plane->io_surface.reset();
219     if (!accumulated_damage_dip_rect_.IsEmpty()) {
220       // Keep existing partial damage layer unchanged.
221       pending_overlay_planes_.push_back(linked_ptr<OverlayPlane>(
222           new OverlayPlane(OverlayPlane::ROOT_PARTIAL_DAMAGE, 0,
223                            base::ScopedCFTypeRef<IOSurfaceRef>(),
224                            accumulated_damage_dip_rect_, gfx::Rect())));
225     }
226     return;
227   }
229   // Grow the partial damage rect to include the new damage.
230   accumulated_damage_dip_rect_.Union(this_damage_dip_rect);
232   if (accumulated_damage_dip_rect_ == root_plane->dip_frame_rect) {
233     accumulated_damage_dip_rect_ = gfx::Rect();
234     return;
235   }
237   // Compute the fraction of the accumulated partial damage rect that has been
238   // damaged. If this gets too small (<75%), just re-damage the full window,
239   // so we can re-create a smaller partial damage layer next frame.
240   const double kMinimumFractionOfPartialDamage = 0.75;
241   double fraction_of_damage =
242       this_damage_dip_rect.size().GetArea() / static_cast<double>(
243           accumulated_damage_dip_rect_.size().GetArea());
244   if (fraction_of_damage <= kMinimumFractionOfPartialDamage) {
245     // Early-out if we decided to damage the full window.
246     accumulated_damage_dip_rect_ = gfx::Rect();
247     return;
248   }
250   // Create a new overlay plane for the partial damage, and un-set the root
251   // plane's damage by un-setting its IOSurface.
252   gfx::RectF contents_rect = gfx::RectF(accumulated_damage_dip_rect_);
253   contents_rect.Scale(1. / root_plane->dip_frame_rect.width(),
254                       1. / root_plane->dip_frame_rect.height());
255   pending_overlay_planes_.push_back(linked_ptr<OverlayPlane>(new OverlayPlane(
256       OverlayPlane::ROOT_PARTIAL_DAMAGE, 0, root_plane->io_surface,
257       accumulated_damage_dip_rect_, contents_rect)));
259   root_plane->io_surface.reset();
262 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal() {
263   TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffersInternal");
265   // Use the same concept of 'now' for the entire function. The duration of
266   // this function only affect the result if this function lasts across a vsync
267   // boundary, in which case smooth animation is out the window anyway.
268   const base::TimeTicks now = base::TimeTicks::Now();
270   // Decide if the frame should be drawn immediately, or if we should wait until
271   // its work finishes before drawing immediately.
272   bool display_immediately = false;
273   if (vsync_parameters_valid_ &&
274       now - last_swap_time_ >
275           kMaximumVSyncsBetweenSwapsForSmoothAnimation * vsync_interval_) {
276     display_immediately = true;
277   }
278   last_swap_time_ = now;
280   // If the previous swap is ready to display, do it before flushing the
281   // new swap. It is desirable to always be hitting this path when trying to
282   // animate smoothly with vsync.
283   if (!pending_swaps_.empty()) {
284     if (IsFirstPendingSwapReadyToDisplay(now))
285       DisplayFirstPendingSwapImmediately();
286   }
288   // The remainder of the function will populate the PendingSwap structure and
289   // then enqueue it.
290   linked_ptr<PendingSwap> new_swap(new PendingSwap);
291   new_swap->pixel_size = pixel_size_;
292   new_swap->scale_factor = scale_factor_;
293   new_swap->overlay_planes.swap(pending_overlay_planes_);
294   new_swap->latency_info.swap(latency_info_);
296   // A flush is required to ensure that all content appears in the layer.
297   {
298     gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
299     TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::glFlush");
300     CheckGLErrors("before flushing frame");
301     new_swap->cgl_context.reset(CGLGetCurrentContext(),
302                                 base::scoped_policy::RETAIN);
303     if (gfx::GLFence::IsSupported() && !display_immediately)
304       new_swap->gl_fence.reset(gfx::GLFence::Create());
305     else
306       glFlush();
307     CheckGLErrors("while flushing frame");
308   }
310   // Compute the deadlines for drawing this frame.
311   if (display_immediately) {
312     new_swap->earliest_display_time_allowed = now;
313     new_swap->target_display_time = now;
314   } else {
315     new_swap->earliest_display_time_allowed =
316         GetNextVSyncTimeAfter(now, kVSyncIntervalFractionForEarliestDisplay);
317     new_swap->target_display_time =
318         GetNextVSyncTimeAfter(now, kVSyncIntervalFractionForDisplayCallback);
319   }
321   pending_swaps_.push_back(new_swap);
322   if (display_immediately)
323     DisplayFirstPendingSwapImmediately();
324   else
325     PostCheckPendingSwapsCallbackIfNeeded(now);
326   return gfx::SwapResult::SWAP_ACK;
329 bool ImageTransportSurfaceOverlayMac::IsFirstPendingSwapReadyToDisplay(
330     const base::TimeTicks& now) {
331   DCHECK(!pending_swaps_.empty());
332   linked_ptr<PendingSwap> swap = pending_swaps_.front();
334   // Frames are disallowed from drawing until the vsync interval after their
335   // swap is issued.
336   if (now < swap->earliest_display_time_allowed)
337     return false;
339   // If we've passed that marker, then wait for the work behind the fence to
340   // complete.
341   if (swap->gl_fence) {
342     gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
343     gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context);
345     CheckGLErrors("before waiting on fence");
346     if (!swap->gl_fence->HasCompleted()) {
347       TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::ClientWait");
348       swap->gl_fence->ClientWait();
349     }
350     swap->gl_fence.reset();
351     CheckGLErrors("after waiting on fence");
352   }
353   return true;
356 void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() {
357   TRACE_EVENT0("gpu",
358       "ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately");
359   DCHECK(!pending_swaps_.empty());
360   linked_ptr<PendingSwap> swap = pending_swaps_.front();
362   // If there is a fence for this object, delete it.
363   if (swap->gl_fence) {
364     gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
365     gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context);
367     CheckGLErrors("before deleting active fence");
368     swap->gl_fence.reset();
369     CheckGLErrors("while deleting active fence");
370   }
372   // Update the CALayer hierarchy.
373   UpdateCALayerTree(layer_.get(), swap.get());
375   // Send acknowledgement to the browser.
376   GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
377   params.surface_handle =
378       ui::SurfaceHandleFromCAContextID([ca_context_ contextId]);
379   params.size = swap->pixel_size;
380   params.scale_factor = swap->scale_factor;
381   params.latency_info.swap(swap->latency_info);
382   helper_->SendAcceleratedSurfaceBuffersSwapped(params);
384   // Remove this from the queue, and reset any callback timers.
385   pending_swaps_.pop_front();
388 // static
389 void ImageTransportSurfaceOverlayMac::UpdateCALayerTree(
390     CALayer* root_layer, PendingSwap* swap) {
391   TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::UpdateCALayerTree");
392   ScopedCAActionDisabler disabler;
394   // Sort the planes by z-index.
395   std::sort(swap->overlay_planes.begin(), swap->overlay_planes.end(),
396             OverlayPlane::Compare);
398   NSUInteger child_index = 0;
399   for (linked_ptr<OverlayPlane>& plane : swap->overlay_planes) {
400     // Look up or create the CALayer for this plane.
401     CALayer* plane_layer = nil;
402     if (plane->type == OverlayPlane::ROOT) {
403       plane_layer = root_layer;
404     } else {
405       if (child_index >= [[root_layer sublayers] count]) {
406         base::scoped_nsobject<CALayer> new_layer([[CALayer alloc] init]);
407         [new_layer setOpaque:YES];
408         [root_layer addSublayer:new_layer];
409       }
410       plane_layer = [[root_layer sublayers] objectAtIndex:child_index];
411       child_index += 1;
412     }
414     // Update layer contents if needed.
415     if (plane->io_surface) {
416       // Note that calling setContents with the same IOSurface twice will result
417       // in the screen not being updated, even if the IOSurface's content has
418       // changed. This can be avoided by calling setContentsChanged. Only call
419       // this on the root layer, because it is the only layer that will ignore
420       // damage.
421       id new_contents = static_cast<id>(plane->io_surface.get());
422       if ([plane_layer contents] == new_contents) {
423         if (plane->type == OverlayPlane::ROOT)
424           [plane_layer setContentsChanged];
425       } else {
426         [plane_layer setContents:new_contents];
427       }
428     } else {
429       // No content update is needed for this layer.
430       DCHECK(plane->type == OverlayPlane::ROOT ||
431              plane->type == OverlayPlane::ROOT_PARTIAL_DAMAGE);
432     }
434     static bool show_borders =
435         base::CommandLine::ForCurrentProcess()->HasSwitch(
436             switches::kShowMacOverlayBorders);
437     if (show_borders) {
438       base::ScopedCFTypeRef<CGColorRef> color;
439       if (!plane->io_surface) {
440         // Green represents contents that are unchanged across frames.
441         color.reset(CGColorCreateGenericRGB(0, 1, 0, 1));
442       } else if (plane->type == OverlayPlane::OVERLAY) {
443         // Pink represents overlay planes
444         color.reset(CGColorCreateGenericRGB(1, 0, 1, 1));
445       } else {
446         // Red represents damaged contents.
447         color.reset(CGColorCreateGenericRGB(1, 0, 0, 1));
448       }
449       [plane_layer setBorderWidth:plane_layer == root_layer ? 1 : 2];
450       [plane_layer setBorderColor:color];
451     }
453     [plane_layer setFrame:plane->dip_frame_rect.ToCGRect()];
454     [plane_layer setContentsRect:plane->contents_rect.ToCGRect()];
455   }
457   // Remove any now-obsolete children.
458   while ([[root_layer sublayers] count] > child_index) {
459     CALayer* layer = [[root_layer sublayers] objectAtIndex:child_index];
460     [layer setContents:nil];
461     [layer removeFromSuperlayer];
462   }
465 void ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps() {
466   TRACE_EVENT0("gpu",
467       "ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps");
468   while (!pending_swaps_.empty())
469     DisplayFirstPendingSwapImmediately();
472 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() {
473   TRACE_EVENT0("gpu",
474       "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback");
476   if (pending_swaps_.empty())
477     return;
479   const base::TimeTicks now = base::TimeTicks::Now();
480   if (IsFirstPendingSwapReadyToDisplay(now))
481     DisplayFirstPendingSwapImmediately();
482   PostCheckPendingSwapsCallbackIfNeeded(now);
485 void ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded(
486     const base::TimeTicks& now) {
487   TRACE_EVENT0("gpu",
488       "ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded");
490   if (pending_swaps_.empty()) {
491     display_pending_swap_timer_.Stop();
492   } else {
493     display_pending_swap_timer_.Start(
494         FROM_HERE,
495         pending_swaps_.front()->target_display_time - now,
496         base::Bind(&ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback,
497                        weak_factory_.GetWeakPtr()));
498   }
501 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() {
502   return PostSubBuffer(0, 0, pixel_size_.width(), pixel_size_.height());
505 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x,
506                                                                int y,
507                                                                int width,
508                                                                int height) {
509   ScheduleOverlayPlaneForPartialDamage(gfx::Rect(x, y, width, height));
510   return SwapBuffersInternal();
513 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() {
514   return true;
517 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() {
518   return gfx::Size();
521 void* ImageTransportSurfaceOverlayMac::GetHandle() {
522   return nullptr;
525 bool ImageTransportSurfaceOverlayMac::OnMakeCurrent(gfx::GLContext* context) {
526   // Ensure that the context is on the appropriate GL renderer. The GL renderer
527   // will generally only change when the GPU changes.
528   if (gl_renderer_id_ && context)
529     context->share_group()->SetRendererID(gl_renderer_id_);
530   return true;
533 bool ImageTransportSurfaceOverlayMac::SetBackbufferAllocation(bool allocated) {
534   if (!allocated) {
535     DisplayAndClearAllPendingSwaps();
536     last_swap_time_ = base::TimeTicks();
537   }
538   return true;
541 bool ImageTransportSurfaceOverlayMac::ScheduleOverlayPlane(
542     int z_order,
543     gfx::OverlayTransform transform,
544     gfx::GLImage* image,
545     const gfx::Rect& bounds_rect,
546     const gfx::RectF& crop_rect) {
547   DCHECK_GE(z_order, 0);
548   DCHECK_EQ(transform, gfx::OVERLAY_TRANSFORM_NONE);
549   if (z_order < 0 || transform != gfx::OVERLAY_TRANSFORM_NONE)
550     return false;
552   OverlayPlane::Type type = z_order == 0 ?
553       OverlayPlane::ROOT : OverlayPlane::OVERLAY;
554   gfx::Rect dip_frame_rect = gfx::ConvertRectToDIP(
555       scale_factor_, bounds_rect);
556   gfx::RectF contents_rect = crop_rect;
558   gfx::GLImageIOSurface* image_io_surface =
559       static_cast<gfx::GLImageIOSurface*>(image);
561   pending_overlay_planes_.push_back(linked_ptr<OverlayPlane>(
562       new OverlayPlane(
563           type, z_order, image_io_surface->io_surface(), dip_frame_rect,
564           contents_rect)));
565   return true;
568 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const {
569   return true;
572 void ImageTransportSurfaceOverlayMac::OnBufferPresented(
573     const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
574   vsync_timebase_ = params.vsync_timebase;
575   vsync_interval_ = params.vsync_interval;
576   vsync_parameters_valid_ = (vsync_interval_ != base::TimeDelta());
578   // Compute |vsync_timebase_| to be the first vsync after time zero.
579   if (vsync_parameters_valid_) {
580     vsync_timebase_ -=
581         vsync_interval_ *
582         ((vsync_timebase_ - base::TimeTicks()) / vsync_interval_);
583   }
586 void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size,
587                                                float scale_factor) {
588   // Flush through any pending frames.
589   DisplayAndClearAllPendingSwaps();
590   pixel_size_ = pixel_size;
591   scale_factor_ = scale_factor;
594 void ImageTransportSurfaceOverlayMac::SetLatencyInfo(
595     const std::vector<ui::LatencyInfo>& latency_info) {
596   latency_info_.insert(
597       latency_info_.end(), latency_info.begin(), latency_info.end());
600 void ImageTransportSurfaceOverlayMac::WakeUpGpu() {}
602 void ImageTransportSurfaceOverlayMac::OnGpuSwitched() {
603   // Create a new context, and use the GL renderer ID that the new context gets.
604   scoped_refptr<ui::IOSurfaceContext> context_on_new_gpu =
605       ui::IOSurfaceContext::Get(ui::IOSurfaceContext::kCALayerContext);
606   if (!context_on_new_gpu)
607     return;
608   GLint context_renderer_id = -1;
609   if (CGLGetParameter(context_on_new_gpu->cgl_context(),
610                       kCGLCPCurrentRendererID,
611                       &context_renderer_id) != kCGLNoError) {
612     LOG(ERROR) << "Failed to create test context after GPU switch";
613     return;
614   }
615   gl_renderer_id_ = context_renderer_id & kCGLRendererIDMatchingMask;
617   // Post a task holding a reference to the new GL context. The reason for
618   // this is to avoid creating-then-destroying the context for every image
619   // transport surface that is observing the GPU switch.
620   base::MessageLoop::current()->PostTask(
621       FROM_HERE, base::Bind(&IOSurfaceContextNoOp, context_on_new_gpu));
624 base::TimeTicks ImageTransportSurfaceOverlayMac::GetNextVSyncTimeAfter(
625     const base::TimeTicks& from, double interval_fraction) {
626   if (!vsync_parameters_valid_)
627     return from;
629   // Compute the previous vsync time.
630   base::TimeTicks previous_vsync =
631       vsync_interval_ * ((from - vsync_timebase_) / vsync_interval_) +
632       vsync_timebase_;
634   // Return |interval_fraction| through the next vsync.
635   return previous_vsync + (1 + interval_fraction) * vsync_interval_;
638 }  // namespace content