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