Mac: Don't use a dummy NSView as a gfx::AcceleratedWidget
[chromium-blink-merge.git] / content / browser / compositor / browser_compositor_view_private_mac.mm
bloba21f8c5bfc2aab7ce2c2e371887209255fd63bbf
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"
7 #include <map>
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/public/browser/context_factory.h"
19 #include "ui/base/cocoa/animation_utils.h"
20 #include "ui/gl/scoped_cgl.h"
22 namespace content {
23 namespace {
25 typedef std::map<gfx::AcceleratedWidget,BrowserCompositorViewMacInternal*>
26     WidgetToInternalsMap;
27 base::LazyInstance<WidgetToInternalsMap> g_widget_to_internals_map;
31 ////////////////////////////////////////////////////////////////////////////////
32 // BrowserCompositorViewMacInternal
34 BrowserCompositorViewMacInternal::BrowserCompositorViewMacInternal()
35     : client_(NULL),
36       accelerated_layer_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
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];
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(
60       native_widget_,
61       content::GetContextFactory(),
62       RenderWidgetResizeHelper::Get()->task_runner()));
65 BrowserCompositorViewMacInternal::~BrowserCompositorViewMacInternal() {
66   DCHECK(!client_);
67   g_widget_to_internals_map.Pointer()->erase(native_widget_);
70 void BrowserCompositorViewMacInternal::SetClient(
71     BrowserCompositorViewMacClient* client) {
72   // Disable the fade-in animation as the view is added.
73   ScopedCAActionDisabler disabler;
75   DCHECK(client && !client_);
76   client_ = client;
77   compositor_->SetRootLayer(client_->BrowserCompositorRootLayer());
79   CALayer* background_layer = [client_->BrowserCompositorSuperview() layer];
80   DCHECK(background_layer);
81   [flipped_layer_ setBounds:[background_layer bounds]];
82   [background_layer addSublayer:flipped_layer_];
85 void BrowserCompositorViewMacInternal::ResetClient() {
86   if (!client_)
87     return;
89   // Disable the fade-out animation as the view is removed.
90   ScopedCAActionDisabler disabler;
92   [flipped_layer_ removeFromSuperlayer];
94   [accelerated_layer_ removeFromSuperlayer];
95   [accelerated_layer_ resetClient];
96   accelerated_layer_.reset();
97   accelerated_layer_output_surface_id_ = 0;
99   [software_layer_ removeFromSuperlayer];
100   software_layer_.reset();
102   last_swap_size_dip_ = gfx::Size();
104   compositor_->SetScaleAndSize(1.0, gfx::Size(0, 0));
105   compositor_->SetRootLayer(NULL);
106   client_ = NULL;
109 bool BrowserCompositorViewMacInternal::HasFrameOfSize(
110     const gfx::Size& dip_size) const {
111   return last_swap_size_dip_ == dip_size;
114 void BrowserCompositorViewMacInternal::BeginPumpingFrames() {
115   [accelerated_layer_ beginPumpingFrames];
118 void BrowserCompositorViewMacInternal::EndPumpingFrames() {
119   [accelerated_layer_ endPumpingFrames];
122 void BrowserCompositorViewMacInternal::GotAcceleratedIOSurfaceFrame(
123     IOSurfaceID io_surface_id,
124     int output_surface_id,
125     const std::vector<ui::LatencyInfo>& latency_info,
126     gfx::Size pixel_size,
127     float scale_factor) {
128   DCHECK(!accelerated_layer_output_surface_id_);
129   accelerated_layer_output_surface_id_ = output_surface_id;
130   accelerated_latency_info_.insert(accelerated_latency_info_.end(),
131                                    latency_info.begin(), latency_info.end());
133   // If there is no client and therefore no superview to draw into, early-out.
134   if (!client_) {
135     AcceleratedLayerDidDrawFrame(true);
136     return;
137   }
139   // Disable the fade-in or fade-out effect if we create or remove layers.
140   ScopedCAActionDisabler disabler;
142   // If there is already an accelerated layer, but it has the wrong scale
143   // factor or it was poisoned, remove the old layer and replace it.
144   base::scoped_nsobject<CompositingIOSurfaceLayer> old_accelerated_layer;
145   if (accelerated_layer_ && (
146           [accelerated_layer_ context]->HasBeenPoisoned() ||
147           [accelerated_layer_ iosurface]->scale_factor() != scale_factor)) {
148     old_accelerated_layer = accelerated_layer_;
149     accelerated_layer_.reset();
150   }
152   // If there is not a layer for accelerated frames, create one.
153   if (!accelerated_layer_) {
154     scoped_refptr<content::CompositingIOSurfaceMac> iosurface =
155         content::CompositingIOSurfaceMac::Create();
156     accelerated_layer_.reset([[CompositingIOSurfaceLayer alloc]
157         initWithIOSurface:iosurface
158           withScaleFactor:scale_factor
159                withClient:this]);
160     [flipped_layer_ addSublayer:accelerated_layer_];
161   }
163   // Open the provided IOSurface.
164   {
165     bool result = true;
166     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
167         [accelerated_layer_ context]->cgl_context());
168     result = [accelerated_layer_ iosurface]->SetIOSurfaceWithContextCurrent(
169         [accelerated_layer_ context], io_surface_id, pixel_size, scale_factor);
170     if (!result)
171       LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
172   }
173   [accelerated_layer_ gotNewFrame];
175   // Set the bounds of the accelerated layer to match the size of the frame.
176   // If the bounds changed, force the content to be displayed immediately.
177   last_swap_size_dip_ = [accelerated_layer_ iosurface]->dip_io_surface_size();
178   CGRect new_layer_bounds = CGRectMake(
179       0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height());
180   bool bounds_changed = !CGRectEqualToRect(
181       new_layer_bounds, [accelerated_layer_ bounds]);
182   [accelerated_layer_ setBounds:new_layer_bounds];
183   if (bounds_changed)
184     [accelerated_layer_ setNeedsDisplayAndDisplayAndAck];
186   // If there was a software layer or an old accelerated layer, remove it.
187   // Disable the fade-out animation as the layer is removed.
188   {
189     [software_layer_ removeFromSuperlayer];
190     software_layer_.reset();
191     [old_accelerated_layer resetClient];
192     [old_accelerated_layer removeFromSuperlayer];
193     old_accelerated_layer.reset();
194   }
197 void BrowserCompositorViewMacInternal::GotSoftwareFrame(
198     cc::SoftwareFrameData* frame_data,
199     float scale_factor,
200     SkCanvas* canvas) {
201   if (!frame_data || !canvas || !client_)
202     return;
204   // Disable the fade-in or fade-out effect if we create or remove layers.
205   ScopedCAActionDisabler disabler;
207   // If there is not a layer for software frames, create one.
208   if (!software_layer_) {
209     software_layer_.reset([[SoftwareLayer alloc] init]);
210     [flipped_layer_ addSublayer:software_layer_];
211   }
213   // Set the software layer to draw the provided canvas.
214   SkImageInfo info;
215   size_t row_bytes;
216   const void* pixels = canvas->peekPixels(&info, &row_bytes);
217   gfx::Size pixel_size(info.fWidth, info.fHeight);
218   [software_layer_ setContentsToData:pixels
219                         withRowBytes:row_bytes
220                        withPixelSize:pixel_size
221                      withScaleFactor:scale_factor];
222   last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size);
224   // If there was an accelerated layer, remove it.
225   {
226     [accelerated_layer_ resetClient];
227     [accelerated_layer_ removeFromSuperlayer];
228     accelerated_layer_.reset();
229   }
232 bool BrowserCompositorViewMacInternal::AcceleratedLayerShouldAckImmediately()
233     const {
234   // If there is no client then the accelerated layer is not in the hierarchy
235   // and will never draw.
236   if (!client_)
237     return true;
238   return client_->BrowserCompositorViewShouldAckImmediately();
241 void BrowserCompositorViewMacInternal::AcceleratedLayerDidDrawFrame(
242     bool succeeded) {
243   if (accelerated_layer_output_surface_id_) {
244     content::ImageTransportFactory::GetInstance()->OnSurfaceDisplayed(
245         accelerated_layer_output_surface_id_);
246     accelerated_layer_output_surface_id_ = 0;
247   }
249   if (client_)
250     client_->BrowserCompositorViewFrameSwapped(accelerated_latency_info_);
252   accelerated_latency_info_.clear();
254   if (!succeeded) {
255     if (accelerated_layer_)
256       [accelerated_layer_ context]->PoisonContextAndSharegroup();
257     compositor_->ScheduleFullRedraw();
258   }
261 // static
262 BrowserCompositorViewMacInternal* BrowserCompositorViewMacInternal::
263     FromAcceleratedWidget(gfx::AcceleratedWidget widget) {
264   WidgetToInternalsMap::const_iterator found =
265       g_widget_to_internals_map.Pointer()->find(widget);
266   // This can end up being accessed after the underlying widget has been
267   // destroyed, but while the ui::Compositor is still being destroyed.
268   // Return NULL in these cases.
269   if (found == g_widget_to_internals_map.Pointer()->end())
270     return NULL;
271   return found->second;
275 }  // namespace content