1 // Copyright (c) 2013 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/browser_plugin/browser_plugin_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_manager.h"
20 #include "content/renderer/render_thread_impl.h"
21 #include "skia/ext/image_operations.h"
22 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
23 #include "third_party/WebKit/public/web/WebPluginContainer.h"
24 #include "third_party/khronos/GLES2/gl2.h"
25 #include "ui/gfx/size_conversions.h"
26 #include "ui/gfx/skia_util.h"
27 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
31 BrowserPluginCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
39 BrowserPluginCompositingHelper::BrowserPluginCompositingHelper(
40 blink::WebPluginContainer
* container
,
41 BrowserPluginManager
* manager
,
44 : instance_id_(instance_id
),
45 host_routing_id_(host_routing_id
),
47 last_output_surface_id_(0),
49 last_mailbox_valid_(false),
51 software_ack_pending_(false),
53 container_(container
),
54 browser_plugin_manager_(manager
) {
57 BrowserPluginCompositingHelper::~BrowserPluginCompositingHelper() {
60 void BrowserPluginCompositingHelper::CopyFromCompositingSurface(
62 gfx::Rect source_rect
,
63 gfx::Size dest_size
) {
64 CHECK(background_layer_
);
65 scoped_ptr
<cc::CopyOutputRequest
> request
=
66 cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
67 &BrowserPluginCompositingHelper::CopyFromCompositingSurfaceHasResult
,
71 request
->set_area(source_rect
);
72 background_layer_
->RequestCopyOfOutput(request
.Pass());
75 void BrowserPluginCompositingHelper::DidCommitCompositorFrame() {
76 if (software_ack_pending_
) {
77 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
78 params
.producing_host_id
= last_host_id_
;
79 params
.producing_route_id
= last_route_id_
;
80 params
.output_surface_id
= last_output_surface_id_
;
81 if (!unacked_software_frames_
.empty()) {
82 params
.ack
.last_software_frame_id
= unacked_software_frames_
.back();
83 unacked_software_frames_
.pop_back();
86 browser_plugin_manager_
->Send(
87 new BrowserPluginHostMsg_CompositorFrameSwappedACK(
92 software_ack_pending_
= false;
94 if (!resource_collection_
.get() || !ack_pending_
)
97 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
98 params
.producing_host_id
= last_host_id_
;
99 params
.producing_route_id
= last_route_id_
;
100 params
.output_surface_id
= last_output_surface_id_
;
101 resource_collection_
->TakeUnusedResourcesForChildCompositor(
102 ¶ms
.ack
.resources
);
104 browser_plugin_manager_
->Send(
105 new BrowserPluginHostMsg_CompositorFrameSwappedACK(
110 ack_pending_
= false;
113 void BrowserPluginCompositingHelper::EnableCompositing(bool enable
) {
114 if (enable
&& !background_layer_
.get()) {
115 background_layer_
= cc::SolidColorLayer::Create();
116 background_layer_
->SetMasksToBounds(true);
117 background_layer_
->SetBackgroundColor(
118 SkColorSetARGBInline(255, 255, 255, 255));
119 web_layer_
.reset(new webkit::WebLayerImpl(background_layer_
));
122 container_
->setWebLayer(enable
? web_layer_
.get() : NULL
);
125 void BrowserPluginCompositingHelper::CheckSizeAndAdjustLayerProperties(
126 const gfx::Size
& new_size
,
127 float device_scale_factor
,
129 if (buffer_size_
!= new_size
) {
130 buffer_size_
= new_size
;
131 // The container size is in DIP, so is the layer size.
132 // Buffer size is in physical pixels, so we need to adjust
133 // it by the device scale factor.
134 gfx::Size device_scale_adjusted_size
= gfx::ToFlooredSize(
135 gfx::ScaleSize(buffer_size_
, 1.0f
/ device_scale_factor
));
136 layer
->SetBounds(device_scale_adjusted_size
);
139 // Manually manage background layer for transparent webview.
141 background_layer_
->SetIsDrawable(false);
144 void BrowserPluginCompositingHelper::MailboxReleased(
145 SwapBuffersInfo mailbox
,
147 bool lost_resource
) {
148 if (mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
) {
149 delete mailbox
.shared_memory
;
150 mailbox
.shared_memory
= NULL
;
151 } else if (lost_resource
) {
152 // Reset mailbox's name if the resource was lost.
153 mailbox
.name
.SetZero();
156 // This means the GPU process crashed or guest crashed.
157 if (last_host_id_
!= mailbox
.host_id
||
158 last_output_surface_id_
!= mailbox
.output_surface_id
||
159 last_route_id_
!= mailbox
.route_id
)
162 if (mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
)
163 unacked_software_frames_
.push_back(mailbox
.software_frame_id
);
165 // We need to send an ACK to for every buffer sent to us.
166 // However, if a buffer is freed up from
167 // the compositor in cases like switching back to SW mode without a new
168 // buffer arriving, no ACK is needed.
170 last_mailbox_valid_
= false;
173 ack_pending_
= false;
174 switch (mailbox
.type
) {
175 case TEXTURE_IMAGE_TRANSPORT
: {
176 std::string
mailbox_name(reinterpret_cast<const char*>(mailbox
.name
.name
),
177 sizeof(mailbox
.name
.name
));
178 FrameHostMsg_BuffersSwappedACK_Params params
;
179 params
.gpu_host_id
= mailbox
.host_id
;
180 params
.gpu_route_id
= mailbox
.route_id
;
181 params
.mailbox_name
= mailbox_name
;
182 params
.sync_point
= sync_point
;
183 browser_plugin_manager_
->Send(
184 new BrowserPluginHostMsg_BuffersSwappedACK(
190 case GL_COMPOSITOR_FRAME
: {
191 FrameHostMsg_CompositorFrameSwappedACK_Params params
;
192 params
.producing_host_id
= mailbox
.host_id
;
193 params
.producing_route_id
= mailbox
.route_id
;
194 params
.output_surface_id
= mailbox
.output_surface_id
;
195 params
.ack
.gl_frame_data
.reset(new cc::GLFrameData());
196 params
.ack
.gl_frame_data
->mailbox
= mailbox
.name
;
197 params
.ack
.gl_frame_data
->size
= mailbox
.size
;
198 params
.ack
.gl_frame_data
->sync_point
= sync_point
;
200 browser_plugin_manager_
->Send(
201 new BrowserPluginHostMsg_CompositorFrameSwappedACK(
207 case SOFTWARE_COMPOSITOR_FRAME
:
212 void BrowserPluginCompositingHelper::OnContainerDestroy() {
214 container_
->setWebLayer(NULL
);
217 if (resource_collection_
)
218 resource_collection_
->SetClient(NULL
);
220 ack_pending_
= false;
221 software_ack_pending_
= false;
222 resource_collection_
= NULL
;
223 frame_provider_
= NULL
;
224 texture_layer_
= NULL
;
225 delegated_layer_
= NULL
;
226 background_layer_
= NULL
;
230 void BrowserPluginCompositingHelper::OnBuffersSwappedPrivate(
231 const SwapBuffersInfo
& mailbox
,
233 float device_scale_factor
) {
234 DCHECK(!delegated_layer_
.get());
235 // If these mismatch, we are either just starting up, GPU process crashed or
236 // guest renderer crashed.
237 // In this case, we are communicating with a new image transport
238 // surface and must ACK with the new ID's and an empty mailbox.
239 if (last_route_id_
!= mailbox
.route_id
||
240 last_output_surface_id_
!= mailbox
.output_surface_id
||
241 last_host_id_
!= mailbox
.host_id
)
242 last_mailbox_valid_
= false;
244 last_route_id_
= mailbox
.route_id
;
245 last_output_surface_id_
= mailbox
.output_surface_id
;
246 last_host_id_
= mailbox
.host_id
;
249 // Browser plugin getting destroyed, do a fast ACK.
250 if (!background_layer_
.get()) {
251 MailboxReleased(mailbox
, sync_point
, false);
255 if (!texture_layer_
.get()) {
256 texture_layer_
= cc::TextureLayer::CreateForMailbox(NULL
);
257 texture_layer_
->SetIsDrawable(true);
258 SetContentsOpaque(opaque_
);
260 background_layer_
->AddChild(texture_layer_
);
263 // The size of browser plugin container is not always equal to the size
264 // of the buffer that arrives here. This could be for a number of reasons,
265 // including autosize and a resize in progress.
266 // During resize, the container size changes first and then some time
267 // later, a new buffer with updated size will arrive. During this process,
268 // we need to make sure that things are still displayed pixel perfect.
269 // We accomplish this by modifying bounds of the texture layer only
270 // when a new buffer arrives.
271 // Visually, this will either display a smaller part of the buffer
272 // or introduce a gutter around it.
273 CheckSizeAndAdjustLayerProperties(mailbox
.size
,
275 texture_layer_
.get());
277 bool is_software_frame
= mailbox
.type
== SOFTWARE_COMPOSITOR_FRAME
;
278 bool current_mailbox_valid
= is_software_frame
?
279 mailbox
.shared_memory
!= NULL
: !mailbox
.name
.IsZero();
280 if (!is_software_frame
&& !last_mailbox_valid_
) {
281 SwapBuffersInfo empty_info
= mailbox
;
282 empty_info
.name
.SetZero();
283 MailboxReleased(empty_info
, 0, false);
284 if (!current_mailbox_valid
)
288 cc::TextureMailbox texture_mailbox
;
289 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
290 if (current_mailbox_valid
) {
291 release_callback
= cc::SingleReleaseCallback::Create(
292 base::Bind(&BrowserPluginCompositingHelper::MailboxReleased
,
293 scoped_refptr
<BrowserPluginCompositingHelper
>(this),
295 if (is_software_frame
)
296 texture_mailbox
= cc::TextureMailbox(mailbox
.shared_memory
, mailbox
.size
);
298 texture_mailbox
= cc::TextureMailbox(mailbox
.name
, sync_point
);
301 texture_layer_
->SetFlipped(!is_software_frame
);
302 texture_layer_
->SetTextureMailbox(texture_mailbox
, release_callback
.Pass());
303 texture_layer_
->SetNeedsDisplay();
304 last_mailbox_valid_
= current_mailbox_valid
;
307 void BrowserPluginCompositingHelper::OnBuffersSwapped(
308 const gfx::Size
& size
,
309 const std::string
& mailbox_name
,
312 float device_scale_factor
) {
313 SwapBuffersInfo swap_info
;
314 swap_info
.name
.SetName(reinterpret_cast<const int8
*>(mailbox_name
.data()));
315 swap_info
.type
= TEXTURE_IMAGE_TRANSPORT
;
316 swap_info
.size
= size
;
317 swap_info
.route_id
= gpu_route_id
;
318 swap_info
.output_surface_id
= 0;
319 swap_info
.host_id
= gpu_host_id
;
320 OnBuffersSwappedPrivate(swap_info
, 0, device_scale_factor
);
323 void BrowserPluginCompositingHelper::OnCompositorFrameSwapped(
324 scoped_ptr
<cc::CompositorFrame
> frame
,
326 uint32 output_surface_id
,
328 if (frame
->gl_frame_data
) {
329 SwapBuffersInfo swap_info
;
330 swap_info
.name
= frame
->gl_frame_data
->mailbox
;
331 swap_info
.type
= GL_COMPOSITOR_FRAME
;
332 swap_info
.size
= frame
->gl_frame_data
->size
;
333 swap_info
.route_id
= route_id
;
334 swap_info
.output_surface_id
= output_surface_id
;
335 swap_info
.host_id
= host_id
;
336 OnBuffersSwappedPrivate(swap_info
,
337 frame
->gl_frame_data
->sync_point
,
338 frame
->metadata
.device_scale_factor
);
342 if (frame
->software_frame_data
) {
343 cc::SoftwareFrameData
* frame_data
= frame
->software_frame_data
.get();
345 SwapBuffersInfo swap_info
;
346 swap_info
.type
= SOFTWARE_COMPOSITOR_FRAME
;
347 swap_info
.size
= frame_data
->size
;
348 swap_info
.route_id
= route_id
;
349 swap_info
.output_surface_id
= output_surface_id
;
350 swap_info
.host_id
= host_id
;
351 swap_info
.software_frame_id
= frame_data
->id
;
353 scoped_ptr
<base::SharedMemory
> shared_memory(
354 new base::SharedMemory(frame_data
->handle
, true));
355 const size_t size_in_bytes
= 4 * frame_data
->size
.GetArea();
356 if (!shared_memory
->Map(size_in_bytes
)) {
357 LOG(ERROR
) << "Failed to map shared memory of size "
359 // Send ACK right away.
360 software_ack_pending_
= true;
361 MailboxReleased(swap_info
, 0, false);
362 DidCommitCompositorFrame();
366 swap_info
.shared_memory
= shared_memory
.release();
367 OnBuffersSwappedPrivate(swap_info
, 0,
368 frame
->metadata
.device_scale_factor
);
369 software_ack_pending_
= true;
370 last_route_id_
= route_id
;
371 last_output_surface_id_
= output_surface_id
;
372 last_host_id_
= host_id
;
376 DCHECK(!texture_layer_
.get());
378 cc::DelegatedFrameData
* frame_data
= frame
->delegated_frame_data
.get();
379 // Do nothing if we are getting destroyed or have no frame data.
380 if (!frame_data
|| !background_layer_
)
383 DCHECK(!frame_data
->render_pass_list
.empty());
384 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
385 gfx::Size frame_size
= root_pass
->output_rect
.size();
387 if (last_route_id_
!= route_id
||
388 last_output_surface_id_
!= output_surface_id
||
389 last_host_id_
!= host_id
) {
390 // Resource ids are scoped by the output surface.
391 // If the originating output surface doesn't match the last one, it
392 // indicates the guest's output surface may have been recreated, in which
393 // case we should recreate the DelegatedRendererLayer, to avoid matching
394 // resources from the old one with resources from the new one which would
396 frame_provider_
= NULL
;
398 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
399 // any resources from the old output surface with the new output surface id.
400 if (resource_collection_
) {
401 resource_collection_
->SetClient(NULL
);
403 if (resource_collection_
->LoseAllResources())
404 SendReturnedDelegatedResources();
405 resource_collection_
= NULL
;
407 last_output_surface_id_
= output_surface_id
;
408 last_route_id_
= route_id
;
409 last_host_id_
= host_id
;
411 if (!resource_collection_
) {
412 resource_collection_
= new cc::DelegatedFrameResourceCollection
;
413 resource_collection_
->SetClient(this);
415 if (!frame_provider_
.get() || frame_provider_
->frame_size() != frame_size
) {
416 frame_provider_
= new cc::DelegatedFrameProvider(
417 resource_collection_
.get(), frame
->delegated_frame_data
.Pass());
418 if (delegated_layer_
.get())
419 delegated_layer_
->RemoveFromParent();
421 cc::DelegatedRendererLayer::Create(frame_provider_
.get());
422 delegated_layer_
->SetIsDrawable(true);
423 SetContentsOpaque(opaque_
);
424 background_layer_
->AddChild(delegated_layer_
);
426 frame_provider_
->SetFrameData(frame
->delegated_frame_data
.Pass());
429 CheckSizeAndAdjustLayerProperties(
430 frame_data
->render_pass_list
.back()->output_rect
.size(),
431 frame
->metadata
.device_scale_factor
,
432 delegated_layer_
.get());
437 void BrowserPluginCompositingHelper::UpdateVisibility(bool visible
) {
438 if (texture_layer_
.get())
439 texture_layer_
->SetIsDrawable(visible
);
440 if (delegated_layer_
.get())
441 delegated_layer_
->SetIsDrawable(visible
);
444 void BrowserPluginCompositingHelper::UnusedResourcesAreAvailable() {
448 SendReturnedDelegatedResources();
451 void BrowserPluginCompositingHelper::SendReturnedDelegatedResources() {
452 cc::CompositorFrameAck ack
;
453 if (resource_collection_
)
454 resource_collection_
->TakeUnusedResourcesForChildCompositor(&ack
.resources
);
455 DCHECK(!ack
.resources
.empty());
457 browser_plugin_manager_
->Send(
458 new BrowserPluginHostMsg_ReclaimCompositorResources(
462 last_output_surface_id_
,
467 void BrowserPluginCompositingHelper::SetContentsOpaque(bool opaque
) {
470 if (texture_layer_
.get())
471 texture_layer_
->SetContentsOpaque(opaque_
);
472 if (delegated_layer_
.get())
473 delegated_layer_
->SetContentsOpaque(opaque_
);
476 void BrowserPluginCompositingHelper::CopyFromCompositingSurfaceHasResult(
479 scoped_ptr
<cc::CopyOutputResult
> result
) {
480 scoped_ptr
<SkBitmap
> bitmap
;
481 if (result
&& result
->HasBitmap() && !result
->size().IsEmpty())
482 bitmap
= result
->TakeBitmap();
484 SkBitmap resized_bitmap
;
486 resized_bitmap
= skia::ImageOperations::Resize(*bitmap
,
487 skia::ImageOperations::RESIZE_BEST
,
491 browser_plugin_manager_
->Send(
492 new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
493 host_routing_id_
, instance_id_
, request_id
,
497 } // namespace content