Make castv2 performance test work.
[chromium-blink-merge.git] / ui / accelerated_widget_mac / accelerated_widget_mac.mm
blob82ec24840f2d6186280700797a2ccea5106b1cc7
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 uintptr_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     gfx::Size pixel_size, float scale_factor,
131     const base::Closure& drawn_callback) {
132   // Record the surface and latency info to use when acknowledging this frame.
133   DCHECK(accelerated_frame_drawn_callback_.is_null());
134   accelerated_frame_drawn_callback_ = drawn_callback;
135   accelerated_latency_info_.insert(accelerated_latency_info_.end(),
136                                    latency_info.begin(), latency_info.end());
138   // If there is no view and therefore no superview to draw into, early-out.
139   if (!view_) {
140     AcknowledgeAcceleratedFrame();
141     return;
142   }
144   // Disable the fade-in or fade-out effect if we create or remove layers.
145   ScopedCAActionDisabler disabler;
147   last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
148   switch (GetSurfaceHandleType(surface_handle)) {
149     case kSurfaceHandleTypeIOSurface: {
150       IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
151       GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
152       break;
153     }
154     case kSurfaceHandleTypeCAContext: {
155       CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
156       GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
157       break;
158     }
159     default:
160       LOG(ERROR) << "Unrecognized accelerated frame type.";
161       return;
162   }
165 void AcceleratedWidgetMac::GotAcceleratedCAContextFrame(
166     CAContextID ca_context_id,
167     gfx::Size pixel_size,
168     float scale_factor) {
169   // In the layer is replaced, keep the old one around until after the new one
170   // is installed to avoid flashes.
171   base::scoped_nsobject<CALayerHost> old_ca_context_layer =
172       ca_context_layer_;
174   // Create the layer to host the layer exported by the GPU process with this
175   // particular CAContext ID.
176   if ([ca_context_layer_ contextId] != ca_context_id) {
177     ca_context_layer_.reset([[CALayerHost alloc] init]);
178     [ca_context_layer_ setContextId:ca_context_id];
179     [ca_context_layer_
180         setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
181     [flipped_layer_ addSublayer:ca_context_layer_];
182   }
184   // Acknowledge the frame to unblock the compositor immediately (the GPU
185   // process will do any required throttling).
186   AcknowledgeAcceleratedFrame();
188   // If this replacing a same-type layer, remove it now that the new layer is
189   // in the hierarchy.
190   if (old_ca_context_layer != ca_context_layer_)
191     DestroyCAContextLayer(old_ca_context_layer);
193   // Remove any different-type layers that this is replacing.
194   DestroyIOSurfaceLayer(io_surface_layer_);
195   DestroySoftwareLayer();
198 void AcceleratedWidgetMac::GotAcceleratedIOSurfaceFrame(
199     IOSurfaceID io_surface_id,
200     gfx::Size pixel_size,
201     float scale_factor) {
202   // In the layer is replaced, keep the old one around until after the new one
203   // is installed to avoid flashes.
204   base::scoped_nsobject<IOSurfaceLayer> old_io_surface_layer =
205       io_surface_layer_;
207   // Create or re-create an IOSurface layer if needed. If there already exists
208   // a layer but it has the wrong scale factor or it was poisoned, re-create the
209   // layer.
210   bool needs_new_layer =
211       !io_surface_layer_ ||
212       [io_surface_layer_ hasBeenPoisoned] ||
213       [io_surface_layer_ scaleFactor] != scale_factor;
214   if (needs_new_layer) {
215     io_surface_layer_.reset(
216         [[IOSurfaceLayer alloc] initWithClient:this
217                                withScaleFactor:scale_factor
218                        needsGLFinishWorkaround:needs_gl_finish_workaround_]);
219     if (io_surface_layer_)
220       [flipped_layer_ addSublayer:io_surface_layer_];
221     else
222       LOG(ERROR) << "Failed to create IOSurfaceLayer";
223   }
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   }
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   }
244   // Make the CALayer draw and set its size appropriately.
245   if (io_surface_layer_) {
246     [io_surface_layer_ gotNewFrame];
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   }
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);
264   // Remove any different-type layers that this is replacing.
265   DestroyCAContextLayer(ca_context_layer_);
266   DestroySoftwareLayer();
269 void AcceleratedWidgetMac::GotSoftwareFrame(float scale_factor,
270                                             SkCanvas* canvas) {
271   if (!canvas || !view_)
272     return;
274   // Disable the fade-in or fade-out effect if we create or remove layers.
275   ScopedCAActionDisabler disabler;
277   // If there is not a layer for software frames, create one.
278   if (!software_layer_) {
279     software_layer_.reset([[SoftwareLayer alloc] init]);
280     [flipped_layer_ addSublayer:software_layer_];
281   }
283   // Set the software layer to draw the provided canvas.
284   SkImageInfo info;
285   size_t row_bytes;
286   const void* pixels = canvas->peekPixels(&info, &row_bytes);
287   gfx::Size pixel_size(info.fWidth, info.fHeight);
288   [software_layer_ setContentsToData:pixels
289                         withRowBytes:row_bytes
290                        withPixelSize:pixel_size
291                      withScaleFactor:scale_factor];
292   last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
294   // Remove any different-type layers that this is replacing.
295   DestroyCAContextLayer(ca_context_layer_);
296   DestroyIOSurfaceLayer(io_surface_layer_);
299 void AcceleratedWidgetMac::DestroyCAContextLayer(
300     base::scoped_nsobject<CALayerHost> ca_context_layer) {
301   if (!ca_context_layer)
302     return;
303   [ca_context_layer removeFromSuperlayer];
304   if (ca_context_layer == ca_context_layer_)
305     ca_context_layer_.reset();
308 void AcceleratedWidgetMac::DestroyIOSurfaceLayer(
309     base::scoped_nsobject<IOSurfaceLayer> io_surface_layer) {
310   if (!io_surface_layer)
311     return;
312   [io_surface_layer resetClient];
313   [io_surface_layer removeFromSuperlayer];
314   if (io_surface_layer == io_surface_layer_)
315     io_surface_layer_.reset();
318 void AcceleratedWidgetMac::DestroySoftwareLayer() {
319   if (!software_layer_)
320     return;
321   [software_layer_ removeFromSuperlayer];
322   software_layer_.reset();
325 bool AcceleratedWidgetMac::IOSurfaceLayerShouldAckImmediately() const {
326   // If there is no view then the accelerated layer is not in the view
327   // hierarchy and will never draw.
328   if (!view_)
329     return true;
330   return view_->AcceleratedWidgetShouldIgnoreBackpressure();
333 void AcceleratedWidgetMac::IOSurfaceLayerDidDrawFrame() {
334   AcknowledgeAcceleratedFrame();
337 void AcceleratedWidgetMac::AcknowledgeAcceleratedFrame() {
338   if (accelerated_frame_drawn_callback_.is_null())
339     return;
340   accelerated_frame_drawn_callback_.Run();
341   accelerated_frame_drawn_callback_.Reset();
342   if (view_)
343     view_->AcceleratedWidgetSwapCompleted(accelerated_latency_info_);
344   accelerated_latency_info_.clear();
347 void AcceleratedWidgetMac::IOSurfaceLayerHitError() {
348   // Perform all acks that would have been done if the frame had succeeded, to
349   // un-block the compositor and renderer.
350   AcknowledgeAcceleratedFrame();
352   // Poison the context being used and request a mulligan.
353   [io_surface_layer_ poisonContextAndSharegroup];
355   if (view_)
356     view_->AcceleratedWidgetHitError();
359 void AcceleratedWidgetMacGotAcceleratedFrame(
360     gfx::AcceleratedWidget widget, uint64 surface_handle,
361     const std::vector<ui::LatencyInfo>& latency_info,
362     gfx::Size pixel_size, float scale_factor,
363     const base::Closure& drawn_callback,
364     bool* disable_throttling, int* renderer_id) {
365   AcceleratedWidgetMac* accelerated_widget_mac =
366       GetHelperFromAcceleratedWidget(widget);
367   if (accelerated_widget_mac) {
368     accelerated_widget_mac->GotAcceleratedFrame(
369         surface_handle, latency_info, pixel_size, scale_factor, drawn_callback);
370     *disable_throttling =
371         accelerated_widget_mac->IsRendererThrottlingDisabled();
372     *renderer_id = accelerated_widget_mac->GetRendererID();
373   } else {
374     *disable_throttling = false;
375     *renderer_id = 0;
376   }
379 void AcceleratedWidgetMacGotSoftwareFrame(
380     gfx::AcceleratedWidget widget, float scale_factor, SkCanvas* canvas) {
381   AcceleratedWidgetMac* accelerated_widget_mac =
382       GetHelperFromAcceleratedWidget(widget);
383   if (accelerated_widget_mac)
384     accelerated_widget_mac->GotSoftwareFrame(scale_factor, canvas);
387 }  // namespace ui