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"
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,BrowserCompositorViewMacInternal*>
27 base::LazyInstance<WidgetToInternalsMap> g_widget_to_internals_map;
31 ////////////////////////////////////////////////////////////////////////////////
32 // BrowserCompositorViewMacInternal
34 BrowserCompositorViewMacInternal::BrowserCompositorViewMacInternal()
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 BrowserCompositorViewMacInternal::~BrowserCompositorViewMacInternal() {
68 g_widget_to_internals_map.Pointer()->erase(native_widget_);
71 void BrowserCompositorViewMacInternal::SetClient(
72 BrowserCompositorViewMacClient* client) {
73 // Disable the fade-in animation as the view is added.
74 ScopedCAActionDisabler disabler;
76 DCHECK(client && !client_);
78 compositor_->SetRootLayer(client_->BrowserCompositorRootLayer());
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);
87 void BrowserCompositorViewMacInternal::ResetClient() {
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 BrowserCompositorViewMacInternal::HasFrameOfSize(
109 const gfx::Size& dip_size) const {
110 return last_swap_size_dip_ == dip_size;
113 int BrowserCompositorViewMacInternal::GetRendererID() const {
114 if (io_surface_layer_)
115 return [io_surface_layer_ rendererID];
119 void BrowserCompositorViewMacInternal::BeginPumpingFrames() {
120 [io_surface_layer_ beginPumpingFrames];
123 void BrowserCompositorViewMacInternal::EndPumpingFrames() {
124 [io_surface_layer_ endPumpingFrames];
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());
137 // If there is no client and therefore no superview to draw into, early-out.
139 IOSurfaceLayerDidDrawFrame();
143 // Disable the fade-in or fade-out effect if we create or remove layers.
144 ScopedCAActionDisabler disabler;
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);
153 case kSurfaceHandleTypeCAContext: {
154 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
155 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
159 LOG(ERROR) << "Unrecognized accelerated frame type.";
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 =
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];
179 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
180 [flipped_layer_ addSublayer:ca_context_layer_];
183 // Acknowledge the frame to unblock the compositor immediately (the GPU
184 // process will do any required throttling).
185 IOSurfaceLayerDidDrawFrame();
187 // If this replacing a same-type layer, remove it now that the new layer is
189 if (old_ca_context_layer != ca_context_layer_)
190 DestroyCAContextLayer(old_ca_context_layer);
192 // Remove any different-type layers that this is replacing.
193 DestroyIOSurfaceLayer(io_surface_layer_);
194 DestroySoftwareLayer();
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 =
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
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_];
220 LOG(ERROR) << "Failed to create IOSurfaceLayer";
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];
229 DestroyIOSurfaceLayer(io_surface_layer_);
230 LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
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();
242 // Make the CALayer draw and set its size appropriately.
243 if (io_surface_layer_) {
244 [io_surface_layer_ gotNewFrame];
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];
254 [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
257 // If this replacing a same-type layer, remove it now that the new layer is
259 if (old_io_surface_layer != io_surface_layer_)
260 DestroyIOSurfaceLayer(old_io_surface_layer);
262 // Remove any different-type layers that this is replacing.
263 DestroyCAContextLayer(ca_context_layer_);
264 DestroySoftwareLayer();
267 void BrowserCompositorViewMacInternal::GotSoftwareFrame(
268 cc::SoftwareFrameData* frame_data,
271 if (!frame_data || !canvas || !client_)
274 // Disable the fade-in or fade-out effect if we create or remove layers.
275 ScopedCAActionDisabler disabler;
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_];
283 // Set the software layer to draw the provided canvas.
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);
294 // Remove any different-type layers that this is replacing.
295 DestroyCAContextLayer(ca_context_layer_);
296 DestroyIOSurfaceLayer(io_surface_layer_);
299 void BrowserCompositorViewMacInternal::DestroyCAContextLayer(
300 base::scoped_nsobject<CALayerHost> ca_context_layer) {
301 if (!ca_context_layer)
303 [ca_context_layer removeFromSuperlayer];
304 if (ca_context_layer == ca_context_layer_)
305 ca_context_layer_.reset();
308 void BrowserCompositorViewMacInternal::DestroyIOSurfaceLayer(
309 base::scoped_nsobject<IOSurfaceLayer> io_surface_layer) {
310 if (!io_surface_layer)
312 [io_surface_layer resetClient];
313 [io_surface_layer removeFromSuperlayer];
314 if (io_surface_layer == io_surface_layer_)
315 io_surface_layer_.reset();
318 void BrowserCompositorViewMacInternal::DestroySoftwareLayer() {
319 if (!software_layer_)
321 [software_layer_ removeFromSuperlayer];
322 software_layer_.reset();
325 bool BrowserCompositorViewMacInternal::IOSurfaceLayerShouldAckImmediately()
327 // If there is no client then the accelerated layer is not in the hierarchy
328 // and will never draw.
331 return client_->BrowserCompositorViewShouldAckImmediately();
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;
342 client_->BrowserCompositorViewFrameSwapped(accelerated_latency_info_);
344 accelerated_latency_info_.clear();
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();
352 // Poison the context being used and request a mulligan.
353 [io_surface_layer_ poisonContextAndSharegroup];
354 compositor_->ScheduleFullRedraw();
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())
367 return found->second;
370 } // namespace content