Chromecast: extracts Linux window creation code to a common place.
[chromium-blink-merge.git] / content / browser / compositor / browser_compositor_ca_layer_tree_mac.mm
blob3abee8cd7630ff56ae0a427617a7313555cb26a0
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"
7 #include <map>
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"
22 namespace content {
23 namespace {
25 typedef std::map<gfx::AcceleratedWidget,BrowserCompositorCALayerTreeMac*>
26     WidgetToInternalsMap;
27 base::LazyInstance<WidgetToInternalsMap> g_widget_to_internals_map;
31 ////////////////////////////////////////////////////////////////////////////////
32 // BrowserCompositorCALayerTreeMac
34 BrowserCompositorCALayerTreeMac::BrowserCompositorCALayerTreeMac()
35     : view_(NULL),
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
42   // origin.
43   flipped_layer_.reset([[CALayer alloc] init]);
44   [flipped_layer_ setGeometryFlipped:YES];
45   [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
46   [flipped_layer_
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(
60       native_widget_,
61       content::GetContextFactory(),
62       RenderWidgetResizeHelper::Get()->task_runner()));
63   compositor_->SetVisible(false);
66 BrowserCompositorCALayerTreeMac::~BrowserCompositorCALayerTreeMac() {
67   DCHECK(!view_);
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_);
77   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() {
88   if (!view_)
89     return;
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);
105   view_ = 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];
116   return 0;
119 bool BrowserCompositorCALayerTreeMac::IsRendererThrottlingDisabled() const {
120   if (view_)
121     return view_->client()->BrowserCompositorViewShouldAckImmediately();
122   return false;
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.
144   if (!view_) {
145     IOSurfaceLayerDidDrawFrame();
146     return;
147   }
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);
157       break;
158     }
159     case kSurfaceHandleTypeCAContext: {
160       CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
161       GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
162       break;
163     }
164     default:
165       LOG(ERROR) << "Unrecognized accelerated frame type.";
166       return;
167   }
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 =
177       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];
184     [ca_context_layer_
185         setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
186     [flipped_layer_ addSublayer:ca_context_layer_];
187   }
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
194   // in the hierarchy.
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 =
210       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
214   // layer.
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_];
225     else
226       LOG(ERROR) << "Failed to create IOSurfaceLayer";
227   }
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];
234     if (!result) {
235       DestroyIOSurfaceLayer(io_surface_layer_);
236       LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
237     }
238   }
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();
246   }
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];
259     if (bounds_changed)
260       [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
261   }
263   // If this replacing a same-type layer, remove it now that the new layer is
264   // in the hierarchy.
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,
275     float scale_factor,
276     SkCanvas* canvas) {
277   if (!frame_data || !canvas || !view_)
278     return;
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_];
287   }
289   // Set the software layer to draw the provided canvas.
290   SkImageInfo info;
291   size_t row_bytes;
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)
308     return;
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)
317     return;
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_)
326     return;
327   [software_layer_ removeFromSuperlayer];
328   software_layer_.reset();
331 bool BrowserCompositorCALayerTreeMac::IOSurfaceLayerShouldAckImmediately()
332     const {
333   // If there is no view then the accelerated layer is not in the hierarchy
334   // and will never draw.
335   if (!view_)
336     return true;
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;
345   }
347   if (view_) {
348     view_->client()->BrowserCompositorViewFrameSwapped(
349         accelerated_latency_info_);
350   }
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();
365 // static
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())
374     return NULL;
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);
386   if (ca_layer_tree) {
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();
391   } else {
392     *disable_throttling = false;
393     *renderer_id = 0;
394   }
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);
402   if (ca_layer_tree)
403     ca_layer_tree->GotSoftwareFrame(frame_data, scale_factor, canvas);
406 }  // namespace content