Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / compositor / browser_compositor_view_private_mac.mm
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.
4
5 #include "content/browser/compositor/browser_compositor_view_private_mac.h"
6
7 #include <map>
8
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"
21
22 namespace content {
23 namespace {
24
25 typedef std::map<gfx::AcceleratedWidget,BrowserCompositorViewMacInternal*>
26     WidgetToInternalsMap;
27 base::LazyInstance<WidgetToInternalsMap> g_widget_to_internals_map;
28
29 }
30
31 ////////////////////////////////////////////////////////////////////////////////
32 // BrowserCompositorViewMacInternal
33
34 BrowserCompositorViewMacInternal::BrowserCompositorViewMacInternal()
35     : client_(NULL),
36       accelerated_output_surface_id_(0) {
37   // Disable the fade-in animation as the layers are added.
38   ScopedCAActionDisabler disabler;
39
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];
48
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));
57
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);
64 }
65
66 BrowserCompositorViewMacInternal::~BrowserCompositorViewMacInternal() {
67   DCHECK(!client_);
68   g_widget_to_internals_map.Pointer()->erase(native_widget_);
69 }
70
71 void BrowserCompositorViewMacInternal::SetClient(
72     BrowserCompositorViewMacClient* client) {
73   // Disable the fade-in animation as the view is added.
74   ScopedCAActionDisabler disabler;
75
76   DCHECK(client && !client_);
77   client_ = client;
78   compositor_->SetRootLayer(client_->BrowserCompositorRootLayer());
79
80   CALayer* background_layer = [client_->BrowserCompositorSuperview() layer];
81   DCHECK(background_layer);
82   [flipped_layer_ setBounds:[background_layer bounds]];
83   [background_layer addSublayer:flipped_layer_];
84   compositor_->SetVisible(true);
85 }
86
87 void BrowserCompositorViewMacInternal::ResetClient() {
88   if (!client_)
89     return;
90
91   // Disable the fade-out animation as the view is removed.
92   ScopedCAActionDisabler disabler;
93
94   [flipped_layer_ removeFromSuperlayer];
95   DestroyIOSurfaceLayer(io_surface_layer_);
96   DestroyCAContextLayer(ca_context_layer_);
97   DestroySoftwareLayer();
98
99   accelerated_output_surface_id_ = 0;
100   last_swap_size_dip_ = gfx::Size();
101
102   compositor_->SetVisible(false);
103   compositor_->SetScaleAndSize(1.0, gfx::Size(0, 0));
104   compositor_->SetRootLayer(NULL);
105   client_ = NULL;
106 }
107
108 bool BrowserCompositorViewMacInternal::HasFrameOfSize(
109     const gfx::Size& dip_size) const {
110   return last_swap_size_dip_ == dip_size;
111 }
112
113 int BrowserCompositorViewMacInternal::GetRendererID() const {
114   if (io_surface_layer_)
115     return [io_surface_layer_ rendererID];
116   return 0;
117 }
118
119 void BrowserCompositorViewMacInternal::BeginPumpingFrames() {
120   [io_surface_layer_ beginPumpingFrames];
121 }
122
123 void BrowserCompositorViewMacInternal::EndPumpingFrames() {
124   [io_surface_layer_ endPumpingFrames];
125 }
126
127 void BrowserCompositorViewMacInternal::GotAcceleratedFrame(
128     uint64 surface_handle, int output_surface_id,
129     const std::vector<ui::LatencyInfo>& latency_info,
130     gfx::Size pixel_size, float scale_factor) {
131   // Record the surface and latency info to use when acknowledging this frame.
132   DCHECK(!accelerated_output_surface_id_);
133   accelerated_output_surface_id_ = output_surface_id;
134   accelerated_latency_info_.insert(accelerated_latency_info_.end(),
135                                    latency_info.begin(), latency_info.end());
136
137   // If there is no client and therefore no superview to draw into, early-out.
138   if (!client_) {
139     IOSurfaceLayerDidDrawFrame();
140     return;
141   }
142
143   // Disable the fade-in or fade-out effect if we create or remove layers.
144   ScopedCAActionDisabler disabler;
145
146   last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size);
147   switch (GetSurfaceHandleType(surface_handle)) {
148     case kSurfaceHandleTypeIOSurface: {
149       IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
150       GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
151       break;
152     }
153     case kSurfaceHandleTypeCAContext: {
154       CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
155       GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
156       break;
157     }
158     default:
159       LOG(ERROR) << "Unrecognized accelerated frame type.";
160       return;
161   }
162 }
163
164 void BrowserCompositorViewMacInternal::GotAcceleratedCAContextFrame(
165     CAContextID ca_context_id,
166     gfx::Size pixel_size,
167     float scale_factor) {
168   // In the layer is replaced, keep the old one around until after the new one
169   // is installed to avoid flashes.
170   base::scoped_nsobject<CALayerHost> old_ca_context_layer =
171       ca_context_layer_;
172
173   // Create the layer to host the layer exported by the GPU process with this
174   // particular CAContext ID.
175   if ([ca_context_layer_ contextId] != ca_context_id) {
176     ca_context_layer_.reset([[CALayerHost alloc] init]);
177     [ca_context_layer_ setContextId:ca_context_id];
178     [ca_context_layer_
179         setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
180     [flipped_layer_ addSublayer:ca_context_layer_];
181   }
182
183   // Acknowledge the frame to unblock the compositor immediately (the GPU
184   // process will do any required throttling).
185   IOSurfaceLayerDidDrawFrame();
186
187   // If this replacing a same-type layer, remove it now that the new layer is
188   // in the hierarchy.
189   if (old_ca_context_layer != ca_context_layer_)
190     DestroyCAContextLayer(old_ca_context_layer);
191
192   // Remove any different-type layers that this is replacing.
193   DestroyIOSurfaceLayer(io_surface_layer_);
194   DestroySoftwareLayer();
195 }
196
197 void BrowserCompositorViewMacInternal::GotAcceleratedIOSurfaceFrame(
198     IOSurfaceID io_surface_id,
199     gfx::Size pixel_size,
200     float scale_factor) {
201   // In the layer is replaced, keep the old one around until after the new one
202   // is installed to avoid flashes.
203   base::scoped_nsobject<IOSurfaceLayer> old_io_surface_layer =
204       io_surface_layer_;
205
206   // Create or re-create an IOSurface layer if needed. If there already exists
207   // a layer but it has the wrong scale factor or it was poisoned, re-create the
208   // layer.
209   bool needs_new_layer =
210       !io_surface_layer_ ||
211       [io_surface_layer_ hasBeenPoisoned] ||
212       [io_surface_layer_ scaleFactor] != scale_factor;
213   if (needs_new_layer) {
214     io_surface_layer_.reset(
215         [[IOSurfaceLayer alloc] initWithClient:this
216                                withScaleFactor:scale_factor]);
217     if (io_surface_layer_)
218       [flipped_layer_ addSublayer:io_surface_layer_];
219     else
220       LOG(ERROR) << "Failed to create IOSurfaceLayer";
221   }
222
223   // Open the provided IOSurface.
224   if (io_surface_layer_) {
225     bool result = [io_surface_layer_ gotFrameWithIOSurface:io_surface_id
226                                              withPixelSize:pixel_size
227                                            withScaleFactor:scale_factor];
228     if (!result) {
229       DestroyIOSurfaceLayer(io_surface_layer_);
230       LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
231     }
232   }
233
234   // Give a final complaint if anything with the layer's creation went wrong.
235   // This frame will appear blank, the compositor will try to create another,
236   // and maybe that will go better.
237   if (!io_surface_layer_) {
238     LOG(ERROR) << "IOSurfaceLayer is nil, tab will be blank";
239     IOSurfaceLayerHitError();
240   }
241
242   // Make the CALayer draw and set its size appropriately.
243   if (io_surface_layer_) {
244     [io_surface_layer_ gotNewFrame];
245
246     // Set the bounds of the accelerated layer to match the size of the frame.
247     // If the bounds changed, force the content to be displayed immediately.
248     CGRect new_layer_bounds = CGRectMake(
249         0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height());
250     bool bounds_changed = !CGRectEqualToRect(
251         new_layer_bounds, [io_surface_layer_ bounds]);
252     [io_surface_layer_ setBounds:new_layer_bounds];
253     if (bounds_changed)
254       [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
255   }
256
257   // If this replacing a same-type layer, remove it now that the new layer is
258   // in the hierarchy.
259   if (old_io_surface_layer != io_surface_layer_)
260     DestroyIOSurfaceLayer(old_io_surface_layer);
261
262   // Remove any different-type layers that this is replacing.
263   DestroyCAContextLayer(ca_context_layer_);
264   DestroySoftwareLayer();
265 }
266
267 void BrowserCompositorViewMacInternal::GotSoftwareFrame(
268     cc::SoftwareFrameData* frame_data,
269     float scale_factor,
270     SkCanvas* canvas) {
271   if (!frame_data || !canvas || !client_)
272     return;
273
274   // Disable the fade-in or fade-out effect if we create or remove layers.
275   ScopedCAActionDisabler disabler;
276
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_];
281   }
282
283   // Set the software layer to draw the provided canvas.
284   SkImageInfo info;
285   size_t row_bytes;
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_ = ConvertSizeToDIP(scale_factor, pixel_size);
293
294   // Remove any different-type layers that this is replacing.
295   DestroyCAContextLayer(ca_context_layer_);
296   DestroyIOSurfaceLayer(io_surface_layer_);
297 }
298
299 void BrowserCompositorViewMacInternal::DestroyCAContextLayer(
300     base::scoped_nsobject<CALayerHost> ca_context_layer) {
301   if (!ca_context_layer)
302     return;
303   [ca_context_layer removeFromSuperlayer];
304   if (ca_context_layer == ca_context_layer_)
305     ca_context_layer_.reset();
306 }
307
308 void BrowserCompositorViewMacInternal::DestroyIOSurfaceLayer(
309     base::scoped_nsobject<IOSurfaceLayer> io_surface_layer) {
310   if (!io_surface_layer)
311     return;
312   [io_surface_layer resetClient];
313   [io_surface_layer removeFromSuperlayer];
314   if (io_surface_layer == io_surface_layer_)
315     io_surface_layer_.reset();
316 }
317
318 void BrowserCompositorViewMacInternal::DestroySoftwareLayer() {
319   if (!software_layer_)
320     return;
321   [software_layer_ removeFromSuperlayer];
322   software_layer_.reset();
323 }
324
325 bool BrowserCompositorViewMacInternal::IOSurfaceLayerShouldAckImmediately()
326     const {
327   // If there is no client then the accelerated layer is not in the hierarchy
328   // and will never draw.
329   if (!client_)
330     return true;
331   return client_->BrowserCompositorViewShouldAckImmediately();
332 }
333
334 void BrowserCompositorViewMacInternal::IOSurfaceLayerDidDrawFrame() {
335   if (accelerated_output_surface_id_) {
336     content::ImageTransportFactory::GetInstance()->OnSurfaceDisplayed(
337         accelerated_output_surface_id_);
338     accelerated_output_surface_id_ = 0;
339   }
340
341   if (client_)
342     client_->BrowserCompositorViewFrameSwapped(accelerated_latency_info_);
343
344   accelerated_latency_info_.clear();
345 }
346
347 void BrowserCompositorViewMacInternal::IOSurfaceLayerHitError() {
348   // Perform all acks that would have been done if the frame had succeeded, to
349   // un-block the compositor and renderer.
350   IOSurfaceLayerDidDrawFrame();
351
352   // Poison the context being used and request a mulligan.
353   [io_surface_layer_ poisonContextAndSharegroup];
354   compositor_->ScheduleFullRedraw();
355 }
356
357 // static
358 BrowserCompositorViewMacInternal* BrowserCompositorViewMacInternal::
359     FromAcceleratedWidget(gfx::AcceleratedWidget widget) {
360   WidgetToInternalsMap::const_iterator found =
361       g_widget_to_internals_map.Pointer()->find(widget);
362   // This can end up being accessed after the underlying widget has been
363   // destroyed, but while the ui::Compositor is still being destroyed.
364   // Return NULL in these cases.
365   if (found == g_widget_to_internals_map.Pointer()->end())
366     return NULL;
367   return found->second;
368 }
369
370 }  // namespace content