Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / accelerated_widget_mac / accelerated_widget_mac.mm
blob2d31cdac759c9f3f7f31335cbcb769aab0e53744
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 void AcceleratedWidgetMac::GetVSyncParameters(
114     base::TimeTicks* timebase, base::TimeDelta* interval) const {
115   if (view_) {
116     view_->AcceleratedWidgetGetVSyncParameters(timebase, interval);
117   } else {
118     *timebase = base::TimeTicks();
119     *interval = base::TimeDelta();
120   }
123 bool AcceleratedWidgetMac::IsRendererThrottlingDisabled() const {
124   if (view_)
125     return view_->AcceleratedWidgetShouldIgnoreBackpressure();
126   return false;
129 void AcceleratedWidgetMac::BeginPumpingFrames() {
130   [io_surface_layer_ beginPumpingFrames];
133 void AcceleratedWidgetMac::EndPumpingFrames() {
134   [io_surface_layer_ endPumpingFrames];
137 void AcceleratedWidgetMac::GotAcceleratedFrame(
138     uint64 surface_handle,
139     const std::vector<ui::LatencyInfo>& latency_info,
140     const gfx::Size& pixel_size,
141     float scale_factor,
142     const gfx::Rect& pixel_damage_rect,
143     const base::Closure& drawn_callback) {
144   // Record the surface and latency info to use when acknowledging this frame.
145   DCHECK(accelerated_frame_drawn_callback_.is_null());
146   accelerated_frame_drawn_callback_ = drawn_callback;
147   accelerated_latency_info_.insert(accelerated_latency_info_.end(),
148                                    latency_info.begin(), latency_info.end());
150   // If there is no view and therefore no superview to draw into, early-out.
151   if (!view_) {
152     AcknowledgeAcceleratedFrame();
153     return;
154   }
156   // Disable the fade-in or fade-out effect if we create or remove layers.
157   ScopedCAActionDisabler disabler;
159   last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
160   switch (GetSurfaceHandleType(surface_handle)) {
161     case kSurfaceHandleTypeIOSurface: {
162       IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
163       GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
164       break;
165     }
166     case kSurfaceHandleTypeCAContext: {
167       CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
168       GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
169       break;
170     }
171     default:
172       LOG(ERROR) << "Unrecognized accelerated frame type.";
173       return;
174   }
177 void AcceleratedWidgetMac::GotAcceleratedCAContextFrame(
178     CAContextID ca_context_id,
179     const gfx::Size& pixel_size,
180     float scale_factor) {
181   // In the layer is replaced, keep the old one around until after the new one
182   // is installed to avoid flashes.
183   base::scoped_nsobject<CALayerHost> old_ca_context_layer =
184       ca_context_layer_;
186   // Create the layer to host the layer exported by the GPU process with this
187   // particular CAContext ID.
188   if ([ca_context_layer_ contextId] != ca_context_id) {
189     ca_context_layer_.reset([[CALayerHost alloc] init]);
190     [ca_context_layer_ setContextId:ca_context_id];
191     [ca_context_layer_
192         setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
193     [flipped_layer_ addSublayer:ca_context_layer_];
194   }
196   // Acknowledge the frame to unblock the compositor immediately (the GPU
197   // process will do any required throttling).
198   AcknowledgeAcceleratedFrame();
200   // If this replacing a same-type layer, remove it now that the new layer is
201   // in the hierarchy.
202   if (old_ca_context_layer != ca_context_layer_)
203     DestroyCAContextLayer(old_ca_context_layer);
205   // Remove any different-type layers that this is replacing.
206   DestroyIOSurfaceLayer(io_surface_layer_);
207   DestroySoftwareLayer();
210 void AcceleratedWidgetMac::GotAcceleratedIOSurfaceFrame(
211     IOSurfaceID io_surface_id,
212     const gfx::Size& pixel_size,
213     float scale_factor) {
214   // In the layer is replaced, keep the old one around until after the new one
215   // is installed to avoid flashes.
216   base::scoped_nsobject<IOSurfaceLayer> old_io_surface_layer =
217       io_surface_layer_;
219   // Create or re-create an IOSurface layer if needed. If there already exists
220   // a layer but it has the wrong scale factor or it was poisoned, re-create the
221   // layer.
222   bool needs_new_layer =
223       !io_surface_layer_ ||
224       [io_surface_layer_ hasBeenPoisoned] ||
225       [io_surface_layer_ scaleFactor] != scale_factor;
226   if (needs_new_layer) {
227     io_surface_layer_.reset(
228         [[IOSurfaceLayer alloc] initWithClient:this
229                                withScaleFactor:scale_factor
230                        needsGLFinishWorkaround:needs_gl_finish_workaround_]);
231     if (io_surface_layer_)
232       [flipped_layer_ addSublayer:io_surface_layer_];
233     else
234       LOG(ERROR) << "Failed to create IOSurfaceLayer";
235   }
237   // Open the provided IOSurface.
238   if (io_surface_layer_) {
239     bool result = [io_surface_layer_ gotFrameWithIOSurface:io_surface_id
240                                              withPixelSize:pixel_size
241                                            withScaleFactor:scale_factor];
242     if (!result) {
243       DestroyIOSurfaceLayer(io_surface_layer_);
244       LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
245     }
246   }
248   // Give a final complaint if anything with the layer's creation went wrong.
249   // This frame will appear blank, the compositor will try to create another,
250   // and maybe that will go better.
251   if (!io_surface_layer_) {
252     LOG(ERROR) << "IOSurfaceLayer is nil, tab will be blank";
253     IOSurfaceLayerHitError();
254   }
256   // Make the CALayer draw and set its size appropriately.
257   if (io_surface_layer_) {
258     [io_surface_layer_ gotNewFrame];
260     // Set the bounds of the accelerated layer to match the size of the frame.
261     // If the bounds changed, force the content to be displayed immediately.
262     CGRect new_layer_bounds = CGRectMake(
263         0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height());
264     bool bounds_changed = !CGRectEqualToRect(
265         new_layer_bounds, [io_surface_layer_ bounds]);
266     [io_surface_layer_ setBounds:new_layer_bounds];
267     if (bounds_changed)
268       [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
269   }
271   // If this replacing a same-type layer, remove it now that the new layer is
272   // in the hierarchy.
273   if (old_io_surface_layer != io_surface_layer_)
274     DestroyIOSurfaceLayer(old_io_surface_layer);
276   // Remove any different-type layers that this is replacing.
277   DestroyCAContextLayer(ca_context_layer_);
278   DestroySoftwareLayer();
281 void AcceleratedWidgetMac::GotSoftwareFrame(float scale_factor,
282                                             SkCanvas* canvas) {
283   if (!canvas || !view_)
284     return;
286   // Disable the fade-in or fade-out effect if we create or remove layers.
287   ScopedCAActionDisabler disabler;
289   // If there is not a layer for software frames, create one.
290   if (!software_layer_) {
291     software_layer_.reset([[SoftwareLayer alloc] init]);
292     [flipped_layer_ addSublayer:software_layer_];
293   }
295   // Set the software layer to draw the provided canvas.
296   SkImageInfo info;
297   size_t row_bytes;
298   const void* pixels = canvas->peekPixels(&info, &row_bytes);
299   gfx::Size pixel_size(info.width(), info.height());
300   [software_layer_ setContentsToData:pixels
301                         withRowBytes:row_bytes
302                        withPixelSize:pixel_size
303                      withScaleFactor:scale_factor];
304   last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
306   // Remove any different-type layers that this is replacing.
307   DestroyCAContextLayer(ca_context_layer_);
308   DestroyIOSurfaceLayer(io_surface_layer_);
311 void AcceleratedWidgetMac::DestroyCAContextLayer(
312     base::scoped_nsobject<CALayerHost> ca_context_layer) {
313   if (!ca_context_layer)
314     return;
315   [ca_context_layer removeFromSuperlayer];
316   if (ca_context_layer == ca_context_layer_)
317     ca_context_layer_.reset();
320 void AcceleratedWidgetMac::DestroyIOSurfaceLayer(
321     base::scoped_nsobject<IOSurfaceLayer> io_surface_layer) {
322   if (!io_surface_layer)
323     return;
324   [io_surface_layer resetClient];
325   [io_surface_layer removeFromSuperlayer];
326   if (io_surface_layer == io_surface_layer_)
327     io_surface_layer_.reset();
330 void AcceleratedWidgetMac::DestroySoftwareLayer() {
331   if (!software_layer_)
332     return;
333   [software_layer_ removeFromSuperlayer];
334   software_layer_.reset();
337 bool AcceleratedWidgetMac::IOSurfaceLayerShouldAckImmediately() const {
338   // If there is no view then the accelerated layer is not in the view
339   // hierarchy and will never draw.
340   if (!view_)
341     return true;
342   return view_->AcceleratedWidgetShouldIgnoreBackpressure();
345 void AcceleratedWidgetMac::IOSurfaceLayerDidDrawFrame() {
346   AcknowledgeAcceleratedFrame();
349 void AcceleratedWidgetMac::AcknowledgeAcceleratedFrame() {
350   if (accelerated_frame_drawn_callback_.is_null())
351     return;
352   accelerated_frame_drawn_callback_.Run();
353   accelerated_frame_drawn_callback_.Reset();
354   if (view_)
355     view_->AcceleratedWidgetSwapCompleted(accelerated_latency_info_);
356   accelerated_latency_info_.clear();
359 void AcceleratedWidgetMac::IOSurfaceLayerHitError() {
360   // Perform all acks that would have been done if the frame had succeeded, to
361   // un-block the compositor and renderer.
362   AcknowledgeAcceleratedFrame();
364   // Poison the context being used and request a mulligan.
365   [io_surface_layer_ poisonContextAndSharegroup];
367   if (view_)
368     view_->AcceleratedWidgetHitError();
371 void AcceleratedWidgetMacGotAcceleratedFrame(
372     gfx::AcceleratedWidget widget, uint64 surface_handle,
373     const std::vector<ui::LatencyInfo>& latency_info,
374     const gfx::Size& pixel_size,
375     float scale_factor,
376     const gfx::Rect& pixel_damage_rect,
377     const base::Closure& drawn_callback,
378     bool* disable_throttling, int* renderer_id,
379     base::TimeTicks* vsync_timebase, base::TimeDelta* vsync_interval) {
380   AcceleratedWidgetMac* accelerated_widget_mac =
381       GetHelperFromAcceleratedWidget(widget);
382   if (accelerated_widget_mac) {
383     accelerated_widget_mac->GotAcceleratedFrame(
384         surface_handle, latency_info, pixel_size, scale_factor,
385         pixel_damage_rect, drawn_callback);
386     *disable_throttling =
387         accelerated_widget_mac->IsRendererThrottlingDisabled();
388     *renderer_id = accelerated_widget_mac->GetRendererID();
389     accelerated_widget_mac->GetVSyncParameters(vsync_timebase, vsync_interval);
390   } else {
391     *disable_throttling = false;
392     *renderer_id = 0;
393     *vsync_timebase = base::TimeTicks();
394     *vsync_interval = base::TimeDelta();
395   }
398 void AcceleratedWidgetMacGotSoftwareFrame(
399     gfx::AcceleratedWidget widget, float scale_factor, SkCanvas* canvas) {
400   AcceleratedWidgetMac* accelerated_widget_mac =
401       GetHelperFromAcceleratedWidget(widget);
402   if (accelerated_widget_mac)
403     accelerated_widget_mac->GotSoftwareFrame(scale_factor, canvas);
406 }  // namespace ui