Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / accelerated_widget_mac / accelerated_widget_mac.mm
blob82390e5d6b93005fa28da5bc69a024f451282c16
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.
5 #include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
7 #include <map>
9 #include "base/lazy_instance.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/trace_event/trace_event.h"
12 #include "third_party/skia/include/core/SkCanvas.h"
13 #include "ui/accelerated_widget_mac/io_surface_layer.h"
14 #include "ui/accelerated_widget_mac/surface_handle_types.h"
15 #include "ui/base/cocoa/animation_utils.h"
16 #include "ui/gfx/geometry/dip_util.h"
17 #include "ui/gl/scoped_cgl.h"
19 namespace ui {
20 namespace {
22 typedef std::map<gfx::AcceleratedWidget,AcceleratedWidgetMac*>
23     WidgetToHelperMap;
24 base::LazyInstance<WidgetToHelperMap> g_widget_to_helper_map;
26 AcceleratedWidgetMac* GetHelperFromAcceleratedWidget(
27     gfx::AcceleratedWidget widget) {
28   WidgetToHelperMap::const_iterator found =
29       g_widget_to_helper_map.Pointer()->find(widget);
30   // This can end up being accessed after the underlying widget has been
31   // destroyed, but while the ui::Compositor is still being destroyed.
32   // Return NULL in these cases.
33   if (found == g_widget_to_helper_map.Pointer()->end())
34     return NULL;
35   return found->second;
38 }  // namespace
40 ////////////////////////////////////////////////////////////////////////////////
41 // AcceleratedWidgetMac
43 AcceleratedWidgetMac::AcceleratedWidgetMac(bool needs_gl_finish_workaround)
44     : view_(NULL),
45       needs_gl_finish_workaround_(needs_gl_finish_workaround) {
46   // Disable the fade-in animation as the layers are added.
47   ScopedCAActionDisabler disabler;
49   // Add a flipped transparent layer as a child, so that we don't need to
50   // fiddle with the position of sub-layers -- they will always be at the
51   // origin.
52   flipped_layer_.reset([[CALayer alloc] init]);
53   [flipped_layer_ setGeometryFlipped:YES];
54   [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
55   [flipped_layer_
56       setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
58   // Use a sequence number as the accelerated widget handle that we can use
59   // to look up the internals structure.
60   static intptr_t last_sequence_number = 0;
61   last_sequence_number += 1;
62   native_widget_ = reinterpret_cast<gfx::AcceleratedWidget>(
63       last_sequence_number);
64   g_widget_to_helper_map.Pointer()->insert(
65       std::make_pair(native_widget_, this));
68 AcceleratedWidgetMac::~AcceleratedWidgetMac() {
69   DCHECK(!view_);
70   g_widget_to_helper_map.Pointer()->erase(native_widget_);
73 void AcceleratedWidgetMac::SetNSView(AcceleratedWidgetMacNSView* view) {
74   // Disable the fade-in animation as the view is added.
75   ScopedCAActionDisabler disabler;
77   DCHECK(view && !view_);
78   view_ = view;
80   CALayer* background_layer = [view_->AcceleratedWidgetGetNSView() layer];
81   DCHECK(background_layer);
82   [flipped_layer_ setBounds:[background_layer bounds]];
83   [background_layer addSublayer:flipped_layer_];
86 void AcceleratedWidgetMac::ResetNSView() {
87   if (!view_)
88     return;
90   // Disable the fade-out animation as the view is removed.
91   ScopedCAActionDisabler disabler;
93   [flipped_layer_ removeFromSuperlayer];
94   DestroyIOSurfaceLayer(io_surface_layer_);
95   DestroyCAContextLayer(ca_context_layer_);
96   DestroySoftwareLayer();
98   last_swap_size_dip_ = gfx::Size();
99   view_ = NULL;
102 bool AcceleratedWidgetMac::HasFrameOfSize(
103     const gfx::Size& dip_size) const {
104   return last_swap_size_dip_ == dip_size;
107 int AcceleratedWidgetMac::GetRendererID() const {
108   if (io_surface_layer_)
109     return [io_surface_layer_ rendererID];
110   return 0;
113 bool AcceleratedWidgetMac::IsRendererThrottlingDisabled() const {
114   if (view_)
115     return view_->AcceleratedWidgetShouldIgnoreBackpressure();
116   return false;
119 void AcceleratedWidgetMac::BeginPumpingFrames() {
120   [io_surface_layer_ beginPumpingFrames];
123 void AcceleratedWidgetMac::EndPumpingFrames() {
124   [io_surface_layer_ endPumpingFrames];
127 void AcceleratedWidgetMac::GotAcceleratedFrame(
128     uint64 surface_handle,
129     const std::vector<ui::LatencyInfo>& latency_info,
130     const gfx::Size& pixel_size,
131     float scale_factor,
132     const gfx::Rect& pixel_damage_rect,
133     const base::Closure& drawn_callback) {
134   // Record the surface and latency info to use when acknowledging this frame.
135   DCHECK(accelerated_frame_drawn_callback_.is_null());
136   accelerated_frame_drawn_callback_ = drawn_callback;
137   accelerated_latency_info_.insert(accelerated_latency_info_.end(),
138                                    latency_info.begin(), latency_info.end());
140   // If there is no view and therefore no superview to draw into, early-out.
141   if (!view_) {
142     AcknowledgeAcceleratedFrame();
143     return;
144   }
146   // Disable the fade-in or fade-out effect if we create or remove layers.
147   ScopedCAActionDisabler disabler;
149   last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
150   switch (GetSurfaceHandleType(surface_handle)) {
151     case kSurfaceHandleTypeIOSurface: {
152       IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
153       GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
154       break;
155     }
156     case kSurfaceHandleTypeCAContext: {
157       CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
158       GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
159       break;
160     }
161     default:
162       LOG(ERROR) << "Unrecognized accelerated frame type.";
163       return;
164   }
167 void AcceleratedWidgetMac::GotAcceleratedCAContextFrame(
168     CAContextID ca_context_id,
169     const gfx::Size& pixel_size,
170     float scale_factor) {
171   // In the layer is replaced, keep the old one around until after the new one
172   // is installed to avoid flashes.
173   base::scoped_nsobject<CALayerHost> old_ca_context_layer =
174       ca_context_layer_;
176   // Create the layer to host the layer exported by the GPU process with this
177   // particular CAContext ID.
178   if ([ca_context_layer_ contextId] != ca_context_id) {
179     ca_context_layer_.reset([[CALayerHost alloc] init]);
180     [ca_context_layer_ setContextId:ca_context_id];
181     [ca_context_layer_
182         setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
183     [flipped_layer_ addSublayer:ca_context_layer_];
184   }
186   // Acknowledge the frame to unblock the compositor immediately (the GPU
187   // process will do any required throttling).
188   AcknowledgeAcceleratedFrame();
190   // If this replacing a same-type layer, remove it now that the new layer is
191   // in the hierarchy.
192   if (old_ca_context_layer != ca_context_layer_)
193     DestroyCAContextLayer(old_ca_context_layer);
195   // Remove any different-type layers that this is replacing.
196   DestroyIOSurfaceLayer(io_surface_layer_);
197   DestroySoftwareLayer();
200 void AcceleratedWidgetMac::GotAcceleratedIOSurfaceFrame(
201     IOSurfaceID io_surface_id,
202     const gfx::Size& pixel_size,
203     float scale_factor) {
204   // In the layer is replaced, keep the old one around until after the new one
205   // is installed to avoid flashes.
206   base::scoped_nsobject<IOSurfaceLayer> old_io_surface_layer =
207       io_surface_layer_;
209   // Create or re-create an IOSurface layer if needed. If there already exists
210   // a layer but it has the wrong scale factor or it was poisoned, re-create the
211   // layer.
212   bool needs_new_layer =
213       !io_surface_layer_ ||
214       [io_surface_layer_ hasBeenPoisoned] ||
215       [io_surface_layer_ scaleFactor] != scale_factor;
216   if (needs_new_layer) {
217     io_surface_layer_.reset(
218         [[IOSurfaceLayer alloc] initWithClient:this
219                                withScaleFactor:scale_factor
220                        needsGLFinishWorkaround:needs_gl_finish_workaround_]);
221     if (io_surface_layer_)
222       [flipped_layer_ addSublayer:io_surface_layer_];
223     else
224       LOG(ERROR) << "Failed to create IOSurfaceLayer";
225   }
227   // Open the provided IOSurface.
228   if (io_surface_layer_) {
229     bool result = [io_surface_layer_ gotFrameWithIOSurface:io_surface_id
230                                              withPixelSize:pixel_size
231                                            withScaleFactor:scale_factor];
232     if (!result) {
233       DestroyIOSurfaceLayer(io_surface_layer_);
234       LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
235     }
236   }
238   // Give a final complaint if anything with the layer's creation went wrong.
239   // This frame will appear blank, the compositor will try to create another,
240   // and maybe that will go better.
241   if (!io_surface_layer_) {
242     LOG(ERROR) << "IOSurfaceLayer is nil, tab will be blank";
243     IOSurfaceLayerHitError();
244   }
246   // Make the CALayer draw and set its size appropriately.
247   if (io_surface_layer_) {
248     [io_surface_layer_ gotNewFrame];
250     // Set the bounds of the accelerated layer to match the size of the frame.
251     // If the bounds changed, force the content to be displayed immediately.
252     CGRect new_layer_bounds = CGRectMake(
253         0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height());
254     bool bounds_changed = !CGRectEqualToRect(
255         new_layer_bounds, [io_surface_layer_ bounds]);
256     [io_surface_layer_ setBounds:new_layer_bounds];
257     if (bounds_changed)
258       [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
259   }
261   // If this replacing a same-type layer, remove it now that the new layer is
262   // in the hierarchy.
263   if (old_io_surface_layer != io_surface_layer_)
264     DestroyIOSurfaceLayer(old_io_surface_layer);
266   // Remove any different-type layers that this is replacing.
267   DestroyCAContextLayer(ca_context_layer_);
268   DestroySoftwareLayer();
271 void AcceleratedWidgetMac::GotSoftwareFrame(float scale_factor,
272                                             SkCanvas* canvas) {
273   if (!canvas || !view_)
274     return;
276   // Disable the fade-in or fade-out effect if we create or remove layers.
277   ScopedCAActionDisabler disabler;
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   }
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.width(), info.height());
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);
296   // Remove any different-type layers that this is replacing.
297   DestroyCAContextLayer(ca_context_layer_);
298   DestroyIOSurfaceLayer(io_surface_layer_);
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();
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();
320 void AcceleratedWidgetMac::DestroySoftwareLayer() {
321   if (!software_layer_)
322     return;
323   [software_layer_ removeFromSuperlayer];
324   software_layer_.reset();
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();
335 void AcceleratedWidgetMac::IOSurfaceLayerDidDrawFrame() {
336   AcknowledgeAcceleratedFrame();
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();
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();
354   // Poison the context being used and request a mulligan.
355   [io_surface_layer_ poisonContextAndSharegroup];
357   if (view_)
358     view_->AcceleratedWidgetHitError();
361 void AcceleratedWidgetMacGotAcceleratedFrame(
362     gfx::AcceleratedWidget widget, uint64 surface_handle,
363     const std::vector<ui::LatencyInfo>& latency_info,
364     const gfx::Size& pixel_size,
365     float scale_factor,
366     const gfx::Rect& pixel_damage_rect,
367     const base::Closure& drawn_callback,
368     bool* disable_throttling, int* renderer_id) {
369   AcceleratedWidgetMac* accelerated_widget_mac =
370       GetHelperFromAcceleratedWidget(widget);
371   if (accelerated_widget_mac) {
372     accelerated_widget_mac->GotAcceleratedFrame(
373         surface_handle, latency_info, pixel_size, scale_factor,
374         pixel_damage_rect, drawn_callback);
375     *disable_throttling =
376         accelerated_widget_mac->IsRendererThrottlingDisabled();
377     *renderer_id = accelerated_widget_mac->GetRendererID();
378   } else {
379     *disable_throttling = false;
380     *renderer_id = 0;
381   }
384 void AcceleratedWidgetMacGotSoftwareFrame(
385     gfx::AcceleratedWidget widget, float scale_factor, SkCanvas* canvas) {
386   AcceleratedWidgetMac* accelerated_widget_mac =
387       GetHelperFromAcceleratedWidget(widget);
388   if (accelerated_widget_mac)
389     accelerated_widget_mac->GotSoftwareFrame(scale_factor, canvas);
392 }  // namespace ui