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"
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"
22 typedef std::map<gfx::AcceleratedWidget,AcceleratedWidgetMac*>
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())
40 ////////////////////////////////////////////////////////////////////////////////
41 // AcceleratedWidgetMac
43 AcceleratedWidgetMac::AcceleratedWidgetMac(bool needs_gl_finish_workaround)
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
52 flipped_layer_.reset([[CALayer alloc] init]);
53 [flipped_layer_ setGeometryFlipped:YES];
54 [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
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() {
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_);
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() {
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();
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];
113 bool AcceleratedWidgetMac::IsRendererThrottlingDisabled() const {
115 return view_->AcceleratedWidgetShouldIgnoreBackpressure();
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,
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.
142 AcknowledgeAcceleratedFrame();
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);
156 case kSurfaceHandleTypeCAContext: {
157 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
158 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
162 LOG(ERROR) << "Unrecognized accelerated frame type.";
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 =
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];
182 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
183 [flipped_layer_ addSublayer:ca_context_layer_];
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
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 =
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
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_];
224 LOG(ERROR) << "Failed to create IOSurfaceLayer";
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];
233 DestroyIOSurfaceLayer(io_surface_layer_);
234 LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
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();
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];
258 [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
261 // If this replacing a same-type layer, remove it now that the new layer is
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,
273 if (!canvas || !view_)
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_];
285 // Set the software layer to draw the provided canvas.
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)
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)
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_)
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.
332 return view_->AcceleratedWidgetShouldIgnoreBackpressure();
335 void AcceleratedWidgetMac::IOSurfaceLayerDidDrawFrame() {
336 AcknowledgeAcceleratedFrame();
339 void AcceleratedWidgetMac::AcknowledgeAcceleratedFrame() {
340 if (accelerated_frame_drawn_callback_.is_null())
342 accelerated_frame_drawn_callback_.Run();
343 accelerated_frame_drawn_callback_.Reset();
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];
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,
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();
379 *disable_throttling = false;
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);