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_manager.h"
20 #include "content/renderer/render_frame_impl.h"
21 #include "content/renderer/render_thread_impl.h"
22 #include "skia/ext/image_operations.h"
23 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
24 #include "third_party/WebKit/public/web/WebFrame.h"
25 #include "third_party/WebKit/public/web/WebPluginContainer.h"
26 #include "third_party/khronos/GLES2/gl2.h"
27 #include "ui/gfx/size_conversions.h"
28 #include "ui/gfx/skia_util.h"
29 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
33 ChildFrameCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
38 shared_memory(NULL) {}
40 ChildFrameCompositingHelper*
41 ChildFrameCompositingHelper::CreateCompositingHelperForBrowserPlugin(
42 blink::WebPluginContainer* container,
43 BrowserPluginManager* manager,
45 int host_routing_id) {
46 return new ChildFrameCompositingHelper(
47 container, NULL, manager, NULL, instance_id, host_routing_id);
50 ChildFrameCompositingHelper*
51 ChildFrameCompositingHelper::CreateCompositingHelperForRenderFrame(
52 blink::WebFrame* frame,
53 RenderFrameImpl* render_frame,
54 int host_routing_id) {
55 return new ChildFrameCompositingHelper(
56 NULL, frame, NULL, render_frame, 0, host_routing_id);
59 ChildFrameCompositingHelper::ChildFrameCompositingHelper(
60 blink::WebPluginContainer* container,
61 blink::WebFrame* frame,
62 BrowserPluginManager* manager,
63 RenderFrameImpl* render_frame,
66 : instance_id_(instance_id),
67 host_routing_id_(host_routing_id),
69 last_output_surface_id_(0),
71 last_mailbox_valid_(false),
73 software_ack_pending_(false),
75 container_(container),
77 browser_plugin_manager_(manager),
78 render_frame_(render_frame) {}
80 ChildFrameCompositingHelper::~ChildFrameCompositingHelper() {}
82 void ChildFrameCompositingHelper::SendCompositorFrameSwappedACKToBrowser(
83 FrameHostMsg_CompositorFrameSwappedACK_Params& params) {
84 // This function will be removed when BrowserPluginManager is removed and
85 // BrowserPlugin is modified to use a RenderFrame.
86 if (browser_plugin_manager_) {
87 browser_plugin_manager_->Send(
88 new BrowserPluginHostMsg_CompositorFrameSwappedACK(
89 host_routing_id_, instance_id_, params));
91 DCHECK(render_frame_);
93 new FrameHostMsg_CompositorFrameSwappedACK(host_routing_id_, params));
97 void ChildFrameCompositingHelper::SendBuffersSwappedACKToBrowser(
98 FrameHostMsg_BuffersSwappedACK_Params& params) {
99 // This function will be removed when BrowserPluginManager is removed and
100 // BrowserPlugin is modified to use a RenderFrame.
101 if (browser_plugin_manager_) {
102 browser_plugin_manager_->Send(new BrowserPluginHostMsg_BuffersSwappedACK(
103 host_routing_id_, instance_id_, params));
105 DCHECK(render_frame_);
107 new FrameHostMsg_BuffersSwappedACK(host_routing_id_, params));
111 void ChildFrameCompositingHelper::SendReclaimCompositorResourcesToBrowser(
112 FrameHostMsg_ReclaimCompositorResources_Params& params) {
113 // This function will be removed when BrowserPluginManager is removed and
114 // BrowserPlugin is modified to use a RenderFrame.
115 if (browser_plugin_manager_) {
116 browser_plugin_manager_->Send(
117 new BrowserPluginHostMsg_ReclaimCompositorResources(
118 host_routing_id_, instance_id_, params));
120 DCHECK(render_frame_);
122 new FrameHostMsg_ReclaimCompositorResources(host_routing_id_, params));
126 void ChildFrameCompositingHelper::CopyFromCompositingSurface(
128 gfx::Rect source_rect,
129 gfx::Size dest_size) {
130 CHECK(background_layer_);
131 scoped_ptr<cc::CopyOutputRequest> request =
132 cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
133 &ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult,
137 request->set_area(source_rect);
138 background_layer_->RequestCopyOfOutput(request.Pass());
141 void ChildFrameCompositingHelper::DidCommitCompositorFrame() {
142 if (software_ack_pending_) {
143 FrameHostMsg_CompositorFrameSwappedACK_Params params;
144 params.producing_host_id = last_host_id_;
145 params.producing_route_id = last_route_id_;
146 params.output_surface_id = last_output_surface_id_;
147 if (!unacked_software_frames_.empty()) {
148 params.ack.last_software_frame_id = unacked_software_frames_.back();
149 unacked_software_frames_.pop_back();
152 SendCompositorFrameSwappedACKToBrowser(params);
154 software_ack_pending_ = false;
156 if (!resource_collection_.get() || !ack_pending_)
159 FrameHostMsg_CompositorFrameSwappedACK_Params params;
160 params.producing_host_id = last_host_id_;
161 params.producing_route_id = last_route_id_;
162 params.output_surface_id = last_output_surface_id_;
163 resource_collection_->TakeUnusedResourcesForChildCompositor(
164 ¶ms.ack.resources);
166 SendCompositorFrameSwappedACKToBrowser(params);
168 ack_pending_ = false;
171 void ChildFrameCompositingHelper::EnableCompositing(bool enable) {
172 if (enable && !background_layer_.get()) {
173 background_layer_ = cc::SolidColorLayer::Create();
174 background_layer_->SetMasksToBounds(true);
175 background_layer_->SetBackgroundColor(
176 SkColorSetARGBInline(255, 255, 255, 255));
177 web_layer_.reset(new webkit::WebLayerImpl(background_layer_));
181 container_->setWebLayer(enable ? web_layer_.get() : NULL);
183 frame_->setRemoteWebLayer(enable ? web_layer_.get() : NULL);
187 void ChildFrameCompositingHelper::CheckSizeAndAdjustLayerProperties(
188 const gfx::Size& new_size,
189 float device_scale_factor,
191 if (buffer_size_ != new_size) {
192 buffer_size_ = new_size;
193 // The container size is in DIP, so is the layer size.
194 // Buffer size is in physical pixels, so we need to adjust
195 // it by the device scale factor.
196 gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize(
197 gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor));
198 layer->SetBounds(device_scale_adjusted_size);
201 // Manually manage background layer for transparent webview.
203 background_layer_->SetIsDrawable(false);
206 void ChildFrameCompositingHelper::MailboxReleased(SwapBuffersInfo mailbox,
208 bool lost_resource) {
209 if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME) {
210 delete mailbox.shared_memory;
211 mailbox.shared_memory = NULL;
212 } else if (lost_resource) {
213 // Reset mailbox's name if the resource was lost.
214 mailbox.name.SetZero();
217 // This means the GPU process crashed or guest crashed.
218 if (last_host_id_ != mailbox.host_id ||
219 last_output_surface_id_ != mailbox.output_surface_id ||
220 last_route_id_ != mailbox.route_id)
223 if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME)
224 unacked_software_frames_.push_back(mailbox.software_frame_id);
226 // We need to send an ACK to for every buffer sent to us.
227 // However, if a buffer is freed up from
228 // the compositor in cases like switching back to SW mode without a new
229 // buffer arriving, no ACK is needed.
231 last_mailbox_valid_ = false;
234 ack_pending_ = false;
235 switch (mailbox.type) {
236 case TEXTURE_IMAGE_TRANSPORT: {
237 std::string mailbox_name(reinterpret_cast<const char*>(mailbox.name.name),
238 sizeof(mailbox.name.name));
239 FrameHostMsg_BuffersSwappedACK_Params params;
240 params.gpu_host_id = mailbox.host_id;
241 params.gpu_route_id = mailbox.route_id;
242 params.mailbox_name = mailbox_name;
243 params.sync_point = sync_point;
244 SendBuffersSwappedACKToBrowser(params);
247 case GL_COMPOSITOR_FRAME: {
248 FrameHostMsg_CompositorFrameSwappedACK_Params params;
249 params.producing_host_id = mailbox.host_id;
250 params.producing_route_id = mailbox.route_id;
251 params.output_surface_id = mailbox.output_surface_id;
252 params.ack.gl_frame_data.reset(new cc::GLFrameData());
253 params.ack.gl_frame_data->mailbox = mailbox.name;
254 params.ack.gl_frame_data->size = mailbox.size;
255 params.ack.gl_frame_data->sync_point = sync_point;
256 SendCompositorFrameSwappedACKToBrowser(params);
259 case SOFTWARE_COMPOSITOR_FRAME:
264 void ChildFrameCompositingHelper::OnContainerDestroy() {
266 container_->setWebLayer(NULL);
269 if (resource_collection_)
270 resource_collection_->SetClient(NULL);
272 ack_pending_ = false;
273 software_ack_pending_ = false;
274 resource_collection_ = NULL;
275 frame_provider_ = NULL;
276 texture_layer_ = NULL;
277 delegated_layer_ = NULL;
278 background_layer_ = NULL;
282 void ChildFrameCompositingHelper::OnBuffersSwappedPrivate(
283 const SwapBuffersInfo& mailbox,
285 float device_scale_factor) {
286 DCHECK(!delegated_layer_.get());
287 // If these mismatch, we are either just starting up, GPU process crashed or
288 // guest renderer crashed.
289 // In this case, we are communicating with a new image transport
290 // surface and must ACK with the new ID's and an empty mailbox.
291 if (last_route_id_ != mailbox.route_id ||
292 last_output_surface_id_ != mailbox.output_surface_id ||
293 last_host_id_ != mailbox.host_id)
294 last_mailbox_valid_ = false;
296 last_route_id_ = mailbox.route_id;
297 last_output_surface_id_ = mailbox.output_surface_id;
298 last_host_id_ = mailbox.host_id;
301 // Browser plugin getting destroyed, do a fast ACK.
302 if (!background_layer_.get()) {
303 MailboxReleased(mailbox, sync_point, false);
307 if (!texture_layer_.get()) {
308 texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL);
309 texture_layer_->SetIsDrawable(true);
310 SetContentsOpaque(opaque_);
312 background_layer_->AddChild(texture_layer_);
315 // The size of browser plugin container is not always equal to the size
316 // of the buffer that arrives here. This could be for a number of reasons,
317 // including autosize and a resize in progress.
318 // During resize, the container size changes first and then some time
319 // later, a new buffer with updated size will arrive. During this process,
320 // we need to make sure that things are still displayed pixel perfect.
321 // We accomplish this by modifying bounds of the texture layer only
322 // when a new buffer arrives.
323 // Visually, this will either display a smaller part of the buffer
324 // or introduce a gutter around it.
325 CheckSizeAndAdjustLayerProperties(
326 mailbox.size, device_scale_factor, texture_layer_.get());
328 bool is_software_frame = mailbox.type == SOFTWARE_COMPOSITOR_FRAME;
329 bool current_mailbox_valid = is_software_frame ? mailbox.shared_memory != NULL
330 : !mailbox.name.IsZero();
331 if (!is_software_frame && !last_mailbox_valid_) {
332 SwapBuffersInfo empty_info = mailbox;
333 empty_info.name.SetZero();
334 MailboxReleased(empty_info, 0, false);
335 if (!current_mailbox_valid)
339 cc::TextureMailbox texture_mailbox;
340 scoped_ptr<cc::SingleReleaseCallback> release_callback;
341 if (current_mailbox_valid) {
343 cc::SingleReleaseCallback::Create(
344 base::Bind(&ChildFrameCompositingHelper::MailboxReleased,
345 scoped_refptr<ChildFrameCompositingHelper>(this),
347 if (is_software_frame)
348 texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, mailbox.size);
350 texture_mailbox = cc::TextureMailbox(mailbox.name, sync_point);
353 texture_layer_->SetFlipped(!is_software_frame);
354 texture_layer_->SetTextureMailbox(texture_mailbox, release_callback.Pass());
355 texture_layer_->SetNeedsDisplay();
356 last_mailbox_valid_ = current_mailbox_valid;
359 void ChildFrameCompositingHelper::OnBuffersSwapped(
360 const gfx::Size& size,
361 const std::string& mailbox_name,
364 float device_scale_factor) {
365 SwapBuffersInfo swap_info;
366 swap_info.name.SetName(reinterpret_cast<const int8*>(mailbox_name.data()));
367 swap_info.type = TEXTURE_IMAGE_TRANSPORT;
368 swap_info.size = size;
369 swap_info.route_id = gpu_route_id;
370 swap_info.output_surface_id = 0;
371 swap_info.host_id = gpu_host_id;
372 OnBuffersSwappedPrivate(swap_info, 0, device_scale_factor);
375 void ChildFrameCompositingHelper::OnCompositorFrameSwapped(
376 scoped_ptr<cc::CompositorFrame> frame,
378 uint32 output_surface_id,
381 if (frame->gl_frame_data) {
382 SwapBuffersInfo swap_info;
383 swap_info.name = frame->gl_frame_data->mailbox;
384 swap_info.type = GL_COMPOSITOR_FRAME;
385 swap_info.size = frame->gl_frame_data->size;
386 swap_info.route_id = route_id;
387 swap_info.output_surface_id = output_surface_id;
388 swap_info.host_id = host_id;
389 OnBuffersSwappedPrivate(swap_info,
390 frame->gl_frame_data->sync_point,
391 frame->metadata.device_scale_factor);
395 if (frame->software_frame_data) {
396 cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();
398 SwapBuffersInfo swap_info;
399 swap_info.type = SOFTWARE_COMPOSITOR_FRAME;
400 swap_info.size = frame_data->size;
401 swap_info.route_id = route_id;
402 swap_info.output_surface_id = output_surface_id;
403 swap_info.host_id = host_id;
404 swap_info.software_frame_id = frame_data->id;
406 scoped_ptr<base::SharedMemory> shared_memory(
407 new base::SharedMemory(frame_data->handle, true));
408 const size_t size_in_bytes = 4 * frame_data->size.GetArea();
409 if (!shared_memory->Map(size_in_bytes)) {
410 LOG(ERROR) << "Failed to map shared memory of size " << size_in_bytes;
411 // Send ACK right away.
412 software_ack_pending_ = true;
413 MailboxReleased(swap_info, 0, false);
414 DidCommitCompositorFrame();
418 swap_info.shared_memory = shared_memory.release();
419 OnBuffersSwappedPrivate(swap_info, 0, frame->metadata.device_scale_factor);
420 software_ack_pending_ = true;
421 last_route_id_ = route_id;
422 last_output_surface_id_ = output_surface_id;
423 last_host_id_ = host_id;
427 DCHECK(!texture_layer_.get());
429 cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
430 // Do nothing if we are getting destroyed or have no frame data.
431 if (!frame_data || !background_layer_)
434 DCHECK(!frame_data->render_pass_list.empty());
435 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
436 gfx::Size frame_size = root_pass->output_rect.size();
438 if (last_route_id_ != route_id ||
439 last_output_surface_id_ != output_surface_id ||
440 last_host_id_ != host_id) {
441 // Resource ids are scoped by the output surface.
442 // If the originating output surface doesn't match the last one, it
443 // indicates the guest's output surface may have been recreated, in which
444 // case we should recreate the DelegatedRendererLayer, to avoid matching
445 // resources from the old one with resources from the new one which would
447 frame_provider_ = NULL;
449 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
450 // any resources from the old output surface with the new output surface id.
451 if (resource_collection_) {
452 resource_collection_->SetClient(NULL);
454 if (resource_collection_->LoseAllResources())
455 SendReturnedDelegatedResources();
456 resource_collection_ = NULL;
458 last_output_surface_id_ = output_surface_id;
459 last_route_id_ = route_id;
460 last_host_id_ = host_id;
462 if (!resource_collection_) {
463 resource_collection_ = new cc::DelegatedFrameResourceCollection;
464 resource_collection_->SetClient(this);
466 if (!frame_provider_.get() || frame_provider_->frame_size() != frame_size) {
467 frame_provider_ = new cc::DelegatedFrameProvider(
468 resource_collection_.get(), frame->delegated_frame_data.Pass());
469 if (delegated_layer_.get())
470 delegated_layer_->RemoveFromParent();
472 cc::DelegatedRendererLayer::Create(frame_provider_.get());
473 delegated_layer_->SetIsDrawable(true);
474 SetContentsOpaque(opaque_);
475 background_layer_->AddChild(delegated_layer_);
477 frame_provider_->SetFrameData(frame->delegated_frame_data.Pass());
480 CheckSizeAndAdjustLayerProperties(
481 frame_data->render_pass_list.back()->output_rect.size(),
482 frame->metadata.device_scale_factor,
483 delegated_layer_.get());
488 void ChildFrameCompositingHelper::UpdateVisibility(bool visible) {
489 if (texture_layer_.get())
490 texture_layer_->SetIsDrawable(visible);
491 if (delegated_layer_.get())
492 delegated_layer_->SetIsDrawable(visible);
495 void ChildFrameCompositingHelper::UnusedResourcesAreAvailable() {
499 SendReturnedDelegatedResources();
502 void ChildFrameCompositingHelper::SendReturnedDelegatedResources() {
503 FrameHostMsg_ReclaimCompositorResources_Params params;
504 if (resource_collection_)
505 resource_collection_->TakeUnusedResourcesForChildCompositor(
506 ¶ms.ack.resources);
507 DCHECK(!params.ack.resources.empty());
509 params.route_id = last_route_id_;
510 params.output_surface_id = last_output_surface_id_;
511 params.renderer_host_id = last_host_id_;
512 SendReclaimCompositorResourcesToBrowser(params);
515 void ChildFrameCompositingHelper::SetContentsOpaque(bool opaque) {
518 if (texture_layer_.get())
519 texture_layer_->SetContentsOpaque(opaque_);
520 if (delegated_layer_.get())
521 delegated_layer_->SetContentsOpaque(opaque_);
524 void ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult(
527 scoped_ptr<cc::CopyOutputResult> result) {
528 scoped_ptr<SkBitmap> bitmap;
529 if (result && result->HasBitmap() && !result->size().IsEmpty())
530 bitmap = result->TakeBitmap();
532 SkBitmap resized_bitmap;
535 skia::ImageOperations::Resize(*bitmap,
536 skia::ImageOperations::RESIZE_BEST,
540 browser_plugin_manager_->Send(
541 new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
542 host_routing_id_, instance_id_, request_id, resized_bitmap));
545 } // namespace content