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 "content/browser/compositor/browser_compositor_ca_layer_tree_mac.h"
9 #include "cc/output/software_frame_data.h"
10 #include "base/debug/trace_event.h"
11 #include "base/lazy_instance.h"
12 #include "base/message_loop/message_loop.h"
13 #include "content/browser/compositor/io_surface_layer_mac.h"
14 #include "content/browser/compositor/software_layer_mac.h"
15 #include "content/browser/renderer_host/dip_util.h"
16 #include "content/common/gpu/surface_handle_types_mac.h"
17 #include "content/public/browser/context_factory.h"
18 #include "ui/base/cocoa/animation_utils.h"
19 #include "ui/gl/scoped_cgl.h"
24 typedef std::map<gfx::AcceleratedWidget,AcceleratedWidgetMac*>
26 base::LazyInstance<WidgetToHelperMap> g_widget_to_helper_map;
28 AcceleratedWidgetMac* GetHelperFromAcceleratedWidget(
29 gfx::AcceleratedWidget widget) {
30 WidgetToHelperMap::const_iterator found =
31 g_widget_to_helper_map.Pointer()->find(widget);
32 // This can end up being accessed after the underlying widget has been
33 // destroyed, but while the ui::Compositor is still being destroyed.
34 // Return NULL in these cases.
35 if (found == g_widget_to_helper_map.Pointer()->end())
42 ////////////////////////////////////////////////////////////////////////////////
43 // AcceleratedWidgetMac
45 AcceleratedWidgetMac::AcceleratedWidgetMac()
47 // Disable the fade-in animation as the layers are added.
48 ScopedCAActionDisabler disabler;
50 // Add a flipped transparent layer as a child, so that we don't need to
51 // fiddle with the position of sub-layers -- they will always be at the
53 flipped_layer_.reset([[CALayer alloc] init]);
54 [flipped_layer_ setGeometryFlipped:YES];
55 [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
57 setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
59 // Use a sequence number as the accelerated widget handle that we can use
60 // to look up the internals structure.
61 static uintptr_t last_sequence_number = 0;
62 last_sequence_number += 1;
63 native_widget_ = reinterpret_cast<gfx::AcceleratedWidget>(
64 last_sequence_number);
65 g_widget_to_helper_map.Pointer()->insert(
66 std::make_pair(native_widget_, this));
69 AcceleratedWidgetMac::~AcceleratedWidgetMac() {
71 g_widget_to_helper_map.Pointer()->erase(native_widget_);
74 void AcceleratedWidgetMac::SetNSView(AcceleratedWidgetMacNSView* view) {
75 // Disable the fade-in animation as the view is added.
76 ScopedCAActionDisabler disabler;
78 DCHECK(view && !view_);
81 CALayer* background_layer = [view_->AcceleratedWidgetGetNSView() layer];
82 DCHECK(background_layer);
83 [flipped_layer_ setBounds:[background_layer bounds]];
84 [background_layer addSublayer:flipped_layer_];
87 void AcceleratedWidgetMac::ResetNSView() {
91 // Disable the fade-out animation as the view is removed.
92 ScopedCAActionDisabler disabler;
94 [flipped_layer_ removeFromSuperlayer];
95 DestroyIOSurfaceLayer(io_surface_layer_);
96 DestroyCAContextLayer(ca_context_layer_);
97 DestroySoftwareLayer();
99 last_swap_size_dip_ = gfx::Size();
103 bool AcceleratedWidgetMac::HasFrameOfSize(
104 const gfx::Size& dip_size) const {
105 return last_swap_size_dip_ == dip_size;
108 int AcceleratedWidgetMac::GetRendererID() const {
109 if (io_surface_layer_)
110 return [io_surface_layer_ rendererID];
114 bool AcceleratedWidgetMac::IsRendererThrottlingDisabled() const {
116 return view_->AcceleratedWidgetShouldIgnoreBackpressure();
120 void AcceleratedWidgetMac::BeginPumpingFrames() {
121 [io_surface_layer_ beginPumpingFrames];
124 void AcceleratedWidgetMac::EndPumpingFrames() {
125 [io_surface_layer_ endPumpingFrames];
128 void AcceleratedWidgetMac::GotAcceleratedFrame(
129 uint64 surface_handle,
130 const std::vector<ui::LatencyInfo>& latency_info,
131 gfx::Size pixel_size, float scale_factor,
132 const base::Closure& drawn_callback) {
133 // Record the surface and latency info to use when acknowledging this frame.
134 DCHECK(accelerated_frame_drawn_callback_.is_null());
135 accelerated_frame_drawn_callback_ = drawn_callback;
136 accelerated_latency_info_.insert(accelerated_latency_info_.end(),
137 latency_info.begin(), latency_info.end());
139 // If there is no view and therefore no superview to draw into, early-out.
141 AcknowledgeAcceleratedFrame();
145 // Disable the fade-in or fade-out effect if we create or remove layers.
146 ScopedCAActionDisabler disabler;
148 last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size);
149 switch (GetSurfaceHandleType(surface_handle)) {
150 case kSurfaceHandleTypeIOSurface: {
151 IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
152 GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
155 case kSurfaceHandleTypeCAContext: {
156 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
157 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
161 LOG(ERROR) << "Unrecognized accelerated frame type.";
166 void AcceleratedWidgetMac::GotAcceleratedCAContextFrame(
167 CAContextID ca_context_id,
168 gfx::Size pixel_size,
169 float scale_factor) {
170 // In the layer is replaced, keep the old one around until after the new one
171 // is installed to avoid flashes.
172 base::scoped_nsobject<CALayerHost> old_ca_context_layer =
175 // Create the layer to host the layer exported by the GPU process with this
176 // particular CAContext ID.
177 if ([ca_context_layer_ contextId] != ca_context_id) {
178 ca_context_layer_.reset([[CALayerHost alloc] init]);
179 [ca_context_layer_ setContextId:ca_context_id];
181 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
182 [flipped_layer_ addSublayer:ca_context_layer_];
185 // Acknowledge the frame to unblock the compositor immediately (the GPU
186 // process will do any required throttling).
187 AcknowledgeAcceleratedFrame();
189 // If this replacing a same-type layer, remove it now that the new layer is
191 if (old_ca_context_layer != ca_context_layer_)
192 DestroyCAContextLayer(old_ca_context_layer);
194 // Remove any different-type layers that this is replacing.
195 DestroyIOSurfaceLayer(io_surface_layer_);
196 DestroySoftwareLayer();
199 void AcceleratedWidgetMac::GotAcceleratedIOSurfaceFrame(
200 IOSurfaceID io_surface_id,
201 gfx::Size pixel_size,
202 float scale_factor) {
203 // In the layer is replaced, keep the old one around until after the new one
204 // is installed to avoid flashes.
205 base::scoped_nsobject<IOSurfaceLayer> old_io_surface_layer =
208 // Create or re-create an IOSurface layer if needed. If there already exists
209 // a layer but it has the wrong scale factor or it was poisoned, re-create the
211 bool needs_new_layer =
212 !io_surface_layer_ ||
213 [io_surface_layer_ hasBeenPoisoned] ||
214 [io_surface_layer_ scaleFactor] != scale_factor;
215 if (needs_new_layer) {
216 io_surface_layer_.reset(
217 [[IOSurfaceLayer alloc] initWithClient:this
218 withScaleFactor:scale_factor]);
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(
270 cc::SoftwareFrameData* frame_data,
273 if (!frame_data || !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.fWidth, info.fHeight);
290 [software_layer_ setContentsToData:pixels
291 withRowBytes:row_bytes
292 withPixelSize:pixel_size
293 withScaleFactor:scale_factor];
294 last_swap_size_dip_ = 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 gfx::Size pixel_size, float scale_factor,
365 const base::Closure& drawn_callback,
366 bool* disable_throttling, int* renderer_id) {
367 AcceleratedWidgetMac* accelerated_widget_mac =
368 GetHelperFromAcceleratedWidget(widget);
369 if (accelerated_widget_mac) {
370 accelerated_widget_mac->GotAcceleratedFrame(
371 surface_handle, latency_info, pixel_size, scale_factor, drawn_callback);
372 *disable_throttling =
373 accelerated_widget_mac->IsRendererThrottlingDisabled();
374 *renderer_id = accelerated_widget_mac->GetRendererID();
376 *disable_throttling = false;
381 void AcceleratedWidgetMacGotSoftwareFrame(
382 gfx::AcceleratedWidget widget,
383 cc::SoftwareFrameData* frame_data, float scale_factor, SkCanvas* canvas) {
384 AcceleratedWidgetMac* accelerated_widget_mac =
385 GetHelperFromAcceleratedWidget(widget);
386 if (accelerated_widget_mac)
387 accelerated_widget_mac->GotSoftwareFrame(frame_data, scale_factor, canvas);
390 } // namespace content