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/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"
26 typedef std::map<gfx::AcceleratedWidget,BrowserCompositorViewMacInternal*>
28 base::LazyInstance<WidgetToInternalsMap> g_widget_to_internals_map;
32 ////////////////////////////////////////////////////////////////////////////////
33 // BrowserCompositorViewMacInternal
35 BrowserCompositorViewMacInternal::BrowserCompositorViewMacInternal()
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
44 flipped_layer_.reset([[CALayer alloc] init]);
45 [flipped_layer_ setGeometryFlipped:YES];
46 [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
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(
62 content::GetContextFactory(),
63 RenderWidgetResizeHelper::Get()->task_runner()));
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_];
86 void BrowserCompositorViewMacInternal::ResetClient() {
90 // Disable the fade-out animation as the view is removed.
91 ScopedCAActionDisabler disabler;
93 [flipped_layer_ removeFromSuperlayer];
94 DestroyIOSurfaceLayer(io_surface_layer_);
95 DestroyCAContextLayer(ca_context_layer_);
96 DestroySoftwareLayer();
98 accelerated_output_surface_id_ = 0;
99 last_swap_size_dip_ = gfx::Size();
101 compositor_->SetScaleAndSize(1.0, gfx::Size(0, 0));
102 compositor_->SetRootLayer(NULL);
106 bool BrowserCompositorViewMacInternal::HasFrameOfSize(
107 const gfx::Size& dip_size) const {
108 return last_swap_size_dip_ == dip_size;
111 int BrowserCompositorViewMacInternal::GetRendererID() const {
112 if (io_surface_layer_)
113 return [io_surface_layer_ iosurface]->GetRendererID();
117 void BrowserCompositorViewMacInternal::BeginPumpingFrames() {
118 [io_surface_layer_ beginPumpingFrames];
121 void BrowserCompositorViewMacInternal::EndPumpingFrames() {
122 [io_surface_layer_ endPumpingFrames];
125 void BrowserCompositorViewMacInternal::GotAcceleratedFrame(
126 uint64 surface_handle, int output_surface_id,
127 const std::vector<ui::LatencyInfo>& latency_info,
128 gfx::Size pixel_size, float scale_factor) {
129 // Record the surface and latency info to use when acknowledging this frame.
130 DCHECK(!accelerated_output_surface_id_);
131 accelerated_output_surface_id_ = output_surface_id;
132 accelerated_latency_info_.insert(accelerated_latency_info_.end(),
133 latency_info.begin(), latency_info.end());
135 // If there is no client and therefore no superview to draw into, early-out.
137 AcceleratedLayerDidDrawFrame();
141 // Disable the fade-in or fade-out effect if we create or remove layers.
142 ScopedCAActionDisabler disabler;
144 last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size);
145 switch (GetSurfaceHandleType(surface_handle)) {
146 case kSurfaceHandleTypeIOSurface: {
147 IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
148 GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
151 case kSurfaceHandleTypeCAContext: {
152 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
153 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
157 LOG(ERROR) << "Unrecognized accelerated frame type.";
162 void BrowserCompositorViewMacInternal::GotAcceleratedCAContextFrame(
163 CAContextID ca_context_id,
164 gfx::Size pixel_size,
165 float scale_factor) {
166 // In the layer is replaced, keep the old one around until after the new one
167 // is installed to avoid flashes.
168 base::scoped_nsobject<CALayerHost> old_ca_context_layer =
171 // Create the layer to host the layer exported by the GPU process with this
172 // particular CAContext ID.
173 if ([ca_context_layer_ contextId] != ca_context_id) {
174 ca_context_layer_.reset([[CALayerHost alloc] init]);
175 [ca_context_layer_ setContextId:ca_context_id];
177 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
178 [flipped_layer_ addSublayer:ca_context_layer_];
181 // Acknowledge the frame to unblock the compositor immediately (the GPU
182 // process will do any required throttling).
183 AcceleratedLayerDidDrawFrame();
185 // If this replacing a same-type layer, remove it now that the new layer is
187 if (old_ca_context_layer != ca_context_layer_)
188 DestroyCAContextLayer(old_ca_context_layer);
190 // Remove any different-type layers that this is replacing.
191 DestroyIOSurfaceLayer(io_surface_layer_);
192 DestroySoftwareLayer();
195 void BrowserCompositorViewMacInternal::GotAcceleratedIOSurfaceFrame(
196 IOSurfaceID io_surface_id,
197 gfx::Size pixel_size,
198 float scale_factor) {
199 // In the layer is replaced, keep the old one around until after the new one
200 // is installed to avoid flashes.
201 base::scoped_nsobject<CompositingIOSurfaceLayer> old_io_surface_layer =
204 // Create or re-create an IOSurface layer if needed. If there already exists
205 // a layer but it has the wrong scale factor or it was poisoned, re-create the
207 bool needs_new_layer =
208 !io_surface_layer_ ||
209 [io_surface_layer_ context]->HasBeenPoisoned() ||
210 [io_surface_layer_ iosurface]->scale_factor() != scale_factor;
211 if (needs_new_layer) {
212 scoped_refptr<content::CompositingIOSurfaceMac> iosurface =
213 content::CompositingIOSurfaceMac::Create();
215 LOG(ERROR) << "Failed to create CompositingIOSurfaceMac";
217 io_surface_layer_.reset([[CompositingIOSurfaceLayer alloc]
218 initWithIOSurface:iosurface
219 withScaleFactor:scale_factor
221 if (io_surface_layer_)
222 [flipped_layer_ addSublayer:io_surface_layer_];
224 LOG(ERROR) << "Failed to create CompositingIOSurfaceLayer";
228 // Open the provided IOSurface.
229 if (io_surface_layer_) {
231 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
232 [io_surface_layer_ context]->cgl_context());
233 result = [io_surface_layer_ iosurface]->SetIOSurfaceWithContextCurrent(
234 [io_surface_layer_ context], io_surface_id, pixel_size, scale_factor);
236 DestroyIOSurfaceLayer(io_surface_layer_);
237 LOG(ERROR) << "Failed open IOSurface in CompositingIOSurfaceLayer";
241 // Give a final complaint if anything with the layer's creation went wrong.
242 // This frame will appear blank, the compositor will try to create another,
243 // and maybe that will go better.
244 if (!io_surface_layer_) {
245 LOG(ERROR) << "CompositingIOSurfaceLayer is nil, tab will be blank";
246 AcceleratedLayerHitError();
249 // Make the CALayer draw and set its size appropriately.
250 if (io_surface_layer_) {
251 [io_surface_layer_ gotNewFrame];
253 // Set the bounds of the accelerated layer to match the size of the frame.
254 // If the bounds changed, force the content to be displayed immediately.
255 CGRect new_layer_bounds = CGRectMake(
256 0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height());
257 bool bounds_changed = !CGRectEqualToRect(
258 new_layer_bounds, [io_surface_layer_ bounds]);
259 [io_surface_layer_ setBounds:new_layer_bounds];
261 [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
264 // If this replacing a same-type layer, remove it now that the new layer is
266 if (old_io_surface_layer != io_surface_layer_)
267 DestroyIOSurfaceLayer(old_io_surface_layer);
269 // Remove any different-type layers that this is replacing.
270 DestroyCAContextLayer(ca_context_layer_);
271 DestroySoftwareLayer();
274 void BrowserCompositorViewMacInternal::GotSoftwareFrame(
275 cc::SoftwareFrameData* frame_data,
278 if (!frame_data || !canvas || !client_)
281 // Disable the fade-in or fade-out effect if we create or remove layers.
282 ScopedCAActionDisabler disabler;
284 // If there is not a layer for software frames, create one.
285 if (!software_layer_) {
286 software_layer_.reset([[SoftwareLayer alloc] init]);
287 [flipped_layer_ addSublayer:software_layer_];
290 // Set the software layer to draw the provided canvas.
293 const void* pixels = canvas->peekPixels(&info, &row_bytes);
294 gfx::Size pixel_size(info.fWidth, info.fHeight);
295 [software_layer_ setContentsToData:pixels
296 withRowBytes:row_bytes
297 withPixelSize:pixel_size
298 withScaleFactor:scale_factor];
299 last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size);
301 // Remove any different-type layers that this is replacing.
302 DestroyCAContextLayer(ca_context_layer_);
303 DestroyIOSurfaceLayer(io_surface_layer_);
306 void BrowserCompositorViewMacInternal::DestroyCAContextLayer(
307 base::scoped_nsobject<CALayerHost> ca_context_layer) {
308 if (!ca_context_layer)
310 [ca_context_layer removeFromSuperlayer];
311 if (ca_context_layer == ca_context_layer_)
312 ca_context_layer_.reset();
315 void BrowserCompositorViewMacInternal::DestroyIOSurfaceLayer(
316 base::scoped_nsobject<CompositingIOSurfaceLayer> io_surface_layer) {
317 if (!io_surface_layer)
319 [io_surface_layer resetClient];
320 [io_surface_layer removeFromSuperlayer];
321 if (io_surface_layer == io_surface_layer_)
322 io_surface_layer_.reset();
325 void BrowserCompositorViewMacInternal::DestroySoftwareLayer() {
326 if (!software_layer_)
328 [software_layer_ removeFromSuperlayer];
329 software_layer_.reset();
332 bool BrowserCompositorViewMacInternal::AcceleratedLayerShouldAckImmediately()
334 // If there is no client then the accelerated layer is not in the hierarchy
335 // and will never draw.
338 return client_->BrowserCompositorViewShouldAckImmediately();
341 void BrowserCompositorViewMacInternal::AcceleratedLayerDidDrawFrame() {
342 if (accelerated_output_surface_id_) {
343 content::ImageTransportFactory::GetInstance()->OnSurfaceDisplayed(
344 accelerated_output_surface_id_);
345 accelerated_output_surface_id_ = 0;
349 client_->BrowserCompositorViewFrameSwapped(accelerated_latency_info_);
351 accelerated_latency_info_.clear();
354 void BrowserCompositorViewMacInternal::AcceleratedLayerHitError() {
355 // Perform all acks that would have been done if the frame had succeeded, to
356 // un-block the compositor and renderer.
357 AcceleratedLayerDidDrawFrame();
359 // Poison the context being used and request a mulligan.
360 if (io_surface_layer_)
361 [io_surface_layer_ context]->PoisonContextAndSharegroup();
362 compositor_->ScheduleFullRedraw();
366 BrowserCompositorViewMacInternal* BrowserCompositorViewMacInternal::
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 } // namespace content