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"
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
15 // http://crbug.com/517208
16 #ifndef GL_OES_EGL_image
17 typedef void* GLeglImageOES;
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"
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
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
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
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) {
69 while ((gl_error = glGetError()) != GL_NO_ERROR) {
70 LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error;
74 void IOSurfaceContextNoOp(scoped_refptr<ui::IOSurfaceContext>) {
79 @interface CALayer(Private)
80 -(void)setContentsChanged;
85 class ImageTransportSurfaceOverlayMac::OverlayPlane {
89 ROOT_PARTIAL_DAMAGE = 1,
93 OverlayPlane(Type type,
95 base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
96 const gfx::Rect& dip_frame_rect,
97 const gfx::RectF& contents_rect)
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); }
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)
122 if (a->z_order > b->z_order)
124 // Then ensure that the root partial damage is after the root.
125 if (a->type < b->type)
127 if (a->type > b->type)
130 if (a->dip_frame_rect.x() < b->dip_frame_rect.x())
132 if (a->dip_frame_rect.x() > b->dip_frame_rect.x())
135 if (a->dip_frame_rect.y() < b->dip_frame_rect.y())
137 if (a->dip_frame_rect.y() > b->dip_frame_rect.y())
143 void TakeCALayerFrom(OverlayPlane* other_plane) {
144 ca_layer.swap(other_plane->ca_layer);
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];
156 [ca_layer setContents:new_contents];
159 static bool show_borders =
160 base::CommandLine::ForCurrentProcess()->HasSwitch(
161 switches::kShowMacOverlayBorders);
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));
171 // Red represents damaged contents.
172 color.reset(CGColorCreateGenericRGB(1, 0, 0, 1));
174 [ca_layer setBorderWidth:2];
175 [ca_layer setBorderColor:color];
177 layer_needs_update = false;
183 [ca_layer setContents:nil];
185 [ca_layer removeFromSuperlayer];
190 class ImageTransportSurfaceOverlayMac::PendingSwap {
193 ~PendingSwap() { DCHECK(!gl_fence); }
195 gfx::Size pixel_size;
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);
231 bool ImageTransportSurfaceOverlayMac::Initialize() {
232 if (!helper_->Initialize())
235 // Create the CAContext to send this to the GPU process, and the layer for
237 CGSConnectionID connection_id = CGSMainConnectionID();
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_];
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_)
255 current_partial_damage_planes_.clear();
256 for (auto& plane : current_overlay_planes_)
258 current_overlay_planes_.clear();
261 bool ImageTransportSurfaceOverlayMac::IsOffscreen() {
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;
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();
292 // The remainder of the function will populate the PendingSwap structure and
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.
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());
312 CheckGLErrors("while flushing frame");
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;
320 new_swap->earliest_display_time_allowed =
321 GetNextVSyncTimeAfter(now, kVSyncIntervalFractionForEarliestDisplay);
322 new_swap->target_display_time =
323 GetNextVSyncTimeAfter(now, kVSyncIntervalFractionForDisplayCallback);
326 pending_swaps_.push_back(new_swap);
327 if (display_immediately)
328 DisplayFirstPendingSwapImmediately();
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
341 if (now < swap->earliest_display_time_allowed)
344 // If we've passed that marker, then wait for the work behind the fence to
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();
355 swap->gl_fence.reset();
356 CheckGLErrors("after waiting on fence");
361 void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() {
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");
377 // Update the plane lists.
379 // Sort the input planes by z-index and type, and remove any overlays from
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);
390 ScopedCAActionDisabler disabler;
391 UpdateRootAndPartialDamagePlanes(swap->overlay_planes, dip_damage_rect);
392 UpdateOverlayPlanes(swap->overlay_planes);
394 swap->overlay_planes.clear();
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();
423 current_overlay_planes_.push_back(new_plane);
427 // Destroy any of the previous |current_overlay_planes_| that we couldn't
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;
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
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)
469 plane_to_reuse = old_plane;
470 plane_to_reuse_dip_enlarged_rect.Union(dip_enlarged_rect);
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];
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));
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();
519 DCHECK(old_plane->ca_layer);
520 current_partial_damage_planes_.push_back(old_plane);
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());
529 plane_for_swap->ca_layer = ca_root_layer_;
531 current_root_plane_ = new_root_plane;
532 } else if (plane_for_swap.get()) {
533 current_partial_damage_planes_.push_back(plane_for_swap);
537 void ImageTransportSurfaceOverlayMac::UpdateCALayerTree() {
538 // Allocate new CALayers as needed. Overlay layers are always added to the
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];
546 if (!first_overlay_ca_layer)
547 first_overlay_ca_layer = plane->ca_layer;
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]);
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];
561 [ca_root_layer_ addSublayer:plane->ca_layer];
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();
574 static_cast<size_t>([[ca_root_layer_ sublayers] count]),
575 current_partial_damage_planes_.size() + current_overlay_planes_.size());
578 void ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps() {
580 "ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps");
581 while (!pending_swaps_.empty())
582 DisplayFirstPendingSwapImmediately();
585 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() {
587 "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback");
589 if (pending_swaps_.empty())
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) {
601 "ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded");
603 if (pending_swaps_.empty()) {
604 display_pending_swap_timer_.Stop();
606 display_pending_swap_timer_.Start(
608 pending_swaps_.front()->target_display_time - now,
609 base::Bind(&ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback,
610 weak_factory_.GetWeakPtr()));
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,
623 return SwapBuffersInternal(gfx::Rect(x, y, width, height));
626 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() {
630 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() {
634 void* ImageTransportSurfaceOverlayMac::GetHandle() {
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_);
646 bool ImageTransportSurfaceOverlayMac::SetBackbufferAllocation(bool allocated) {
648 DisplayAndClearAllPendingSwaps();
649 last_swap_time_ = base::TimeTicks();
654 bool ImageTransportSurfaceOverlayMac::ScheduleOverlayPlane(
656 gfx::OverlayTransform transform,
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)
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>(
676 type, z_order, image_io_surface->io_surface(), dip_frame_rect,
681 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const {
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_) {
695 ((vsync_timebase_ - base::TimeTicks()) / vsync_interval_);
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)
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";
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_)
742 // Compute the previous vsync time.
743 base::TimeTicks previous_vsync =
744 vsync_interval_ * ((from - vsync_timebase_) / vsync_interval_) +
747 // Return |interval_fraction| through the next vsync.
748 return previous_vsync + (1 + interval_fraction) * vsync_interval_;
751 } // namespace content