browser_compositor_view_private_mac.mm revision 116680a4aac90f2aa7413d9095a592090648e557
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.
4
5#include "content/browser/compositor/browser_compositor_view_private_mac.h"
6
7#include "base/debug/trace_event.h"
8#include "content/browser/compositor/gpu_process_transport_factory.h"
9#include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
10#include "content/browser/renderer_host/compositing_iosurface_mac.h"
11#include "content/browser/renderer_host/software_layer_mac.h"
12#include "content/public/browser/context_factory.h"
13#include "ui/base/cocoa/animation_utils.h"
14#include "ui/gl/scoped_cgl.h"
15
16////////////////////////////////////////////////////////////////////////////////
17// BrowserCompositorViewMacInternal
18
19namespace content {
20
21BrowserCompositorViewMacInternal::BrowserCompositorViewMacInternal()
22    : client_(NULL),
23      accelerated_layer_output_surface_id_(0) {
24  // Disable the fade-in animation as the layers are added.
25  ScopedCAActionDisabler disabler;
26
27  // Add a flipped transparent layer as a child, so that we don't need to
28  // fiddle with the position of sub-layers -- they will always be at the
29  // origin.
30  flipped_layer_.reset([[CALayer alloc] init]);
31  [flipped_layer_ setGeometryFlipped:YES];
32  [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
33  [flipped_layer_
34      setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
35
36  // Set the Cocoa view to be hosting the un-flipped background layer (hosting
37  // a flipped layer results in unpredictable behavior).
38  cocoa_view_.reset([[BrowserCompositorViewCocoa alloc] initWithClient:this]);
39
40  // Create a compositor to draw the contents of |cocoa_view_|.
41  compositor_.reset(new ui::Compositor(
42      cocoa_view_, content::GetContextFactory()));
43}
44
45BrowserCompositorViewMacInternal::~BrowserCompositorViewMacInternal() {
46  DCHECK(!client_);
47  [cocoa_view_ resetClient];
48}
49
50void BrowserCompositorViewMacInternal::SetClient(
51    BrowserCompositorViewMacClient* client) {
52  // Disable the fade-in animation as the view is added.
53  ScopedCAActionDisabler disabler;
54
55  DCHECK(client && !client_);
56  client_ = client;
57  compositor_->SetRootLayer(client_->BrowserCompositorRootLayer());
58
59  CALayer* background_layer = [client_->BrowserCompositorSuperview() layer];
60  DCHECK(background_layer);
61  [flipped_layer_ setBounds:[background_layer bounds]];
62  [background_layer addSublayer:flipped_layer_];
63}
64
65void BrowserCompositorViewMacInternal::ResetClient() {
66  if (!client_)
67    return;
68
69  // Disable the fade-out animation as the view is removed.
70  ScopedCAActionDisabler disabler;
71
72  [flipped_layer_ removeFromSuperlayer];
73
74  [accelerated_layer_ removeFromSuperlayer];
75  [accelerated_layer_ resetClient];
76  accelerated_layer_.reset();
77  accelerated_layer_output_surface_id_ = 0;
78
79  [software_layer_ removeFromSuperlayer];
80  software_layer_.reset();
81
82  compositor_->SetScaleAndSize(1.0, gfx::Size(0, 0));
83  compositor_->SetRootLayer(NULL);
84  client_ = NULL;
85}
86
87void BrowserCompositorViewMacInternal::GotAcceleratedIOSurfaceFrame(
88    IOSurfaceID io_surface_id,
89    int output_surface_id,
90    const std::vector<ui::LatencyInfo>& latency_info,
91    gfx::Size pixel_size,
92    float scale_factor) {
93  DCHECK(!accelerated_layer_output_surface_id_);
94  accelerated_layer_output_surface_id_ = output_surface_id;
95  accelerated_latency_info_.insert(accelerated_latency_info_.end(),
96                                   latency_info.begin(), latency_info.end());
97
98  // If there is no client and therefore no superview to draw into, early-out.
99  if (!client_) {
100    AcceleratedLayerDidDrawFrame(true);
101    return;
102  }
103
104  // Disable the fade-in or fade-out effect if we create or remove layers.
105  ScopedCAActionDisabler disabler;
106
107  // If there is already an accelerated layer, but it has the wrong scale
108  // factor or it was poisoned, remove the old layer and replace it.
109  base::scoped_nsobject<CompositingIOSurfaceLayer> old_accelerated_layer;
110  if (accelerated_layer_ && (
111          [accelerated_layer_ context]->HasBeenPoisoned() ||
112          [accelerated_layer_ iosurface]->scale_factor() != scale_factor)) {
113    old_accelerated_layer = accelerated_layer_;
114    accelerated_layer_.reset();
115  }
116
117  // If there is not a layer for accelerated frames, create one.
118  if (!accelerated_layer_) {
119    scoped_refptr<content::CompositingIOSurfaceMac> iosurface =
120        content::CompositingIOSurfaceMac::Create();
121    accelerated_layer_.reset([[CompositingIOSurfaceLayer alloc]
122        initWithIOSurface:iosurface
123          withScaleFactor:scale_factor
124               withClient:this]);
125    [flipped_layer_ addSublayer:accelerated_layer_];
126  }
127
128  // Open the provided IOSurface.
129  {
130    bool result = true;
131    gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
132        [accelerated_layer_ context]->cgl_context());
133    result = [accelerated_layer_ iosurface]->SetIOSurfaceWithContextCurrent(
134        [accelerated_layer_ context], io_surface_id, pixel_size, scale_factor);
135    if (!result)
136      LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
137  }
138  [accelerated_layer_ gotNewFrame];
139
140  // Set the bounds of the accelerated layer to match the size of the frame.
141  // If the bounds changed, force the content to be displayed immediately.
142  CGRect new_layer_bounds = CGRectMake(
143    0,
144    0,
145    [accelerated_layer_ iosurface]->dip_io_surface_size().width(),
146    [accelerated_layer_ iosurface]->dip_io_surface_size().height());
147  bool bounds_changed = !CGRectEqualToRect(
148      new_layer_bounds, [accelerated_layer_ bounds]);
149  [accelerated_layer_ setBounds:new_layer_bounds];
150  if (bounds_changed) {
151    [accelerated_layer_ setNeedsDisplay];
152    [accelerated_layer_ displayIfNeeded];
153  }
154
155  // If there was a software layer or an old accelerated layer, remove it.
156  // Disable the fade-out animation as the layer is removed.
157  {
158    [software_layer_ removeFromSuperlayer];
159    software_layer_.reset();
160    [old_accelerated_layer resetClient];
161    [old_accelerated_layer removeFromSuperlayer];
162    old_accelerated_layer.reset();
163  }
164}
165
166void BrowserCompositorViewMacInternal::GotSoftwareFrame(
167    cc::SoftwareFrameData* frame_data,
168    float scale_factor,
169    SkCanvas* canvas) {
170  if (!frame_data || !canvas || !client_)
171    return;
172
173  // Disable the fade-in or fade-out effect if we create or remove layers.
174  ScopedCAActionDisabler disabler;
175
176  // If there is not a layer for software frames, create one.
177  if (!software_layer_) {
178    software_layer_.reset([[SoftwareLayer alloc] init]);
179    [flipped_layer_ addSublayer:software_layer_];
180  }
181
182  // Set the software layer to draw the provided canvas.
183  SkImageInfo info;
184  size_t row_bytes;
185  const void* pixels = canvas->peekPixels(&info, &row_bytes);
186  [software_layer_ setContentsToData:pixels
187                        withRowBytes:row_bytes
188                       withPixelSize:gfx::Size(info.fWidth, info.fHeight)
189                     withScaleFactor:scale_factor];
190
191  // If there was an accelerated layer, remove it.
192  {
193    [accelerated_layer_ resetClient];
194    [accelerated_layer_ removeFromSuperlayer];
195    accelerated_layer_.reset();
196  }
197}
198
199void BrowserCompositorViewMacInternal::AcceleratedLayerDidDrawFrame(
200    bool succeeded) {
201  if (accelerated_layer_output_surface_id_) {
202    content::ImageTransportFactory::GetInstance()->OnSurfaceDisplayed(
203        accelerated_layer_output_surface_id_);
204    accelerated_layer_output_surface_id_ = 0;
205  }
206
207  if (client_)
208    client_->BrowserCompositorViewFrameSwapped(accelerated_latency_info_);
209
210  accelerated_latency_info_.clear();
211
212  if (!succeeded) {
213    if (accelerated_layer_)
214      [accelerated_layer_ context]->PoisonContextAndSharegroup();
215    compositor_->ScheduleFullRedraw();
216  }
217}
218
219}  // namespace content
220
221////////////////////////////////////////////////////////////////////////////////
222// BrowserCompositorViewCocoa
223
224@implementation BrowserCompositorViewCocoa
225
226- (id)initWithClient:(content::BrowserCompositorViewCocoaClient*)client {
227  if (self = [super init]) {
228    client_ = client;
229  }
230  return self;
231}
232
233- (void)dealloc {
234  DCHECK(!client_);
235  [super dealloc];
236}
237
238- (void)resetClient {
239  client_ = NULL;
240}
241
242- (void)gotAcceleratedIOSurfaceFrame:(IOSurfaceID)surface_handle
243                 withOutputSurfaceID:(int)surface_id
244                     withLatencyInfo:(std::vector<ui::LatencyInfo>)latency_info
245                       withPixelSize:(gfx::Size)pixel_size
246                     withScaleFactor:(float)scale_factor {
247  if (!client_)
248    return;
249  client_->GotAcceleratedIOSurfaceFrame(
250      surface_handle, surface_id, latency_info, pixel_size, scale_factor);
251}
252
253- (void)gotSoftwareFrame:(cc::SoftwareFrameData*)frame_data
254         withScaleFactor:(float)scale_factor
255              withCanvas:(SkCanvas*)canvas {
256  if (!client_)
257    return;
258  client_->GotSoftwareFrame(frame_data, scale_factor, canvas);
259}
260
261@end  // BrowserCompositorViewCocoa
262
263