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/output/copy_output_request.h" 14 #include "cc/output/copy_output_result.h" 15 #include "cc/resources/single_release_callback.h" 16 #include "content/common/browser_plugin/browser_plugin_messages.h" 17 #include "content/common/gpu/client/context_provider_command_buffer.h" 18 #include "content/renderer/browser_plugin/browser_plugin_manager.h" 19 #include "content/renderer/render_thread_impl.h" 20 #include "skia/ext/image_operations.h" 21 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" 22 #include "third_party/WebKit/public/web/WebPluginContainer.h" 23 #include "third_party/khronos/GLES2/gl2.h" 24 #include "ui/gfx/size_conversions.h" 25 #include "ui/gfx/skia_util.h" 26 #include "webkit/renderer/compositor_bindings/web_layer_impl.h" 27 28 namespace content { 29 30 BrowserPluginCompositingHelper::SwapBuffersInfo::SwapBuffersInfo() 31 : route_id(0), 32 output_surface_id(0), 33 host_id(0), 34 software_frame_id(0), 35 shared_memory(NULL) { 36 } 37 38 BrowserPluginCompositingHelper::BrowserPluginCompositingHelper( 39 blink::WebPluginContainer* container, 40 BrowserPluginManager* manager, 41 int instance_id, 42 int host_routing_id) 43 : instance_id_(instance_id), 44 host_routing_id_(host_routing_id), 45 last_route_id_(0), 46 last_output_surface_id_(0), 47 last_host_id_(0), 48 last_mailbox_valid_(false), 49 ack_pending_(true), 50 software_ack_pending_(false), 51 opaque_(true), 52 container_(container), 53 browser_plugin_manager_(manager) { 54 } 55 56 BrowserPluginCompositingHelper::~BrowserPluginCompositingHelper() { 57 } 58 59 void BrowserPluginCompositingHelper::CopyFromCompositingSurface( 60 int request_id, 61 gfx::Rect source_rect, 62 gfx::Size dest_size) { 63 CHECK(background_layer_); 64 scoped_ptr<cc::CopyOutputRequest> request = 65 cc::CopyOutputRequest::CreateBitmapRequest(base::Bind( 66 &BrowserPluginCompositingHelper::CopyFromCompositingSurfaceHasResult, 67 this, 68 request_id, 69 dest_size)); 70 request->set_area(source_rect); 71 background_layer_->RequestCopyOfOutput(request.Pass()); 72 } 73 74 void BrowserPluginCompositingHelper::DidCommitCompositorFrame() { 75 if (software_ack_pending_) { 76 cc::CompositorFrameAck ack; 77 if (!unacked_software_frames_.empty()) { 78 ack.last_software_frame_id = unacked_software_frames_.back(); 79 unacked_software_frames_.pop_back(); 80 } 81 82 browser_plugin_manager_->Send( 83 new BrowserPluginHostMsg_CompositorFrameACK( 84 host_routing_id_, 85 instance_id_, 86 last_route_id_, 87 last_output_surface_id_, 88 last_host_id_, 89 ack)); 90 91 software_ack_pending_ = false; 92 } 93 if (!resource_collection_.get() || !ack_pending_) 94 return; 95 96 cc::CompositorFrameAck ack; 97 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources); 98 99 browser_plugin_manager_->Send( 100 new BrowserPluginHostMsg_CompositorFrameACK( 101 host_routing_id_, 102 instance_id_, 103 last_route_id_, 104 last_output_surface_id_, 105 last_host_id_, 106 ack)); 107 108 ack_pending_ = false; 109 } 110 111 void BrowserPluginCompositingHelper::EnableCompositing(bool enable) { 112 if (enable && !background_layer_.get()) { 113 background_layer_ = cc::SolidColorLayer::Create(); 114 background_layer_->SetMasksToBounds(true); 115 background_layer_->SetBackgroundColor( 116 SkColorSetARGBInline(255, 255, 255, 255)); 117 web_layer_.reset(new webkit::WebLayerImpl(background_layer_)); 118 } 119 120 container_->setWebLayer(enable ? web_layer_.get() : NULL); 121 } 122 123 void BrowserPluginCompositingHelper::CheckSizeAndAdjustLayerProperties( 124 const gfx::Size& new_size, 125 float device_scale_factor, 126 cc::Layer* layer) { 127 if (buffer_size_ != new_size) { 128 buffer_size_ = new_size; 129 // The container size is in DIP, so is the layer size. 130 // Buffer size is in physical pixels, so we need to adjust 131 // it by the device scale factor. 132 gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize( 133 gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor)); 134 layer->SetBounds(device_scale_adjusted_size); 135 } 136 137 // Manually manage background layer for transparent webview. 138 if (!opaque_) 139 background_layer_->SetIsDrawable(false); 140 } 141 142 void BrowserPluginCompositingHelper::MailboxReleased( 143 SwapBuffersInfo mailbox, 144 unsigned sync_point, 145 bool lost_resource) { 146 if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME) { 147 delete mailbox.shared_memory; 148 mailbox.shared_memory = NULL; 149 } else if (lost_resource) { 150 // Reset mailbox's name if the resource was lost. 151 mailbox.name.SetZero(); 152 } 153 154 // This means the GPU process crashed or guest crashed. 155 if (last_host_id_ != mailbox.host_id || 156 last_output_surface_id_ != mailbox.output_surface_id || 157 last_route_id_ != mailbox.route_id) 158 return; 159 160 if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME) 161 unacked_software_frames_.push_back(mailbox.software_frame_id); 162 163 // We need to send an ACK to for every buffer sent to us. 164 // However, if a buffer is freed up from 165 // the compositor in cases like switching back to SW mode without a new 166 // buffer arriving, no ACK is needed. 167 if (!ack_pending_) { 168 last_mailbox_valid_ = false; 169 return; 170 } 171 ack_pending_ = false; 172 switch (mailbox.type) { 173 case TEXTURE_IMAGE_TRANSPORT: { 174 std::string mailbox_name(reinterpret_cast<const char*>(mailbox.name.name), 175 sizeof(mailbox.name.name)); 176 browser_plugin_manager_->Send( 177 new BrowserPluginHostMsg_BuffersSwappedACK( 178 host_routing_id_, 179 instance_id_, 180 mailbox.route_id, 181 mailbox.host_id, 182 mailbox_name, 183 sync_point)); 184 break; 185 } 186 case GL_COMPOSITOR_FRAME: { 187 cc::CompositorFrameAck ack; 188 ack.gl_frame_data.reset(new cc::GLFrameData()); 189 ack.gl_frame_data->mailbox = mailbox.name; 190 ack.gl_frame_data->size = mailbox.size; 191 ack.gl_frame_data->sync_point = sync_point; 192 193 browser_plugin_manager_->Send( 194 new BrowserPluginHostMsg_CompositorFrameACK( 195 host_routing_id_, 196 instance_id_, 197 mailbox.route_id, 198 mailbox.output_surface_id, 199 mailbox.host_id, 200 ack)); 201 break; 202 } 203 case SOFTWARE_COMPOSITOR_FRAME: 204 break; 205 } 206 } 207 208 void BrowserPluginCompositingHelper::OnContainerDestroy() { 209 if (container_) 210 container_->setWebLayer(NULL); 211 container_ = NULL; 212 213 if (resource_collection_) 214 resource_collection_->SetClient(NULL); 215 216 ack_pending_ = false; 217 software_ack_pending_ = false; 218 resource_collection_ = NULL; 219 frame_provider_ = NULL; 220 texture_layer_ = NULL; 221 delegated_layer_ = NULL; 222 background_layer_ = NULL; 223 web_layer_.reset(); 224 } 225 226 void BrowserPluginCompositingHelper::OnBuffersSwappedPrivate( 227 const SwapBuffersInfo& mailbox, 228 unsigned sync_point, 229 float device_scale_factor) { 230 DCHECK(!delegated_layer_.get()); 231 // If these mismatch, we are either just starting up, GPU process crashed or 232 // guest renderer crashed. 233 // In this case, we are communicating with a new image transport 234 // surface and must ACK with the new ID's and an empty mailbox. 235 if (last_route_id_ != mailbox.route_id || 236 last_output_surface_id_ != mailbox.output_surface_id || 237 last_host_id_ != mailbox.host_id) 238 last_mailbox_valid_ = false; 239 240 last_route_id_ = mailbox.route_id; 241 last_output_surface_id_ = mailbox.output_surface_id; 242 last_host_id_ = mailbox.host_id; 243 244 ack_pending_ = true; 245 // Browser plugin getting destroyed, do a fast ACK. 246 if (!background_layer_.get()) { 247 MailboxReleased(mailbox, sync_point, false); 248 return; 249 } 250 251 if (!texture_layer_.get()) { 252 texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL); 253 texture_layer_->SetIsDrawable(true); 254 SetContentsOpaque(opaque_); 255 256 background_layer_->AddChild(texture_layer_); 257 } 258 259 // The size of browser plugin container is not always equal to the size 260 // of the buffer that arrives here. This could be for a number of reasons, 261 // including autosize and a resize in progress. 262 // During resize, the container size changes first and then some time 263 // later, a new buffer with updated size will arrive. During this process, 264 // we need to make sure that things are still displayed pixel perfect. 265 // We accomplish this by modifying bounds of the texture layer only 266 // when a new buffer arrives. 267 // Visually, this will either display a smaller part of the buffer 268 // or introduce a gutter around it. 269 CheckSizeAndAdjustLayerProperties(mailbox.size, 270 device_scale_factor, 271 texture_layer_.get()); 272 273 bool is_software_frame = mailbox.type == SOFTWARE_COMPOSITOR_FRAME; 274 bool current_mailbox_valid = is_software_frame ? 275 mailbox.shared_memory != NULL : !mailbox.name.IsZero(); 276 if (!is_software_frame && !last_mailbox_valid_) { 277 SwapBuffersInfo empty_info = mailbox; 278 empty_info.name.SetZero(); 279 MailboxReleased(empty_info, 0, false); 280 if (!current_mailbox_valid) 281 return; 282 } 283 284 cc::TextureMailbox texture_mailbox; 285 scoped_ptr<cc::SingleReleaseCallback> release_callback; 286 if (current_mailbox_valid) { 287 release_callback = cc::SingleReleaseCallback::Create( 288 base::Bind(&BrowserPluginCompositingHelper::MailboxReleased, 289 scoped_refptr<BrowserPluginCompositingHelper>(this), 290 mailbox)).Pass(); 291 if (is_software_frame) 292 texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, mailbox.size); 293 else 294 texture_mailbox = cc::TextureMailbox(mailbox.name, sync_point); 295 } 296 297 texture_layer_->SetFlipped(!is_software_frame); 298 texture_layer_->SetTextureMailbox(texture_mailbox, release_callback.Pass()); 299 texture_layer_->SetNeedsDisplay(); 300 last_mailbox_valid_ = current_mailbox_valid; 301 } 302 303 void BrowserPluginCompositingHelper::OnBuffersSwapped( 304 const gfx::Size& size, 305 const std::string& mailbox_name, 306 int gpu_route_id, 307 int gpu_host_id, 308 float device_scale_factor) { 309 SwapBuffersInfo swap_info; 310 swap_info.name.SetName(reinterpret_cast<const int8*>(mailbox_name.data())); 311 swap_info.type = TEXTURE_IMAGE_TRANSPORT; 312 swap_info.size = size; 313 swap_info.route_id = gpu_route_id; 314 swap_info.output_surface_id = 0; 315 swap_info.host_id = gpu_host_id; 316 OnBuffersSwappedPrivate(swap_info, 0, device_scale_factor); 317 } 318 319 void BrowserPluginCompositingHelper::OnCompositorFrameSwapped( 320 scoped_ptr<cc::CompositorFrame> frame, 321 int route_id, 322 uint32 output_surface_id, 323 int host_id) { 324 if (frame->gl_frame_data) { 325 SwapBuffersInfo swap_info; 326 swap_info.name = frame->gl_frame_data->mailbox; 327 swap_info.type = GL_COMPOSITOR_FRAME; 328 swap_info.size = frame->gl_frame_data->size; 329 swap_info.route_id = route_id; 330 swap_info.output_surface_id = output_surface_id; 331 swap_info.host_id = host_id; 332 OnBuffersSwappedPrivate(swap_info, 333 frame->gl_frame_data->sync_point, 334 frame->metadata.device_scale_factor); 335 return; 336 } 337 338 if (frame->software_frame_data) { 339 cc::SoftwareFrameData* frame_data = frame->software_frame_data.get(); 340 341 SwapBuffersInfo swap_info; 342 swap_info.type = SOFTWARE_COMPOSITOR_FRAME; 343 swap_info.size = frame_data->size; 344 swap_info.route_id = route_id; 345 swap_info.output_surface_id = output_surface_id; 346 swap_info.host_id = host_id; 347 swap_info.software_frame_id = frame_data->id; 348 349 scoped_ptr<base::SharedMemory> shared_memory( 350 new base::SharedMemory(frame_data->handle, true)); 351 const size_t size_in_bytes = 4 * frame_data->size.GetArea(); 352 if (!shared_memory->Map(size_in_bytes)) { 353 LOG(ERROR) << "Failed to map shared memory of size " 354 << size_in_bytes; 355 // Send ACK right away. 356 software_ack_pending_ = true; 357 MailboxReleased(swap_info, 0, false); 358 DidCommitCompositorFrame(); 359 return; 360 } 361 362 swap_info.shared_memory = shared_memory.release(); 363 OnBuffersSwappedPrivate(swap_info, 0, 364 frame->metadata.device_scale_factor); 365 software_ack_pending_ = true; 366 last_route_id_ = route_id; 367 last_output_surface_id_ = output_surface_id; 368 last_host_id_ = host_id; 369 return; 370 } 371 372 DCHECK(!texture_layer_.get()); 373 374 cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get(); 375 // Do nothing if we are getting destroyed or have no frame data. 376 if (!frame_data || !background_layer_) 377 return; 378 379 DCHECK(!frame_data->render_pass_list.empty()); 380 cc::RenderPass* root_pass = frame_data->render_pass_list.back(); 381 gfx::Size frame_size = root_pass->output_rect.size(); 382 383 if (last_route_id_ != route_id || 384 last_output_surface_id_ != output_surface_id || 385 last_host_id_ != host_id) { 386 // Resource ids are scoped by the output surface. 387 // If the originating output surface doesn't match the last one, it 388 // indicates the guest's output surface may have been recreated, in which 389 // case we should recreate the DelegatedRendererLayer, to avoid matching 390 // resources from the old one with resources from the new one which would 391 // have the same id. 392 frame_provider_ = NULL; 393 394 // Drop the cc::DelegatedFrameResourceCollection so that we will not return 395 // any resources from the old output surface with the new output surface id. 396 if (resource_collection_) { 397 resource_collection_->SetClient(NULL); 398 399 if (resource_collection_->LoseAllResources()) 400 SendReturnedDelegatedResources(); 401 resource_collection_ = NULL; 402 } 403 last_output_surface_id_ = output_surface_id; 404 last_route_id_ = route_id; 405 last_host_id_ = host_id; 406 } 407 if (!resource_collection_) { 408 resource_collection_ = new cc::DelegatedFrameResourceCollection; 409 resource_collection_->SetClient(this); 410 } 411 if (!frame_provider_.get() || frame_provider_->frame_size() != frame_size) { 412 frame_provider_ = new cc::DelegatedFrameProvider( 413 resource_collection_.get(), frame->delegated_frame_data.Pass()); 414 if (delegated_layer_.get()) 415 delegated_layer_->RemoveFromParent(); 416 delegated_layer_ = 417 cc::DelegatedRendererLayer::Create(frame_provider_.get()); 418 delegated_layer_->SetIsDrawable(true); 419 SetContentsOpaque(opaque_); 420 background_layer_->AddChild(delegated_layer_); 421 } else { 422 frame_provider_->SetFrameData(frame->delegated_frame_data.Pass()); 423 } 424 425 CheckSizeAndAdjustLayerProperties( 426 frame_data->render_pass_list.back()->output_rect.size(), 427 frame->metadata.device_scale_factor, 428 delegated_layer_.get()); 429 430 ack_pending_ = true; 431 } 432 433 void BrowserPluginCompositingHelper::UpdateVisibility(bool visible) { 434 if (texture_layer_.get()) 435 texture_layer_->SetIsDrawable(visible); 436 if (delegated_layer_.get()) 437 delegated_layer_->SetIsDrawable(visible); 438 } 439 440 void BrowserPluginCompositingHelper::UnusedResourcesAreAvailable() { 441 if (ack_pending_) 442 return; 443 444 SendReturnedDelegatedResources(); 445 } 446 447 void BrowserPluginCompositingHelper::SendReturnedDelegatedResources() { 448 cc::CompositorFrameAck ack; 449 if (resource_collection_) 450 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources); 451 DCHECK(!ack.resources.empty()); 452 453 browser_plugin_manager_->Send( 454 new BrowserPluginHostMsg_ReclaimCompositorResources( 455 host_routing_id_, 456 instance_id_, 457 last_route_id_, 458 last_output_surface_id_, 459 last_host_id_, 460 ack)); 461 } 462 463 void BrowserPluginCompositingHelper::SetContentsOpaque(bool opaque) { 464 opaque_ = opaque; 465 466 if (texture_layer_.get()) 467 texture_layer_->SetContentsOpaque(opaque_); 468 if (delegated_layer_.get()) 469 delegated_layer_->SetContentsOpaque(opaque_); 470 } 471 472 void BrowserPluginCompositingHelper::CopyFromCompositingSurfaceHasResult( 473 int request_id, 474 gfx::Size dest_size, 475 scoped_ptr<cc::CopyOutputResult> result) { 476 scoped_ptr<SkBitmap> bitmap; 477 if (result && result->HasBitmap() && !result->size().IsEmpty()) 478 bitmap = result->TakeBitmap(); 479 480 SkBitmap resized_bitmap; 481 if (bitmap) { 482 resized_bitmap = skia::ImageOperations::Resize(*bitmap, 483 skia::ImageOperations::RESIZE_BEST, 484 dest_size.width(), 485 dest_size.height()); 486 } 487 browser_plugin_manager_->Send( 488 new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck( 489 host_routing_id_, instance_id_, request_id, 490 resized_bitmap)); 491 } 492 493 } // namespace content 494