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/debug/trace_event.h"
10 #include "base/lazy_instance.h"
11 #include "base/message_loop/message_loop.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 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.
140 AcknowledgeAcceleratedFrame();
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);
154 case kSurfaceHandleTypeCAContext: {
155 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
156 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
160 LOG(ERROR) << "Unrecognized accelerated frame type.";
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 =
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];
180 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
181 [flipped_layer_ addSublayer:ca_context_layer_];
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
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 =
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
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_];
222 LOG(ERROR) << "Failed to create IOSurfaceLayer";
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];
231 DestroyIOSurfaceLayer(io_surface_layer_);
232 LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
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();
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];
256 [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
259 // If this replacing a same-type layer, remove it now that the new layer is
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,
271 if (!canvas || !view_)
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_];
283 // Set the software layer to draw the provided canvas.
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)
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)
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_)
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.
330 return view_->AcceleratedWidgetShouldIgnoreBackpressure();
333 void AcceleratedWidgetMac::IOSurfaceLayerDidDrawFrame() {
334 AcknowledgeAcceleratedFrame();
337 void AcceleratedWidgetMac::AcknowledgeAcceleratedFrame() {
338 if (accelerated_frame_drawn_callback_.is_null())
340 accelerated_frame_drawn_callback_.Run();
341 accelerated_frame_drawn_callback_.Reset();
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];
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();
374 *disable_throttling = false;
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);