browser_plugin_compositing_helper.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/Source/Platform/chromium/public/WebGraphicsContext3D.h"
16#include "third_party/WebKit/Source/WebKit/chromium/public/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::BrowserPluginCompositingHelper(
24    WebKit::WebPluginContainer* container,
25    BrowserPluginManager* manager,
26    int instance_id,
27    int host_routing_id)
28    : instance_id_(instance_id),
29      host_routing_id_(host_routing_id),
30      last_route_id_(0),
31      last_host_id_(0),
32      last_mailbox_valid_(false),
33      ack_pending_(true),
34      ack_pending_for_crashed_guest_(false),
35      container_(container),
36      browser_plugin_manager_(manager) {
37}
38
39BrowserPluginCompositingHelper::~BrowserPluginCompositingHelper() {
40}
41
42void BrowserPluginCompositingHelper::DidCommitCompositorFrame() {
43  if (!delegated_layer_ || !ack_pending_)
44    return;
45
46  cc::CompositorFrameAck ack;
47  delegated_layer_->TakeUnusedResourcesForChildCompositor(&ack.resources);
48
49  browser_plugin_manager_->Send(
50      new BrowserPluginHostMsg_CompositorFrameACK(
51          host_routing_id_,
52          instance_id_,
53          last_route_id_,
54          last_host_id_,
55          ack));
56
57  ack_pending_ = false;
58}
59
60void BrowserPluginCompositingHelper::EnableCompositing(bool enable) {
61  if (enable && !background_layer_) {
62    background_layer_ = cc::SolidColorLayer::Create();
63    background_layer_->SetMasksToBounds(true);
64    background_layer_->SetBackgroundColor(
65        SkColorSetARGBInline(255, 255, 255, 255));
66    web_layer_.reset(new webkit::WebLayerImpl(background_layer_));
67  }
68
69  container_->setWebLayer(enable ? web_layer_.get() : NULL);
70}
71
72void BrowserPluginCompositingHelper::CheckSizeAndAdjustLayerBounds(
73    const gfx::Size& new_size,
74    float device_scale_factor,
75    cc::Layer* layer) {
76  if (buffer_size_ != new_size) {
77    buffer_size_ = new_size;
78    // The container size is in DIP, so is the layer size.
79    // Buffer size is in physical pixels, so we need to adjust
80    // it by the device scale factor.
81    gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize(
82        gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor));
83    layer->SetBounds(device_scale_adjusted_size);
84  }
85}
86
87// If we have a mailbox that was freed up from the compositor,
88// but we are not expected to return it to the guest renderer
89// via an ACK, we should free it because we now own it.
90// To free the mailbox memory, we need a context to consume it
91// into a texture ID and then delete this texture ID.
92// We use a shared graphics context accessible from the main
93// thread to do it.
94void BrowserPluginCompositingHelper::FreeMailboxMemory(
95    const std::string& mailbox_name,
96    unsigned sync_point) {
97  if (mailbox_name.empty())
98    return;
99
100  scoped_refptr<cc::ContextProvider> context_provider =
101      RenderThreadImpl::current()->OffscreenContextProviderForMainThread();
102  if (!context_provider)
103    return;
104
105  WebKit::WebGraphicsContext3D *context = context_provider->Context3d();
106  // When a buffer is released from the compositor, we also get a
107  // sync point that specifies when in the command buffer
108  // it's safe to use it again.
109  // If the sync point is non-zero, we need to tell our context
110  // to wait until this sync point is reached before we can safely
111  // delete the buffer.
112  if (sync_point)
113    context->waitSyncPoint(sync_point);
114
115  unsigned texture_id = context->createTexture();
116  context->bindTexture(GL_TEXTURE_2D, texture_id);
117  context->consumeTextureCHROMIUM(
118      GL_TEXTURE_2D,
119      reinterpret_cast<const int8*>(mailbox_name.data()));
120  context->deleteTexture(texture_id);
121}
122
123void BrowserPluginCompositingHelper::MailboxReleased(
124    const std::string& mailbox_name,
125    int gpu_route_id,
126    int gpu_host_id,
127    unsigned sync_point,
128    bool lost_resource) {
129  if (lost_resource) {
130    // Recurse with an empty mailbox if the one being released was lost.
131    MailboxReleased(std::string(), gpu_route_id, gpu_host_id, 0, false);
132    return;
133  }
134
135  // This means the GPU process crashed and we have nothing further to do.
136  // Nobody is expecting an ACK and the buffer doesn't need to be deleted
137  // because it went away with the GPU process.
138  if (last_host_id_ != gpu_host_id)
139    return;
140
141  // This means the guest crashed.
142  // Either ACK the last buffer, so texture transport could
143  // be destroyed of delete the mailbox if nobody wants it back.
144  if (last_route_id_ != gpu_route_id) {
145    if (!ack_pending_for_crashed_guest_) {
146      FreeMailboxMemory(mailbox_name, sync_point);
147    } else {
148      ack_pending_for_crashed_guest_ = false;
149      browser_plugin_manager_->Send(
150          new BrowserPluginHostMsg_BuffersSwappedACK(
151              host_routing_id_,
152              instance_id_,
153              gpu_route_id,
154              gpu_host_id,
155              mailbox_name,
156              sync_point));
157    }
158    return;
159  }
160
161  // We need to send an ACK to TextureImageTransportSurface
162  // for every buffer it sends us. However, if a buffer is freed up from
163  // the compositor in cases like switching back to SW mode without a new
164  // buffer arriving, no ACK is needed and we destroy this buffer.
165  if (!ack_pending_) {
166    FreeMailboxMemory(mailbox_name, sync_point);
167    last_mailbox_valid_ = false;
168    return;
169  }
170  ack_pending_ = false;
171  browser_plugin_manager_->Send(
172      new BrowserPluginHostMsg_BuffersSwappedACK(
173          host_routing_id_,
174          instance_id_,
175          gpu_route_id,
176          gpu_host_id,
177          mailbox_name,
178          sync_point));
179}
180
181void BrowserPluginCompositingHelper::OnContainerDestroy() {
182  if (container_)
183    container_->setWebLayer(NULL);
184  container_ = NULL;
185
186  texture_layer_ = NULL;
187  delegated_layer_ = NULL;
188  background_layer_ = NULL;
189  web_layer_.reset();
190}
191
192void BrowserPluginCompositingHelper::OnBuffersSwapped(
193    const gfx::Size& size,
194    const std::string& mailbox_name,
195    int gpu_route_id,
196    int gpu_host_id,
197    float device_scale_factor) {
198  DCHECK(!delegated_layer_);
199  // If the guest crashed but the GPU process didn't, we may still have
200  // a transport surface waiting on an ACK, which we must send to
201  // avoid leaking.
202  if (last_route_id_ != gpu_route_id && last_host_id_ == gpu_host_id)
203    ack_pending_for_crashed_guest_ = ack_pending_;
204
205  // If these mismatch, we are either just starting up, GPU process crashed or
206  // guest renderer crashed.
207  // In this case, we are communicating with a new image transport
208  // surface and must ACK with the new ID's and an empty mailbox.
209  if (last_route_id_ != gpu_route_id || last_host_id_ != gpu_host_id)
210    last_mailbox_valid_ = false;
211
212  last_route_id_ = gpu_route_id;
213  last_host_id_ = gpu_host_id;
214
215  ack_pending_ = true;
216  // Browser plugin getting destroyed, do a fast ACK.
217  if (!background_layer_) {
218    MailboxReleased(mailbox_name, gpu_route_id, gpu_host_id, 0, false);
219    return;
220  }
221
222  if (!texture_layer_) {
223    texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL);
224    texture_layer_->SetIsDrawable(true);
225    texture_layer_->SetContentsOpaque(true);
226
227    background_layer_->AddChild(texture_layer_);
228  }
229
230  // The size of browser plugin container is not always equal to the size
231  // of the buffer that arrives here. This could be for a number of reasons,
232  // including autosize and a resize in progress.
233  // During resize, the container size changes first and then some time
234  // later, a new buffer with updated size will arrive. During this process,
235  // we need to make sure that things are still displayed pixel perfect.
236  // We accomplish this by modifying bounds of the texture layer only
237  // when a new buffer arrives.
238  // Visually, this will either display a smaller part of the buffer
239  // or introduce a gutter around it.
240  CheckSizeAndAdjustLayerBounds(size,
241                                device_scale_factor,
242                                texture_layer_.get());
243
244  bool current_mailbox_valid = !mailbox_name.empty();
245  if (!last_mailbox_valid_) {
246    MailboxReleased(std::string(), gpu_route_id, gpu_host_id, 0, false);
247    if (!current_mailbox_valid)
248      return;
249  }
250
251  cc::TextureMailbox::ReleaseCallback callback;
252  if (current_mailbox_valid) {
253    callback = base::Bind(&BrowserPluginCompositingHelper::MailboxReleased,
254                          scoped_refptr<BrowserPluginCompositingHelper>(this),
255                          mailbox_name,
256                          gpu_route_id,
257                          gpu_host_id);
258  }
259  texture_layer_->SetTextureMailbox(cc::TextureMailbox(mailbox_name,
260                                                       callback));
261  texture_layer_->SetNeedsDisplay();
262  last_mailbox_valid_ = current_mailbox_valid;
263}
264
265void BrowserPluginCompositingHelper::OnCompositorFrameSwapped(
266    scoped_ptr<cc::CompositorFrame> frame,
267    int route_id,
268    int host_id) {
269  DCHECK(!texture_layer_);
270  if (!delegated_layer_) {
271    delegated_layer_ = cc::DelegatedRendererLayer::Create(NULL);
272    delegated_layer_->SetIsDrawable(true);
273    delegated_layer_->SetContentsOpaque(true);
274
275    background_layer_->AddChild(delegated_layer_);
276  }
277
278  cc::DelegatedFrameData *frame_data = frame->delegated_frame_data.get();
279  if (!frame_data)
280    return;
281
282  CheckSizeAndAdjustLayerBounds(
283      frame_data->render_pass_list.back()->output_rect.size(),
284      frame->metadata.device_scale_factor,
285      delegated_layer_.get());
286
287  delegated_layer_->SetFrameData(frame->delegated_frame_data.Pass());
288  last_route_id_ = route_id;
289  last_host_id_ = host_id;
290  ack_pending_ = true;
291}
292
293void BrowserPluginCompositingHelper::UpdateVisibility(bool visible) {
294  if (texture_layer_)
295    texture_layer_->SetIsDrawable(visible);
296  if (delegated_layer_)
297    delegated_layer_->SetIsDrawable(visible);
298}
299
300}  // namespace content
301