OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "content/common/gpu/image_transport_surface_overlay_mac.h" |
| 6 |
| 7 #include <CoreGraphics/CoreGraphics.h> |
| 8 #include <IOSurface/IOSurface.h> |
| 9 #include <OpenGL/CGLRenderers.h> |
| 10 #include <OpenGL/CGLTypes.h> |
| 11 #include <OpenGL/gl.h> |
| 12 #include <stddef.h> |
| 13 |
| 14 #include <algorithm> |
| 15 |
| 16 // This type consistently causes problem on Mac, and needs to be dealt with |
| 17 // in a systemic way. |
| 18 // http://crbug.com/517208 |
| 19 #ifndef GL_OES_EGL_image |
| 20 typedef void* GLeglImageOES; |
| 21 #endif |
| 22 |
| 23 #include "base/bind.h" |
| 24 #include "base/bind_helpers.h" |
| 25 #include "base/mac/scoped_cftyperef.h" |
| 26 #include "base/trace_event/trace_event.h" |
| 27 #include "content/common/gpu/ca_layer_partial_damage_tree_mac.h" |
| 28 #include "content/common/gpu/ca_layer_tree_mac.h" |
| 29 #include "content/common/gpu/gpu_channel_manager.h" |
| 30 #include "content/common/gpu/gpu_channel_manager_delegate.h" |
| 31 #include "ui/accelerated_widget_mac/io_surface_context.h" |
| 32 #include "ui/base/cocoa/animation_utils.h" |
| 33 #include "ui/base/cocoa/remote_layer_api.h" |
| 34 #include "ui/gfx/geometry/rect_conversions.h" |
| 35 #include "ui/gfx/transform.h" |
| 36 #include "ui/gl/gl_context.h" |
| 37 #include "ui/gl/gl_fence.h" |
| 38 #include "ui/gl/gl_image_io_surface.h" |
| 39 #include "ui/gl/gpu_switching_manager.h" |
| 40 #include "ui/gl/scoped_api.h" |
| 41 #include "ui/gl/scoped_cgl.h" |
| 42 |
| 43 namespace { |
| 44 |
| 45 // Don't let a frame draw until 5% of the way through the next vsync interval |
| 46 // after the call to SwapBuffers. This slight offset is to ensure that skew |
| 47 // doesn't result in the frame being presented to the previous vsync interval. |
| 48 const double kVSyncIntervalFractionForEarliestDisplay = 0.05; |
| 49 |
| 50 // After doing a glFlush and putting in a fence in SwapBuffers, post a task to |
| 51 // query the fence 50% of the way through the next vsync interval. If we are |
| 52 // trying to animate smoothly, then want to query the fence at the next |
| 53 // SwapBuffers. For this reason we schedule the callback for a long way into |
| 54 // the next frame. |
| 55 const double kVSyncIntervalFractionForDisplayCallback = 0.5; |
| 56 |
| 57 // If swaps arrive regularly and nearly at the vsync rate, then attempt to |
| 58 // make animation smooth (each frame is shown for one vsync interval) by sending |
| 59 // them to the window server only when their GL work completes. If frames are |
| 60 // not coming in with each vsync, then just throw them at the window server as |
| 61 // they come. |
| 62 const double kMaximumVSyncsBetweenSwapsForSmoothAnimation = 1.5; |
| 63 |
| 64 void CheckGLErrors(const char* msg) { |
| 65 GLenum gl_error; |
| 66 while ((gl_error = glGetError()) != GL_NO_ERROR) { |
| 67 LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error; |
| 68 } |
| 69 } |
| 70 |
| 71 void IOSurfaceContextNoOp(scoped_refptr<ui::IOSurfaceContext>) { |
| 72 } |
| 73 |
| 74 } // namespace |
| 75 |
| 76 @interface CALayer(Private) |
| 77 -(void)setContentsChanged; |
| 78 @end |
| 79 |
| 80 namespace content { |
| 81 |
| 82 scoped_refptr<gfx::GLSurface> ImageTransportSurfaceCreateNativeSurface( |
| 83 GpuChannelManager* manager, |
| 84 GpuCommandBufferStub* stub, |
| 85 gpu::SurfaceHandle handle) { |
| 86 return new ImageTransportSurfaceOverlayMac(manager, stub, handle); |
| 87 } |
| 88 |
| 89 class ImageTransportSurfaceOverlayMac::PendingSwap { |
| 90 public: |
| 91 PendingSwap() {} |
| 92 ~PendingSwap() { DCHECK(!gl_fence); } |
| 93 |
| 94 gfx::Size pixel_size; |
| 95 float scale_factor; |
| 96 gfx::Rect pixel_damage_rect; |
| 97 |
| 98 scoped_ptr<CALayerPartialDamageTree> partial_damage_tree; |
| 99 scoped_ptr<CALayerTree> ca_layer_tree; |
| 100 std::vector<ui::LatencyInfo> latency_info; |
| 101 |
| 102 // A fence object, and the CGL context it was issued in. |
| 103 base::ScopedTypeRef<CGLContextObj> cgl_context; |
| 104 scoped_ptr<gfx::GLFence> gl_fence; |
| 105 |
| 106 // The earliest time that this frame may be drawn. A frame is not allowed |
| 107 // to draw until a fraction of the way through the vsync interval after its |
| 108 // This extra latency is to allow wiggle-room for smoothness. |
| 109 base::TimeTicks earliest_display_time_allowed; |
| 110 |
| 111 // The time that this will wake up and draw, if a following swap does not |
| 112 // cause it to draw earlier. |
| 113 base::TimeTicks target_display_time; |
| 114 }; |
| 115 |
| 116 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac( |
| 117 GpuChannelManager* manager, |
| 118 GpuCommandBufferStub* stub, |
| 119 gpu::SurfaceHandle handle) |
| 120 : manager_(manager), |
| 121 stub_(stub->AsWeakPtr()), |
| 122 handle_(handle), |
| 123 use_remote_layer_api_(ui::RemoteLayerAPISupported()), |
| 124 scale_factor_(1), |
| 125 gl_renderer_id_(0), |
| 126 vsync_parameters_valid_(false), |
| 127 display_pending_swap_timer_(true, false), |
| 128 weak_factory_(this) { |
| 129 manager_->AddBufferPresentedCallback( |
| 130 handle_, base::Bind(&ImageTransportSurfaceOverlayMac::BufferPresented, |
| 131 base::Unretained(this))); |
| 132 ui::GpuSwitchingManager::GetInstance()->AddObserver(this); |
| 133 } |
| 134 |
| 135 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() { |
| 136 ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this); |
| 137 if (stub_.get()) { |
| 138 stub_->SetLatencyInfoCallback( |
| 139 base::Callback<void(const std::vector<ui::LatencyInfo>&)>()); |
| 140 } |
| 141 Destroy(); |
| 142 manager_->RemoveBufferPresentedCallback(handle_); |
| 143 } |
| 144 |
| 145 bool ImageTransportSurfaceOverlayMac::Initialize( |
| 146 gfx::GLSurface::Format format) { |
| 147 if (!stub_.get() || !stub_->decoder()) |
| 148 return false; |
| 149 |
| 150 stub_->SetLatencyInfoCallback( |
| 151 base::Bind(&ImageTransportSurfaceOverlayMac::SetLatencyInfo, |
| 152 base::Unretained(this))); |
| 153 |
| 154 // Create the CAContext to send this to the GPU process, and the layer for |
| 155 // the context. |
| 156 if (use_remote_layer_api_) { |
| 157 CGSConnectionID connection_id = CGSMainConnectionID(); |
| 158 ca_context_.reset([ |
| 159 [CAContext contextWithCGSConnection:connection_id options:@{}] retain]); |
| 160 ca_root_layer_.reset([[CALayer alloc] init]); |
| 161 [ca_root_layer_ setGeometryFlipped:YES]; |
| 162 [ca_root_layer_ setOpaque:YES]; |
| 163 [ca_context_ setLayer:ca_root_layer_]; |
| 164 } |
| 165 return true; |
| 166 } |
| 167 |
| 168 void ImageTransportSurfaceOverlayMac::Destroy() { |
| 169 DisplayAndClearAllPendingSwaps(); |
| 170 |
| 171 current_partial_damage_tree_.reset(); |
| 172 current_ca_layer_tree_.reset(); |
| 173 } |
| 174 |
| 175 bool ImageTransportSurfaceOverlayMac::IsOffscreen() { |
| 176 return false; |
| 177 } |
| 178 |
| 179 void ImageTransportSurfaceOverlayMac::SetLatencyInfo( |
| 180 const std::vector<ui::LatencyInfo>& latency_info) { |
| 181 latency_info_.insert(latency_info_.end(), latency_info.begin(), |
| 182 latency_info.end()); |
| 183 } |
| 184 |
| 185 void ImageTransportSurfaceOverlayMac::BufferPresented( |
| 186 int32_t surface_id, |
| 187 const base::TimeTicks& vsync_timebase, |
| 188 const base::TimeDelta& vsync_interval) { |
| 189 vsync_timebase_ = vsync_timebase; |
| 190 vsync_interval_ = vsync_interval; |
| 191 vsync_parameters_valid_ = (vsync_interval_ != base::TimeDelta()); |
| 192 |
| 193 // Compute |vsync_timebase_| to be the first vsync after time zero. |
| 194 if (vsync_parameters_valid_) { |
| 195 vsync_timebase_ -= |
| 196 vsync_interval_ * |
| 197 ((vsync_timebase_ - base::TimeTicks()) / vsync_interval_); |
| 198 } |
| 199 } |
| 200 |
| 201 void ImageTransportSurfaceOverlayMac::SendAcceleratedSurfaceBuffersSwapped( |
| 202 int32_t surface_id, |
| 203 CAContextID ca_context_id, |
| 204 const gfx::ScopedRefCountedIOSurfaceMachPort& io_surface, |
| 205 const gfx::Size& size, |
| 206 float scale_factor, |
| 207 std::vector<ui::LatencyInfo> latency_info) { |
| 208 // TRACE_EVENT for gpu tests: |
| 209 TRACE_EVENT_INSTANT2("test_gpu", "SwapBuffers", TRACE_EVENT_SCOPE_THREAD, |
| 210 "GLImpl", static_cast<int>(gfx::GetGLImplementation()), |
| 211 "width", size.width()); |
| 212 // On mac, handle_ is a surface id. See |
| 213 // GpuProcessTransportFactory::CreatePerCompositorData |
| 214 manager_->delegate()->SendAcceleratedSurfaceBuffersSwapped( |
| 215 surface_id, ca_context_id, io_surface, size, scale_factor, |
| 216 std::move(latency_info)); |
| 217 } |
| 218 |
| 219 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal( |
| 220 const gfx::Rect& pixel_damage_rect) { |
| 221 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffersInternal"); |
| 222 |
| 223 // Use the same concept of 'now' for the entire function. The duration of |
| 224 // this function only affect the result if this function lasts across a vsync |
| 225 // boundary, in which case smooth animation is out the window anyway. |
| 226 const base::TimeTicks now = base::TimeTicks::Now(); |
| 227 |
| 228 // Decide if the frame should be drawn immediately, or if we should wait until |
| 229 // its work finishes before drawing immediately. |
| 230 bool display_immediately = false; |
| 231 if (vsync_parameters_valid_ && |
| 232 now - last_swap_time_ > |
| 233 kMaximumVSyncsBetweenSwapsForSmoothAnimation * vsync_interval_) { |
| 234 display_immediately = true; |
| 235 } |
| 236 last_swap_time_ = now; |
| 237 |
| 238 // If the previous swap is ready to display, do it before flushing the |
| 239 // new swap. It is desirable to always be hitting this path when trying to |
| 240 // animate smoothly with vsync. |
| 241 if (!pending_swaps_.empty()) { |
| 242 if (IsFirstPendingSwapReadyToDisplay(now)) |
| 243 DisplayFirstPendingSwapImmediately(); |
| 244 } |
| 245 |
| 246 // The remainder of the function will populate the PendingSwap structure and |
| 247 // then enqueue it. |
| 248 linked_ptr<PendingSwap> new_swap(new PendingSwap); |
| 249 new_swap->pixel_size = pixel_size_; |
| 250 new_swap->scale_factor = scale_factor_; |
| 251 new_swap->pixel_damage_rect = pixel_damage_rect; |
| 252 new_swap->partial_damage_tree.swap(pending_partial_damage_tree_); |
| 253 new_swap->ca_layer_tree.swap(pending_ca_layer_tree_); |
| 254 new_swap->latency_info.swap(latency_info_); |
| 255 |
| 256 // A flush is required to ensure that all content appears in the layer. |
| 257 { |
| 258 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| 259 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::glFlush"); |
| 260 CheckGLErrors("before flushing frame"); |
| 261 new_swap->cgl_context.reset(CGLGetCurrentContext(), |
| 262 base::scoped_policy::RETAIN); |
| 263 if (gfx::GLFence::IsSupported() && !display_immediately) |
| 264 new_swap->gl_fence.reset(gfx::GLFence::Create()); |
| 265 else |
| 266 glFlush(); |
| 267 CheckGLErrors("while flushing frame"); |
| 268 } |
| 269 |
| 270 // Compute the deadlines for drawing this frame. |
| 271 if (display_immediately) { |
| 272 new_swap->earliest_display_time_allowed = now; |
| 273 new_swap->target_display_time = now; |
| 274 } else { |
| 275 new_swap->earliest_display_time_allowed = |
| 276 GetNextVSyncTimeAfter(now, kVSyncIntervalFractionForEarliestDisplay); |
| 277 new_swap->target_display_time = |
| 278 GetNextVSyncTimeAfter(now, kVSyncIntervalFractionForDisplayCallback); |
| 279 } |
| 280 |
| 281 pending_swaps_.push_back(new_swap); |
| 282 if (display_immediately) |
| 283 DisplayFirstPendingSwapImmediately(); |
| 284 else |
| 285 PostCheckPendingSwapsCallbackIfNeeded(now); |
| 286 return gfx::SwapResult::SWAP_ACK; |
| 287 } |
| 288 |
| 289 bool ImageTransportSurfaceOverlayMac::IsFirstPendingSwapReadyToDisplay( |
| 290 const base::TimeTicks& now) { |
| 291 DCHECK(!pending_swaps_.empty()); |
| 292 linked_ptr<PendingSwap> swap = pending_swaps_.front(); |
| 293 |
| 294 // Frames are disallowed from drawing until the vsync interval after their |
| 295 // swap is issued. |
| 296 if (now < swap->earliest_display_time_allowed) |
| 297 return false; |
| 298 |
| 299 // If we've passed that marker, then wait for the work behind the fence to |
| 300 // complete. |
| 301 if (swap->gl_fence) { |
| 302 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| 303 gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context); |
| 304 |
| 305 CheckGLErrors("before waiting on fence"); |
| 306 if (!swap->gl_fence->HasCompleted()) { |
| 307 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::ClientWait"); |
| 308 swap->gl_fence->ClientWait(); |
| 309 } |
| 310 swap->gl_fence.reset(); |
| 311 CheckGLErrors("after waiting on fence"); |
| 312 } |
| 313 return true; |
| 314 } |
| 315 |
| 316 void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() { |
| 317 TRACE_EVENT0("gpu", |
| 318 "ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately"); |
| 319 DCHECK(!pending_swaps_.empty()); |
| 320 linked_ptr<PendingSwap> swap = pending_swaps_.front(); |
| 321 |
| 322 // If there is a fence for this object, delete it. |
| 323 if (swap->gl_fence) { |
| 324 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| 325 gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context); |
| 326 |
| 327 CheckGLErrors("before deleting active fence"); |
| 328 swap->gl_fence.reset(); |
| 329 CheckGLErrors("while deleting active fence"); |
| 330 } |
| 331 |
| 332 // Update the CALayer hierarchy. |
| 333 { |
| 334 gfx::RectF pixel_damage_rect = gfx::RectF(swap->pixel_damage_rect); |
| 335 ScopedCAActionDisabler disabler; |
| 336 if (swap->ca_layer_tree) { |
| 337 swap->ca_layer_tree->CommitScheduledCALayers( |
| 338 ca_root_layer_.get(), std::move(current_ca_layer_tree_), |
| 339 swap->scale_factor); |
| 340 current_ca_layer_tree_.swap(swap->ca_layer_tree); |
| 341 current_partial_damage_tree_.reset(); |
| 342 } else if (swap->partial_damage_tree) { |
| 343 swap->partial_damage_tree->CommitCALayers( |
| 344 ca_root_layer_.get(), std::move(current_partial_damage_tree_), |
| 345 swap->scale_factor, swap->pixel_damage_rect); |
| 346 current_partial_damage_tree_.swap(swap->partial_damage_tree); |
| 347 current_ca_layer_tree_.reset(); |
| 348 } else { |
| 349 TRACE_EVENT0("gpu", "Blank frame: No overlays or CALayers"); |
| 350 [ca_root_layer_ setSublayers:nil]; |
| 351 current_partial_damage_tree_.reset(); |
| 352 current_ca_layer_tree_.reset(); |
| 353 } |
| 354 swap->ca_layer_tree.reset(); |
| 355 swap->partial_damage_tree.reset(); |
| 356 } |
| 357 |
| 358 // Update the latency info to reflect the swap time. |
| 359 base::TimeTicks swap_time = base::TimeTicks::Now(); |
| 360 for (auto latency_info : swap->latency_info) { |
| 361 latency_info.AddLatencyNumberWithTimestamp( |
| 362 ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, 0, swap_time, 1); |
| 363 latency_info.AddLatencyNumberWithTimestamp( |
| 364 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0, |
| 365 swap_time, 1); |
| 366 } |
| 367 |
| 368 // Send acknowledgement to the browser. |
| 369 CAContextID ca_context_id = 0; |
| 370 gfx::ScopedRefCountedIOSurfaceMachPort io_surface; |
| 371 if (use_remote_layer_api_) { |
| 372 ca_context_id = [ca_context_ contextId]; |
| 373 } else if (current_partial_damage_tree_) { |
| 374 io_surface.reset(IOSurfaceCreateMachPort( |
| 375 current_partial_damage_tree_->RootLayerIOSurface())); |
| 376 } |
| 377 gfx::Size size = swap->pixel_size; |
| 378 float scale_factor = swap->scale_factor; |
| 379 std::vector<ui::LatencyInfo> latency_info; |
| 380 latency_info.swap(swap->latency_info); |
| 381 SendAcceleratedSurfaceBuffersSwapped(handle_, ca_context_id, io_surface, size, |
| 382 scale_factor, std::move(latency_info)); |
| 383 |
| 384 // Remove this from the queue, and reset any callback timers. |
| 385 pending_swaps_.pop_front(); |
| 386 } |
| 387 |
| 388 void ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps() { |
| 389 TRACE_EVENT0("gpu", |
| 390 "ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps"); |
| 391 while (!pending_swaps_.empty()) |
| 392 DisplayFirstPendingSwapImmediately(); |
| 393 } |
| 394 |
| 395 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() { |
| 396 TRACE_EVENT0("gpu", |
| 397 "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback"); |
| 398 |
| 399 if (pending_swaps_.empty()) |
| 400 return; |
| 401 |
| 402 const base::TimeTicks now = base::TimeTicks::Now(); |
| 403 if (IsFirstPendingSwapReadyToDisplay(now)) |
| 404 DisplayFirstPendingSwapImmediately(); |
| 405 PostCheckPendingSwapsCallbackIfNeeded(now); |
| 406 } |
| 407 |
| 408 void ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded( |
| 409 const base::TimeTicks& now) { |
| 410 TRACE_EVENT0("gpu", |
| 411 "ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded"); |
| 412 |
| 413 if (pending_swaps_.empty()) { |
| 414 display_pending_swap_timer_.Stop(); |
| 415 } else { |
| 416 display_pending_swap_timer_.Start( |
| 417 FROM_HERE, |
| 418 pending_swaps_.front()->target_display_time - now, |
| 419 base::Bind(&ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback, |
| 420 weak_factory_.GetWeakPtr())); |
| 421 } |
| 422 } |
| 423 |
| 424 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { |
| 425 return SwapBuffersInternal( |
| 426 gfx::Rect(0, 0, pixel_size_.width(), pixel_size_.height())); |
| 427 } |
| 428 |
| 429 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x, |
| 430 int y, |
| 431 int width, |
| 432 int height) { |
| 433 return SwapBuffersInternal(gfx::Rect(x, y, width, height)); |
| 434 } |
| 435 |
| 436 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() { |
| 437 return true; |
| 438 } |
| 439 |
| 440 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() { |
| 441 return gfx::Size(); |
| 442 } |
| 443 |
| 444 void* ImageTransportSurfaceOverlayMac::GetHandle() { |
| 445 return nullptr; |
| 446 } |
| 447 |
| 448 bool ImageTransportSurfaceOverlayMac::OnMakeCurrent(gfx::GLContext* context) { |
| 449 // Ensure that the context is on the appropriate GL renderer. The GL renderer |
| 450 // will generally only change when the GPU changes. |
| 451 if (gl_renderer_id_ && context) |
| 452 context->share_group()->SetRendererID(gl_renderer_id_); |
| 453 return true; |
| 454 } |
| 455 |
| 456 bool ImageTransportSurfaceOverlayMac::SetBackbufferAllocation(bool allocated) { |
| 457 if (!allocated) { |
| 458 DisplayAndClearAllPendingSwaps(); |
| 459 last_swap_time_ = base::TimeTicks(); |
| 460 } |
| 461 return true; |
| 462 } |
| 463 |
| 464 bool ImageTransportSurfaceOverlayMac::ScheduleOverlayPlane( |
| 465 int z_order, |
| 466 gfx::OverlayTransform transform, |
| 467 gl::GLImage* image, |
| 468 const gfx::Rect& pixel_frame_rect, |
| 469 const gfx::RectF& crop_rect) { |
| 470 if (transform != gfx::OVERLAY_TRANSFORM_NONE) { |
| 471 DLOG(ERROR) << "Invalid overlay plane transform."; |
| 472 return false; |
| 473 } |
| 474 if (z_order) { |
| 475 DLOG(ERROR) << "Invalid non-zero Z order."; |
| 476 return false; |
| 477 } |
| 478 if (pending_partial_damage_tree_) { |
| 479 DLOG(ERROR) << "Only one overlay per swap is allowed."; |
| 480 return false; |
| 481 } |
| 482 pending_partial_damage_tree_.reset(new CALayerPartialDamageTree( |
| 483 use_remote_layer_api_, |
| 484 static_cast<gl::GLImageIOSurface*>(image)->io_surface(), |
| 485 pixel_frame_rect)); |
| 486 return true; |
| 487 } |
| 488 |
| 489 bool ImageTransportSurfaceOverlayMac::ScheduleCALayer( |
| 490 gl::GLImage* contents_image, |
| 491 const gfx::RectF& contents_rect, |
| 492 float opacity, |
| 493 unsigned background_color, |
| 494 unsigned edge_aa_mask, |
| 495 const gfx::RectF& rect, |
| 496 bool is_clipped, |
| 497 const gfx::RectF& clip_rect, |
| 498 const gfx::Transform& transform, |
| 499 int sorting_context_id) { |
| 500 base::ScopedCFTypeRef<IOSurfaceRef> io_surface; |
| 501 if (contents_image) { |
| 502 io_surface = |
| 503 static_cast<gl::GLImageIOSurface*>(contents_image)->io_surface(); |
| 504 } |
| 505 if (!pending_ca_layer_tree_) |
| 506 pending_ca_layer_tree_.reset(new CALayerTree); |
| 507 return pending_ca_layer_tree_->ScheduleCALayer( |
| 508 is_clipped, gfx::ToEnclosingRect(clip_rect), sorting_context_id, |
| 509 transform, io_surface, contents_rect, gfx::ToEnclosingRect(rect), |
| 510 background_color, edge_aa_mask, opacity); |
| 511 } |
| 512 |
| 513 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const { |
| 514 return true; |
| 515 } |
| 516 |
| 517 bool ImageTransportSurfaceOverlayMac::Resize(const gfx::Size& pixel_size, |
| 518 float scale_factor, |
| 519 bool has_alpha) { |
| 520 // Flush through any pending frames. |
| 521 DisplayAndClearAllPendingSwaps(); |
| 522 pixel_size_ = pixel_size; |
| 523 scale_factor_ = scale_factor; |
| 524 return true; |
| 525 } |
| 526 |
| 527 void ImageTransportSurfaceOverlayMac::OnGpuSwitched() { |
| 528 // Create a new context, and use the GL renderer ID that the new context gets. |
| 529 scoped_refptr<ui::IOSurfaceContext> context_on_new_gpu = |
| 530 ui::IOSurfaceContext::Get(ui::IOSurfaceContext::kCALayerContext); |
| 531 if (!context_on_new_gpu) |
| 532 return; |
| 533 GLint context_renderer_id = -1; |
| 534 if (CGLGetParameter(context_on_new_gpu->cgl_context(), |
| 535 kCGLCPCurrentRendererID, |
| 536 &context_renderer_id) != kCGLNoError) { |
| 537 LOG(ERROR) << "Failed to create test context after GPU switch"; |
| 538 return; |
| 539 } |
| 540 gl_renderer_id_ = context_renderer_id & kCGLRendererIDMatchingMask; |
| 541 |
| 542 // Post a task holding a reference to the new GL context. The reason for |
| 543 // this is to avoid creating-then-destroying the context for every image |
| 544 // transport surface that is observing the GPU switch. |
| 545 base::MessageLoop::current()->PostTask( |
| 546 FROM_HERE, base::Bind(&IOSurfaceContextNoOp, context_on_new_gpu)); |
| 547 } |
| 548 |
| 549 base::TimeTicks ImageTransportSurfaceOverlayMac::GetNextVSyncTimeAfter( |
| 550 const base::TimeTicks& from, double interval_fraction) { |
| 551 if (!vsync_parameters_valid_) |
| 552 return from; |
| 553 |
| 554 // Compute the previous vsync time. |
| 555 base::TimeTicks previous_vsync = |
| 556 vsync_interval_ * ((from - vsync_timebase_) / vsync_interval_) + |
| 557 vsync_timebase_; |
| 558 |
| 559 // Return |interval_fraction| through the next vsync. |
| 560 return previous_vsync + (1 + interval_fraction) * vsync_interval_; |
| 561 } |
| 562 |
| 563 } // namespace content |
OLD | NEW |