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()));
64 compositor_->SetVisible(false);
67 BrowserCompositorViewMacInternal::~BrowserCompositorViewMacInternal() {
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_);
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() {
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);
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();
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.
140 AcceleratedLayerDidDrawFrame();
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);
154 case kSurfaceHandleTypeCAContext: {
155 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
156 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
160 LOG(ERROR) << "Unrecognized accelerated frame type.";
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 =
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];
180 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
181 [flipped_layer_ addSublayer:ca_context_layer_];
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
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 =
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
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();
218 LOG(ERROR) << "Failed to create CompositingIOSurfaceMac";
220 io_surface_layer_.reset([[CompositingIOSurfaceLayer alloc]
221 initWithIOSurface:iosurface
222 withScaleFactor:scale_factor
224 if (io_surface_layer_)
225 [flipped_layer_ addSublayer:io_surface_layer_];
227 LOG(ERROR) << "Failed to create CompositingIOSurfaceLayer";
231 // Open the provided IOSurface.
232 if (io_surface_layer_) {
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);
239 DestroyIOSurfaceLayer(io_surface_layer_);
240 LOG(ERROR) << "Failed open IOSurface in CompositingIOSurfaceLayer";
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();
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];
264 [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
267 // If this replacing a same-type layer, remove it now that the new layer is
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,
281 if (!frame_data || !canvas || !client_)
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_];
293 // Set the software layer to draw the provided canvas.
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)
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)
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_)
331 [software_layer_ removeFromSuperlayer];
332 software_layer_.reset();
335 bool BrowserCompositorViewMacInternal::AcceleratedLayerShouldAckImmediately()
337 // If there is no client then the accelerated layer is not in the hierarchy
338 // and will never draw.
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;
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();
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())
378 return found->second;
381 } // namespace content