Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/common/gpu/image_transport_surface_overlay_mac.h" | 5 #include "content/common/gpu/image_transport_surface_overlay_mac.h" |
| 6 | 6 |
| 7 #include <OpenGL/gl.h> | 7 #include <IOSurface/IOSurface.h> |
| 8 #include <OpenGL/GL.h> | |
| 8 | 9 |
| 10 // This type consistently causes problem on Mac, and needs to be dealt with | |
| 11 // in a systemic way. | |
| 12 // http://crbug.com/517208 | |
| 13 #ifndef GL_OES_EGL_image | |
| 14 typedef void* GLeglImageOES; | |
| 15 #endif | |
| 16 | |
| 17 #include "base/mac/scoped_cftyperef.h" | |
| 9 #include "content/common/gpu/gpu_messages.h" | 18 #include "content/common/gpu/gpu_messages.h" |
| 10 #include "ui/accelerated_widget_mac/surface_handle_types.h" | 19 #include "ui/accelerated_widget_mac/surface_handle_types.h" |
| 11 #include "ui/base/cocoa/animation_utils.h" | 20 #include "ui/base/cocoa/animation_utils.h" |
| 12 #include "ui/base/cocoa/remote_layer_api.h" | 21 #include "ui/base/cocoa/remote_layer_api.h" |
| 13 #include "ui/gfx/geometry/dip_util.h" | 22 #include "ui/gfx/geometry/dip_util.h" |
| 23 #include "ui/gl/gl_fence.h" | |
| 14 #include "ui/gl/gl_image_io_surface.h" | 24 #include "ui/gl/gl_image_io_surface.h" |
| 25 #include "ui/gl/scoped_api.h" | |
| 26 #include "ui/gl/scoped_cgl.h" | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 // Don't let a frame draw until 5% of the way through the next vsync interval | |
| 31 // after the call to SwapBuffers. This slight offset is to ensure that skew | |
| 32 // doesn't result in the frame being presented to the previous vsync interval. | |
| 33 | |
| 34 const double kVSyncIntervalFractionForEarliestDisplay = 0.05; | |
| 35 // Target 50% of the way through the next vsync interval. Empirically, it has | |
| 36 // been determined to be a good target for smooth animation. | |
| 37 const double kVSyncIntervalFractionForDisplayCallback = 0.5; | |
| 38 | |
| 39 // If a frame takes more than 1/4th of a second for its fence to finish, just | |
| 40 // pretend that the frame is ready to draw. | |
| 41 const double kMaximumDelayWaitingForFenceInSeconds = 0.25; | |
| 42 | |
| 43 void CheckGLErrors(const char* msg) { | |
| 44 GLenum gl_error; | |
| 45 while ((gl_error = glGetError()) != GL_NO_ERROR) { | |
| 46 LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error; | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 } // namespace | |
| 51 | |
| 52 @interface CALayer(Private) | |
| 53 -(void)setContentsChanged; | |
| 54 @end | |
| 15 | 55 |
| 16 namespace content { | 56 namespace content { |
| 17 | 57 |
| 58 class ImageTransportSurfaceOverlayMac::PendingSwap { | |
| 59 public: | |
| 60 PendingSwap() : scale_factor(1) {} | |
| 61 ~PendingSwap() { DCHECK(!gl_fence); } | |
| 62 | |
| 63 gfx::Size pixel_size; | |
| 64 float scale_factor; | |
| 65 std::vector<ui::LatencyInfo> latency_info; | |
| 66 | |
| 67 // The IOSurface with new content for this swap. | |
| 68 base::ScopedCFTypeRef<IOSurfaceRef> io_surface; | |
| 69 | |
| 70 // A fence object, and the CGL context it was issued in. | |
| 71 base::ScopedTypeRef<CGLContextObj> cgl_context; | |
| 72 scoped_ptr<gfx::GLFence> gl_fence; | |
| 73 | |
| 74 // The earliest time that this frame may be drawn. A frame is not allowed | |
| 75 // to draw until a fraction of the way through the vsync interval after its | |
| 76 // This extra latency is to allow wiggle-room for smoothness. | |
| 77 base::TimeTicks earliest_allowed_draw_time; | |
| 78 // After this time, always draw the frame, no matter what its fence says. This | |
| 79 // is to prevent GL bugs from locking the compositor forever. | |
| 80 base::TimeTicks latest_allowed_draw_time; | |
| 81 }; | |
| 82 | |
| 18 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac( | 83 ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac( |
| 19 GpuChannelManager* manager, | 84 GpuChannelManager* manager, |
| 20 GpuCommandBufferStub* stub, | 85 GpuCommandBufferStub* stub, |
| 21 gfx::PluginWindowHandle handle) | 86 gfx::PluginWindowHandle handle) |
| 22 : scale_factor_(1), pending_overlay_image_(nullptr) { | 87 : scale_factor_(1), pending_overlay_image_(nullptr), |
| 88 has_pending_callback_(false), weak_factory_(this) { | |
| 23 helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); | 89 helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); |
| 24 } | 90 } |
| 25 | 91 |
| 26 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() { | 92 ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() { |
| 27 gfx::GLImageIOSurface::SetLayerForWidget(widget_, nil); | |
| 28 } | 93 } |
| 29 | 94 |
| 30 bool ImageTransportSurfaceOverlayMac::Initialize() { | 95 bool ImageTransportSurfaceOverlayMac::Initialize() { |
| 31 if (!helper_->Initialize()) | 96 if (!helper_->Initialize()) |
| 32 return false; | 97 return false; |
| 33 | 98 |
| 34 // Create the CAContext to send this to the GPU process, and the layer for | 99 // Create the CAContext to send this to the GPU process, and the layer for |
| 35 // the context. | 100 // the context. |
| 36 CGSConnectionID connection_id = CGSMainConnectionID(); | 101 CGSConnectionID connection_id = CGSMainConnectionID(); |
| 37 ca_context_.reset( | 102 ca_context_.reset( |
| 38 [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]); | 103 [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]); |
| 39 layer_.reset([[CALayer alloc] init]); | 104 layer_.reset([[CALayer alloc] init]); |
| 105 [layer_ setGeometryFlipped:YES]; | |
| 106 [layer_ setOpaque:YES]; | |
| 40 [ca_context_ setLayer:layer_]; | 107 [ca_context_ setLayer:layer_]; |
| 41 | |
| 42 // Register the CALayer so that it can be picked up in GLImageIOSurface. | |
| 43 static intptr_t previous_widget = 0; | |
| 44 previous_widget += 1; | |
| 45 widget_ = reinterpret_cast<gfx::AcceleratedWidget>(previous_widget); | |
| 46 gfx::GLImageIOSurface::SetLayerForWidget(widget_, layer_); | |
| 47 | |
| 48 return true; | 108 return true; |
| 49 } | 109 } |
| 50 | 110 |
| 51 void ImageTransportSurfaceOverlayMac::Destroy() {} | 111 void ImageTransportSurfaceOverlayMac::Destroy() { |
| 112 FinishAllPendingSwaps(); | |
| 113 } | |
| 52 | 114 |
| 53 bool ImageTransportSurfaceOverlayMac::IsOffscreen() { | 115 bool ImageTransportSurfaceOverlayMac::IsOffscreen() { |
| 54 return false; | 116 return false; |
| 55 } | 117 } |
| 56 | 118 |
| 57 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { | 119 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal( |
| 58 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffers"); | 120 const gfx::Rect& pixel_damage_rect) { |
| 59 | 121 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffersInternal"); |
| 60 // A flush is required to ensure that all content appears in the layer. | 122 |
| 61 { | 123 // Use the same concept of 'now' for the entire function. The duration of |
| 62 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::glFlush"); | 124 // this function only affect the result if this function lasts across a vsync |
| 63 glFlush(); | 125 // boundary, in which case smooth animation is out the window anyway. |
| 64 } | 126 const base::TimeTicks now = base::TimeTicks::Now(); |
| 127 | |
| 128 // If the previous swap is ready to display, do it before flushing the | |
| 129 // new swap. It is desirable to always be hitting this path when trying to | |
| 130 // animate smoothly with vsync. | |
| 131 if (!pending_swaps_.empty()) { | |
| 132 if (IsFirstPendingSwapReadyToDisplay(now)) | |
| 133 DisplayFirstPendingSwapImmediately(); | |
| 134 } | |
| 135 | |
| 136 // The remainder of the function will populate the PendingSwap structure and | |
| 137 // then enqueue it. | |
| 138 linked_ptr<PendingSwap> new_swap(new PendingSwap); | |
| 139 new_swap->pixel_size = pixel_size_; | |
| 140 new_swap->scale_factor = scale_factor_; | |
| 141 new_swap->latency_info.swap(latency_info_); | |
| 65 | 142 |
| 66 // There should exist only one overlay image, and it should cover the whole | 143 // There should exist only one overlay image, and it should cover the whole |
| 67 // surface. | 144 // surface. |
| 68 DCHECK(pending_overlay_image_); | 145 DCHECK(pending_overlay_image_); |
| 69 if (pending_overlay_image_) { | 146 if (pending_overlay_image_) { |
| 70 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::setContents"); | 147 gfx::GLImageIOSurface* pending_overlay_image_io_surface = |
| 148 static_cast<gfx::GLImageIOSurface*>(pending_overlay_image_); | |
|
reveman
2015/08/18 08:44:11
this static cast seems scary. how do we know that
| |
| 149 new_swap->io_surface = pending_overlay_image_io_surface->io_surface(); | |
| 150 pending_overlay_image_ = nullptr; | |
| 151 } | |
| 152 | |
| 153 // A flush is required to ensure that all content appears in the layer. | |
| 154 { | |
| 155 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
| 156 TRACE_EVENT1("gpu", "ImageTransportSurfaceOverlayMac::glFlush", "surface", | |
| 157 new_swap->io_surface.get()); | |
| 158 CheckGLErrors("before flushing frame"); | |
| 159 new_swap->cgl_context.reset(CGLGetCurrentContext(), | |
| 160 base::scoped_policy::RETAIN); | |
| 161 if (gfx::GLFence::IsSupported()) | |
| 162 new_swap->gl_fence.reset(gfx::GLFence::Create()); | |
| 163 else | |
| 164 glFlush(); | |
| 165 CheckGLErrors("while flushing frame"); | |
| 166 } | |
| 167 | |
| 168 // Compute the deadlines for drawing this frame. | |
| 169 if (display_link_mac_) { | |
| 170 new_swap->earliest_allowed_draw_time = | |
| 171 display_link_mac_->GetNextVSyncTimeAfter( | |
| 172 now, kVSyncIntervalFractionForEarliestDisplay); | |
| 173 new_swap->latest_allowed_draw_time = now + | |
| 174 base::TimeDelta::FromSecondsD(kMaximumDelayWaitingForFenceInSeconds); | |
| 175 } else { | |
| 176 // If we have no display link (because vsync is disabled or because we have | |
| 177 // not received display parameters yet), immediately attempt to display the | |
| 178 // surface. | |
| 179 new_swap->earliest_allowed_draw_time = now; | |
| 180 new_swap->latest_allowed_draw_time = now; | |
| 181 } | |
| 182 | |
| 183 pending_swaps_.push_back(new_swap); | |
| 184 PostCheckPendingSwapsCallbackIfNeeded(now); | |
| 185 return gfx::SwapResult::SWAP_ACK; | |
| 186 } | |
| 187 | |
| 188 bool ImageTransportSurfaceOverlayMac::IsFirstPendingSwapReadyToDisplay( | |
| 189 const base::TimeTicks& now) { | |
| 190 DCHECK(!pending_swaps_.empty()); | |
| 191 linked_ptr<PendingSwap> swap = pending_swaps_.front(); | |
| 192 | |
| 193 // If more that a certain amount of time has passed since the swap, | |
| 194 // unconditionally continue. | |
| 195 if (now > swap->latest_allowed_draw_time) | |
| 196 return true; | |
| 197 | |
| 198 // Frames are disallowed from drawing until the vsync interval after their | |
| 199 // swap is issued. | |
| 200 if (now < swap->earliest_allowed_draw_time) | |
| 201 return false; | |
| 202 | |
| 203 // If there is no fence then this is either for immediate display, or the | |
| 204 // fence was aready successfully checked and deleted. | |
| 205 if (!swap->gl_fence) | |
| 206 return true; | |
| 207 | |
| 208 // Check if the pending work has finished (and early-out if it is not, and | |
| 209 // this is not forced). | |
| 210 bool has_completed = true; | |
| 211 if (swap->gl_fence) { | |
| 212 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
| 213 gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context); | |
| 214 | |
| 215 CheckGLErrors("before testing fence"); | |
| 216 has_completed= swap->gl_fence->HasCompleted(); | |
| 217 CheckGLErrors("after testing fence"); | |
| 218 if (has_completed) { | |
| 219 swap->gl_fence.reset(); | |
| 220 CheckGLErrors("while deleting fence"); | |
| 221 } | |
| 222 } | |
| 223 return has_completed; | |
| 224 } | |
| 225 | |
| 226 void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() { | |
| 227 TRACE_EVENT0("gpu", | |
| 228 "ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately"); | |
| 229 DCHECK(!pending_swaps_.empty()); | |
| 230 linked_ptr<PendingSwap> swap = pending_swaps_.front(); | |
| 231 | |
| 232 // If there is a fence for this object, delete it. | |
| 233 if (swap->gl_fence) { | |
| 234 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
| 235 gfx::ScopedCGLSetCurrentContext scoped_set_current(swap->cgl_context); | |
| 236 | |
| 237 CheckGLErrors("before deleting active fence"); | |
| 238 swap->gl_fence.reset(); | |
| 239 CheckGLErrors("while deleting active fence"); | |
| 240 } | |
| 241 | |
| 242 // Update the CALayer hierarchy. | |
| 243 { | |
| 244 TRACE_EVENT1("gpu", "ImageTransportSurfaceOverlayMac::setContents", | |
| 245 "surface", swap->io_surface.get()); | |
| 71 ScopedCAActionDisabler disabler; | 246 ScopedCAActionDisabler disabler; |
| 72 gfx::Rect dip_bounds = gfx::ConvertRectToDIP( | 247 |
| 73 scale_factor_, gfx::Rect(pixel_size_)); | 248 id new_contents = static_cast<id>(swap->io_surface.get()); |
| 74 gfx::RectF crop_rect(0, 0, 1, 1); | 249 [layer_ setContents:new_contents]; |
| 75 pending_overlay_image_->ScheduleOverlayPlane( | 250 |
| 76 widget_, 0, gfx::OVERLAY_TRANSFORM_NONE, dip_bounds, crop_rect); | 251 CGRect new_frame = gfx::ConvertRectToDIP( |
| 77 pending_overlay_image_ = nullptr; | 252 swap->scale_factor, gfx::Rect(swap->pixel_size)).ToCGRect(); |
| 78 } | 253 if (!CGRectEqualToRect([layer_ frame], new_frame)) |
| 79 | 254 [layer_ setFrame:new_frame]; |
| 255 } | |
| 256 | |
| 257 // Send acknowledgement to the browser. | |
| 80 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; | 258 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; |
| 81 params.surface_handle = | 259 params.surface_handle = |
| 82 ui::SurfaceHandleFromCAContextID([ca_context_ contextId]); | 260 ui::SurfaceHandleFromCAContextID([ca_context_ contextId]); |
| 83 params.size = pixel_size_; | 261 params.size = pixel_size_; |
| 84 params.scale_factor = scale_factor_; | 262 params.scale_factor = scale_factor_; |
| 85 params.latency_info.swap(latency_info_); | 263 params.latency_info.swap(swap->latency_info); |
| 86 helper_->SendAcceleratedSurfaceBuffersSwapped(params); | 264 helper_->SendAcceleratedSurfaceBuffersSwapped(params); |
| 87 return gfx::SwapResult::SWAP_ACK; | 265 |
| 266 // Remove this from the queue, and reset any callback timers. | |
| 267 pending_swaps_.pop_front(); | |
| 268 } | |
| 269 | |
| 270 void ImageTransportSurfaceOverlayMac::FinishAllPendingSwaps() { | |
| 271 TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::FinishAllPendingSwaps"); | |
| 272 while (!pending_swaps_.empty()) | |
| 273 DisplayFirstPendingSwapImmediately(); | |
| 274 } | |
| 275 | |
| 276 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() { | |
| 277 TRACE_EVENT0("gpu", | |
| 278 "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback"); | |
| 279 | |
| 280 DCHECK(has_pending_callback_); | |
| 281 has_pending_callback_ = false; | |
| 282 | |
| 283 if (pending_swaps_.empty()) | |
| 284 return; | |
| 285 | |
| 286 const base::TimeTicks now = base::TimeTicks::Now(); | |
| 287 if (IsFirstPendingSwapReadyToDisplay(now)) | |
| 288 DisplayFirstPendingSwapImmediately(); | |
| 289 PostCheckPendingSwapsCallbackIfNeeded(now); | |
| 290 } | |
| 291 | |
| 292 void ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded( | |
| 293 const base::TimeTicks& now) { | |
| 294 TRACE_EVENT0("gpu", | |
| 295 "ImageTransportSurfaceOverlayMac::PostCheckPendingSwapsCallbackIfNeeded"); | |
| 296 | |
| 297 if (has_pending_callback_) | |
| 298 return; | |
| 299 if (pending_swaps_.empty()) | |
| 300 return; | |
| 301 | |
| 302 base::TimeTicks target; | |
| 303 if (display_link_mac_) { | |
| 304 target = display_link_mac_->GetNextVSyncTimeAfter( | |
| 305 now, kVSyncIntervalFractionForDisplayCallback); | |
| 306 } else { | |
| 307 target = now; | |
| 308 } | |
| 309 base::TimeDelta delay = target - now; | |
| 310 | |
| 311 base::MessageLoop::current()->PostDelayedTask( | |
| 312 FROM_HERE, | |
| 313 base::Bind(&ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback, | |
| 314 weak_factory_.GetWeakPtr()), delay); | |
| 315 has_pending_callback_ = true; | |
| 316 } | |
| 317 | |
| 318 gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { | |
| 319 return SwapBuffersInternal(gfx::Rect(pixel_size_)); | |
| 88 } | 320 } |
| 89 | 321 |
| 90 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x, | 322 gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x, |
| 91 int y, | 323 int y, |
| 92 int width, | 324 int width, |
| 93 int height) { | 325 int height) { |
| 94 return SwapBuffers(); | 326 return SwapBuffersInternal(gfx::Rect(x, y, width, height)); |
| 95 } | 327 } |
| 96 | 328 |
| 97 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() { | 329 bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() { |
| 98 return true; | 330 return true; |
| 99 } | 331 } |
| 100 | 332 |
| 101 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() { | 333 gfx::Size ImageTransportSurfaceOverlayMac::GetSize() { |
| 102 return gfx::Size(); | 334 return gfx::Size(); |
| 103 } | 335 } |
| 104 | 336 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 122 DCHECK(!pending_overlay_image_); | 354 DCHECK(!pending_overlay_image_); |
| 123 pending_overlay_image_ = image; | 355 pending_overlay_image_ = image; |
| 124 return true; | 356 return true; |
| 125 } | 357 } |
| 126 | 358 |
| 127 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const { | 359 bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const { |
| 128 return true; | 360 return true; |
| 129 } | 361 } |
| 130 | 362 |
| 131 void ImageTransportSurfaceOverlayMac::OnBufferPresented( | 363 void ImageTransportSurfaceOverlayMac::OnBufferPresented( |
| 132 const AcceleratedSurfaceMsg_BufferPresented_Params& params) {} | 364 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { |
| 365 if (display_link_mac_ && | |
| 366 display_link_mac_->display_id() == params.display_id_for_vsync) | |
| 367 return; | |
| 368 display_link_mac_ = ui::DisplayLinkMac::GetForDisplay( | |
| 369 params.display_id_for_vsync); | |
| 370 } | |
| 133 | 371 |
| 134 void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size, | 372 void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size, |
| 135 float scale_factor) { | 373 float scale_factor) { |
| 374 // Flush through any pending frames. | |
| 375 FinishAllPendingSwaps(); | |
| 136 pixel_size_ = pixel_size; | 376 pixel_size_ = pixel_size; |
| 137 scale_factor_ = scale_factor; | 377 scale_factor_ = scale_factor; |
| 138 } | 378 } |
| 139 | 379 |
| 140 void ImageTransportSurfaceOverlayMac::SetLatencyInfo( | 380 void ImageTransportSurfaceOverlayMac::SetLatencyInfo( |
| 141 const std::vector<ui::LatencyInfo>& latency_info) { | 381 const std::vector<ui::LatencyInfo>& latency_info) { |
| 142 latency_info_.insert( | 382 latency_info_.insert( |
| 143 latency_info_.end(), latency_info.begin(), latency_info.end()); | 383 latency_info_.end(), latency_info.begin(), latency_info.end()); |
| 144 } | 384 } |
| 145 | 385 |
| 146 void ImageTransportSurfaceOverlayMac::WakeUpGpu() {} | 386 void ImageTransportSurfaceOverlayMac::WakeUpGpu() {} |
| 147 | 387 |
| 148 } // namespace content | 388 } // namespace content |
| OLD | NEW |