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 "base/debug/trace_event.h"
10 #include "base/lazy_instance.h"
11 #include "base/message_loop/message_loop.h"
12 #include "content/browser/compositor/gpu_process_transport_factory.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/browser/renderer_host/render_widget_resize_helper.h"
17 #include "content/common/gpu/surface_handle_types_mac.h"
18 #include "content/public/browser/context_factory.h"
19 #include "ui/base/cocoa/animation_utils.h"
20 #include "ui/gl/scoped_cgl.h"
25 typedef std::map<gfx::AcceleratedWidget,BrowserCompositorCALayerTreeMac*>
27 base::LazyInstance<WidgetToInternalsMap> g_widget_to_internals_map;
31 ////////////////////////////////////////////////////////////////////////////////
32 // BrowserCompositorCALayerTreeMac
34 BrowserCompositorCALayerTreeMac::BrowserCompositorCALayerTreeMac()
36 accelerated_output_surface_id_(0) {
37 // Disable the fade-in animation as the layers are added.
38 ScopedCAActionDisabler disabler;
40 // Add a flipped transparent layer as a child, so that we don't need to
41 // fiddle with the position of sub-layers -- they will always be at the
43 flipped_layer_.reset([[CALayer alloc] init]);
44 [flipped_layer_ setGeometryFlipped:YES];
45 [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
47 setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
49 // Use a sequence number as the accelerated widget handle that we can use
50 // to look up the internals structure.
51 static uintptr_t last_sequence_number = 0;
52 last_sequence_number += 1;
53 native_widget_ = reinterpret_cast<gfx::AcceleratedWidget>(
54 last_sequence_number);
55 g_widget_to_internals_map.Pointer()->insert(
56 std::make_pair(native_widget_, this));
58 // Create a compositor to draw the contents of this view.
59 compositor_.reset(new ui::Compositor(
61 content::GetContextFactory(),
62 RenderWidgetResizeHelper::Get()->task_runner()));
63 compositor_->SetVisible(false);
66 BrowserCompositorCALayerTreeMac::~BrowserCompositorCALayerTreeMac() {
68 g_widget_to_internals_map.Pointer()->erase(native_widget_);
71 void BrowserCompositorCALayerTreeMac::SetView(
72 BrowserCompositorViewMac* view) {
73 // Disable the fade-in animation as the view is added.
74 ScopedCAActionDisabler disabler;
76 DCHECK(view && !view_);
78 compositor_->SetRootLayer(view_->ui_root_layer());
80 CALayer* background_layer = [view_->native_view() layer];
81 DCHECK(background_layer);
82 [flipped_layer_ setBounds:[background_layer bounds]];
83 [background_layer addSublayer:flipped_layer_];
84 compositor_->SetVisible(true);
87 void BrowserCompositorCALayerTreeMac::ResetView() {
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 accelerated_output_surface_id_ = 0;
100 last_swap_size_dip_ = gfx::Size();
102 compositor_->SetVisible(false);
103 compositor_->SetScaleAndSize(1.0, gfx::Size(0, 0));
104 compositor_->SetRootLayer(NULL);
108 bool BrowserCompositorCALayerTreeMac::HasFrameOfSize(
109 const gfx::Size& dip_size) const {
110 return last_swap_size_dip_ == dip_size;
113 int BrowserCompositorCALayerTreeMac::GetRendererID() const {
114 if (io_surface_layer_)
115 return [io_surface_layer_ rendererID];
119 bool BrowserCompositorCALayerTreeMac::IsRendererThrottlingDisabled() const {
121 return view_->client()->BrowserCompositorViewShouldAckImmediately();
125 void BrowserCompositorCALayerTreeMac::BeginPumpingFrames() {
126 [io_surface_layer_ beginPumpingFrames];
129 void BrowserCompositorCALayerTreeMac::EndPumpingFrames() {
130 [io_surface_layer_ endPumpingFrames];
133 void BrowserCompositorCALayerTreeMac::GotAcceleratedFrame(
134 uint64 surface_handle, int output_surface_id,
135 const std::vector<ui::LatencyInfo>& latency_info,
136 gfx::Size pixel_size, float scale_factor) {
137 // Record the surface and latency info to use when acknowledging this frame.
138 DCHECK(!accelerated_output_surface_id_);
139 accelerated_output_surface_id_ = output_surface_id;
140 accelerated_latency_info_.insert(accelerated_latency_info_.end(),
141 latency_info.begin(), latency_info.end());
143 // If there is no view and therefore no superview to draw into, early-out.
145 IOSurfaceLayerDidDrawFrame();
149 // Disable the fade-in or fade-out effect if we create or remove layers.
150 ScopedCAActionDisabler disabler;
152 last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size);
153 switch (GetSurfaceHandleType(surface_handle)) {
154 case kSurfaceHandleTypeIOSurface: {
155 IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
156 GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
159 case kSurfaceHandleTypeCAContext: {
160 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
161 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
165 LOG(ERROR) << "Unrecognized accelerated frame type.";
170 void BrowserCompositorCALayerTreeMac::GotAcceleratedCAContextFrame(
171 CAContextID ca_context_id,
172 gfx::Size pixel_size,
173 float scale_factor) {
174 // In the layer is replaced, keep the old one around until after the new one
175 // is installed to avoid flashes.
176 base::scoped_nsobject<CALayerHost> old_ca_context_layer =
179 // Create the layer to host the layer exported by the GPU process with this
180 // particular CAContext ID.
181 if ([ca_context_layer_ contextId] != ca_context_id) {
182 ca_context_layer_.reset([[CALayerHost alloc] init]);
183 [ca_context_layer_ setContextId:ca_context_id];
185 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
186 [flipped_layer_ addSublayer:ca_context_layer_];
189 // Acknowledge the frame to unblock the compositor immediately (the GPU
190 // process will do any required throttling).
191 IOSurfaceLayerDidDrawFrame();
193 // If this replacing a same-type layer, remove it now that the new layer is
195 if (old_ca_context_layer != ca_context_layer_)
196 DestroyCAContextLayer(old_ca_context_layer);
198 // Remove any different-type layers that this is replacing.
199 DestroyIOSurfaceLayer(io_surface_layer_);
200 DestroySoftwareLayer();
203 void BrowserCompositorCALayerTreeMac::GotAcceleratedIOSurfaceFrame(
204 IOSurfaceID io_surface_id,
205 gfx::Size pixel_size,
206 float scale_factor) {
207 // In the layer is replaced, keep the old one around until after the new one
208 // is installed to avoid flashes.
209 base::scoped_nsobject<IOSurfaceLayer> old_io_surface_layer =
212 // Create or re-create an IOSurface layer if needed. If there already exists
213 // a layer but it has the wrong scale factor or it was poisoned, re-create the
215 bool needs_new_layer =
216 !io_surface_layer_ ||
217 [io_surface_layer_ hasBeenPoisoned] ||
218 [io_surface_layer_ scaleFactor] != scale_factor;
219 if (needs_new_layer) {
220 io_surface_layer_.reset(
221 [[IOSurfaceLayer alloc] initWithClient:this
222 withScaleFactor:scale_factor]);
223 if (io_surface_layer_)
224 [flipped_layer_ addSublayer:io_surface_layer_];
226 LOG(ERROR) << "Failed to create IOSurfaceLayer";
229 // Open the provided IOSurface.
230 if (io_surface_layer_) {
231 bool result = [io_surface_layer_ gotFrameWithIOSurface:io_surface_id
232 withPixelSize:pixel_size
233 withScaleFactor:scale_factor];
235 DestroyIOSurfaceLayer(io_surface_layer_);
236 LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
240 // Give a final complaint if anything with the layer's creation went wrong.
241 // This frame will appear blank, the compositor will try to create another,
242 // and maybe that will go better.
243 if (!io_surface_layer_) {
244 LOG(ERROR) << "IOSurfaceLayer is nil, tab will be blank";
245 IOSurfaceLayerHitError();
248 // Make the CALayer draw and set its size appropriately.
249 if (io_surface_layer_) {
250 [io_surface_layer_ gotNewFrame];
252 // Set the bounds of the accelerated layer to match the size of the frame.
253 // If the bounds changed, force the content to be displayed immediately.
254 CGRect new_layer_bounds = CGRectMake(
255 0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height());
256 bool bounds_changed = !CGRectEqualToRect(
257 new_layer_bounds, [io_surface_layer_ bounds]);
258 [io_surface_layer_ setBounds:new_layer_bounds];
260 [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
263 // If this replacing a same-type layer, remove it now that the new layer is
265 if (old_io_surface_layer != io_surface_layer_)
266 DestroyIOSurfaceLayer(old_io_surface_layer);
268 // Remove any different-type layers that this is replacing.
269 DestroyCAContextLayer(ca_context_layer_);
270 DestroySoftwareLayer();
273 void BrowserCompositorCALayerTreeMac::GotSoftwareFrame(
274 cc::SoftwareFrameData* frame_data,
277 if (!frame_data || !canvas || !view_)
280 // Disable the fade-in or fade-out effect if we create or remove layers.
281 ScopedCAActionDisabler disabler;
283 // If there is not a layer for software frames, create one.
284 if (!software_layer_) {
285 software_layer_.reset([[SoftwareLayer alloc] init]);
286 [flipped_layer_ addSublayer:software_layer_];
289 // Set the software layer to draw the provided canvas.
292 const void* pixels = canvas->peekPixels(&info, &row_bytes);
293 gfx::Size pixel_size(info.fWidth, info.fHeight);
294 [software_layer_ setContentsToData:pixels
295 withRowBytes:row_bytes
296 withPixelSize:pixel_size
297 withScaleFactor:scale_factor];
298 last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size);
300 // Remove any different-type layers that this is replacing.
301 DestroyCAContextLayer(ca_context_layer_);
302 DestroyIOSurfaceLayer(io_surface_layer_);
305 void BrowserCompositorCALayerTreeMac::DestroyCAContextLayer(
306 base::scoped_nsobject<CALayerHost> ca_context_layer) {
307 if (!ca_context_layer)
309 [ca_context_layer removeFromSuperlayer];
310 if (ca_context_layer == ca_context_layer_)
311 ca_context_layer_.reset();
314 void BrowserCompositorCALayerTreeMac::DestroyIOSurfaceLayer(
315 base::scoped_nsobject<IOSurfaceLayer> io_surface_layer) {
316 if (!io_surface_layer)
318 [io_surface_layer resetClient];
319 [io_surface_layer removeFromSuperlayer];
320 if (io_surface_layer == io_surface_layer_)
321 io_surface_layer_.reset();
324 void BrowserCompositorCALayerTreeMac::DestroySoftwareLayer() {
325 if (!software_layer_)
327 [software_layer_ removeFromSuperlayer];
328 software_layer_.reset();
331 bool BrowserCompositorCALayerTreeMac::IOSurfaceLayerShouldAckImmediately()
333 // If there is no view then the accelerated layer is not in the hierarchy
334 // and will never draw.
337 return view_->client()->BrowserCompositorViewShouldAckImmediately();
340 void BrowserCompositorCALayerTreeMac::IOSurfaceLayerDidDrawFrame() {
341 if (accelerated_output_surface_id_) {
342 content::ImageTransportFactory::GetInstance()->OnSurfaceDisplayed(
343 accelerated_output_surface_id_);
344 accelerated_output_surface_id_ = 0;
348 view_->client()->BrowserCompositorViewFrameSwapped(
349 accelerated_latency_info_);
352 accelerated_latency_info_.clear();
355 void BrowserCompositorCALayerTreeMac::IOSurfaceLayerHitError() {
356 // Perform all acks that would have been done if the frame had succeeded, to
357 // un-block the compositor and renderer.
358 IOSurfaceLayerDidDrawFrame();
360 // Poison the context being used and request a mulligan.
361 [io_surface_layer_ poisonContextAndSharegroup];
362 compositor_->ScheduleFullRedraw();
366 BrowserCompositorCALayerTreeMac* BrowserCompositorCALayerTreeMac::
367 FromAcceleratedWidget(gfx::AcceleratedWidget widget) {
368 WidgetToInternalsMap::const_iterator found =
369 g_widget_to_internals_map.Pointer()->find(widget);
370 // This can end up being accessed after the underlying widget has been
371 // destroyed, but while the ui::Compositor is still being destroyed.
372 // Return NULL in these cases.
373 if (found == g_widget_to_internals_map.Pointer()->end())
375 return found->second;
378 void BrowserCompositorCALayerTreeMacGotAcceleratedFrame(
379 gfx::AcceleratedWidget widget,
380 uint64 surface_handle, int surface_id,
381 const std::vector<ui::LatencyInfo>& latency_info,
382 gfx::Size pixel_size, float scale_factor,
383 bool* disable_throttling, int* renderer_id) {
384 BrowserCompositorCALayerTreeMac* ca_layer_tree =
385 BrowserCompositorCALayerTreeMac::FromAcceleratedWidget(widget);
387 ca_layer_tree->GotAcceleratedFrame(
388 surface_handle, surface_id, latency_info, pixel_size, scale_factor);
389 *disable_throttling = ca_layer_tree->IsRendererThrottlingDisabled();
390 *renderer_id = ca_layer_tree->GetRendererID();
392 *disable_throttling = false;
397 void BrowserCompositorCALayerTreeMacGotSoftwareFrame(
398 gfx::AcceleratedWidget widget,
399 cc::SoftwareFrameData* frame_data, float scale_factor, SkCanvas* canvas) {
400 BrowserCompositorCALayerTreeMac* ca_layer_tree =
401 BrowserCompositorCALayerTreeMac::FromAcceleratedWidget(widget);
403 ca_layer_tree->GotSoftwareFrame(frame_data, scale_factor, canvas);
406 } // namespace content