Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / content / renderer / child_frame_compositing_helper.cc
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.
4
5 #include "content/renderer/child_frame_compositing_helper.h"
6
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"
30
31 namespace content {
32
33 ChildFrameCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
34     : route_id(0),
35       output_surface_id(0),
36       host_id(0),
37       software_frame_id(0),
38       shared_memory(NULL) {}
39
40 ChildFrameCompositingHelper*
41 ChildFrameCompositingHelper::CreateCompositingHelperForBrowserPlugin(
42     blink::WebPluginContainer* container,
43     BrowserPluginManager* manager,
44     int instance_id,
45     int host_routing_id) {
46   return new ChildFrameCompositingHelper(
47       container, NULL, manager, NULL, instance_id, host_routing_id);
48 }
49
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);
57 }
58
59 ChildFrameCompositingHelper::ChildFrameCompositingHelper(
60     blink::WebPluginContainer* container,
61     blink::WebFrame* frame,
62     BrowserPluginManager* manager,
63     RenderFrameImpl* render_frame,
64     int instance_id,
65     int host_routing_id)
66     : instance_id_(instance_id),
67       host_routing_id_(host_routing_id),
68       last_route_id_(0),
69       last_output_surface_id_(0),
70       last_host_id_(0),
71       last_mailbox_valid_(false),
72       ack_pending_(true),
73       software_ack_pending_(false),
74       opaque_(true),
75       container_(container),
76       frame_(frame),
77       browser_plugin_manager_(manager),
78       render_frame_(render_frame) {}
79
80 ChildFrameCompositingHelper::~ChildFrameCompositingHelper() {}
81
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));
90   } else {
91     DCHECK(render_frame_);
92     render_frame_->Send(
93         new FrameHostMsg_CompositorFrameSwappedACK(host_routing_id_, params));
94   }
95 }
96
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));
104   } else {
105     DCHECK(render_frame_);
106     render_frame_->Send(
107         new FrameHostMsg_BuffersSwappedACK(host_routing_id_, params));
108   }
109 }
110
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));
119   } else {
120     DCHECK(render_frame_);
121     render_frame_->Send(
122         new FrameHostMsg_ReclaimCompositorResources(host_routing_id_, params));
123   }
124 }
125
126 void ChildFrameCompositingHelper::CopyFromCompositingSurface(
127     int request_id,
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,
134           this,
135           request_id,
136           dest_size));
137   request->set_area(source_rect);
138   background_layer_->RequestCopyOfOutput(request.Pass());
139 }
140
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();
150     }
151
152     SendCompositorFrameSwappedACKToBrowser(params);
153
154     software_ack_pending_ = false;
155   }
156   if (!resource_collection_.get() || !ack_pending_)
157     return;
158
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       &params.ack.resources);
165
166   SendCompositorFrameSwappedACKToBrowser(params);
167
168   ack_pending_ = false;
169 }
170
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_));
178   }
179
180   if (container_) {
181     container_->setWebLayer(enable ? web_layer_.get() : NULL);
182   } else {
183     frame_->setRemoteWebLayer(enable ? web_layer_.get() : NULL);
184   }
185 }
186
187 void ChildFrameCompositingHelper::CheckSizeAndAdjustLayerProperties(
188     const gfx::Size& new_size,
189     float device_scale_factor,
190     cc::Layer* layer) {
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);
199   }
200
201   // Manually manage background layer for transparent webview.
202   if (!opaque_)
203     background_layer_->SetIsDrawable(false);
204 }
205
206 void ChildFrameCompositingHelper::MailboxReleased(SwapBuffersInfo mailbox,
207                                                   unsigned sync_point,
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();
215   }
216
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)
221     return;
222
223   if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME)
224     unacked_software_frames_.push_back(mailbox.software_frame_id);
225
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.
230   if (!ack_pending_) {
231     last_mailbox_valid_ = false;
232     return;
233   }
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);
245       break;
246     }
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);
257       break;
258     }
259     case SOFTWARE_COMPOSITOR_FRAME:
260       break;
261   }
262 }
263
264 void ChildFrameCompositingHelper::OnContainerDestroy() {
265   if (container_)
266     container_->setWebLayer(NULL);
267   container_ = NULL;
268
269   if (resource_collection_)
270     resource_collection_->SetClient(NULL);
271
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;
279   web_layer_.reset();
280 }
281
282 void ChildFrameCompositingHelper::OnBuffersSwappedPrivate(
283     const SwapBuffersInfo& mailbox,
284     unsigned sync_point,
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;
295
296   last_route_id_ = mailbox.route_id;
297   last_output_surface_id_ = mailbox.output_surface_id;
298   last_host_id_ = mailbox.host_id;
299
300   ack_pending_ = true;
301   // Browser plugin getting destroyed, do a fast ACK.
302   if (!background_layer_.get()) {
303     MailboxReleased(mailbox, sync_point, false);
304     return;
305   }
306
307   if (!texture_layer_.get()) {
308     texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL);
309     texture_layer_->SetIsDrawable(true);
310     SetContentsOpaque(opaque_);
311
312     background_layer_->AddChild(texture_layer_);
313   }
314
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());
327
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)
336       return;
337   }
338
339   cc::TextureMailbox texture_mailbox;
340   scoped_ptr<cc::SingleReleaseCallback> release_callback;
341   if (current_mailbox_valid) {
342     release_callback =
343         cc::SingleReleaseCallback::Create(
344             base::Bind(&ChildFrameCompositingHelper::MailboxReleased,
345                        scoped_refptr<ChildFrameCompositingHelper>(this),
346                        mailbox)).Pass();
347     if (is_software_frame)
348       texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, mailbox.size);
349     else
350       texture_mailbox = cc::TextureMailbox(mailbox.name, sync_point);
351   }
352
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;
357 }
358
359 void ChildFrameCompositingHelper::OnBuffersSwapped(
360     const gfx::Size& size,
361     const std::string& mailbox_name,
362     int gpu_route_id,
363     int gpu_host_id,
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);
373 }
374
375 void ChildFrameCompositingHelper::OnCompositorFrameSwapped(
376     scoped_ptr<cc::CompositorFrame> frame,
377     int route_id,
378     uint32 output_surface_id,
379     int host_id) {
380
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);
392     return;
393   }
394
395   if (frame->software_frame_data) {
396     cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();
397
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;
405
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();
415       return;
416     }
417
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;
424     return;
425   }
426
427   DCHECK(!texture_layer_.get());
428
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_)
432     return;
433
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();
437
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
446     // have the same id.
447     frame_provider_ = NULL;
448
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);
453
454       if (resource_collection_->LoseAllResources())
455         SendReturnedDelegatedResources();
456       resource_collection_ = NULL;
457     }
458     last_output_surface_id_ = output_surface_id;
459     last_route_id_ = route_id;
460     last_host_id_ = host_id;
461   }
462   if (!resource_collection_) {
463     resource_collection_ = new cc::DelegatedFrameResourceCollection;
464     resource_collection_->SetClient(this);
465   }
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();
471     delegated_layer_ =
472         cc::DelegatedRendererLayer::Create(frame_provider_.get());
473     delegated_layer_->SetIsDrawable(true);
474     SetContentsOpaque(opaque_);
475     background_layer_->AddChild(delegated_layer_);
476   } else {
477     frame_provider_->SetFrameData(frame->delegated_frame_data.Pass());
478   }
479
480   CheckSizeAndAdjustLayerProperties(
481       frame_data->render_pass_list.back()->output_rect.size(),
482       frame->metadata.device_scale_factor,
483       delegated_layer_.get());
484
485   ack_pending_ = true;
486 }
487
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);
493 }
494
495 void ChildFrameCompositingHelper::UnusedResourcesAreAvailable() {
496   if (ack_pending_)
497     return;
498
499   SendReturnedDelegatedResources();
500 }
501
502 void ChildFrameCompositingHelper::SendReturnedDelegatedResources() {
503   FrameHostMsg_ReclaimCompositorResources_Params params;
504   if (resource_collection_)
505     resource_collection_->TakeUnusedResourcesForChildCompositor(
506         &params.ack.resources);
507   DCHECK(!params.ack.resources.empty());
508
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);
513 }
514
515 void ChildFrameCompositingHelper::SetContentsOpaque(bool opaque) {
516   opaque_ = opaque;
517
518   if (texture_layer_.get())
519     texture_layer_->SetContentsOpaque(opaque_);
520   if (delegated_layer_.get())
521     delegated_layer_->SetContentsOpaque(opaque_);
522 }
523
524 void ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult(
525     int request_id,
526     gfx::Size dest_size,
527     scoped_ptr<cc::CopyOutputResult> result) {
528   scoped_ptr<SkBitmap> bitmap;
529   if (result && result->HasBitmap() && !result->size().IsEmpty())
530     bitmap = result->TakeBitmap();
531
532   SkBitmap resized_bitmap;
533   if (bitmap) {
534     resized_bitmap =
535         skia::ImageOperations::Resize(*bitmap,
536                                       skia::ImageOperations::RESIZE_BEST,
537                                       dest_size.width(),
538                                       dest_size.height());
539   }
540   browser_plugin_manager_->Send(
541       new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
542           host_routing_id_, instance_id_, request_id, resized_bitmap));
543 }
544
545 }  // namespace content