Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_overlay_mac.mm
blob118748b40f140f89afc8d57162ae3bab5b5726f7
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 // When selecting a CALayer to re-use for partial damage, this is the maximum
58 // fraction of the merged layer's pixels that may be not-updated by the swap
59 // before we consider the CALayer to not be a good enough match, and create a
60 // new one.
61 const float kMaximumPartialDamageWasteFraction = 1.2f;
63 // The maximum number of partial damage layers that may be created before we
64 // give up and remove them all (doing full damage in the process).
65 const size_t kMaximumPartialDamageLayers = 8;
67 void CheckGLErrors(const char* msg) {
68   GLenum gl_error;
69   while ((gl_error = glGetError()) != GL_NO_ERROR) {
70     LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error;
71   }
74 void IOSurfaceContextNoOp(scoped_refptr<ui::IOSurfaceContext>) {
77 }  // namespace
79 @interface CALayer(Private)
80 -(void)setContentsChanged;
81 @end
83 namespace content {
85 class ImageTransportSurfaceOverlayMac::OverlayPlane {
86  public:
87   enum Type {
88     ROOT = 0,
89     ROOT_PARTIAL_DAMAGE = 1,
90     OVERLAY = 2,
91   };
93   OverlayPlane(Type type,
94                int z_order,
95                base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
96                const gfx::Rect& dip_frame_rect,
97                const gfx::RectF& contents_rect)
98       : type(type),
99         z_order(z_order),
100         io_surface(io_surface),
101         dip_frame_rect(dip_frame_rect),
102         contents_rect(contents_rect),
103         layer_needs_update(true) {}
104   ~OverlayPlane() { DCHECK(!ca_layer); }
106   const Type type;
107   const int z_order;
108   base::scoped_nsobject<CALayer> ca_layer;
110   // The IOSurface to set the CALayer's contents to.
111   const base::ScopedCFTypeRef<IOSurfaceRef> io_surface;
112   const gfx::Rect dip_frame_rect;
113   const gfx::RectF contents_rect;
115   bool layer_needs_update;
117   static bool Compare(const linked_ptr<OverlayPlane>& a,
118                       const linked_ptr<OverlayPlane>& b) {
119     // Sort by z_order first.
120     if (a->z_order < b->z_order)
121       return true;
122     if (a->z_order > b->z_order)
123       return false;
124     // Then ensure that the root partial damage is after the root.
125     if (a->type < b->type)
126       return true;
127     if (a->type > b->type)
128       return false;
129     // Then sort by x.
130     if (a->dip_frame_rect.x() < b->dip_frame_rect.x())
131       return true;
132     if (a->dip_frame_rect.x() > b->dip_frame_rect.x())
133       return false;
134     // Then sort by y.
135     if (a->dip_frame_rect.y() < b->dip_frame_rect.y())
136       return true;
137     if (a->dip_frame_rect.y() > b->dip_frame_rect.y())
138       return false;
140     return false;
141   }
143   void TakeCALayerFrom(OverlayPlane* other_plane) {
144     ca_layer.swap(other_plane->ca_layer);
145   }
147   void UpdateProperties() {
148     if (layer_needs_update) {
149       [ca_layer setOpaque:YES];
150       [ca_layer setFrame:dip_frame_rect.ToCGRect()];
151       [ca_layer setContentsRect:contents_rect.ToCGRect()];
152       id new_contents = static_cast<id>(io_surface.get());
153       if ([ca_layer contents] == new_contents && type != OVERLAY) {
154         [ca_layer setContentsChanged];
155       } else {
156         [ca_layer setContents:new_contents];
157       }
158     }
159     static bool show_borders =
160         base::CommandLine::ForCurrentProcess()->HasSwitch(
161             switches::kShowMacOverlayBorders);
162     if (show_borders) {
163       base::ScopedCFTypeRef<CGColorRef> color;
164       if (!layer_needs_update) {
165         // Green represents contents that are unchanged across frames.
166         color.reset(CGColorCreateGenericRGB(0, 1, 0, 1));
167       } else if (type == OverlayPlane::OVERLAY) {
168         // Pink represents overlay planes
169         color.reset(CGColorCreateGenericRGB(1, 0, 1, 1));
170       } else {
171         // Red represents damaged contents.
172         color.reset(CGColorCreateGenericRGB(1, 0, 0, 1));
173       }
174       [ca_layer setBorderWidth:2];
175       [ca_layer setBorderColor:color];
176     }
177     layer_needs_update = false;
178   }
180   void Destroy() {
181     if (!ca_layer)
182       return;
183     [ca_layer setContents:nil];
184     if (type != ROOT)
185       [ca_layer removeFromSuperlayer];
186     ca_layer.reset();
187   }
190 class ImageTransportSurfaceOverlayMac::PendingSwap {
191  public:
192   PendingSwap() {}
193   ~PendingSwap() { DCHECK(!gl_fence); }
195   gfx::Size pixel_size;
196   float scale_factor;
197   gfx::Rect pixel_damage_rect;
199   std::vector<linked_ptr<OverlayPlane>> overlay_planes;
200   std::vector<ui::LatencyInfo> latency_info;
202   // A fence object, and the CGL context it was issued in.
203   base::ScopedTypeRef<CGLContextObj> cgl_context;
204   scoped_ptr<gfx::GLFence> gl_fence;
206   // The earliest time that this frame may be drawn. A frame is not allowed
207   // to draw until a fraction of the way through the vsync interval after its
208   // This extra latency is to allow wiggle-room for smoothness.
209   base::TimeTicks earliest_display_time_allowed;
211   // The time that this will wake up and draw, if a following swap does not
212   // cause it to draw earlier.
213   base::TimeTicks target_display_time;
216 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac(
217     GpuChannelManager* manager,
218     GpuCommandBufferStub* stub,
219     gfx::PluginWindowHandle handle)
220     : scale_factor_(1), gl_renderer_id_(0), vsync_parameters_valid_(false),
221       display_pending_swap_timer_(true, false), weak_factory_(this) {
222   helper_.reset(new ImageTransportHelper(this, manager, stub, handle));
223   ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
226 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() {
227   ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
228   Destroy();
231 bool ImageTransportSurfaceOverlayMac::Initialize() {
232   if (!helper_->Initialize())
233     return false;
235   // Create the CAContext to send this to the GPU process, and the layer for
236   // the context.
237   CGSConnectionID connection_id = CGSMainConnectionID();
238   ca_context_.reset(
239       [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]);
240   ca_root_layer_.reset([[CALayer alloc] init]);
241   [ca_root_layer_ setGeometryFlipped:YES];
242   [ca_root_layer_ setOpaque:YES];
243   [ca_context_ setLayer:ca_root_layer_];
244   return true;
247 void ImageTransportSurfaceOverlayMac::Destroy() {
248   DisplayAndClearAllPendingSwaps();
250   if (current_root_plane_.get())
251     current_root_plane_->Destroy();
252   current_root_plane_.reset();
253   for (auto& plane : current_partial_damage_planes_)
254     plane->Destroy();
255   current_partial_damage_planes_.clear();
256   for (auto& plane : current_overlay_planes_)
257     plane->Destroy();
258   current_overlay_planes_.clear();
261 bool ImageTransportSurfaceOverlayMac::IsOffscreen() {
262   return false;
265 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal(
266     const gfx::Rect& pixel_damage_rect) {
267   TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffersInternal");
269   // Use the same concept of 'now' for the entire function. The duration of
270   // this function only affect the result if this function lasts across a vsync
271   // boundary, in which case smooth animation is out the window anyway.
272   const base::TimeTicks now = base::TimeTicks::Now();
274   // Decide if the frame should be drawn immediately, or if we should wait until
275   // its work finishes before drawing immediately.
276   bool display_immediately = false;
277   if (vsync_parameters_valid_ &&
278       now - last_swap_time_ >
279           kMaximumVSyncsBetweenSwapsForSmoothAnimation * vsync_interval_) {
280     display_immediately = true;
281   }
282   last_swap_time_ = now;
284   // If the previous swap is ready to display, do it before flushing the
285   // new swap. It is desirable to always be hitting this path when trying to
286   // animate smoothly with vsync.
287   if (!pending_swaps_.empty()) {
288     if (IsFirstPendingSwapReadyToDisplay(now))
289       DisplayFirstPendingSwapImmediately();
290   }
292   // The remainder of the function will populate the PendingSwap structure and
293   // then enqueue it.
294   linked_ptr<PendingSwap> new_swap(new PendingSwap);
295   new_swap->pixel_size = pixel_size_;
296   new_swap->scale_factor = scale_factor_;
297   new_swap->pixel_damage_rect = pixel_damage_rect;
298   new_swap->overlay_planes.swap(pending_overlay_planes_);
299   new_swap->latency_info.swap(latency_info_);
301   // A flush is required to ensure that all content appears in the layer.
302   {
303     gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
304     TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::glFlush");
305     CheckGLErrors("before flushing frame");
306     new_swap->cgl_context.reset(CGLGetCurrentContext(),
307                                 base::scoped_policy::RETAIN);
308     if (gfx::GLFence::IsSupported() && !display_immediately)
309       new_swap->gl_fence.reset(gfx::GLFence::Create());
310     else
311       glFlush();
312     CheckGLErrors("while flushing frame");
313   }
315   // Compute the deadlines for drawing this frame.
316   if (display_immediately) {
317     new_swap->earliest_display_time_allowed = now;
318     new_swap->target_display_time = now;
319   } else {
320     new_swap->earliest_display_time_allowed =
321         GetNextVSyncTimeAfter(now, kVSyncIntervalFractionForEarliestDisplay);
322     new_swap->target_display_time =
323         GetNextVSyncTimeAfter(now, kVSyncIntervalFractionForDisplayCallback);
324   }
326   pending_swaps_.push_back(new_swap);
327   if (display_immediately)
328     DisplayFirstPendingSwapImmediately();
329   else
330     PostCheckPendingSwapsCallbackIfNeeded(now);
331   return gfx::SwapResult::SWAP_ACK;
334 bool ImageTransportSurfaceOverlayMac::IsFirstPendingSwapReadyToDisplay(
335     const base::TimeTicks& now) {
336   DCHECK(!pending_swaps_.empty());
337   linked_ptr<PendingSwap> swap = pending_swaps_.front();
339   // Frames are disallowed from drawing until the vsync interval after their
340   // swap is issued.
341   if (now < swap->earliest_display_time_allowed)
342     return false;
344   // If we've passed that marker, then wait for the work behind the fence to
345   // complete.
346   if (swap->gl_fence) {
347     gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
348     gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context);
350     CheckGLErrors("before waiting on fence");
351     if (!swap->gl_fence->HasCompleted()) {
352       TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::ClientWait");
353       swap->gl_fence->ClientWait();
354     }
355     swap->gl_fence.reset();
356     CheckGLErrors("after waiting on fence");
357   }
358   return true;
361 void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() {
362   TRACE_EVENT0("gpu",
363       "ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately");
364   DCHECK(!pending_swaps_.empty());
365   linked_ptr<PendingSwap> swap = pending_swaps_.front();
367   // If there is a fence for this object, delete it.
368   if (swap->gl_fence) {
369     gfx::ScopedSetGLToRealGLApi scoped_set_gl_api;
370     gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context);
372     CheckGLErrors("before deleting active fence");
373     swap->gl_fence.reset();
374     CheckGLErrors("while deleting active fence");
375   }
377   // Update the plane lists.
378   {
379     // Sort the input planes by z-index and type, and remove any overlays from
380     // the damage rect.
381     gfx::Rect dip_damage_rect =
382         gfx::ConvertRectToDIP(swap->scale_factor, swap->pixel_damage_rect);
383     std::sort(swap->overlay_planes.begin(), swap->overlay_planes.end(),
384               OverlayPlane::Compare);
385     for (auto& plane : swap->overlay_planes) {
386       if (plane->type == OverlayPlane::OVERLAY)
387         dip_damage_rect.Subtract(plane->dip_frame_rect);
388     }
390     ScopedCAActionDisabler disabler;
391     UpdateRootAndPartialDamagePlanes(swap->overlay_planes, dip_damage_rect);
392     UpdateOverlayPlanes(swap->overlay_planes);
393     UpdateCALayerTree();
394     swap->overlay_planes.clear();
395   }
397   // Send acknowledgement to the browser.
398   GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
399   params.surface_handle =
400       ui::SurfaceHandleFromCAContextID([ca_context_ contextId]);
401   params.size = swap->pixel_size;
402   params.scale_factor = swap->scale_factor;
403   params.latency_info.swap(swap->latency_info);
404   helper_->SendAcceleratedSurfaceBuffersSwapped(params);
406   // Remove this from the queue, and reset any callback timers.
407   pending_swaps_.pop_front();
410 void ImageTransportSurfaceOverlayMac::UpdateOverlayPlanes(
411     const std::vector<linked_ptr<OverlayPlane>>& new_overlay_planes) {
412   std::list<linked_ptr<OverlayPlane>> old_overlay_planes;
413   old_overlay_planes.swap(current_overlay_planes_);
415   // Move the new overlay planes into the |current_overlay_planes_| list,
416   // cannibalizing from the old |current_overlay_planes_| as much as possible.
417   for (auto& new_plane : new_overlay_planes) {
418     if (new_plane->type == OverlayPlane::OVERLAY) {
419       if (!old_overlay_planes.empty()) {
420         new_plane->TakeCALayerFrom(old_overlay_planes.front().get());
421         old_overlay_planes.pop_front();
422       }
423       current_overlay_planes_.push_back(new_plane);
424     }
425   }
427   // Destroy any of the previous |current_overlay_planes_| that we couldn't
428   // cannibalize.
429   for (auto& old_plane : old_overlay_planes)
430     old_plane->Destroy();
433 void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamagePlanes(
434     const std::vector<linked_ptr<OverlayPlane>>& new_overlay_planes,
435     const gfx::Rect& dip_damage_rect) {
436   std::list<linked_ptr<OverlayPlane>> old_partial_damage_planes;
437   old_partial_damage_planes.swap(current_partial_damage_planes_);
438   linked_ptr<OverlayPlane> new_root_plane = new_overlay_planes.front();
439   linked_ptr<OverlayPlane> plane_for_swap;
441   // If the frame's size changed, if we haven't updated the root layer, or if
442   // we have full damage, then use the root layer directly.
443   if (!current_root_plane_.get() ||
444       current_root_plane_->dip_frame_rect != new_root_plane->dip_frame_rect ||
445       dip_damage_rect == new_root_plane->dip_frame_rect) {
446     plane_for_swap = new_root_plane;
447   }
449   // Walk though the existing partial damage layers and see if there is one that
450   // is appropriate to re-use.
451   if (!plane_for_swap.get() && !dip_damage_rect.IsEmpty()) {
452     gfx::Rect plane_to_reuse_dip_enlarged_rect;
454     // Find the last partial damage plane to re-use the CALayer from. Grow the
455     // new rect for this layer to include this damage, and all nearby partial
456     // damage layers.
457     linked_ptr<OverlayPlane> plane_to_reuse;
458     for (auto& old_plane : old_partial_damage_planes) {
459       gfx::Rect dip_enlarged_rect = old_plane->dip_frame_rect;
460       dip_enlarged_rect.Union(dip_damage_rect);
462       // Compute the fraction of the pixels that would not be updated by this
463       // swap. If it is too big, try another layer.
464       float waste_fraction = dip_enlarged_rect.size().GetArea() * 1.f /
465                              dip_damage_rect.size().GetArea();
466       if (waste_fraction > kMaximumPartialDamageWasteFraction)
467         continue;
469       plane_to_reuse = old_plane;
470       plane_to_reuse_dip_enlarged_rect.Union(dip_enlarged_rect);
471     }
473     if (plane_to_reuse.get()) {
474       gfx::RectF enlarged_contents_rect =
475           gfx::RectF(plane_to_reuse_dip_enlarged_rect);
476       enlarged_contents_rect.Scale(
477           1. / new_root_plane->dip_frame_rect.width(),
478           1. / new_root_plane->dip_frame_rect.height());
480       plane_for_swap = linked_ptr<OverlayPlane>(new OverlayPlane(
481           OverlayPlane::ROOT_PARTIAL_DAMAGE, 0, new_root_plane->io_surface,
482           plane_to_reuse_dip_enlarged_rect, enlarged_contents_rect));
484       plane_for_swap->TakeCALayerFrom(plane_to_reuse.get());
485       if (plane_to_reuse != old_partial_damage_planes.back())
486         [plane_for_swap->ca_layer removeFromSuperlayer];
487     }
488   }
490   // If we haven't found an appropriate layer to re-use, create a new one, if
491   // we haven't already created too many.
492   if (!plane_for_swap.get() && !dip_damage_rect.IsEmpty() &&
493       old_partial_damage_planes.size() < kMaximumPartialDamageLayers) {
494     gfx::RectF contents_rect = gfx::RectF(dip_damage_rect);
495     contents_rect.Scale(1. / new_root_plane->dip_frame_rect.width(),
496                         1. / new_root_plane->dip_frame_rect.height());
497     plane_for_swap = linked_ptr<OverlayPlane>(new OverlayPlane(
498         OverlayPlane::ROOT_PARTIAL_DAMAGE, 0, new_root_plane->io_surface,
499         dip_damage_rect, contents_rect));
500   }
502   // And if we still don't have a layer, use the root layer.
503   if (!plane_for_swap.get() && !dip_damage_rect.IsEmpty())
504     plane_for_swap = new_root_plane;
506   // Walk all old partial damage planes. Remove anything that is now completely
507   // covered, and move everything else into the new
508   // |current_partial_damage_planes_|.
509   for (auto& old_plane : old_partial_damage_planes) {
510     // Intersect the planes' frames with the new root plane to ensure that
511     // they don't get kept alive inappropriately.
512     gfx::Rect old_plane_frame_rect = old_plane->dip_frame_rect;
513     old_plane_frame_rect.Intersect(new_root_plane->dip_frame_rect);
515     if (plane_for_swap.get() &&
516         plane_for_swap->dip_frame_rect.Contains(old_plane_frame_rect)) {
517       old_plane->Destroy();
518     } else {
519       DCHECK(old_plane->ca_layer);
520       current_partial_damage_planes_.push_back(old_plane);
521     }
522   }
524   // Finally, add the new swap's plane at the back of the list, if it exists.
525   if (plane_for_swap == new_root_plane) {
526     if (current_root_plane_.get()) {
527       plane_for_swap->TakeCALayerFrom(current_root_plane_.get());
528     } else {
529       plane_for_swap->ca_layer = ca_root_layer_;
530     }
531     current_root_plane_ = new_root_plane;
532   } else if (plane_for_swap.get()) {
533     current_partial_damage_planes_.push_back(plane_for_swap);
534   }
537 void ImageTransportSurfaceOverlayMac::UpdateCALayerTree() {
538   // Allocate new CALayers as needed. Overlay layers are always added to the
539   // back of the list.
540   CALayer* first_overlay_ca_layer = nil;
541   for (auto& plane : current_overlay_planes_) {
542     if (!plane->ca_layer) {
543       plane->ca_layer.reset([[CALayer alloc] init]);
544       [ca_root_layer_ addSublayer:plane->ca_layer];
545     }
546     if (!first_overlay_ca_layer)
547       first_overlay_ca_layer = plane->ca_layer;
548   }
549   // Partial damage layers are inserted below the overlay layers.
550   for (auto& plane : current_partial_damage_planes_) {
551     if (!plane->ca_layer) {
552       DCHECK(plane == current_partial_damage_planes_.back());
553       plane->ca_layer.reset([[CALayer alloc] init]);
554     }
555     if (![plane->ca_layer superlayer]) {
556       DCHECK(plane == current_partial_damage_planes_.back());
557       if (first_overlay_ca_layer) {
558         [ca_root_layer_ insertSublayer:plane->ca_layer
559                                  below:first_overlay_ca_layer];
560       } else {
561         [ca_root_layer_ addSublayer:plane->ca_layer];
562       }
563     }
564   }
566   // Update CALayer contents, frames, and borders.
567   current_root_plane_->UpdateProperties();
568   for (auto& plane : current_partial_damage_planes_)
569     plane->UpdateProperties();
570   for (auto& plane : current_overlay_planes_)
571     plane->UpdateProperties();
573   DCHECK_EQ(
574       static_cast<size_t>([[ca_root_layer_ sublayers] count]),
575       current_partial_damage_planes_.size() + current_overlay_planes_.size());
578 void ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps() {
579   TRACE_EVENT0("gpu",
580       "ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps");
581   while (!pending_swaps_.empty())
582     DisplayFirstPendingSwapImmediately();
585 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() {
586   TRACE_EVENT0("gpu",
587       "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback");
589   if (pending_swaps_.empty())
590     return;
592   const base::TimeTicks now = base::TimeTicks::Now();
593   if (IsFirstPendingSwapReadyToDisplay(now))
594     DisplayFirstPendingSwapImmediately();
595   PostCheckPendingSwapsCallbackIfNeeded(now);
598 void ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded(
599     const base::TimeTicks& now) {
600   TRACE_EVENT0("gpu",
601       "ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded");
603   if (pending_swaps_.empty()) {
604     display_pending_swap_timer_.Stop();
605   } else {
606     display_pending_swap_timer_.Start(
607         FROM_HERE,
608         pending_swaps_.front()->target_display_time - now,
609         base::Bind(&ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback,
610                        weak_factory_.GetWeakPtr()));
611   }
614 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() {
615   return SwapBuffersInternal(
616       gfx::Rect(0, 0, pixel_size_.width(), pixel_size_.height()));
619 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x,
620                                                                int y,
621                                                                int width,
622                                                                int height) {
623   return SwapBuffersInternal(gfx::Rect(x, y, width, height));
626 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() {
627   return true;
630 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() {
631   return gfx::Size();
634 void* ImageTransportSurfaceOverlayMac::GetHandle() {
635   return nullptr;
638 bool ImageTransportSurfaceOverlayMac::OnMakeCurrent(gfx::GLContext* context) {
639   // Ensure that the context is on the appropriate GL renderer. The GL renderer
640   // will generally only change when the GPU changes.
641   if (gl_renderer_id_ && context)
642     context->share_group()->SetRendererID(gl_renderer_id_);
643   return true;
646 bool ImageTransportSurfaceOverlayMac::SetBackbufferAllocation(bool allocated) {
647   if (!allocated) {
648     DisplayAndClearAllPendingSwaps();
649     last_swap_time_ = base::TimeTicks();
650   }
651   return true;
654 bool ImageTransportSurfaceOverlayMac::ScheduleOverlayPlane(
655     int z_order,
656     gfx::OverlayTransform transform,
657     gfx::GLImage* image,
658     const gfx::Rect& bounds_rect,
659     const gfx::RectF& crop_rect) {
660   DCHECK_GE(z_order, 0);
661   DCHECK_EQ(transform, gfx::OVERLAY_TRANSFORM_NONE);
662   if (z_order < 0 || transform != gfx::OVERLAY_TRANSFORM_NONE)
663     return false;
665   OverlayPlane::Type type = z_order == 0 ?
666       OverlayPlane::ROOT : OverlayPlane::OVERLAY;
667   gfx::Rect dip_frame_rect = gfx::ConvertRectToDIP(
668       scale_factor_, bounds_rect);
669   gfx::RectF contents_rect = crop_rect;
671   gfx::GLImageIOSurface* image_io_surface =
672       static_cast<gfx::GLImageIOSurface*>(image);
674   pending_overlay_planes_.push_back(linked_ptr<OverlayPlane>(
675       new OverlayPlane(
676           type, z_order, image_io_surface->io_surface(), dip_frame_rect,
677           contents_rect)));
678   return true;
681 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const {
682   return true;
685 void ImageTransportSurfaceOverlayMac::OnBufferPresented(
686     const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
687   vsync_timebase_ = params.vsync_timebase;
688   vsync_interval_ = params.vsync_interval;
689   vsync_parameters_valid_ = (vsync_interval_ != base::TimeDelta());
691   // Compute |vsync_timebase_| to be the first vsync after time zero.
692   if (vsync_parameters_valid_) {
693     vsync_timebase_ -=
694         vsync_interval_ *
695         ((vsync_timebase_ - base::TimeTicks()) / vsync_interval_);
696   }
699 void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size,
700                                                float scale_factor) {
701   // Flush through any pending frames.
702   DisplayAndClearAllPendingSwaps();
703   pixel_size_ = pixel_size;
704   scale_factor_ = scale_factor;
707 void ImageTransportSurfaceOverlayMac::SetLatencyInfo(
708     const std::vector<ui::LatencyInfo>& latency_info) {
709   latency_info_.insert(
710       latency_info_.end(), latency_info.begin(), latency_info.end());
713 void ImageTransportSurfaceOverlayMac::WakeUpGpu() {}
715 void ImageTransportSurfaceOverlayMac::OnGpuSwitched() {
716   // Create a new context, and use the GL renderer ID that the new context gets.
717   scoped_refptr<ui::IOSurfaceContext> context_on_new_gpu =
718       ui::IOSurfaceContext::Get(ui::IOSurfaceContext::kCALayerContext);
719   if (!context_on_new_gpu)
720     return;
721   GLint context_renderer_id = -1;
722   if (CGLGetParameter(context_on_new_gpu->cgl_context(),
723                       kCGLCPCurrentRendererID,
724                       &context_renderer_id) != kCGLNoError) {
725     LOG(ERROR) << "Failed to create test context after GPU switch";
726     return;
727   }
728   gl_renderer_id_ = context_renderer_id & kCGLRendererIDMatchingMask;
730   // Post a task holding a reference to the new GL context. The reason for
731   // this is to avoid creating-then-destroying the context for every image
732   // transport surface that is observing the GPU switch.
733   base::MessageLoop::current()->PostTask(
734       FROM_HERE, base::Bind(&IOSurfaceContextNoOp, context_on_new_gpu));
737 base::TimeTicks ImageTransportSurfaceOverlayMac::GetNextVSyncTimeAfter(
738     const base::TimeTicks& from, double interval_fraction) {
739   if (!vsync_parameters_valid_)
740     return from;
742   // Compute the previous vsync time.
743   base::TimeTicks previous_vsync =
744       vsync_interval_ * ((from - vsync_timebase_) / vsync_interval_) +
745       vsync_timebase_;
747   // Return |interval_fraction| through the next vsync.
748   return previous_vsync + (1 + interval_fraction) * vsync_interval_;
751 }  // namespace content