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 "ui/accelerated_widget_mac/accelerated_widget_mac.h"
9 #include "base/lazy_instance.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/trace_event/trace_event.h"
12 #include "third_party/skia/include/core/SkCanvas.h"
13 #include "ui/accelerated_widget_mac/io_surface_layer.h"
14 #include "ui/accelerated_widget_mac/surface_handle_types.h"
15 #include "ui/base/cocoa/animation_utils.h"
16 #include "ui/gfx/geometry/dip_util.h"
17 #include "ui/gl/scoped_cgl.h"
22 typedef std::map<gfx::AcceleratedWidget,AcceleratedWidgetMac*>
24 base::LazyInstance<WidgetToHelperMap> g_widget_to_helper_map;
26 AcceleratedWidgetMac* GetHelperFromAcceleratedWidget(
27 gfx::AcceleratedWidget widget) {
28 WidgetToHelperMap::const_iterator found =
29 g_widget_to_helper_map.Pointer()->find(widget);
30 // This can end up being accessed after the underlying widget has been
31 // destroyed, but while the ui::Compositor is still being destroyed.
32 // Return NULL in these cases.
33 if (found == g_widget_to_helper_map.Pointer()->end())
40 ////////////////////////////////////////////////////////////////////////////////
41 // AcceleratedWidgetMac
43 AcceleratedWidgetMac::AcceleratedWidgetMac(bool needs_gl_finish_workaround)
45 needs_gl_finish_workaround_(needs_gl_finish_workaround) {
46 // Disable the fade-in animation as the layers are added.
47 ScopedCAActionDisabler disabler;
49 // Add a flipped transparent layer as a child, so that we don't need to
50 // fiddle with the position of sub-layers -- they will always be at the
52 flipped_layer_.reset([[CALayer alloc] init]);
53 [flipped_layer_ setGeometryFlipped:YES];
54 [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
56 setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
58 // Use a sequence number as the accelerated widget handle that we can use
59 // to look up the internals structure.
60 static intptr_t last_sequence_number = 0;
61 last_sequence_number += 1;
62 native_widget_ = reinterpret_cast<gfx::AcceleratedWidget>(
63 last_sequence_number);
64 g_widget_to_helper_map.Pointer()->insert(
65 std::make_pair(native_widget_, this));
68 AcceleratedWidgetMac::~AcceleratedWidgetMac() {
70 g_widget_to_helper_map.Pointer()->erase(native_widget_);
73 void AcceleratedWidgetMac::SetNSView(AcceleratedWidgetMacNSView* view) {
74 // Disable the fade-in animation as the view is added.
75 ScopedCAActionDisabler disabler;
77 DCHECK(view && !view_);
80 CALayer* background_layer = [view_->AcceleratedWidgetGetNSView() layer];
81 DCHECK(background_layer);
82 [flipped_layer_ setBounds:[background_layer bounds]];
83 [background_layer addSublayer:flipped_layer_];
86 void AcceleratedWidgetMac::ResetNSView() {
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 last_swap_size_dip_ = gfx::Size();
102 bool AcceleratedWidgetMac::HasFrameOfSize(
103 const gfx::Size& dip_size) const {
104 return last_swap_size_dip_ == dip_size;
107 int AcceleratedWidgetMac::GetRendererID() const {
108 if (io_surface_layer_)
109 return [io_surface_layer_ rendererID];
113 void AcceleratedWidgetMac::GetVSyncParameters(
114 base::TimeTicks* timebase, base::TimeDelta* interval) const {
116 view_->AcceleratedWidgetGetVSyncParameters(timebase, interval);
118 *timebase = base::TimeTicks();
119 *interval = base::TimeDelta();
123 bool AcceleratedWidgetMac::IsRendererThrottlingDisabled() const {
125 return view_->AcceleratedWidgetShouldIgnoreBackpressure();
129 void AcceleratedWidgetMac::BeginPumpingFrames() {
130 [io_surface_layer_ beginPumpingFrames];
133 void AcceleratedWidgetMac::EndPumpingFrames() {
134 [io_surface_layer_ endPumpingFrames];
137 void AcceleratedWidgetMac::GotAcceleratedFrame(
138 uint64 surface_handle,
139 const std::vector<ui::LatencyInfo>& latency_info,
140 const gfx::Size& pixel_size,
142 const gfx::Rect& pixel_damage_rect,
143 const base::Closure& drawn_callback) {
144 // Record the surface and latency info to use when acknowledging this frame.
145 DCHECK(accelerated_frame_drawn_callback_.is_null());
146 accelerated_frame_drawn_callback_ = drawn_callback;
147 accelerated_latency_info_.insert(accelerated_latency_info_.end(),
148 latency_info.begin(), latency_info.end());
150 // If there is no view and therefore no superview to draw into, early-out.
152 AcknowledgeAcceleratedFrame();
156 // Disable the fade-in or fade-out effect if we create or remove layers.
157 ScopedCAActionDisabler disabler;
159 last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
160 switch (GetSurfaceHandleType(surface_handle)) {
161 case kSurfaceHandleTypeIOSurface: {
162 IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
163 GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
166 case kSurfaceHandleTypeCAContext: {
167 CAContextID ca_context_id = CAContextIDFromSurfaceHandle(surface_handle);
168 GotAcceleratedCAContextFrame(ca_context_id, pixel_size, scale_factor);
172 LOG(ERROR) << "Unrecognized accelerated frame type.";
177 void AcceleratedWidgetMac::GotAcceleratedCAContextFrame(
178 CAContextID ca_context_id,
179 const gfx::Size& pixel_size,
180 float scale_factor) {
181 // In the layer is replaced, keep the old one around until after the new one
182 // is installed to avoid flashes.
183 base::scoped_nsobject<CALayerHost> old_ca_context_layer =
186 // Create the layer to host the layer exported by the GPU process with this
187 // particular CAContext ID.
188 if ([ca_context_layer_ contextId] != ca_context_id) {
189 ca_context_layer_.reset([[CALayerHost alloc] init]);
190 [ca_context_layer_ setContextId:ca_context_id];
192 setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin];
193 [flipped_layer_ addSublayer:ca_context_layer_];
196 // Acknowledge the frame to unblock the compositor immediately (the GPU
197 // process will do any required throttling).
198 AcknowledgeAcceleratedFrame();
200 // If this replacing a same-type layer, remove it now that the new layer is
202 if (old_ca_context_layer != ca_context_layer_)
203 DestroyCAContextLayer(old_ca_context_layer);
205 // Remove any different-type layers that this is replacing.
206 DestroyIOSurfaceLayer(io_surface_layer_);
207 DestroySoftwareLayer();
210 void AcceleratedWidgetMac::GotAcceleratedIOSurfaceFrame(
211 IOSurfaceID io_surface_id,
212 const gfx::Size& pixel_size,
213 float scale_factor) {
214 // In the layer is replaced, keep the old one around until after the new one
215 // is installed to avoid flashes.
216 base::scoped_nsobject<IOSurfaceLayer> old_io_surface_layer =
219 // Create or re-create an IOSurface layer if needed. If there already exists
220 // a layer but it has the wrong scale factor or it was poisoned, re-create the
222 bool needs_new_layer =
223 !io_surface_layer_ ||
224 [io_surface_layer_ hasBeenPoisoned] ||
225 [io_surface_layer_ scaleFactor] != scale_factor;
226 if (needs_new_layer) {
227 io_surface_layer_.reset(
228 [[IOSurfaceLayer alloc] initWithClient:this
229 withScaleFactor:scale_factor
230 needsGLFinishWorkaround:needs_gl_finish_workaround_]);
231 if (io_surface_layer_)
232 [flipped_layer_ addSublayer:io_surface_layer_];
234 LOG(ERROR) << "Failed to create IOSurfaceLayer";
237 // Open the provided IOSurface.
238 if (io_surface_layer_) {
239 bool result = [io_surface_layer_ gotFrameWithIOSurface:io_surface_id
240 withPixelSize:pixel_size
241 withScaleFactor:scale_factor];
243 DestroyIOSurfaceLayer(io_surface_layer_);
244 LOG(ERROR) << "Failed open IOSurface in IOSurfaceLayer";
248 // Give a final complaint if anything with the layer's creation went wrong.
249 // This frame will appear blank, the compositor will try to create another,
250 // and maybe that will go better.
251 if (!io_surface_layer_) {
252 LOG(ERROR) << "IOSurfaceLayer is nil, tab will be blank";
253 IOSurfaceLayerHitError();
256 // Make the CALayer draw and set its size appropriately.
257 if (io_surface_layer_) {
258 [io_surface_layer_ gotNewFrame];
260 // Set the bounds of the accelerated layer to match the size of the frame.
261 // If the bounds changed, force the content to be displayed immediately.
262 CGRect new_layer_bounds = CGRectMake(
263 0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height());
264 bool bounds_changed = !CGRectEqualToRect(
265 new_layer_bounds, [io_surface_layer_ bounds]);
266 [io_surface_layer_ setBounds:new_layer_bounds];
268 [io_surface_layer_ setNeedsDisplayAndDisplayAndAck];
271 // If this replacing a same-type layer, remove it now that the new layer is
273 if (old_io_surface_layer != io_surface_layer_)
274 DestroyIOSurfaceLayer(old_io_surface_layer);
276 // Remove any different-type layers that this is replacing.
277 DestroyCAContextLayer(ca_context_layer_);
278 DestroySoftwareLayer();
281 void AcceleratedWidgetMac::GotSoftwareFrame(float scale_factor,
283 if (!canvas || !view_)
286 // Disable the fade-in or fade-out effect if we create or remove layers.
287 ScopedCAActionDisabler disabler;
289 // If there is not a layer for software frames, create one.
290 if (!software_layer_) {
291 software_layer_.reset([[SoftwareLayer alloc] init]);
292 [flipped_layer_ addSublayer:software_layer_];
295 // Set the software layer to draw the provided canvas.
298 const void* pixels = canvas->peekPixels(&info, &row_bytes);
299 gfx::Size pixel_size(info.width(), info.height());
300 [software_layer_ setContentsToData:pixels
301 withRowBytes:row_bytes
302 withPixelSize:pixel_size
303 withScaleFactor:scale_factor];
304 last_swap_size_dip_ = gfx::ConvertSizeToDIP(scale_factor, pixel_size);
306 // Remove any different-type layers that this is replacing.
307 DestroyCAContextLayer(ca_context_layer_);
308 DestroyIOSurfaceLayer(io_surface_layer_);
311 void AcceleratedWidgetMac::DestroyCAContextLayer(
312 base::scoped_nsobject<CALayerHost> ca_context_layer) {
313 if (!ca_context_layer)
315 [ca_context_layer removeFromSuperlayer];
316 if (ca_context_layer == ca_context_layer_)
317 ca_context_layer_.reset();
320 void AcceleratedWidgetMac::DestroyIOSurfaceLayer(
321 base::scoped_nsobject<IOSurfaceLayer> io_surface_layer) {
322 if (!io_surface_layer)
324 [io_surface_layer resetClient];
325 [io_surface_layer removeFromSuperlayer];
326 if (io_surface_layer == io_surface_layer_)
327 io_surface_layer_.reset();
330 void AcceleratedWidgetMac::DestroySoftwareLayer() {
331 if (!software_layer_)
333 [software_layer_ removeFromSuperlayer];
334 software_layer_.reset();
337 bool AcceleratedWidgetMac::IOSurfaceLayerShouldAckImmediately() const {
338 // If there is no view then the accelerated layer is not in the view
339 // hierarchy and will never draw.
342 return view_->AcceleratedWidgetShouldIgnoreBackpressure();
345 void AcceleratedWidgetMac::IOSurfaceLayerDidDrawFrame() {
346 AcknowledgeAcceleratedFrame();
349 void AcceleratedWidgetMac::AcknowledgeAcceleratedFrame() {
350 if (accelerated_frame_drawn_callback_.is_null())
352 accelerated_frame_drawn_callback_.Run();
353 accelerated_frame_drawn_callback_.Reset();
355 view_->AcceleratedWidgetSwapCompleted(accelerated_latency_info_);
356 accelerated_latency_info_.clear();
359 void AcceleratedWidgetMac::IOSurfaceLayerHitError() {
360 // Perform all acks that would have been done if the frame had succeeded, to
361 // un-block the compositor and renderer.
362 AcknowledgeAcceleratedFrame();
364 // Poison the context being used and request a mulligan.
365 [io_surface_layer_ poisonContextAndSharegroup];
368 view_->AcceleratedWidgetHitError();
371 void AcceleratedWidgetMacGotAcceleratedFrame(
372 gfx::AcceleratedWidget widget, uint64 surface_handle,
373 const std::vector<ui::LatencyInfo>& latency_info,
374 const gfx::Size& pixel_size,
376 const gfx::Rect& pixel_damage_rect,
377 const base::Closure& drawn_callback,
378 bool* disable_throttling, int* renderer_id,
379 base::TimeTicks* vsync_timebase, base::TimeDelta* vsync_interval) {
380 AcceleratedWidgetMac* accelerated_widget_mac =
381 GetHelperFromAcceleratedWidget(widget);
382 if (accelerated_widget_mac) {
383 accelerated_widget_mac->GotAcceleratedFrame(
384 surface_handle, latency_info, pixel_size, scale_factor,
385 pixel_damage_rect, drawn_callback);
386 *disable_throttling =
387 accelerated_widget_mac->IsRendererThrottlingDisabled();
388 *renderer_id = accelerated_widget_mac->GetRendererID();
389 accelerated_widget_mac->GetVSyncParameters(vsync_timebase, vsync_interval);
391 *disable_throttling = false;
393 *vsync_timebase = base::TimeTicks();
394 *vsync_interval = base::TimeDelta();
398 void AcceleratedWidgetMacGotSoftwareFrame(
399 gfx::AcceleratedWidget widget, float scale_factor, SkCanvas* canvas) {
400 AcceleratedWidgetMac* accelerated_widget_mac =
401 GetHelperFromAcceleratedWidget(widget);
402 if (accelerated_widget_mac)
403 accelerated_widget_mac->GotSoftwareFrame(scale_factor, canvas);