- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / browser_plugin / browser_plugin_compositing_helper.cc
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.
4
5 #include "content/renderer/browser_plugin/browser_plugin_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/resources/single_release_callback.h"
14 #include "content/common/browser_plugin/browser_plugin_messages.h"
15 #include "content/common/gpu/client/context_provider_command_buffer.h"
16 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
17 #include "content/renderer/render_thread_impl.h"
18 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
19 #include "third_party/WebKit/public/web/WebPluginContainer.h"
20 #include "third_party/khronos/GLES2/gl2.h"
21 #include "ui/gfx/size_conversions.h"
22 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
23
24 namespace content {
25
26 BrowserPluginCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
27     : route_id(0),
28       output_surface_id(0),
29       host_id(0),
30       software_frame_id(0),
31       shared_memory(NULL) {
32 }
33
34 BrowserPluginCompositingHelper::BrowserPluginCompositingHelper(
35     WebKit::WebPluginContainer* container,
36     BrowserPluginManager* manager,
37     int instance_id,
38     int host_routing_id)
39     : instance_id_(instance_id),
40       host_routing_id_(host_routing_id),
41       last_route_id_(0),
42       last_output_surface_id_(0),
43       last_host_id_(0),
44       last_mailbox_valid_(false),
45       ack_pending_(true),
46       software_ack_pending_(false),
47       container_(container),
48       browser_plugin_manager_(manager) {
49 }
50
51 BrowserPluginCompositingHelper::~BrowserPluginCompositingHelper() {
52 }
53
54 void BrowserPluginCompositingHelper::DidCommitCompositorFrame() {
55   if (software_ack_pending_) {
56     cc::CompositorFrameAck ack;
57     if (!unacked_software_frames_.empty()) {
58       ack.last_software_frame_id = unacked_software_frames_.back();
59       unacked_software_frames_.pop_back();
60     }
61
62     browser_plugin_manager_->Send(
63         new BrowserPluginHostMsg_CompositorFrameACK(
64             host_routing_id_,
65             instance_id_,
66             last_route_id_,
67             last_output_surface_id_,
68             last_host_id_,
69             ack));
70
71     software_ack_pending_ = false;
72   }
73   if (!resource_collection_.get() || !ack_pending_)
74     return;
75
76   cc::CompositorFrameAck ack;
77   resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
78
79   browser_plugin_manager_->Send(
80       new BrowserPluginHostMsg_CompositorFrameACK(
81           host_routing_id_,
82           instance_id_,
83           last_route_id_,
84           last_output_surface_id_,
85           last_host_id_,
86           ack));
87
88   ack_pending_ = false;
89 }
90
91 void BrowserPluginCompositingHelper::EnableCompositing(bool enable) {
92   if (enable && !background_layer_.get()) {
93     background_layer_ = cc::SolidColorLayer::Create();
94     background_layer_->SetMasksToBounds(true);
95     background_layer_->SetBackgroundColor(
96         SkColorSetARGBInline(255, 255, 255, 255));
97     web_layer_.reset(new webkit::WebLayerImpl(background_layer_));
98   }
99
100   container_->setWebLayer(enable ? web_layer_.get() : NULL);
101 }
102
103 void BrowserPluginCompositingHelper::CheckSizeAndAdjustLayerBounds(
104     const gfx::Size& new_size,
105     float device_scale_factor,
106     cc::Layer* layer) {
107   if (buffer_size_ != new_size) {
108     buffer_size_ = new_size;
109     // The container size is in DIP, so is the layer size.
110     // Buffer size is in physical pixels, so we need to adjust
111     // it by the device scale factor.
112     gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize(
113         gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor));
114     layer->SetBounds(device_scale_adjusted_size);
115   }
116 }
117
118 void BrowserPluginCompositingHelper::MailboxReleased(
119     SwapBuffersInfo mailbox,
120     unsigned sync_point,
121     bool lost_resource) {
122   if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME) {
123     delete mailbox.shared_memory;
124     mailbox.shared_memory = NULL;
125   } else if (lost_resource) {
126     // Reset mailbox's name if the resource was lost.
127     mailbox.name.SetZero();
128   }
129
130   // This means the GPU process crashed or guest crashed.
131   if (last_host_id_ != mailbox.host_id ||
132       last_output_surface_id_ != mailbox.output_surface_id ||
133       last_route_id_ != mailbox.route_id)
134     return;
135
136   if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME)
137     unacked_software_frames_.push_back(mailbox.software_frame_id);
138
139   // We need to send an ACK to for every buffer sent to us.
140   // However, if a buffer is freed up from
141   // the compositor in cases like switching back to SW mode without a new
142   // buffer arriving, no ACK is needed.
143   if (!ack_pending_) {
144     last_mailbox_valid_ = false;
145     return;
146   }
147   ack_pending_ = false;
148   switch (mailbox.type) {
149     case TEXTURE_IMAGE_TRANSPORT: {
150       std::string mailbox_name(reinterpret_cast<const char*>(mailbox.name.name),
151                                sizeof(mailbox.name.name));
152       browser_plugin_manager_->Send(
153           new BrowserPluginHostMsg_BuffersSwappedACK(
154               host_routing_id_,
155               instance_id_,
156               mailbox.route_id,
157               mailbox.host_id,
158               mailbox_name,
159               sync_point));
160       break;
161     }
162     case GL_COMPOSITOR_FRAME: {
163       cc::CompositorFrameAck ack;
164       ack.gl_frame_data.reset(new cc::GLFrameData());
165       ack.gl_frame_data->mailbox = mailbox.name;
166       ack.gl_frame_data->size = mailbox.size;
167       ack.gl_frame_data->sync_point = sync_point;
168
169       browser_plugin_manager_->Send(
170          new BrowserPluginHostMsg_CompositorFrameACK(
171              host_routing_id_,
172              instance_id_,
173              mailbox.route_id,
174              mailbox.output_surface_id,
175              mailbox.host_id,
176              ack));
177       break;
178     }
179     case SOFTWARE_COMPOSITOR_FRAME:
180       break;
181   }
182 }
183
184 void BrowserPluginCompositingHelper::OnContainerDestroy() {
185   if (container_)
186     container_->setWebLayer(NULL);
187   container_ = NULL;
188
189   texture_layer_ = NULL;
190   delegated_layer_ = NULL;
191   background_layer_ = NULL;
192   web_layer_.reset();
193 }
194
195 void BrowserPluginCompositingHelper::OnBuffersSwappedPrivate(
196     const SwapBuffersInfo& mailbox,
197     unsigned sync_point,
198     float device_scale_factor) {
199   DCHECK(!delegated_layer_.get());
200   // If these mismatch, we are either just starting up, GPU process crashed or
201   // guest renderer crashed.
202   // In this case, we are communicating with a new image transport
203   // surface and must ACK with the new ID's and an empty mailbox.
204   if (last_route_id_ != mailbox.route_id ||
205       last_output_surface_id_ != mailbox.output_surface_id ||
206       last_host_id_ != mailbox.host_id)
207     last_mailbox_valid_ = false;
208
209   last_route_id_ = mailbox.route_id;
210   last_output_surface_id_ = mailbox.output_surface_id;
211   last_host_id_ = mailbox.host_id;
212
213   ack_pending_ = true;
214   // Browser plugin getting destroyed, do a fast ACK.
215   if (!background_layer_.get()) {
216     MailboxReleased(mailbox, sync_point, false);
217     return;
218   }
219
220   if (!texture_layer_.get()) {
221     texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL);
222     texture_layer_->SetIsDrawable(true);
223     texture_layer_->SetContentsOpaque(true);
224
225     background_layer_->AddChild(texture_layer_);
226   }
227
228   // The size of browser plugin container is not always equal to the size
229   // of the buffer that arrives here. This could be for a number of reasons,
230   // including autosize and a resize in progress.
231   // During resize, the container size changes first and then some time
232   // later, a new buffer with updated size will arrive. During this process,
233   // we need to make sure that things are still displayed pixel perfect.
234   // We accomplish this by modifying bounds of the texture layer only
235   // when a new buffer arrives.
236   // Visually, this will either display a smaller part of the buffer
237   // or introduce a gutter around it.
238   CheckSizeAndAdjustLayerBounds(mailbox.size,
239                                 device_scale_factor,
240                                 texture_layer_.get());
241
242   bool is_software_frame = mailbox.type == SOFTWARE_COMPOSITOR_FRAME;
243   bool current_mailbox_valid = is_software_frame ?
244       mailbox.shared_memory != NULL : !mailbox.name.IsZero();
245   if (!is_software_frame && !last_mailbox_valid_) {
246     SwapBuffersInfo empty_info = mailbox;
247     empty_info.name.SetZero();
248     MailboxReleased(empty_info, 0, false);
249     if (!current_mailbox_valid)
250       return;
251   }
252
253   cc::TextureMailbox texture_mailbox;
254   scoped_ptr<cc::SingleReleaseCallback> release_callback;
255   if (current_mailbox_valid) {
256     release_callback = cc::SingleReleaseCallback::Create(
257         base::Bind(&BrowserPluginCompositingHelper::MailboxReleased,
258                    scoped_refptr<BrowserPluginCompositingHelper>(this),
259                    mailbox)).Pass();
260     if (is_software_frame)
261       texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, mailbox.size);
262     else
263       texture_mailbox = cc::TextureMailbox(mailbox.name, sync_point);
264   }
265
266   texture_layer_->SetFlipped(!is_software_frame);
267   texture_layer_->SetTextureMailbox(texture_mailbox, release_callback.Pass());
268   texture_layer_->SetNeedsDisplay();
269   last_mailbox_valid_ = current_mailbox_valid;
270 }
271
272 void BrowserPluginCompositingHelper::OnBuffersSwapped(
273     const gfx::Size& size,
274     const std::string& mailbox_name,
275     int gpu_route_id,
276     int gpu_host_id,
277     float device_scale_factor) {
278   SwapBuffersInfo swap_info;
279   swap_info.name.SetName(reinterpret_cast<const int8*>(mailbox_name.data()));
280   swap_info.type = TEXTURE_IMAGE_TRANSPORT;
281   swap_info.size = size;
282   swap_info.route_id = gpu_route_id;
283   swap_info.output_surface_id = 0;
284   swap_info.host_id = gpu_host_id;
285   OnBuffersSwappedPrivate(swap_info, 0, device_scale_factor);
286 }
287
288 void BrowserPluginCompositingHelper::OnCompositorFrameSwapped(
289     scoped_ptr<cc::CompositorFrame> frame,
290     int route_id,
291     uint32 output_surface_id,
292     int host_id) {
293   if (frame->gl_frame_data) {
294     SwapBuffersInfo swap_info;
295     swap_info.name = frame->gl_frame_data->mailbox;
296     swap_info.type = GL_COMPOSITOR_FRAME;
297     swap_info.size = frame->gl_frame_data->size;
298     swap_info.route_id = route_id;
299     swap_info.output_surface_id = output_surface_id;
300     swap_info.host_id = host_id;
301     OnBuffersSwappedPrivate(swap_info,
302                             frame->gl_frame_data->sync_point,
303                             frame->metadata.device_scale_factor);
304     return;
305   }
306
307   if (frame->software_frame_data) {
308     cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();
309
310     SwapBuffersInfo swap_info;
311     swap_info.type = SOFTWARE_COMPOSITOR_FRAME;
312     swap_info.size = frame_data->size;
313     swap_info.route_id = route_id;
314     swap_info.output_surface_id = output_surface_id;
315     swap_info.host_id = host_id;
316     swap_info.software_frame_id = frame_data->id;
317
318     scoped_ptr<base::SharedMemory> shared_memory(
319         new base::SharedMemory(frame_data->handle, true));
320     const size_t size_in_bytes = 4 * frame_data->size.GetArea();
321     if (!shared_memory->Map(size_in_bytes)) {
322       LOG(ERROR) << "Failed to map shared memory of size "
323                  << size_in_bytes;
324       // Send ACK right away.
325       software_ack_pending_ = true;
326       MailboxReleased(swap_info, 0, false);
327       DidCommitCompositorFrame();
328       return;
329     }
330
331     swap_info.shared_memory = shared_memory.release();
332     OnBuffersSwappedPrivate(swap_info, 0,
333                             frame->metadata.device_scale_factor);
334     software_ack_pending_ = true;
335     last_route_id_ = route_id;
336     last_output_surface_id_ = output_surface_id;
337     last_host_id_ = host_id;
338     return;
339   }
340
341   DCHECK(!texture_layer_.get());
342
343   cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
344   if (!frame_data)
345     return;
346
347   DCHECK(!frame_data->render_pass_list.empty());
348   cc::RenderPass* root_pass = frame_data->render_pass_list.back();
349   gfx::Size frame_size = root_pass->output_rect.size();
350
351   if (!resource_collection_) {
352     resource_collection_ = new cc::DelegatedFrameResourceCollection;
353     // TODO(danakj): Could return resources sooner if we set a client here and
354     // listened for UnusedResourcesAreAvailable().
355   }
356   if (!frame_provider_.get() || frame_provider_->frame_size() != frame_size) {
357     frame_provider_ = new cc::DelegatedFrameProvider(
358         resource_collection_.get(), frame->delegated_frame_data.Pass());
359     if (delegated_layer_.get())
360       delegated_layer_->RemoveFromParent();
361     delegated_layer_ =
362         cc::DelegatedRendererLayer::Create(NULL, frame_provider_.get());
363     delegated_layer_->SetIsDrawable(true);
364     delegated_layer_->SetContentsOpaque(true);
365     background_layer_->AddChild(delegated_layer_);
366   } else {
367     frame_provider_->SetFrameData(frame->delegated_frame_data.Pass());
368   }
369
370   CheckSizeAndAdjustLayerBounds(
371       frame_data->render_pass_list.back()->output_rect.size(),
372       frame->metadata.device_scale_factor,
373       delegated_layer_.get());
374
375   last_route_id_ = route_id;
376   last_output_surface_id_ = output_surface_id;
377   last_host_id_ = host_id;
378   ack_pending_ = true;
379 }
380
381 void BrowserPluginCompositingHelper::UpdateVisibility(bool visible) {
382   if (texture_layer_.get())
383     texture_layer_->SetIsDrawable(visible);
384   if (delegated_layer_.get())
385     delegated_layer_->SetIsDrawable(visible);
386 }
387
388 }  // namespace content