Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / compositor / browser_compositor_view_private_mac.mm
blobccf0094f3fec4d3056f1283229d866b3f4110c79
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_view_private_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/renderer_host/compositing_iosurface_context_mac.h"
14 #include "content/browser/renderer_host/compositing_iosurface_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/browser/renderer_host/software_layer_mac.h"
18 #include "content/common/gpu/surface_handle_types_mac.h"
19 #include "content/public/browser/context_factory.h"
20 #include "ui/base/cocoa/animation_utils.h"
21 #include "ui/gl/scoped_cgl.h"
23 namespace content {
24 namespace {
26 typedef std::map<gfx::AcceleratedWidget,BrowserCompositorViewMacInternal*>
27     WidgetToInternalsMap;
28 base::LazyInstance<WidgetToInternalsMap> g_widget_to_internals_map;
32 ////////////////////////////////////////////////////////////////////////////////
33 // BrowserCompositorViewMacInternal
35 BrowserCompositorViewMacInternal::BrowserCompositorViewMacInternal()
36     : client_(NULL),
37       accelerated_output_surface_id_(0) {
38   // Disable the fade-in animation as the layers are added.
39   ScopedCAActionDisabler disabler;
41   // Add a flipped transparent layer as a child, so that we don't need to
42   // fiddle with the position of sub-layers -- they will always be at the
43   // origin.
44   flipped_layer_.reset([[CALayer alloc] init]);
45   [flipped_layer_ setGeometryFlipped:YES];
46   [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
47   [flipped_layer_
48       setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
50   // Use a sequence number as the accelerated widget handle that we can use
51   // to look up the internals structure.
52   static uintptr_t last_sequence_number = 0;
53   last_sequence_number += 1;
54   native_widget_ = reinterpret_cast<gfx::AcceleratedWidget>(
55       last_sequence_number);
56   g_widget_to_internals_map.Pointer()->insert(
57       std::make_pair(native_widget_, this));
59   // Create a compositor to draw the contents of this view.
60   compositor_.reset(new ui::Compositor(
61       native_widget_,
62       content::GetContextFactory(),
63       RenderWidgetResizeHelper::Get()->task_runner()));
64   compositor_->SetVisible(false);
67 BrowserCompositorViewMacInternal::~BrowserCompositorViewMacInternal() {
68   DCHECK(!client_);
69   g_widget_to_internals_map.Pointer()->erase(native_widget_);
72 void BrowserCompositorViewMacInternal::SetClient(
73     BrowserCompositorViewMacClient* client) {
74   // Disable the fade-in animation as the view is added.
75   ScopedCAActionDisabler disabler;
77   DCHECK(client && !client_);
78   client_ = client;
79   compositor_->SetRootLayer(client_->BrowserCompositorRootLayer());
81   CALayer* background_layer = [client_->BrowserCompositorSuperview() layer];
82   DCHECK(background_layer);
83   [flipped_layer_ setBounds:[background_layer bounds]];
84   [background_layer addSublayer:flipped_layer_];
85   compositor_->SetVisible(true);
88 void BrowserCompositorViewMacInternal::ResetClient() {
89   if (!client_)
90     return;
92   // Disable the fade-out animation as the view is removed.
93   ScopedCAActionDisabler disabler;
95   [flipped_layer_ removeFromSuperlayer];
96   DestroyIOSurfaceLayer(io_surface_layer_);
97   DestroyCAContextLayer(ca_context_layer_);
98   DestroySoftwareLayer();
100   accelerated_output_surface_id_ = 0;
101   last_swap_size_dip_ = gfx::Size();
103   compositor_->SetVisible(false);
104   compositor_->SetScaleAndSize(1.0, gfx::Size(0, 0));
105   compositor_->SetRootLayer(NULL);
106   client_ = NULL;
109 bool BrowserCompositorViewMacInternal::HasFrameOfSize(
110     const gfx::Size& dip_size) const {
111   return last_swap_size_dip_ == dip_size;
114 int BrowserCompositorViewMacInternal::GetRendererID() const {
115   if (io_surface_layer_)
116     return [io_surface_layer_ iosurface]->GetRendererID();
117   return 0;
120 void BrowserCompositorViewMacInternal::BeginPumpingFrames() {
121   [io_surface_layer_ beginPumpingFrames];
124 void BrowserCompositorViewMacInternal::EndPumpingFrames() {
125   [io_surface_layer_ endPumpingFrames];
128 void BrowserCompositorViewMacInternal::GotAcceleratedFrame(
129     uint64 surface_handle, int output_surface_id,
130     const std::vector<ui::LatencyInfo>& latency_info,
131     gfx::Size pixel_size, float scale_factor) {
132   // Record the surface and latency info to use when acknowledging this frame.
133   DCHECK(!accelerated_output_surface_id_);
134   accelerated_output_surface_id_ = output_surface_id;
135   accelerated_latency_info_.insert(accelerated_latency_info_.end(),
136                                    latency_info.begin(), latency_info.end());
138   // If there is no client and therefore no superview to draw into, early-out.
139   if (!client_) {
140     AcceleratedLayerDidDrawFrame();
141     return;
142   }
144   // Disable the fade-in or fade-out effect if we create or remove layers.
145   ScopedCAActionDisabler disabler;
147   last_swap_size_dip_ = 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);
152       break;
153     }
154     case kSurfaceHandleTypeCAContext: {
155       CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
156       GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
157       break;
158     }
159     default:
160       LOG(ERROR) << "Unrecognized accelerated frame type.";
161       return;
162   }
165 void BrowserCompositorViewMacInternal::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 =
172       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];
179     [ca_context_layer_
180         setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
181     [flipped_layer_ addSublayer:ca_context_layer_];
182   }
184   // Acknowledge the frame to unblock the compositor immediately (the GPU
185   // process will do any required throttling).
186   AcceleratedLayerDidDrawFrame();
188   // If this replacing a same-type layer, remove it now that the new layer is
189   // in the hierarchy.
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 BrowserCompositorViewMacInternal::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<CompositingIOSurfaceLayer> old_io_surface_layer =
205       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
209   // layer.
210   bool needs_new_layer =
211       !io_surface_layer_ ||
212       [io_surface_layer_ context]->HasBeenPoisoned() ||
213       [io_surface_layer_ iosurface]->scale_factor() != scale_factor;
214   if (needs_new_layer) {
215     scoped_refptr<content::CompositingIOSurfaceMac> iosurface =
216         content::CompositingIOSurfaceMac::Create();
217     if (!iosurface) {
218       LOG(ERROR) << "Failed to create CompositingIOSurfaceMac";
219     } else {
220       io_surface_layer_.reset([[CompositingIOSurfaceLayer alloc]
221           initWithIOSurface:iosurface
222             withScaleFactor:scale_factor
223                  withClient:this]);
224       if (io_surface_layer_)
225         [flipped_layer_ addSublayer:io_surface_layer_];
226       else
227         LOG(ERROR) << "Failed to create CompositingIOSurfaceLayer";
228     }
229   }
231   // Open the provided IOSurface.
232   if (io_surface_layer_) {
233     bool result = true;
234     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
235         [io_surface_layer_ context]->cgl_context());
236     result = [io_surface_layer_ iosurface]->SetIOSurfaceWithContextCurrent(
237         [io_surface_layer_ context], io_surface_id, pixel_size, scale_factor);
238     if (!result) {
239       DestroyIOSurfaceLayer(io_surface_layer_);
240       LOG(ERROR) << "Failed open IOSurface in CompositingIOSurfaceLayer";
241     }
242   }
244   // Give a final complaint if anything with the layer's creation went wrong.
245   // This frame will appear blank, the compositor will try to create another,
246   // and maybe that will go better.
247   if (!io_surface_layer_) {
248     LOG(ERROR) << "CompositingIOSurfaceLayer is nil, tab will be blank";
249     AcceleratedLayerHitError();
250   }
252   // Make the CALayer draw and set its size appropriately.
253   if (io_surface_layer_) {
254     [io_surface_layer_ gotNewFrame];
256     // Set the bounds of the accelerated layer to match the size of the frame.
257     // If the bounds changed, force the content to be displayed immediately.
258     CGRect new_layer_bounds = CGRectMake(
259         0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height());
260     bool bounds_changed = !CGRectEqualToRect(
261         new_layer_bounds, [io_surface_layer_ bounds]);
262     [io_surface_layer_ setBounds:new_layer_bounds];
263     if (bounds_changed)
264       [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
265   }
267   // If this replacing a same-type layer, remove it now that the new layer is
268   // in the hierarchy.
269   if (old_io_surface_layer != io_surface_layer_)
270     DestroyIOSurfaceLayer(old_io_surface_layer);
272   // Remove any different-type layers that this is replacing.
273   DestroyCAContextLayer(ca_context_layer_);
274   DestroySoftwareLayer();
277 void BrowserCompositorViewMacInternal::GotSoftwareFrame(
278     cc::SoftwareFrameData* frame_data,
279     float scale_factor,
280     SkCanvas* canvas) {
281   if (!frame_data || !canvas || !client_)
282     return;
284   // Disable the fade-in or fade-out effect if we create or remove layers.
285   ScopedCAActionDisabler disabler;
287   // If there is not a layer for software frames, create one.
288   if (!software_layer_) {
289     software_layer_.reset([[SoftwareLayer alloc] init]);
290     [flipped_layer_ addSublayer:software_layer_];
291   }
293   // Set the software layer to draw the provided canvas.
294   SkImageInfo info;
295   size_t row_bytes;
296   const void* pixels = canvas->peekPixels(&info, &row_bytes);
297   gfx::Size pixel_size(info.fWidth, info.fHeight);
298   [software_layer_ setContentsToData:pixels
299                         withRowBytes:row_bytes
300                        withPixelSize:pixel_size
301                      withScaleFactor:scale_factor];
302   last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size);
304   // Remove any different-type layers that this is replacing.
305   DestroyCAContextLayer(ca_context_layer_);
306   DestroyIOSurfaceLayer(io_surface_layer_);
309 void BrowserCompositorViewMacInternal::DestroyCAContextLayer(
310     base::scoped_nsobject<CALayerHost> ca_context_layer) {
311   if (!ca_context_layer)
312     return;
313   [ca_context_layer removeFromSuperlayer];
314   if (ca_context_layer == ca_context_layer_)
315     ca_context_layer_.reset();
318 void BrowserCompositorViewMacInternal::DestroyIOSurfaceLayer(
319     base::scoped_nsobject<CompositingIOSurfaceLayer> io_surface_layer) {
320   if (!io_surface_layer)
321     return;
322   [io_surface_layer resetClient];
323   [io_surface_layer removeFromSuperlayer];
324   if (io_surface_layer == io_surface_layer_)
325     io_surface_layer_.reset();
328 void BrowserCompositorViewMacInternal::DestroySoftwareLayer() {
329   if (!software_layer_)
330     return;
331   [software_layer_ removeFromSuperlayer];
332   software_layer_.reset();
335 bool BrowserCompositorViewMacInternal::AcceleratedLayerShouldAckImmediately()
336     const {
337   // If there is no client then the accelerated layer is not in the hierarchy
338   // and will never draw.
339   if (!client_)
340     return true;
341   return client_->BrowserCompositorViewShouldAckImmediately();
344 void BrowserCompositorViewMacInternal::AcceleratedLayerDidDrawFrame() {
345   if (accelerated_output_surface_id_) {
346     content::ImageTransportFactory::GetInstance()->OnSurfaceDisplayed(
347         accelerated_output_surface_id_);
348     accelerated_output_surface_id_ = 0;
349   }
351   if (client_)
352     client_->BrowserCompositorViewFrameSwapped(accelerated_latency_info_);
354   accelerated_latency_info_.clear();
357 void BrowserCompositorViewMacInternal::AcceleratedLayerHitError() {
358   // Perform all acks that would have been done if the frame had succeeded, to
359   // un-block the compositor and renderer.
360   AcceleratedLayerDidDrawFrame();
362   // Poison the context being used and request a mulligan.
363   if (io_surface_layer_)
364     [io_surface_layer_ context]->PoisonContextAndSharegroup();
365   compositor_->ScheduleFullRedraw();
368 // static
369 BrowserCompositorViewMacInternal* BrowserCompositorViewMacInternal::
370     FromAcceleratedWidget(gfx::AcceleratedWidget widget) {
371   WidgetToInternalsMap::const_iterator found =
372       g_widget_to_internals_map.Pointer()->find(widget);
373   // This can end up being accessed after the underlying widget has been
374   // destroyed, but while the ui::Compositor is still being destroyed.
375   // Return NULL in these cases.
376   if (found == g_widget_to_internals_map.Pointer()->end())
377     return NULL;
378   return found->second;
381 }  // namespace content