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/layers/delegated_frame_provider.h"
8 #include "cc/layers/delegated_frame_resource_collection.h"
9 #include "cc/layers/delegated_renderer_layer.h"
10 #include "cc/layers/solid_color_layer.h"
11 #include "cc/layers/texture_layer.h"
12 #include "cc/output/context_provider.h"
13 #include "cc/output/copy_output_request.h"
14 #include "cc/output/copy_output_result.h"
15 #include "cc/resources/single_release_callback.h"
16 #include "content/common/browser_plugin/browser_plugin_messages.h"
17 #include "content/common/frame_messages.h"
18 #include "content/common/gpu/client/context_provider_command_buffer.h"
19 #include "content/renderer/browser_plugin/browser_plugin.h"
20 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
21 #include "content/renderer/render_frame_impl.h"
22 #include "content/renderer/render_thread_impl.h"
23 #include "skia/ext/image_operations.h"
24 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
25 #include "third_party/WebKit/public/web/WebFrame.h"
26 #include "third_party/WebKit/public/web/WebPluginContainer.h"
27 #include "third_party/khronos/GLES2/gl2.h"
28 #include "ui/gfx/size_conversions.h"
29 #include "ui/gfx/skia_util.h"
30 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
34 ChildFrameCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
39 shared_memory(NULL
) {}
41 ChildFrameCompositingHelper
*
42 ChildFrameCompositingHelper::CreateCompositingHelperForBrowserPlugin(
43 const base::WeakPtr
<BrowserPlugin
>& browser_plugin
) {
44 return new ChildFrameCompositingHelper(
45 browser_plugin
, NULL
, NULL
, browser_plugin
->render_view_routing_id());
48 ChildFrameCompositingHelper
*
49 ChildFrameCompositingHelper::CreateCompositingHelperForRenderFrame(
50 blink::WebFrame
* frame
,
51 RenderFrameImpl
* render_frame
,
52 int host_routing_id
) {
53 return new ChildFrameCompositingHelper(
54 base::WeakPtr
<BrowserPlugin
>(), frame
, render_frame
, host_routing_id
);
57 ChildFrameCompositingHelper::ChildFrameCompositingHelper(
58 const base::WeakPtr
<BrowserPlugin
>& browser_plugin
,
59 blink::WebFrame
* frame
,
60 RenderFrameImpl
* render_frame
,
62 : host_routing_id_(host_routing_id
),
64 last_output_surface_id_(0),
66 last_mailbox_valid_(false),
68 software_ack_pending_(false),
70 browser_plugin_(browser_plugin
),
71 render_frame_(render_frame
),
74 ChildFrameCompositingHelper::~ChildFrameCompositingHelper() {}
76 BrowserPluginManager
* ChildFrameCompositingHelper::GetBrowserPluginManager() {
80 return browser_plugin_
->browser_plugin_manager();
83 blink::WebPluginContainer
* ChildFrameCompositingHelper::GetContainer() {
87 return browser_plugin_
->container();
90 int ChildFrameCompositingHelper::GetInstanceID() {
94 return browser_plugin_
->guest_instance_id();
97 void ChildFrameCompositingHelper::SendCompositorFrameSwappedACKToBrowser(
98 FrameHostMsg_CompositorFrameSwappedACK_Params
& params
) {
99 // This function will be removed when BrowserPluginManager is removed and
100 // BrowserPlugin is modified to use a RenderFrame.
101 if (GetBrowserPluginManager()) {
102 GetBrowserPluginManager()->Send(
103 new BrowserPluginHostMsg_CompositorFrameSwappedACK(
104 host_routing_id_
, GetInstanceID(), params
));
105 } else if (render_frame_
) {
107 new FrameHostMsg_CompositorFrameSwappedACK(host_routing_id_
, params
));
111 void ChildFrameCompositingHelper::SendBuffersSwappedACKToBrowser(
112 FrameHostMsg_BuffersSwappedACK_Params
& params
) {
113 // This function will be removed when BrowserPluginManager is removed and
114 // BrowserPlugin is modified to use a RenderFrame.
115 if (GetBrowserPluginManager()) {
116 GetBrowserPluginManager()->Send(new BrowserPluginHostMsg_BuffersSwappedACK(
117 host_routing_id_
, params
));
118 } else if (render_frame_
) {
120 new FrameHostMsg_BuffersSwappedACK(host_routing_id_
, params
));
124 void ChildFrameCompositingHelper::SendReclaimCompositorResourcesToBrowser(
125 FrameHostMsg_ReclaimCompositorResources_Params
& params
) {
126 // This function will be removed when BrowserPluginManager is removed and
127 // BrowserPlugin is modified to use a RenderFrame.
128 if (GetBrowserPluginManager()) {
129 GetBrowserPluginManager()->Send(
130 new BrowserPluginHostMsg_ReclaimCompositorResources(
131 host_routing_id_
, GetInstanceID(), params
));
132 } else if (render_frame_
) {
134 new FrameHostMsg_ReclaimCompositorResources(host_routing_id_
, params
));
138 void ChildFrameCompositingHelper::CopyFromCompositingSurface(
140 gfx::Rect source_rect
,
141 gfx::Size dest_size
) {
142 CHECK(background_layer_
);
143 scoped_ptr
<cc::CopyOutputRequest
> request
=
144 cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
145 &ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult
,
149 request
->set_area(source_rect
);
150 background_layer_
->RequestCopyOfOutput(request
.Pass());
153 void ChildFrameCompositingHelper::DidCommitCompositorFrame() {
154 if (software_ack_pending_
) {
155 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
156 params
.producing_host_id
= last_host_id_
;
157 params
.producing_route_id
= last_route_id_
;
158 params
.output_surface_id
= last_output_surface_id_
;
159 if (!unacked_software_frames_
.empty()) {
160 params
.ack
.last_software_frame_id
= unacked_software_frames_
.back();
161 unacked_software_frames_
.pop_back();
164 SendCompositorFrameSwappedACKToBrowser(params
);
166 software_ack_pending_
= false;
168 if (!resource_collection_
.get() || !ack_pending_
)
171 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
172 params
.producing_host_id
= last_host_id_
;
173 params
.producing_route_id
= last_route_id_
;
174 params
.output_surface_id
= last_output_surface_id_
;
175 resource_collection_
->TakeUnusedResourcesForChildCompositor(
176 ¶ms
.ack
.resources
);
178 SendCompositorFrameSwappedACKToBrowser(params
);
180 ack_pending_
= false;
183 void ChildFrameCompositingHelper::EnableCompositing(bool enable
) {
184 if (enable
&& !background_layer_
.get()) {
185 background_layer_
= cc::SolidColorLayer::Create();
186 background_layer_
->SetMasksToBounds(true);
187 background_layer_
->SetBackgroundColor(
188 SkColorSetARGBInline(255, 255, 255, 255));
189 web_layer_
.reset(new webkit::WebLayerImpl(background_layer_
));
192 if (GetContainer()) {
193 GetContainer()->setWebLayer(enable
? web_layer_
.get() : NULL
);
195 frame_
->setRemoteWebLayer(enable
? web_layer_
.get() : NULL
);
199 void ChildFrameCompositingHelper::CheckSizeAndAdjustLayerProperties(
200 const gfx::Size
& new_size
,
201 float device_scale_factor
,
203 if (buffer_size_
!= new_size
) {
204 buffer_size_
= new_size
;
205 // The container size is in DIP, so is the layer size.
206 // Buffer size is in physical pixels, so we need to adjust
207 // it by the device scale factor.
208 gfx::Size device_scale_adjusted_size
= gfx::ToFlooredSize(
209 gfx::ScaleSize(buffer_size_
, 1.0f
/ device_scale_factor
));
210 layer
->SetBounds(device_scale_adjusted_size
);
213 // Manually manage background layer for transparent webview.
215 background_layer_
->SetIsDrawable(false);
218 void ChildFrameCompositingHelper::MailboxReleased(SwapBuffersInfo mailbox
,
220 bool lost_resource
) {
221 if (mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
) {
222 delete mailbox
.shared_memory
;
223 mailbox
.shared_memory
= NULL
;
224 } else if (lost_resource
) {
225 // Reset mailbox's name if the resource was lost.
226 mailbox
.name
.SetZero();
229 // This means the GPU process crashed or guest crashed.
230 if (last_host_id_
!= mailbox
.host_id
||
231 last_output_surface_id_
!= mailbox
.output_surface_id
||
232 last_route_id_
!= mailbox
.route_id
)
235 if (mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
)
236 unacked_software_frames_
.push_back(mailbox
.software_frame_id
);
238 // We need to send an ACK to for every buffer sent to us.
239 // However, if a buffer is freed up from
240 // the compositor in cases like switching back to SW mode without a new
241 // buffer arriving, no ACK is needed.
243 last_mailbox_valid_
= false;
246 ack_pending_
= false;
247 switch (mailbox
.type
) {
248 case TEXTURE_IMAGE_TRANSPORT
: {
249 FrameHostMsg_BuffersSwappedACK_Params params
;
250 params
.gpu_host_id
= mailbox
.host_id
;
251 params
.gpu_route_id
= mailbox
.route_id
;
252 params
.mailbox
= mailbox
.name
;
253 params
.sync_point
= sync_point
;
254 SendBuffersSwappedACKToBrowser(params
);
257 case GL_COMPOSITOR_FRAME
: {
258 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
259 params
.producing_host_id
= mailbox
.host_id
;
260 params
.producing_route_id
= mailbox
.route_id
;
261 params
.output_surface_id
= mailbox
.output_surface_id
;
262 params
.ack
.gl_frame_data
.reset(new cc::GLFrameData());
263 params
.ack
.gl_frame_data
->mailbox
= mailbox
.name
;
264 params
.ack
.gl_frame_data
->size
= mailbox
.size
;
265 params
.ack
.gl_frame_data
->sync_point
= sync_point
;
266 SendCompositorFrameSwappedACKToBrowser(params
);
269 case SOFTWARE_COMPOSITOR_FRAME
:
274 void ChildFrameCompositingHelper::OnContainerDestroy() {
276 GetContainer()->setWebLayer(NULL
);
278 if (resource_collection_
)
279 resource_collection_
->SetClient(NULL
);
281 ack_pending_
= false;
282 software_ack_pending_
= false;
283 resource_collection_
= NULL
;
284 frame_provider_
= NULL
;
285 texture_layer_
= NULL
;
286 delegated_layer_
= NULL
;
287 background_layer_
= NULL
;
291 void ChildFrameCompositingHelper::ChildFrameGone() {
292 background_layer_
->SetBackgroundColor(SkColorSetARGBInline(255, 0, 128, 0));
293 background_layer_
->RemoveAllChildren();
294 background_layer_
->SetIsDrawable(true);
295 background_layer_
->SetContentsOpaque(true);
298 void ChildFrameCompositingHelper::OnBuffersSwappedPrivate(
299 const SwapBuffersInfo
& mailbox
,
301 float device_scale_factor
) {
302 DCHECK(!delegated_layer_
.get());
303 // If these mismatch, we are either just starting up, GPU process crashed or
304 // guest renderer crashed.
305 // In this case, we are communicating with a new image transport
306 // surface and must ACK with the new ID's and an empty mailbox.
307 if (last_route_id_
!= mailbox
.route_id
||
308 last_output_surface_id_
!= mailbox
.output_surface_id
||
309 last_host_id_
!= mailbox
.host_id
)
310 last_mailbox_valid_
= false;
312 last_route_id_
= mailbox
.route_id
;
313 last_output_surface_id_
= mailbox
.output_surface_id
;
314 last_host_id_
= mailbox
.host_id
;
317 // Browser plugin getting destroyed, do a fast ACK.
318 if (!background_layer_
.get()) {
319 MailboxReleased(mailbox
, sync_point
, false);
323 if (!texture_layer_
.get()) {
324 texture_layer_
= cc::TextureLayer::CreateForMailbox(NULL
);
325 texture_layer_
->SetIsDrawable(true);
326 SetContentsOpaque(opaque_
);
328 background_layer_
->AddChild(texture_layer_
);
331 // The size of browser plugin container is not always equal to the size
332 // of the buffer that arrives here. This could be for a number of reasons,
333 // including autosize and a resize in progress.
334 // During resize, the container size changes first and then some time
335 // later, a new buffer with updated size will arrive. During this process,
336 // we need to make sure that things are still displayed pixel perfect.
337 // We accomplish this by modifying bounds of the texture layer only
338 // when a new buffer arrives.
339 // Visually, this will either display a smaller part of the buffer
340 // or introduce a gutter around it.
341 CheckSizeAndAdjustLayerProperties(
342 mailbox
.size
, device_scale_factor
, texture_layer_
.get());
344 bool is_software_frame
= mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
;
345 bool current_mailbox_valid
= is_software_frame
? mailbox
.shared_memory
!= NULL
346 : !mailbox
.name
.IsZero();
347 if (!is_software_frame
&& !last_mailbox_valid_
) {
348 SwapBuffersInfo empty_info
= mailbox
;
349 empty_info
.name
.SetZero();
350 MailboxReleased(empty_info
, 0, false);
351 if (!current_mailbox_valid
)
355 cc::TextureMailbox texture_mailbox
;
356 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
357 if (current_mailbox_valid
) {
359 cc::SingleReleaseCallback::Create(
360 base::Bind(&ChildFrameCompositingHelper::MailboxReleased
,
361 scoped_refptr
<ChildFrameCompositingHelper
>(this),
363 if (is_software_frame
) {
364 texture_mailbox
= cc::TextureMailbox(mailbox
.shared_memory
, mailbox
.size
);
367 cc::TextureMailbox(mailbox
.name
, GL_TEXTURE_2D
, sync_point
);
371 texture_layer_
->SetFlipped(!is_software_frame
);
372 texture_layer_
->SetTextureMailbox(texture_mailbox
, release_callback
.Pass());
373 texture_layer_
->SetNeedsDisplay();
374 last_mailbox_valid_
= current_mailbox_valid
;
377 void ChildFrameCompositingHelper::OnBuffersSwapped(
378 const gfx::Size
& size
,
379 const gpu::Mailbox
& mailbox
,
382 float device_scale_factor
) {
383 SwapBuffersInfo swap_info
;
384 swap_info
.name
= mailbox
;
385 swap_info
.type
= TEXTURE_IMAGE_TRANSPORT
;
386 swap_info
.size
= size
;
387 swap_info
.route_id
= gpu_route_id
;
388 swap_info
.output_surface_id
= 0;
389 swap_info
.host_id
= gpu_host_id
;
390 OnBuffersSwappedPrivate(swap_info
, 0, device_scale_factor
);
393 void ChildFrameCompositingHelper::OnCompositorFrameSwapped(
394 scoped_ptr
<cc::CompositorFrame
> frame
,
396 uint32 output_surface_id
,
398 base::SharedMemoryHandle handle
) {
400 if (frame
->gl_frame_data
) {
401 SwapBuffersInfo swap_info
;
402 swap_info
.name
= frame
->gl_frame_data
->mailbox
;
403 swap_info
.type
= GL_COMPOSITOR_FRAME
;
404 swap_info
.size
= frame
->gl_frame_data
->size
;
405 swap_info
.route_id
= route_id
;
406 swap_info
.output_surface_id
= output_surface_id
;
407 swap_info
.host_id
= host_id
;
408 OnBuffersSwappedPrivate(swap_info
,
409 frame
->gl_frame_data
->sync_point
,
410 frame
->metadata
.device_scale_factor
);
414 if (frame
->software_frame_data
) {
415 cc::SoftwareFrameData
* frame_data
= frame
->software_frame_data
.get();
417 SwapBuffersInfo swap_info
;
418 swap_info
.type
= SOFTWARE_COMPOSITOR_FRAME
;
419 swap_info
.size
= frame_data
->size
;
420 swap_info
.route_id
= route_id
;
421 swap_info
.output_surface_id
= output_surface_id
;
422 swap_info
.host_id
= host_id
;
423 swap_info
.software_frame_id
= frame_data
->id
;
425 scoped_ptr
<base::SharedMemory
> shared_memory(
426 new base::SharedMemory(handle
, true));
427 const size_t size_in_bytes
= 4 * frame_data
->size
.GetArea();
428 if (!shared_memory
->Map(size_in_bytes
)) {
429 LOG(ERROR
) << "Failed to map shared memory of size " << size_in_bytes
;
430 // Send ACK right away.
431 software_ack_pending_
= true;
432 MailboxReleased(swap_info
, 0, false);
433 DidCommitCompositorFrame();
437 swap_info
.shared_memory
= shared_memory
.release();
438 OnBuffersSwappedPrivate(swap_info
, 0, frame
->metadata
.device_scale_factor
);
439 software_ack_pending_
= true;
440 last_route_id_
= route_id
;
441 last_output_surface_id_
= output_surface_id
;
442 last_host_id_
= host_id
;
446 DCHECK(!texture_layer_
.get());
448 cc::DelegatedFrameData
* frame_data
= frame
->delegated_frame_data
.get();
449 // Do nothing if we are getting destroyed or have no frame data.
450 if (!frame_data
|| !background_layer_
)
453 DCHECK(!frame_data
->render_pass_list
.empty());
454 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
455 gfx::Size frame_size
= root_pass
->output_rect
.size();
457 if (last_route_id_
!= route_id
||
458 last_output_surface_id_
!= output_surface_id
||
459 last_host_id_
!= host_id
) {
460 // Resource ids are scoped by the output surface.
461 // If the originating output surface doesn't match the last one, it
462 // indicates the guest's output surface may have been recreated, in which
463 // case we should recreate the DelegatedRendererLayer, to avoid matching
464 // resources from the old one with resources from the new one which would
466 frame_provider_
= NULL
;
468 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
469 // any resources from the old output surface with the new output surface id.
470 if (resource_collection_
) {
471 resource_collection_
->SetClient(NULL
);
473 if (resource_collection_
->LoseAllResources())
474 SendReturnedDelegatedResources();
475 resource_collection_
= NULL
;
477 last_output_surface_id_
= output_surface_id
;
478 last_route_id_
= route_id
;
479 last_host_id_
= host_id
;
481 if (!resource_collection_
) {
482 resource_collection_
= new cc::DelegatedFrameResourceCollection
;
483 resource_collection_
->SetClient(this);
485 if (!frame_provider_
.get() || frame_provider_
->frame_size() != frame_size
) {
486 frame_provider_
= new cc::DelegatedFrameProvider(
487 resource_collection_
.get(), frame
->delegated_frame_data
.Pass());
488 if (delegated_layer_
.get())
489 delegated_layer_
->RemoveFromParent();
491 cc::DelegatedRendererLayer::Create(frame_provider_
.get());
492 delegated_layer_
->SetIsDrawable(true);
493 SetContentsOpaque(opaque_
);
494 background_layer_
->AddChild(delegated_layer_
);
496 frame_provider_
->SetFrameData(frame
->delegated_frame_data
.Pass());
499 CheckSizeAndAdjustLayerProperties(
500 frame_data
->render_pass_list
.back()->output_rect
.size(),
501 frame
->metadata
.device_scale_factor
,
502 delegated_layer_
.get());
507 void ChildFrameCompositingHelper::UpdateVisibility(bool visible
) {
508 if (texture_layer_
.get())
509 texture_layer_
->SetIsDrawable(visible
);
510 if (delegated_layer_
.get())
511 delegated_layer_
->SetIsDrawable(visible
);
514 void ChildFrameCompositingHelper::UnusedResourcesAreAvailable() {
518 SendReturnedDelegatedResources();
521 void ChildFrameCompositingHelper::SendReturnedDelegatedResources() {
522 FrameHostMsg_ReclaimCompositorResources_Params params
;
523 if (resource_collection_
)
524 resource_collection_
->TakeUnusedResourcesForChildCompositor(
525 ¶ms
.ack
.resources
);
526 DCHECK(!params
.ack
.resources
.empty());
528 params
.route_id
= last_route_id_
;
529 params
.output_surface_id
= last_output_surface_id_
;
530 params
.renderer_host_id
= last_host_id_
;
531 SendReclaimCompositorResourcesToBrowser(params
);
534 void ChildFrameCompositingHelper::SetContentsOpaque(bool opaque
) {
537 if (texture_layer_
.get())
538 texture_layer_
->SetContentsOpaque(opaque_
);
539 if (delegated_layer_
.get())
540 delegated_layer_
->SetContentsOpaque(opaque_
);
543 void ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult(
546 scoped_ptr
<cc::CopyOutputResult
> result
) {
547 scoped_ptr
<SkBitmap
> bitmap
;
548 if (result
&& result
->HasBitmap() && !result
->size().IsEmpty())
549 bitmap
= result
->TakeBitmap();
551 SkBitmap resized_bitmap
;
554 skia::ImageOperations::Resize(*bitmap
,
555 skia::ImageOperations::RESIZE_BEST
,
559 if (GetBrowserPluginManager()) {
560 GetBrowserPluginManager()->Send(
561 new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
562 host_routing_id_
, GetInstanceID(), request_id
, resized_bitmap
));
566 } // namespace content