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/renderer/child_frame_compositing_helper.h"
7 #include "cc/blink/web_layer_impl.h"
8 #include "cc/layers/delegated_frame_provider.h"
9 #include "cc/layers/delegated_frame_resource_collection.h"
10 #include "cc/layers/delegated_renderer_layer.h"
11 #include "cc/layers/solid_color_layer.h"
12 #include "cc/layers/texture_layer.h"
13 #include "cc/output/context_provider.h"
14 #include "cc/output/copy_output_request.h"
15 #include "cc/output/copy_output_result.h"
16 #include "cc/resources/single_release_callback.h"
17 #include "content/common/browser_plugin/browser_plugin_messages.h"
18 #include "content/common/frame_messages.h"
19 #include "content/common/gpu/client/context_provider_command_buffer.h"
20 #include "content/renderer/browser_plugin/browser_plugin.h"
21 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
22 #include "content/renderer/render_frame_impl.h"
23 #include "content/renderer/render_frame_proxy.h"
24 #include "content/renderer/render_thread_impl.h"
25 #include "skia/ext/image_operations.h"
26 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
27 #include "third_party/WebKit/public/web/WebFrame.h"
28 #include "third_party/WebKit/public/web/WebPluginContainer.h"
29 #include "third_party/khronos/GLES2/gl2.h"
30 #include "ui/gfx/size_conversions.h"
31 #include "ui/gfx/skia_util.h"
35 ChildFrameCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
40 shared_memory(NULL
) {}
42 ChildFrameCompositingHelper
*
43 ChildFrameCompositingHelper::CreateForBrowserPlugin(
44 const base::WeakPtr
<BrowserPlugin
>& browser_plugin
) {
45 return new ChildFrameCompositingHelper(
46 browser_plugin
, NULL
, NULL
, browser_plugin
->render_view_routing_id());
49 ChildFrameCompositingHelper
*
50 ChildFrameCompositingHelper::CreateForRenderFrameProxy(
51 RenderFrameProxy
* render_frame_proxy
) {
52 return new ChildFrameCompositingHelper(base::WeakPtr
<BrowserPlugin
>(),
53 render_frame_proxy
->web_frame(),
55 render_frame_proxy
->routing_id());
58 ChildFrameCompositingHelper::ChildFrameCompositingHelper(
59 const base::WeakPtr
<BrowserPlugin
>& browser_plugin
,
60 blink::WebFrame
* frame
,
61 RenderFrameProxy
* render_frame_proxy
,
63 : host_routing_id_(host_routing_id
),
65 last_output_surface_id_(0),
67 last_mailbox_valid_(false),
69 software_ack_pending_(false),
71 browser_plugin_(browser_plugin
),
72 render_frame_proxy_(render_frame_proxy
),
75 ChildFrameCompositingHelper::~ChildFrameCompositingHelper() {}
77 BrowserPluginManager
* ChildFrameCompositingHelper::GetBrowserPluginManager() {
81 return browser_plugin_
->browser_plugin_manager();
84 blink::WebPluginContainer
* ChildFrameCompositingHelper::GetContainer() {
88 return browser_plugin_
->container();
91 int ChildFrameCompositingHelper::GetInstanceID() {
95 return browser_plugin_
->browser_plugin_instance_id();
98 void ChildFrameCompositingHelper::SendCompositorFrameSwappedACKToBrowser(
99 FrameHostMsg_CompositorFrameSwappedACK_Params
& params
) {
100 // This function will be removed when BrowserPluginManager is removed and
101 // BrowserPlugin is modified to use a RenderFrame.
102 if (GetBrowserPluginManager()) {
103 GetBrowserPluginManager()->Send(
104 new BrowserPluginHostMsg_CompositorFrameSwappedACK(
105 host_routing_id_
, GetInstanceID(), params
));
106 } else if (render_frame_proxy_
) {
107 render_frame_proxy_
->Send(
108 new FrameHostMsg_CompositorFrameSwappedACK(host_routing_id_
, params
));
112 void ChildFrameCompositingHelper::SendBuffersSwappedACKToBrowser(
113 FrameHostMsg_BuffersSwappedACK_Params
& params
) {
114 // This function will be removed when BrowserPluginManager is removed and
115 // BrowserPlugin is modified to use a RenderFrame.
116 if (GetBrowserPluginManager()) {
117 GetBrowserPluginManager()->Send(new BrowserPluginHostMsg_BuffersSwappedACK(
118 host_routing_id_
, params
));
119 } else if (render_frame_proxy_
) {
120 render_frame_proxy_
->Send(
121 new FrameHostMsg_BuffersSwappedACK(host_routing_id_
, params
));
125 void ChildFrameCompositingHelper::SendReclaimCompositorResourcesToBrowser(
126 FrameHostMsg_ReclaimCompositorResources_Params
& params
) {
127 // This function will be removed when BrowserPluginManager is removed and
128 // BrowserPlugin is modified to use a RenderFrame.
129 if (GetBrowserPluginManager()) {
130 GetBrowserPluginManager()->Send(
131 new BrowserPluginHostMsg_ReclaimCompositorResources(
132 host_routing_id_
, GetInstanceID(), params
));
133 } else if (render_frame_proxy_
) {
134 render_frame_proxy_
->Send(
135 new FrameHostMsg_ReclaimCompositorResources(host_routing_id_
, params
));
139 void ChildFrameCompositingHelper::CopyFromCompositingSurface(
141 gfx::Rect source_rect
,
142 gfx::Size dest_size
) {
143 CHECK(background_layer_
.get());
144 scoped_ptr
<cc::CopyOutputRequest
> request
=
145 cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
146 &ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult
,
150 request
->set_area(source_rect
);
151 background_layer_
->RequestCopyOfOutput(request
.Pass());
154 void ChildFrameCompositingHelper::DidCommitCompositorFrame() {
155 if (software_ack_pending_
) {
156 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
157 params
.producing_host_id
= last_host_id_
;
158 params
.producing_route_id
= last_route_id_
;
159 params
.output_surface_id
= last_output_surface_id_
;
160 if (!unacked_software_frames_
.empty()) {
161 params
.ack
.last_software_frame_id
= unacked_software_frames_
.back();
162 unacked_software_frames_
.pop_back();
165 SendCompositorFrameSwappedACKToBrowser(params
);
167 software_ack_pending_
= false;
169 if (!resource_collection_
.get() || !ack_pending_
)
172 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
173 params
.producing_host_id
= last_host_id_
;
174 params
.producing_route_id
= last_route_id_
;
175 params
.output_surface_id
= last_output_surface_id_
;
176 resource_collection_
->TakeUnusedResourcesForChildCompositor(
177 ¶ms
.ack
.resources
);
179 SendCompositorFrameSwappedACKToBrowser(params
);
181 ack_pending_
= false;
184 void ChildFrameCompositingHelper::EnableCompositing(bool enable
) {
185 if (enable
&& !background_layer_
.get()) {
186 background_layer_
= cc::SolidColorLayer::Create();
187 background_layer_
->SetMasksToBounds(true);
188 background_layer_
->SetBackgroundColor(
189 SkColorSetARGBInline(255, 255, 255, 255));
190 web_layer_
.reset(new cc_blink::WebLayerImpl(background_layer_
));
193 if (GetContainer()) {
194 GetContainer()->setWebLayer(enable
? web_layer_
.get() : NULL
);
196 frame_
->setRemoteWebLayer(enable
? web_layer_
.get() : NULL
);
200 void ChildFrameCompositingHelper::CheckSizeAndAdjustLayerProperties(
201 const gfx::Size
& new_size
,
202 float device_scale_factor
,
204 if (buffer_size_
!= new_size
) {
205 buffer_size_
= new_size
;
206 // The container size is in DIP, so is the layer size.
207 // Buffer size is in physical pixels, so we need to adjust
208 // it by the device scale factor.
209 gfx::Size device_scale_adjusted_size
= gfx::ToFlooredSize(
210 gfx::ScaleSize(buffer_size_
, 1.0f
/ device_scale_factor
));
211 layer
->SetBounds(device_scale_adjusted_size
);
214 // Manually manage background layer for transparent webview.
216 background_layer_
->SetIsDrawable(false);
219 void ChildFrameCompositingHelper::MailboxReleased(SwapBuffersInfo mailbox
,
221 bool lost_resource
) {
222 if (mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
) {
223 delete mailbox
.shared_memory
;
224 mailbox
.shared_memory
= NULL
;
225 } else if (lost_resource
) {
226 // Reset mailbox's name if the resource was lost.
227 mailbox
.name
.SetZero();
230 // This means the GPU process crashed or guest crashed.
231 if (last_host_id_
!= mailbox
.host_id
||
232 last_output_surface_id_
!= mailbox
.output_surface_id
||
233 last_route_id_
!= mailbox
.route_id
)
236 if (mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
)
237 unacked_software_frames_
.push_back(mailbox
.software_frame_id
);
239 // We need to send an ACK to for every buffer sent to us.
240 // However, if a buffer is freed up from
241 // the compositor in cases like switching back to SW mode without a new
242 // buffer arriving, no ACK is needed.
244 last_mailbox_valid_
= false;
247 ack_pending_
= false;
248 switch (mailbox
.type
) {
249 case TEXTURE_IMAGE_TRANSPORT
: {
250 FrameHostMsg_BuffersSwappedACK_Params params
;
251 params
.gpu_host_id
= mailbox
.host_id
;
252 params
.gpu_route_id
= mailbox
.route_id
;
253 params
.mailbox
= mailbox
.name
;
254 params
.sync_point
= sync_point
;
255 SendBuffersSwappedACKToBrowser(params
);
258 case GL_COMPOSITOR_FRAME
: {
259 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
260 params
.producing_host_id
= mailbox
.host_id
;
261 params
.producing_route_id
= mailbox
.route_id
;
262 params
.output_surface_id
= mailbox
.output_surface_id
;
263 params
.ack
.gl_frame_data
.reset(new cc::GLFrameData());
264 params
.ack
.gl_frame_data
->mailbox
= mailbox
.name
;
265 params
.ack
.gl_frame_data
->size
= mailbox
.size
;
266 params
.ack
.gl_frame_data
->sync_point
= sync_point
;
267 SendCompositorFrameSwappedACKToBrowser(params
);
270 case SOFTWARE_COMPOSITOR_FRAME
:
275 void ChildFrameCompositingHelper::OnContainerDestroy() {
277 GetContainer()->setWebLayer(NULL
);
279 if (resource_collection_
.get())
280 resource_collection_
->SetClient(NULL
);
282 ack_pending_
= false;
283 software_ack_pending_
= false;
284 resource_collection_
= NULL
;
285 frame_provider_
= NULL
;
286 texture_layer_
= NULL
;
287 delegated_layer_
= NULL
;
288 background_layer_
= NULL
;
292 void ChildFrameCompositingHelper::ChildFrameGone() {
293 background_layer_
->SetBackgroundColor(SkColorSetARGBInline(255, 0, 128, 0));
294 background_layer_
->RemoveAllChildren();
295 background_layer_
->SetIsDrawable(true);
296 background_layer_
->SetContentsOpaque(true);
299 void ChildFrameCompositingHelper::OnBuffersSwappedPrivate(
300 const SwapBuffersInfo
& mailbox
,
302 float device_scale_factor
) {
303 DCHECK(!delegated_layer_
.get());
304 // If these mismatch, we are either just starting up, GPU process crashed or
305 // guest renderer crashed.
306 // In this case, we are communicating with a new image transport
307 // surface and must ACK with the new ID's and an empty mailbox.
308 if (last_route_id_
!= mailbox
.route_id
||
309 last_output_surface_id_
!= mailbox
.output_surface_id
||
310 last_host_id_
!= mailbox
.host_id
)
311 last_mailbox_valid_
= false;
313 last_route_id_
= mailbox
.route_id
;
314 last_output_surface_id_
= mailbox
.output_surface_id
;
315 last_host_id_
= mailbox
.host_id
;
318 // Browser plugin getting destroyed, do a fast ACK.
319 if (!background_layer_
.get()) {
320 MailboxReleased(mailbox
, sync_point
, false);
324 if (!texture_layer_
.get()) {
325 texture_layer_
= cc::TextureLayer::CreateForMailbox(NULL
);
326 texture_layer_
->SetIsDrawable(true);
327 SetContentsOpaque(opaque_
);
329 background_layer_
->AddChild(texture_layer_
);
332 // The size of browser plugin container is not always equal to the size
333 // of the buffer that arrives here. This could be for a number of reasons,
334 // including autosize and a resize in progress.
335 // During resize, the container size changes first and then some time
336 // later, a new buffer with updated size will arrive. During this process,
337 // we need to make sure that things are still displayed pixel perfect.
338 // We accomplish this by modifying bounds of the texture layer only
339 // when a new buffer arrives.
340 // Visually, this will either display a smaller part of the buffer
341 // or introduce a gutter around it.
342 CheckSizeAndAdjustLayerProperties(
343 mailbox
.size
, device_scale_factor
, texture_layer_
.get());
345 bool is_software_frame
= mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
;
346 bool current_mailbox_valid
= is_software_frame
? mailbox
.shared_memory
!= NULL
347 : !mailbox
.name
.IsZero();
348 if (!is_software_frame
&& !last_mailbox_valid_
) {
349 SwapBuffersInfo empty_info
= mailbox
;
350 empty_info
.name
.SetZero();
351 MailboxReleased(empty_info
, 0, false);
352 if (!current_mailbox_valid
)
356 cc::TextureMailbox texture_mailbox
;
357 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
358 if (current_mailbox_valid
) {
360 cc::SingleReleaseCallback::Create(
361 base::Bind(&ChildFrameCompositingHelper::MailboxReleased
,
362 scoped_refptr
<ChildFrameCompositingHelper
>(this),
364 if (is_software_frame
) {
365 texture_mailbox
= cc::TextureMailbox(mailbox
.shared_memory
, mailbox
.size
);
368 cc::TextureMailbox(mailbox
.name
, GL_TEXTURE_2D
, sync_point
);
372 texture_layer_
->SetFlipped(!is_software_frame
);
373 texture_layer_
->SetTextureMailbox(texture_mailbox
, release_callback
.Pass());
374 texture_layer_
->SetNeedsDisplay();
375 last_mailbox_valid_
= current_mailbox_valid
;
378 void ChildFrameCompositingHelper::OnBuffersSwapped(
379 const gfx::Size
& size
,
380 const gpu::Mailbox
& mailbox
,
383 float device_scale_factor
) {
384 SwapBuffersInfo swap_info
;
385 swap_info
.name
= mailbox
;
386 swap_info
.type
= TEXTURE_IMAGE_TRANSPORT
;
387 swap_info
.size
= size
;
388 swap_info
.route_id
= gpu_route_id
;
389 swap_info
.output_surface_id
= 0;
390 swap_info
.host_id
= gpu_host_id
;
391 OnBuffersSwappedPrivate(swap_info
, 0, device_scale_factor
);
394 void ChildFrameCompositingHelper::OnCompositorFrameSwapped(
395 scoped_ptr
<cc::CompositorFrame
> frame
,
397 uint32 output_surface_id
,
399 base::SharedMemoryHandle handle
) {
401 if (frame
->gl_frame_data
) {
402 SwapBuffersInfo swap_info
;
403 swap_info
.name
= frame
->gl_frame_data
->mailbox
;
404 swap_info
.type
= GL_COMPOSITOR_FRAME
;
405 swap_info
.size
= frame
->gl_frame_data
->size
;
406 swap_info
.route_id
= route_id
;
407 swap_info
.output_surface_id
= output_surface_id
;
408 swap_info
.host_id
= host_id
;
409 OnBuffersSwappedPrivate(swap_info
,
410 frame
->gl_frame_data
->sync_point
,
411 frame
->metadata
.device_scale_factor
);
415 if (frame
->software_frame_data
) {
416 cc::SoftwareFrameData
* frame_data
= frame
->software_frame_data
.get();
418 SwapBuffersInfo swap_info
;
419 swap_info
.type
= SOFTWARE_COMPOSITOR_FRAME
;
420 swap_info
.size
= frame_data
->size
;
421 swap_info
.route_id
= route_id
;
422 swap_info
.output_surface_id
= output_surface_id
;
423 swap_info
.host_id
= host_id
;
424 swap_info
.software_frame_id
= frame_data
->id
;
426 scoped_ptr
<base::SharedMemory
> shared_memory(
427 new base::SharedMemory(handle
, true));
428 const size_t size_in_bytes
= 4 * frame_data
->size
.GetArea();
429 if (!shared_memory
->Map(size_in_bytes
)) {
430 LOG(ERROR
) << "Failed to map shared memory of size " << size_in_bytes
;
431 // Send ACK right away.
432 software_ack_pending_
= true;
433 MailboxReleased(swap_info
, 0, false);
434 DidCommitCompositorFrame();
438 swap_info
.shared_memory
= shared_memory
.release();
439 OnBuffersSwappedPrivate(swap_info
, 0, frame
->metadata
.device_scale_factor
);
440 software_ack_pending_
= true;
441 last_route_id_
= route_id
;
442 last_output_surface_id_
= output_surface_id
;
443 last_host_id_
= host_id
;
447 DCHECK(!texture_layer_
.get());
449 cc::DelegatedFrameData
* frame_data
= frame
->delegated_frame_data
.get();
450 // Do nothing if we are getting destroyed or have no frame data.
451 if (!frame_data
|| !background_layer_
.get())
454 DCHECK(!frame_data
->render_pass_list
.empty());
455 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
456 gfx::Size frame_size
= root_pass
->output_rect
.size();
458 if (last_route_id_
!= route_id
||
459 last_output_surface_id_
!= output_surface_id
||
460 last_host_id_
!= host_id
) {
461 // Resource ids are scoped by the output surface.
462 // If the originating output surface doesn't match the last one, it
463 // indicates the guest's output surface may have been recreated, in which
464 // case we should recreate the DelegatedRendererLayer, to avoid matching
465 // resources from the old one with resources from the new one which would
467 frame_provider_
= NULL
;
469 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
470 // any resources from the old output surface with the new output surface id.
471 if (resource_collection_
.get()) {
472 resource_collection_
->SetClient(NULL
);
474 if (resource_collection_
->LoseAllResources())
475 SendReturnedDelegatedResources();
476 resource_collection_
= NULL
;
478 last_output_surface_id_
= output_surface_id
;
479 last_route_id_
= route_id
;
480 last_host_id_
= host_id
;
482 if (!resource_collection_
.get()) {
483 resource_collection_
= new cc::DelegatedFrameResourceCollection
;
484 resource_collection_
->SetClient(this);
486 if (!frame_provider_
.get() || frame_provider_
->frame_size() != frame_size
) {
487 frame_provider_
= new cc::DelegatedFrameProvider(
488 resource_collection_
.get(), frame
->delegated_frame_data
.Pass());
489 if (delegated_layer_
.get())
490 delegated_layer_
->RemoveFromParent();
492 cc::DelegatedRendererLayer::Create(frame_provider_
.get());
493 delegated_layer_
->SetIsDrawable(true);
494 SetContentsOpaque(opaque_
);
495 background_layer_
->AddChild(delegated_layer_
);
497 frame_provider_
->SetFrameData(frame
->delegated_frame_data
.Pass());
500 CheckSizeAndAdjustLayerProperties(
501 frame_data
->render_pass_list
.back()->output_rect
.size(),
502 frame
->metadata
.device_scale_factor
,
503 delegated_layer_
.get());
508 void ChildFrameCompositingHelper::UpdateVisibility(bool visible
) {
509 if (texture_layer_
.get())
510 texture_layer_
->SetIsDrawable(visible
);
511 if (delegated_layer_
.get())
512 delegated_layer_
->SetIsDrawable(visible
);
515 void ChildFrameCompositingHelper::UnusedResourcesAreAvailable() {
519 SendReturnedDelegatedResources();
522 void ChildFrameCompositingHelper::SendReturnedDelegatedResources() {
523 FrameHostMsg_ReclaimCompositorResources_Params params
;
524 if (resource_collection_
.get())
525 resource_collection_
->TakeUnusedResourcesForChildCompositor(
526 ¶ms
.ack
.resources
);
527 DCHECK(!params
.ack
.resources
.empty());
529 params
.route_id
= last_route_id_
;
530 params
.output_surface_id
= last_output_surface_id_
;
531 params
.renderer_host_id
= last_host_id_
;
532 SendReclaimCompositorResourcesToBrowser(params
);
535 void ChildFrameCompositingHelper::SetContentsOpaque(bool opaque
) {
538 if (texture_layer_
.get())
539 texture_layer_
->SetContentsOpaque(opaque_
);
540 if (delegated_layer_
.get())
541 delegated_layer_
->SetContentsOpaque(opaque_
);
544 void ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult(
547 scoped_ptr
<cc::CopyOutputResult
> result
) {
548 scoped_ptr
<SkBitmap
> bitmap
;
549 if (result
&& result
->HasBitmap() && !result
->size().IsEmpty())
550 bitmap
= result
->TakeBitmap();
552 SkBitmap resized_bitmap
;
555 skia::ImageOperations::Resize(*bitmap
,
556 skia::ImageOperations::RESIZE_BEST
,
560 if (GetBrowserPluginManager()) {
561 GetBrowserPluginManager()->Send(
562 new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
563 host_routing_id_
, GetInstanceID(), request_id
, resized_bitmap
));
567 } // namespace content