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/public/browser/context_factory.h"
19 #include "ui/base/cocoa/animation_utils.h"
20 #include "ui/gl/scoped_cgl.h"
25 typedef std::map<gfx::AcceleratedWidget,BrowserCompositorViewMacInternal*>
27 base::LazyInstance<WidgetToInternalsMap> g_widget_to_internals_map;
31 ////////////////////////////////////////////////////////////////////////////////
32 // BrowserCompositorViewMacInternal
34 BrowserCompositorViewMacInternal::BrowserCompositorViewMacInternal()
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
43 flipped_layer_.reset([[CALayer alloc] init]);
44 [flipped_layer_ setGeometryFlipped:YES];
45 [flipped_layer_ setAnchorPoint:CGPointMake(0, 0)];
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(
61 content::GetContextFactory(),
62 RenderWidgetResizeHelper::Get()->task_runner()));
65 BrowserCompositorViewMacInternal::~BrowserCompositorViewMacInternal() {
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_);
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() {
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);
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.
135 AcceleratedLayerDidDrawFrame(true);
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();
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
160 [flipped_layer_ addSublayer:accelerated_layer_];
163 // Open the provided IOSurface.
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);
171 LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
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];
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.
189 [software_layer_ removeFromSuperlayer];
190 software_layer_.reset();
191 [old_accelerated_layer resetClient];
192 [old_accelerated_layer removeFromSuperlayer];
193 old_accelerated_layer.reset();
197 void BrowserCompositorViewMacInternal::GotSoftwareFrame(
198 cc::SoftwareFrameData* frame_data,
201 if (!frame_data || !canvas || !client_)
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_];
213 // Set the software layer to draw the provided canvas.
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.
226 [accelerated_layer_ resetClient];
227 [accelerated_layer_ removeFromSuperlayer];
228 accelerated_layer_.reset();
232 bool BrowserCompositorViewMacInternal::AcceleratedLayerShouldAckImmediately()
234 // If there is no client then the accelerated layer is not in the hierarchy
235 // and will never draw.
238 return client_->BrowserCompositorViewShouldAckImmediately();
241 void BrowserCompositorViewMacInternal::AcceleratedLayerDidDrawFrame(
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;
250 client_->BrowserCompositorViewFrameSwapped(accelerated_latency_info_);
252 accelerated_latency_info_.clear();
255 if (accelerated_layer_)
256 [accelerated_layer_ context]->PoisonContextAndSharegroup();
257 compositor_->ScheduleFullRedraw();
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())
271 return found->second;
275 } // namespace content