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