| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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/browser/compositor/browser_compositor_ca_layer_tree_mac.h" | |
| 6 | |
| 7 #include <map> | |
| 8 | |
| 9 #include "cc/output/software_frame_data.h" | |
| 10 #include "base/debug/trace_event.h" | |
| 11 #include "base/lazy_instance.h" | |
| 12 #include "base/message_loop/message_loop.h" | |
| 13 #include "content/browser/compositor/io_surface_layer_mac.h" | |
| 14 #include "content/browser/compositor/software_layer_mac.h" | |
| 15 #include "content/common/gpu/surface_handle_types_mac.h" | |
| 16 #include "content/public/browser/context_factory.h" | |
| 17 #include "ui/base/cocoa/animation_utils.h" | |
| 18 #include "ui/gfx/geometry/dip_util.h" | |
| 19 #include "ui/gl/scoped_cgl.h" | |
| 20 | |
| 21 namespace content { | |
| 22 namespace { | |
| 23 | |
| 24 typedef std::map<gfx::AcceleratedWidget,AcceleratedWidgetMac*> | |
| 25 WidgetToHelperMap; | |
| 26 base::LazyInstance<WidgetToHelperMap> g_widget_to_helper_map; | |
| 27 | |
| 28 AcceleratedWidgetMac* GetHelperFromAcceleratedWidget( | |
| 29 gfx::AcceleratedWidget widget) { | |
| 30 WidgetToHelperMap::const_iterator found = | |
| 31 g_widget_to_helper_map.Pointer()->find(widget); | |
| 32 // This can end up being accessed after the underlying widget has been | |
| 33 // destroyed, but while the ui::Compositor is still being destroyed. | |
| 34 // Return NULL in these cases. | |
| 35 if (found == g_widget_to_helper_map.Pointer()->end()) | |
| 36 return NULL; | |
| 37 return found->second; | |
| 38 } | |
| 39 | |
| 40 } | |
| 41 | |
| 42 //////////////////////////////////////////////////////////////////////////////// | |
| 43 // AcceleratedWidgetMac | |
| 44 | |
| 45 AcceleratedWidgetMac::AcceleratedWidgetMac() | |
| 46 : view_(NULL) { | |
| 47 // Disable the fade-in animation as the layers are added. | |
| 48 ScopedCAActionDisabler disabler; | |
| 49 | |
| 50 // Add a flipped transparent layer as a child, so that we don't need to | |
| 51 // fiddle with the position of sub-layers -- they will always be at the | |
| 52 // origin. | |
| 53 flipped_layer_.reset([[CALayer alloc] init]); | |
| 54 [flipped_layer_ setGeometryFlipped:YES]; | |
| 55 [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)]; | |
| 56 [flipped_layer_ | |
| 57 setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable]; | |
| 58 | |
| 59 // Use a sequence number as the accelerated widget handle that we can use | |
| 60 // to look up the internals structure. | |
| 61 static uintptr_t last_sequence_number = 0; | |
| 62 last_sequence_number += 1; | |
| 63 native_widget_ = reinterpret_cast<gfx::AcceleratedWidget>( | |
| 64 last_sequence_number); | |
| 65 g_widget_to_helper_map.Pointer()->insert( | |
| 66 std::make_pair(native_widget_, this)); | |
| 67 } | |
| 68 | |
| 69 AcceleratedWidgetMac::~AcceleratedWidgetMac() { | |
| 70 DCHECK(!view_); | |
| 71 g_widget_to_helper_map.Pointer()->erase(native_widget_); | |
| 72 } | |
| 73 | |
| 74 void AcceleratedWidgetMac::SetNSView(AcceleratedWidgetMacNSView* view) { | |
| 75 // Disable the fade-in animation as the view is added. | |
| 76 ScopedCAActionDisabler disabler; | |
| 77 | |
| 78 DCHECK(view && !view_); | |
| 79 view_ = view; | |
| 80 | |
| 81 CALayer* background_layer = [view_->AcceleratedWidgetGetNSView() layer]; | |
| 82 DCHECK(background_layer); | |
| 83 [flipped_layer_ setBounds:[background_layer bounds]]; | |
| 84 [background_layer addSublayer:flipped_layer_]; | |
| 85 } | |
| 86 | |
| 87 void AcceleratedWidgetMac::ResetNSView() { | |
| 88 if (!view_) | |
| 89 return; | |
| 90 | |
| 91 // Disable the fade-out animation as the view is removed. | |
| 92 ScopedCAActionDisabler disabler; | |
| 93 | |
| 94 [flipped_layer_ removeFromSuperlayer]; | |
| 95 DestroyIOSurfaceLayer(io_surface_layer_); | |
| 96 DestroyCAContextLayer(ca_context_layer_); | |
| 97 DestroySoftwareLayer(); | |
| 98 | |
| 99 last_swap_size_dip_ = gfx::Size(); | |
| 100 view_ = NULL; | |
| 101 } | |
| 102 | |
| 103 bool AcceleratedWidgetMac::HasFrameOfSize( | |
| 104 const gfx::Size& dip_size) const { | |
| 105 return last_swap_size_dip_ == dip_size; | |
| 106 } | |
| 107 | |
| 108 int AcceleratedWidgetMac::GetRendererID() const { | |
| 109 if (io_surface_layer_) | |
| 110 return [io_surface_layer_ rendererID]; | |
| 111 return 0; | |
| 112 } | |
| 113 | |
| 114 bool AcceleratedWidgetMac::IsRendererThrottlingDisabled() const { | |
| 115 if (view_) | |
| 116 return view_->AcceleratedWidgetShouldIgnoreBackpressure(); | |
| 117 return false; | |
| 118 } | |
| 119 | |
| 120 void AcceleratedWidgetMac::BeginPumpingFrames() { | |
| 121 [io_surface_layer_ beginPumpingFrames]; | |
| 122 } | |
| 123 | |
| 124 void AcceleratedWidgetMac::EndPumpingFrames() { | |
| 125 [io_surface_layer_ endPumpingFrames]; | |
| 126 } | |
| 127 | |
| 128 void AcceleratedWidgetMac::GotAcceleratedFrame( | |
| 129 uint64 surface_handle, | |
| 130 const std::vector<ui::LatencyInfo>& latency_info, | |
| 131 gfx::Size pixel_size, float scale_factor, | |
| 132 const base::Closure& drawn_callback) { | |
| 133 // Record the surface and latency info to use when acknowledging this frame. | |
| 134 DCHECK(accelerated_frame_drawn_callback_.is_null()); | |
| 135 accelerated_frame_drawn_callback_ = drawn_callback; | |
| 136 accelerated_latency_info_.insert(accelerated_latency_info_.end(), | |
| 137 latency_info.begin(), latency_info.end()); | |
| 138 | |
| 139 // If there is no view and therefore no superview to draw into, early-out. | |
| 140 if (!view_) { | |
| 141 AcknowledgeAcceleratedFrame(); | |
| 142 return; | |
| 143 } | |
| 144 | |
| 145 // Disable the fade-in or fade-out effect if we create or remove layers. | |
| 146 ScopedCAActionDisabler disabler; | |
| 147 | |
| 148 last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size); | |
| 149 switch (GetSurfaceHandleType(surface_handle)) { | |
| 150 case kSurfaceHandleTypeIOSurface: { | |
| 151 IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle); | |
| 152 GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor); | |
| 153 break; | |
| 154 } | |
| 155 case kSurfaceHandleTypeCAContext: { | |
| 156 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle); | |
| 157 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor); | |
| 158 break; | |
| 159 } | |
| 160 default: | |
| 161 LOG(ERROR) << "Unrecognized accelerated frame type."; | |
| 162 return; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 void AcceleratedWidgetMac::GotAcceleratedCAContextFrame( | |
| 167 CAContextID ca_context_id, | |
| 168 gfx::Size pixel_size, | |
| 169 float scale_factor) { | |
| 170 // In the layer is replaced, keep the old one around until after the new one | |
| 171 // is installed to avoid flashes. | |
| 172 base::scoped_nsobject<CALayerHost> old_ca_context_layer = | |
| 173 ca_context_layer_; | |
| 174 | |
| 175 // Create the layer to host the layer exported by the GPU process with this | |
| 176 // particular CAContext ID. | |
| 177 if ([ca_context_layer_ contextId] != ca_context_id) { | |
| 178 ca_context_layer_.reset([[CALayerHost alloc] init]); | |
| 179 [ca_context_layer_ setContextId:ca_context_id]; | |
| 180 [ca_context_layer_ | |
| 181 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin]; | |
| 182 [flipped_layer_ addSublayer:ca_context_layer_]; | |
| 183 } | |
| 184 | |
| 185 // Acknowledge the frame to unblock the compositor immediately (the GPU | |
| 186 // process will do any required throttling). | |
| 187 AcknowledgeAcceleratedFrame(); | |
| 188 | |
| 189 // If this replacing a same-type layer, remove it now that the new layer is | |
| 190 // in the hierarchy. | |
| 191 if (old_ca_context_layer != ca_context_layer_) | |
| 192 DestroyCAContextLayer(old_ca_context_layer); | |
| 193 | |
| 194 // Remove any different-type layers that this is replacing. | |
| 195 DestroyIOSurfaceLayer(io_surface_layer_); | |
| 196 DestroySoftwareLayer(); | |
| 197 } | |
| 198 | |
| 199 void AcceleratedWidgetMac::GotAcceleratedIOSurfaceFrame( | |
| 200 IOSurfaceID io_surface_id, | |
| 201 gfx::Size pixel_size, | |
| 202 float scale_factor) { | |
| 203 // In the layer is replaced, keep the old one around until after the new one | |
| 204 // is installed to avoid flashes. | |
| 205 base::scoped_nsobject<IOSurfaceLayer> old_io_surface_layer = | |
| 206 io_surface_layer_; | |
| 207 | |
| 208 // Create or re-create an IOSurface layer if needed. If there already exists | |
| 209 // a layer but it has the wrong scale factor or it was poisoned, re-create the | |
| 210 // layer. | |
| 211 bool needs_new_layer = | |
| 212 !io_surface_layer_ || | |
| 213 [io_surface_layer_ hasBeenPoisoned] || | |
| 214 [io_surface_layer_ scaleFactor] != scale_factor; | |
| 215 if (needs_new_layer) { | |
| 216 io_surface_layer_.reset( | |
| 217 [[IOSurfaceLayer alloc] initWithClient:this | |
| 218 withScaleFactor:scale_factor]); | |
| 219 if (io_surface_layer_) | |
| 220 [flipped_layer_ addSublayer:io_surface_layer_]; | |
| 221 else | |
| 222 LOG(ERROR) << "Failed to create IOSurfaceLayer"; | |
| 223 } | |
| 224 | |
| 225 // Open the provided IOSurface. | |
| 226 if (io_surface_layer_) { | |
| 227 bool result = [io_surface_layer_ gotFrameWithIOSurface:io_surface_id | |
| 228 withPixelSize:pixel_size | |
| 229 withScaleFactor:scale_factor]; | |
| 230 if (!result) { | |
| 231 DestroyIOSurfaceLayer(io_surface_layer_); | |
| 232 LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer"; | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 // Give a final complaint if anything with the layer's creation went wrong. | |
| 237 // This frame will appear blank, the compositor will try to create another, | |
| 238 // and maybe that will go better. | |
| 239 if (!io_surface_layer_) { | |
| 240 LOG(ERROR) << "IOSurfaceLayer is nil, tab will be blank"; | |
| 241 IOSurfaceLayerHitError(); | |
| 242 } | |
| 243 | |
| 244 // Make the CALayer draw and set its size appropriately. | |
| 245 if (io_surface_layer_) { | |
| 246 [io_surface_layer_ gotNewFrame]; | |
| 247 | |
| 248 // Set the bounds of the accelerated layer to match the size of the frame. | |
| 249 // If the bounds changed, force the content to be displayed immediately. | |
| 250 CGRect new_layer_bounds = CGRectMake( | |
| 251 0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height()); | |
| 252 bool bounds_changed = !CGRectEqualToRect( | |
| 253 new_layer_bounds, [io_surface_layer_ bounds]); | |
| 254 [io_surface_layer_ setBounds:new_layer_bounds]; | |
| 255 if (bounds_changed) | |
| 256 [io_surface_layer_ setNeedsDisplayAndDisplayAndAck]; | |
| 257 } | |
| 258 | |
| 259 // If this replacing a same-type layer, remove it now that the new layer is | |
| 260 // in the hierarchy. | |
| 261 if (old_io_surface_layer != io_surface_layer_) | |
| 262 DestroyIOSurfaceLayer(old_io_surface_layer); | |
| 263 | |
| 264 // Remove any different-type layers that this is replacing. | |
| 265 DestroyCAContextLayer(ca_context_layer_); | |
| 266 DestroySoftwareLayer(); | |
| 267 } | |
| 268 | |
| 269 void AcceleratedWidgetMac::GotSoftwareFrame( | |
| 270 cc::SoftwareFrameData* frame_data, | |
| 271 float scale_factor, | |
| 272 SkCanvas* canvas) { | |
| 273 if (!frame_data || !canvas || !view_) | |
| 274 return; | |
| 275 | |
| 276 // Disable the fade-in or fade-out effect if we create or remove layers. | |
| 277 ScopedCAActionDisabler disabler; | |
| 278 | |
| 279 // If there is not a layer for software frames, create one. | |
| 280 if (!software_layer_) { | |
| 281 software_layer_.reset([[SoftwareLayer alloc] init]); | |
| 282 [flipped_layer_ addSublayer:software_layer_]; | |
| 283 } | |
| 284 | |
| 285 // Set the software layer to draw the provided canvas. | |
| 286 SkImageInfo info; | |
| 287 size_t row_bytes; | |
| 288 const void* pixels = canvas->peekPixels(&info, &row_bytes); | |
| 289 gfx::Size pixel_size(info.fWidth, info.fHeight); | |
| 290 [software_layer_ setContentsToData:pixels | |
| 291 withRowBytes:row_bytes | |
| 292 withPixelSize:pixel_size | |
| 293 withScaleFactor:scale_factor]; | |
| 294 last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size); | |
| 295 | |
| 296 // Remove any different-type layers that this is replacing. | |
| 297 DestroyCAContextLayer(ca_context_layer_); | |
| 298 DestroyIOSurfaceLayer(io_surface_layer_); | |
| 299 } | |
| 300 | |
| 301 void AcceleratedWidgetMac::DestroyCAContextLayer( | |
| 302 base::scoped_nsobject<CALayerHost> ca_context_layer) { | |
| 303 if (!ca_context_layer) | |
| 304 return; | |
| 305 [ca_context_layer removeFromSuperlayer]; | |
| 306 if (ca_context_layer == ca_context_layer_) | |
| 307 ca_context_layer_.reset(); | |
| 308 } | |
| 309 | |
| 310 void AcceleratedWidgetMac::DestroyIOSurfaceLayer( | |
| 311 base::scoped_nsobject<IOSurfaceLayer> io_surface_layer) { | |
| 312 if (!io_surface_layer) | |
| 313 return; | |
| 314 [io_surface_layer resetClient]; | |
| 315 [io_surface_layer removeFromSuperlayer]; | |
| 316 if (io_surface_layer == io_surface_layer_) | |
| 317 io_surface_layer_.reset(); | |
| 318 } | |
| 319 | |
| 320 void AcceleratedWidgetMac::DestroySoftwareLayer() { | |
| 321 if (!software_layer_) | |
| 322 return; | |
| 323 [software_layer_ removeFromSuperlayer]; | |
| 324 software_layer_.reset(); | |
| 325 } | |
| 326 | |
| 327 bool AcceleratedWidgetMac::IOSurfaceLayerShouldAckImmediately() const { | |
| 328 // If there is no view then the accelerated layer is not in the view | |
| 329 // hierarchy and will never draw. | |
| 330 if (!view_) | |
| 331 return true; | |
| 332 return view_->AcceleratedWidgetShouldIgnoreBackpressure(); | |
| 333 } | |
| 334 | |
| 335 void AcceleratedWidgetMac::IOSurfaceLayerDidDrawFrame() { | |
| 336 AcknowledgeAcceleratedFrame(); | |
| 337 } | |
| 338 | |
| 339 void AcceleratedWidgetMac::AcknowledgeAcceleratedFrame() { | |
| 340 if (accelerated_frame_drawn_callback_.is_null()) | |
| 341 return; | |
| 342 accelerated_frame_drawn_callback_.Run(); | |
| 343 accelerated_frame_drawn_callback_.Reset(); | |
| 344 if (view_) | |
| 345 view_->AcceleratedWidgetSwapCompleted(accelerated_latency_info_); | |
| 346 accelerated_latency_info_.clear(); | |
| 347 } | |
| 348 | |
| 349 void AcceleratedWidgetMac::IOSurfaceLayerHitError() { | |
| 350 // Perform all acks that would have been done if the frame had succeeded, to | |
| 351 // un-block the compositor and renderer. | |
| 352 AcknowledgeAcceleratedFrame(); | |
| 353 | |
| 354 // Poison the context being used and request a mulligan. | |
| 355 [io_surface_layer_ poisonContextAndSharegroup]; | |
| 356 | |
| 357 if (view_) | |
| 358 view_->AcceleratedWidgetHitError(); | |
| 359 } | |
| 360 | |
| 361 void AcceleratedWidgetMacGotAcceleratedFrame( | |
| 362 gfx::AcceleratedWidget widget, uint64 surface_handle, | |
| 363 const std::vector<ui::LatencyInfo>& latency_info, | |
| 364 gfx::Size pixel_size, float scale_factor, | |
| 365 const base::Closure& drawn_callback, | |
| 366 bool* disable_throttling, int* renderer_id) { | |
| 367 AcceleratedWidgetMac* accelerated_widget_mac = | |
| 368 GetHelperFromAcceleratedWidget(widget); | |
| 369 if (accelerated_widget_mac) { | |
| 370 accelerated_widget_mac->GotAcceleratedFrame( | |
| 371 surface_handle, latency_info, pixel_size, scale_factor, drawn_callback); | |
| 372 *disable_throttling = | |
| 373 accelerated_widget_mac->IsRendererThrottlingDisabled(); | |
| 374 *renderer_id = accelerated_widget_mac->GetRendererID(); | |
| 375 } else { | |
| 376 *disable_throttling = false; | |
| 377 *renderer_id = 0; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 void AcceleratedWidgetMacGotSoftwareFrame( | |
| 382 gfx::AcceleratedWidget widget, | |
| 383 cc::SoftwareFrameData* frame_data, float scale_factor, SkCanvas* canvas) { | |
| 384 AcceleratedWidgetMac* accelerated_widget_mac = | |
| 385 GetHelperFromAcceleratedWidget(widget); | |
| 386 if (accelerated_widget_mac) | |
| 387 accelerated_widget_mac->GotSoftwareFrame(frame_data, scale_factor, canvas); | |
| 388 } | |
| 389 | |
| 390 } // namespace content | |
| OLD | NEW |