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.
5 #include "content/renderer/browser_plugin/browser_plugin_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/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"
26 BrowserPluginCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
34 BrowserPluginCompositingHelper::BrowserPluginCompositingHelper(
35 WebKit::WebPluginContainer* container,
36 BrowserPluginManager* manager,
39 : instance_id_(instance_id),
40 host_routing_id_(host_routing_id),
42 last_output_surface_id_(0),
44 last_mailbox_valid_(false),
46 software_ack_pending_(false),
47 container_(container),
48 browser_plugin_manager_(manager) {
51 BrowserPluginCompositingHelper::~BrowserPluginCompositingHelper() {
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();
62 browser_plugin_manager_->Send(
63 new BrowserPluginHostMsg_CompositorFrameACK(
67 last_output_surface_id_,
71 software_ack_pending_ = false;
73 if (!resource_collection_.get() || !ack_pending_)
76 cc::CompositorFrameAck ack;
77 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
79 browser_plugin_manager_->Send(
80 new BrowserPluginHostMsg_CompositorFrameACK(
84 last_output_surface_id_,
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_));
100 container_->setWebLayer(enable ? web_layer_.get() : NULL);
103 void BrowserPluginCompositingHelper::CheckSizeAndAdjustLayerBounds(
104 const gfx::Size& new_size,
105 float device_scale_factor,
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);
118 void BrowserPluginCompositingHelper::MailboxReleased(
119 SwapBuffersInfo mailbox,
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();
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)
136 if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME)
137 unacked_software_frames_.push_back(mailbox.software_frame_id);
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.
144 last_mailbox_valid_ = false;
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(
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;
169 browser_plugin_manager_->Send(
170 new BrowserPluginHostMsg_CompositorFrameACK(
174 mailbox.output_surface_id,
179 case SOFTWARE_COMPOSITOR_FRAME:
184 void BrowserPluginCompositingHelper::OnContainerDestroy() {
186 container_->setWebLayer(NULL);
189 texture_layer_ = NULL;
190 delegated_layer_ = NULL;
191 background_layer_ = NULL;
195 void BrowserPluginCompositingHelper::OnBuffersSwappedPrivate(
196 const SwapBuffersInfo& mailbox,
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;
209 last_route_id_ = mailbox.route_id;
210 last_output_surface_id_ = mailbox.output_surface_id;
211 last_host_id_ = mailbox.host_id;
214 // Browser plugin getting destroyed, do a fast ACK.
215 if (!background_layer_.get()) {
216 MailboxReleased(mailbox, sync_point, false);
220 if (!texture_layer_.get()) {
221 texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL);
222 texture_layer_->SetIsDrawable(true);
223 texture_layer_->SetContentsOpaque(true);
225 background_layer_->AddChild(texture_layer_);
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,
240 texture_layer_.get());
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)
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),
260 if (is_software_frame)
261 texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, mailbox.size);
263 texture_mailbox = cc::TextureMailbox(mailbox.name, sync_point);
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;
272 void BrowserPluginCompositingHelper::OnBuffersSwapped(
273 const gfx::Size& size,
274 const std::string& mailbox_name,
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);
288 void BrowserPluginCompositingHelper::OnCompositorFrameSwapped(
289 scoped_ptr<cc::CompositorFrame> frame,
291 uint32 output_surface_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);
307 if (frame->software_frame_data) {
308 cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();
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;
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 "
324 // Send ACK right away.
325 software_ack_pending_ = true;
326 MailboxReleased(swap_info, 0, false);
327 DidCommitCompositorFrame();
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;
341 DCHECK(!texture_layer_.get());
343 cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
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();
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().
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();
362 cc::DelegatedRendererLayer::Create(NULL, frame_provider_.get());
363 delegated_layer_->SetIsDrawable(true);
364 delegated_layer_->SetContentsOpaque(true);
365 background_layer_->AddChild(delegated_layer_);
367 frame_provider_->SetFrameData(frame->delegated_frame_data.Pass());
370 CheckSizeAndAdjustLayerBounds(
371 frame_data->render_pass_list.back()->output_rect.size(),
372 frame->metadata.device_scale_factor,
373 delegated_layer_.get());
375 last_route_id_ = route_id;
376 last_output_surface_id_ = output_surface_id;
377 last_host_id_ = host_id;
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);
388 } // namespace content