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