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/browser/compositor/delegated_frame_host.h" 6 7 #include "base/callback_helpers.h" 8 #include "base/command_line.h" 9 #include "cc/output/compositor_frame.h" 10 #include "cc/output/compositor_frame_ack.h" 11 #include "cc/output/copy_output_request.h" 12 #include "cc/resources/single_release_callback.h" 13 #include "cc/resources/texture_mailbox.h" 14 #include "cc/surfaces/surface_factory.h" 15 #include "content/browser/compositor/resize_lock.h" 16 #include "content/browser/gpu/compositor_util.h" 17 #include "content/common/gpu/client/gl_helper.h" 18 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" 19 #include "content/public/common/content_switches.h" 20 #include "media/base/video_frame.h" 21 #include "media/base/video_util.h" 22 #include "skia/ext/image_operations.h" 23 #include "third_party/skia/include/core/SkCanvas.h" 24 #include "third_party/skia/include/core/SkPaint.h" 25 #include "third_party/skia/include/effects/SkLumaColorFilter.h" 26 #include "ui/gfx/frame_time.h" 27 28 namespace content { 29 30 //////////////////////////////////////////////////////////////////////////////// 31 // DelegatedFrameHostClient 32 33 bool DelegatedFrameHostClient::ShouldCreateResizeLock() { 34 // On Windows and Linux, holding pointer moves will not help throttling 35 // resizes. 36 // TODO(piman): on Windows we need to block (nested message loop?) the 37 // WM_SIZE event. On Linux we need to throttle at the WM level using 38 // _NET_WM_SYNC_REQUEST. 39 // TODO(ccameron): Mac browser window resizing is incompletely implemented. 40 #if !defined(OS_CHROMEOS) 41 return false; 42 #else 43 return GetDelegatedFrameHost()->ShouldCreateResizeLock(); 44 #endif 45 } 46 47 void DelegatedFrameHostClient::RequestCopyOfOutput( 48 scoped_ptr<cc::CopyOutputRequest> request) { 49 GetDelegatedFrameHost()->RequestCopyOfOutput(request.Pass()); 50 } 51 52 //////////////////////////////////////////////////////////////////////////////// 53 // DelegatedFrameHost 54 55 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client) 56 : client_(client), 57 use_surfaces_(UseSurfacesEnabled()), 58 last_output_surface_id_(0), 59 pending_delegated_ack_count_(0), 60 skipped_frames_(false), 61 can_lock_compositor_(YES_CAN_LOCK), 62 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) { 63 ImageTransportFactory::GetInstance()->AddObserver(this); 64 } 65 66 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) { 67 delegated_frame_evictor_->SetVisible(true); 68 69 if (surface_id_.is_null() && !frame_provider_.get() && 70 !released_front_lock_.get()) { 71 ui::Compositor* compositor = client_->GetCompositor(); 72 if (compositor) 73 released_front_lock_ = compositor->GetCompositorLock(); 74 } 75 76 ui::Compositor* compositor = client_->GetCompositor(); 77 if (compositor) { 78 compositor->SetLatencyInfo(latency_info); 79 } 80 } 81 82 bool DelegatedFrameHost::HasSavedFrame() { 83 return delegated_frame_evictor_->HasFrame(); 84 } 85 86 void DelegatedFrameHost::WasHidden() { 87 delegated_frame_evictor_->SetVisible(false); 88 released_front_lock_ = NULL; 89 } 90 91 void DelegatedFrameHost::MaybeCreateResizeLock() { 92 if (!client_->ShouldCreateResizeLock()) 93 return; 94 DCHECK(client_->GetCompositor()); 95 96 // Listen to changes in the compositor lock state. 97 ui::Compositor* compositor = client_->GetCompositor(); 98 if (!compositor->HasObserver(this)) 99 compositor->AddObserver(this); 100 101 bool defer_compositor_lock = 102 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || 103 can_lock_compositor_ == NO_PENDING_COMMIT; 104 105 if (can_lock_compositor_ == YES_CAN_LOCK) 106 can_lock_compositor_ = YES_DID_LOCK; 107 108 resize_lock_ = client_->CreateResizeLock(defer_compositor_lock); 109 } 110 111 bool DelegatedFrameHost::ShouldCreateResizeLock() { 112 RenderWidgetHostImpl* host = client_->GetHost(); 113 114 if (resize_lock_) 115 return false; 116 117 if (host->auto_resize_enabled()) 118 return false; 119 120 gfx::Size desired_size = client_->DesiredFrameSize(); 121 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty()) 122 return false; 123 124 ui::Compositor* compositor = client_->GetCompositor(); 125 if (!compositor) 126 return false; 127 128 return true; 129 } 130 131 void DelegatedFrameHost::RequestCopyOfOutput( 132 scoped_ptr<cc::CopyOutputRequest> request) { 133 if (use_surfaces_) { 134 if (surface_factory_ && !surface_id_.is_null()) 135 surface_factory_->RequestCopyOfSurface(surface_id_, request.Pass()); 136 else 137 request->SendEmptyResult(); 138 } else { 139 client_->GetLayer()->RequestCopyOfOutput(request.Pass()); 140 } 141 } 142 143 void DelegatedFrameHost::CopyFromCompositingSurface( 144 const gfx::Rect& src_subrect, 145 const gfx::Size& output_size, 146 CopyFromCompositingSurfaceCallback& callback, 147 const SkColorType color_type) { 148 // Only ARGB888 and RGB565 supported as of now. 149 bool format_support = ((color_type == kAlpha_8_SkColorType) || 150 (color_type == kRGB_565_SkColorType) || 151 (color_type == kN32_SkColorType)); 152 DCHECK(format_support); 153 if (!CanCopyToBitmap()) { 154 callback.Run(false, SkBitmap()); 155 return; 156 } 157 158 scoped_ptr<cc::CopyOutputRequest> request = 159 cc::CopyOutputRequest::CreateRequest(base::Bind( 160 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult, 161 output_size, 162 color_type, 163 callback)); 164 request->set_area(src_subrect); 165 client_->RequestCopyOfOutput(request.Pass()); 166 } 167 168 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame( 169 const gfx::Rect& src_subrect, 170 const scoped_refptr<media::VideoFrame>& target, 171 const base::Callback<void(bool)>& callback) { 172 if (!CanCopyToVideoFrame()) { 173 callback.Run(false); 174 return; 175 } 176 177 // Try get a texture to reuse. 178 scoped_refptr<OwnedMailbox> subscriber_texture; 179 if (frame_subscriber_) { 180 if (!idle_frame_subscriber_textures_.empty()) { 181 subscriber_texture = idle_frame_subscriber_textures_.back(); 182 idle_frame_subscriber_textures_.pop_back(); 183 } else if (GLHelper* helper = 184 ImageTransportFactory::GetInstance()->GetGLHelper()) { 185 subscriber_texture = new OwnedMailbox(helper); 186 } 187 } 188 189 scoped_ptr<cc::CopyOutputRequest> request = 190 cc::CopyOutputRequest::CreateRequest(base::Bind( 191 &DelegatedFrameHost:: 192 CopyFromCompositingSurfaceHasResultForVideo, 193 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class. 194 subscriber_texture, 195 target, 196 callback)); 197 request->set_area(src_subrect); 198 if (subscriber_texture.get()) { 199 request->SetTextureMailbox( 200 cc::TextureMailbox(subscriber_texture->mailbox(), 201 subscriber_texture->target(), 202 subscriber_texture->sync_point())); 203 } 204 client_->RequestCopyOfOutput(request.Pass()); 205 } 206 207 bool DelegatedFrameHost::CanCopyToBitmap() const { 208 return client_->GetCompositor() && 209 client_->GetLayer()->has_external_content(); 210 } 211 212 bool DelegatedFrameHost::CanCopyToVideoFrame() const { 213 return client_->GetCompositor() && 214 client_->GetLayer()->has_external_content(); 215 } 216 217 bool DelegatedFrameHost::CanSubscribeFrame() const { 218 return true; 219 } 220 221 void DelegatedFrameHost::BeginFrameSubscription( 222 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) { 223 frame_subscriber_ = subscriber.Pass(); 224 } 225 226 void DelegatedFrameHost::EndFrameSubscription() { 227 idle_frame_subscriber_textures_.clear(); 228 frame_subscriber_.reset(); 229 } 230 231 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const { 232 // Should skip a frame only when another frame from the renderer is guaranteed 233 // to replace it. Otherwise may cause hangs when the renderer is waiting for 234 // the completion of latency infos (such as when taking a Snapshot.) 235 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || 236 can_lock_compositor_ == NO_PENDING_COMMIT || 237 !resize_lock_.get()) 238 return false; 239 240 return size_in_dip != resize_lock_->expected_size(); 241 } 242 243 void DelegatedFrameHost::WasResized() { 244 if (client_->DesiredFrameSize() != current_frame_size_in_dip_ && 245 client_->GetHost()->is_hidden()) 246 EvictDelegatedFrame(); 247 MaybeCreateResizeLock(); 248 } 249 250 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const { 251 if (resize_lock_) 252 return resize_lock_->expected_size(); 253 else 254 return client_->DesiredFrameSize(); 255 } 256 257 void DelegatedFrameHost::CheckResizeLock() { 258 if (!resize_lock_ || 259 resize_lock_->expected_size() != current_frame_size_in_dip_) 260 return; 261 262 // Since we got the size we were looking for, unlock the compositor. But delay 263 // the release of the lock until we've kicked a frame with the new texture, to 264 // avoid resizing the UI before we have a chance to draw a "good" frame. 265 resize_lock_->UnlockCompositor(); 266 ui::Compositor* compositor = client_->GetCompositor(); 267 if (compositor) { 268 if (!compositor->HasObserver(this)) 269 compositor->AddObserver(this); 270 } 271 } 272 273 void DelegatedFrameHost::DidReceiveFrameFromRenderer( 274 const gfx::Rect& damage_rect) { 275 if (!frame_subscriber() || !CanCopyToVideoFrame()) 276 return; 277 278 const base::TimeTicks now = gfx::FrameTime::Now(); 279 base::TimeTicks present_time; 280 if (vsync_timebase_.is_null() || vsync_interval_ <= base::TimeDelta()) { 281 present_time = now; 282 } else { 283 const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_; 284 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_; 285 } 286 287 scoped_refptr<media::VideoFrame> frame; 288 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback; 289 if (frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time, 290 &frame, &callback)) { 291 CopyFromCompositingSurfaceToVideoFrame( 292 gfx::Rect(current_frame_size_in_dip_), 293 frame, 294 base::Bind(callback, present_time)); 295 } 296 } 297 298 void DelegatedFrameHost::SwapDelegatedFrame( 299 uint32 output_surface_id, 300 scoped_ptr<cc::DelegatedFrameData> frame_data, 301 float frame_device_scale_factor, 302 const std::vector<ui::LatencyInfo>& latency_info) { 303 RenderWidgetHostImpl* host = client_->GetHost(); 304 DCHECK(!frame_data->render_pass_list.empty()); 305 306 cc::RenderPass* root_pass = frame_data->render_pass_list.back(); 307 308 gfx::Size frame_size = root_pass->output_rect.size(); 309 gfx::Size frame_size_in_dip = 310 ConvertSizeToDIP(frame_device_scale_factor, frame_size); 311 312 gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect); 313 damage_rect.Intersect(gfx::Rect(frame_size)); 314 gfx::Rect damage_rect_in_dip = 315 ConvertRectToDIP(frame_device_scale_factor, damage_rect); 316 317 if (ShouldSkipFrame(frame_size_in_dip)) { 318 cc::CompositorFrameAck ack; 319 cc::TransferableResource::ReturnResources(frame_data->resource_list, 320 &ack.resources); 321 322 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(), 323 latency_info.begin(), latency_info.end()); 324 325 RenderWidgetHostImpl::SendSwapCompositorFrameAck( 326 host->GetRoutingID(), output_surface_id, 327 host->GetProcess()->GetID(), ack); 328 skipped_frames_ = true; 329 return; 330 } 331 332 if (skipped_frames_) { 333 skipped_frames_ = false; 334 damage_rect = gfx::Rect(frame_size); 335 damage_rect_in_dip = gfx::Rect(frame_size_in_dip); 336 337 // Give the same damage rect to the compositor. 338 cc::RenderPass* root_pass = frame_data->render_pass_list.back(); 339 root_pass->damage_rect = damage_rect; 340 } 341 342 if (output_surface_id != last_output_surface_id_) { 343 // Resource ids are scoped by the output surface. 344 // If the originating output surface doesn't match the last one, it 345 // indicates the renderer's output surface may have been recreated, in which 346 // case we should recreate the DelegatedRendererLayer, to avoid matching 347 // resources from the old one with resources from the new one which would 348 // have the same id. Changing the layer to showing painted content destroys 349 // the DelegatedRendererLayer. 350 EvictDelegatedFrame(); 351 352 // Drop the cc::DelegatedFrameResourceCollection so that we will not return 353 // any resources from the old output surface with the new output surface id. 354 if (resource_collection_.get()) { 355 resource_collection_->SetClient(NULL); 356 357 if (resource_collection_->LoseAllResources()) 358 SendReturnedDelegatedResources(last_output_surface_id_); 359 360 resource_collection_ = NULL; 361 } 362 last_output_surface_id_ = output_surface_id; 363 } 364 bool modified_layers = false; 365 ui::Compositor* compositor = client_->GetCompositor(); 366 if (frame_size.IsEmpty()) { 367 DCHECK(frame_data->resource_list.empty()); 368 EvictDelegatedFrame(); 369 modified_layers = true; 370 } else { 371 if (use_surfaces_) { 372 if (!surface_factory_) { 373 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); 374 cc::SurfaceManager* manager = factory->GetSurfaceManager(); 375 id_allocator_ = factory->CreateSurfaceIdAllocator(); 376 surface_factory_ = 377 make_scoped_ptr(new cc::SurfaceFactory(manager, this)); 378 } 379 if (surface_id_.is_null() || frame_size != current_surface_size_ || 380 frame_size_in_dip != current_frame_size_in_dip_) { 381 // TODO(jbauman): Wait to destroy this surface until the parent has 382 // finished using it. 383 if (!surface_id_.is_null()) 384 surface_factory_->Destroy(surface_id_); 385 surface_id_ = id_allocator_->GenerateId(); 386 surface_factory_->Create(surface_id_, frame_size); 387 client_->GetLayer()->SetShowSurface(surface_id_, frame_size_in_dip); 388 current_surface_size_ = frame_size; 389 modified_layers = true; 390 } 391 scoped_ptr<cc::CompositorFrame> compositor_frame = 392 make_scoped_ptr(new cc::CompositorFrame()); 393 compositor_frame->delegated_frame_data = frame_data.Pass(); 394 395 compositor_frame->metadata.latency_info.swap(skipped_latency_info_list_); 396 compositor_frame->metadata.latency_info.insert( 397 compositor_frame->metadata.latency_info.end(), 398 latency_info.begin(), 399 latency_info.end()); 400 401 base::Closure ack_callback; 402 if (compositor) { 403 ack_callback = base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck, 404 AsWeakPtr(), 405 output_surface_id); 406 } 407 surface_factory_->SubmitFrame( 408 surface_id_, compositor_frame.Pass(), ack_callback); 409 } else { 410 if (!resource_collection_.get()) { 411 resource_collection_ = new cc::DelegatedFrameResourceCollection; 412 resource_collection_->SetClient(this); 413 } 414 // If the physical frame size changes, we need a new |frame_provider_|. If 415 // the physical frame size is the same, but the size in DIP changed, we 416 // need to adjust the scale at which the frames will be drawn, and we do 417 // this by making a new |frame_provider_| also to ensure the scale change 418 // is presented in sync with the new frame content. 419 if (!frame_provider_.get() || 420 frame_size != frame_provider_->frame_size() || 421 frame_size_in_dip != current_frame_size_in_dip_) { 422 frame_provider_ = new cc::DelegatedFrameProvider( 423 resource_collection_.get(), frame_data.Pass()); 424 client_->GetLayer()->SetShowDelegatedContent(frame_provider_.get(), 425 frame_size_in_dip); 426 } else { 427 frame_provider_->SetFrameData(frame_data.Pass()); 428 } 429 modified_layers = true; 430 } 431 } 432 released_front_lock_ = NULL; 433 current_frame_size_in_dip_ = frame_size_in_dip; 434 CheckResizeLock(); 435 436 if (modified_layers && !damage_rect_in_dip.IsEmpty()) { 437 // TODO(jbauman): Need to always tell the window observer about the 438 // damage. 439 client_->GetLayer()->OnDelegatedFrameDamage(damage_rect_in_dip); 440 } 441 442 pending_delegated_ack_count_++; 443 444 if (!compositor) { 445 SendDelegatedFrameAck(output_surface_id); 446 } else if (!use_surfaces_) { 447 std::vector<ui::LatencyInfo>::const_iterator it; 448 for (it = latency_info.begin(); it != latency_info.end(); ++it) 449 compositor->SetLatencyInfo(*it); 450 // If we've previously skipped any latency infos add them. 451 for (it = skipped_latency_info_list_.begin(); 452 it != skipped_latency_info_list_.end(); 453 ++it) 454 compositor->SetLatencyInfo(*it); 455 skipped_latency_info_list_.clear(); 456 AddOnCommitCallbackAndDisableLocks( 457 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck, 458 AsWeakPtr(), 459 output_surface_id)); 460 } 461 DidReceiveFrameFromRenderer(damage_rect); 462 if (frame_provider_.get() || !surface_id_.is_null()) 463 delegated_frame_evictor_->SwappedFrame(!host->is_hidden()); 464 // Note: the frame may have been evicted immediately. 465 } 466 467 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) { 468 RenderWidgetHostImpl* host = client_->GetHost(); 469 cc::CompositorFrameAck ack; 470 if (!surface_returned_resources_.empty()) 471 ack.resources.swap(surface_returned_resources_); 472 if (resource_collection_.get()) 473 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources); 474 RenderWidgetHostImpl::SendSwapCompositorFrameAck(host->GetRoutingID(), 475 output_surface_id, 476 host->GetProcess()->GetID(), 477 ack); 478 DCHECK_GT(pending_delegated_ack_count_, 0); 479 pending_delegated_ack_count_--; 480 } 481 482 void DelegatedFrameHost::UnusedResourcesAreAvailable() { 483 if (pending_delegated_ack_count_) 484 return; 485 486 SendReturnedDelegatedResources(last_output_surface_id_); 487 } 488 489 void DelegatedFrameHost::SendReturnedDelegatedResources( 490 uint32 output_surface_id) { 491 RenderWidgetHostImpl* host = client_->GetHost(); 492 493 cc::CompositorFrameAck ack; 494 if (!surface_returned_resources_.empty()) { 495 ack.resources.swap(surface_returned_resources_); 496 } else { 497 DCHECK(resource_collection_.get()); 498 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources); 499 } 500 DCHECK(!ack.resources.empty()); 501 502 RenderWidgetHostImpl::SendReclaimCompositorResources( 503 host->GetRoutingID(), 504 output_surface_id, 505 host->GetProcess()->GetID(), 506 ack); 507 } 508 509 void DelegatedFrameHost::ReturnResources( 510 const cc::ReturnedResourceArray& resources) { 511 if (resources.empty()) 512 return; 513 std::copy(resources.begin(), 514 resources.end(), 515 std::back_inserter(surface_returned_resources_)); 516 if (!pending_delegated_ack_count_) 517 SendReturnedDelegatedResources(last_output_surface_id_); 518 } 519 520 void DelegatedFrameHost::EvictDelegatedFrame() { 521 client_->GetLayer()->SetShowPaintedContent(); 522 frame_provider_ = NULL; 523 if (!surface_id_.is_null()) { 524 surface_factory_->Destroy(surface_id_); 525 surface_id_ = cc::SurfaceId(); 526 } 527 delegated_frame_evictor_->DiscardedFrame(); 528 } 529 530 // static 531 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult( 532 const gfx::Size& dst_size_in_pixel, 533 const SkColorType color_type, 534 const base::Callback<void(bool, const SkBitmap&)>& callback, 535 scoped_ptr<cc::CopyOutputResult> result) { 536 if (result->IsEmpty() || result->size().IsEmpty()) { 537 callback.Run(false, SkBitmap()); 538 return; 539 } 540 541 if (result->HasTexture()) { 542 // GPU-accelerated path 543 PrepareTextureCopyOutputResult(dst_size_in_pixel, color_type, 544 callback, 545 result.Pass()); 546 return; 547 } 548 549 DCHECK(result->HasBitmap()); 550 // Software path 551 PrepareBitmapCopyOutputResult(dst_size_in_pixel, color_type, callback, 552 result.Pass()); 553 } 554 555 static void CopyFromCompositingSurfaceFinished( 556 const base::Callback<void(bool, const SkBitmap&)>& callback, 557 scoped_ptr<cc::SingleReleaseCallback> release_callback, 558 scoped_ptr<SkBitmap> bitmap, 559 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock, 560 bool result) { 561 bitmap_pixels_lock.reset(); 562 563 uint32 sync_point = 0; 564 if (result) { 565 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); 566 sync_point = gl_helper->InsertSyncPoint(); 567 } 568 bool lost_resource = sync_point == 0; 569 release_callback->Run(sync_point, lost_resource); 570 571 callback.Run(result, *bitmap); 572 } 573 574 // static 575 void DelegatedFrameHost::PrepareTextureCopyOutputResult( 576 const gfx::Size& dst_size_in_pixel, 577 const SkColorType color_type, 578 const base::Callback<void(bool, const SkBitmap&)>& callback, 579 scoped_ptr<cc::CopyOutputResult> result) { 580 DCHECK(result->HasTexture()); 581 base::ScopedClosureRunner scoped_callback_runner( 582 base::Bind(callback, false, SkBitmap())); 583 584 // TODO(sikugu): We should be able to validate the format here using 585 // GLHelper::IsReadbackConfigSupported before we processs the result. 586 // See crbug.com/415682. 587 scoped_ptr<SkBitmap> bitmap(new SkBitmap); 588 if (!bitmap->tryAllocPixels(SkImageInfo::Make(dst_size_in_pixel.width(), 589 dst_size_in_pixel.height(), 590 color_type, 591 kOpaque_SkAlphaType))) 592 return; 593 594 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); 595 GLHelper* gl_helper = factory->GetGLHelper(); 596 if (!gl_helper) 597 return; 598 599 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock( 600 new SkAutoLockPixels(*bitmap)); 601 uint8* pixels = static_cast<uint8*>(bitmap->getPixels()); 602 603 cc::TextureMailbox texture_mailbox; 604 scoped_ptr<cc::SingleReleaseCallback> release_callback; 605 result->TakeTexture(&texture_mailbox, &release_callback); 606 DCHECK(texture_mailbox.IsTexture()); 607 608 ignore_result(scoped_callback_runner.Release()); 609 610 gl_helper->CropScaleReadbackAndCleanMailbox( 611 texture_mailbox.mailbox(), 612 texture_mailbox.sync_point(), 613 result->size(), 614 gfx::Rect(result->size()), 615 dst_size_in_pixel, 616 pixels, 617 color_type, 618 base::Bind(&CopyFromCompositingSurfaceFinished, 619 callback, 620 base::Passed(&release_callback), 621 base::Passed(&bitmap), 622 base::Passed(&bitmap_pixels_lock)), 623 GLHelper::SCALER_QUALITY_FAST); 624 } 625 626 // static 627 void DelegatedFrameHost::PrepareBitmapCopyOutputResult( 628 const gfx::Size& dst_size_in_pixel, 629 const SkColorType color_type, 630 const base::Callback<void(bool, const SkBitmap&)>& callback, 631 scoped_ptr<cc::CopyOutputResult> result) { 632 if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) { 633 NOTIMPLEMENTED(); 634 callback.Run(false, SkBitmap()); 635 return; 636 } 637 DCHECK(result->HasBitmap()); 638 scoped_ptr<SkBitmap> source = result->TakeBitmap(); 639 DCHECK(source); 640 SkBitmap scaled_bitmap; 641 if (source->width() != dst_size_in_pixel.width() || 642 source->height() != dst_size_in_pixel.height()) { 643 scaled_bitmap = 644 skia::ImageOperations::Resize(*source, 645 skia::ImageOperations::RESIZE_BEST, 646 dst_size_in_pixel.width(), 647 dst_size_in_pixel.height()); 648 } else { 649 scaled_bitmap = *source; 650 } 651 if (color_type == kN32_SkColorType) { 652 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType); 653 callback.Run(true, scaled_bitmap); 654 return; 655 } 656 DCHECK_EQ(color_type, kAlpha_8_SkColorType); 657 // The software path currently always returns N32 bitmap regardless of the 658 // |color_type| we ask for. 659 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType); 660 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|. 661 SkBitmap grayscale_bitmap; 662 bool success = grayscale_bitmap.tryAllocPixels( 663 SkImageInfo::MakeA8(scaled_bitmap.width(), scaled_bitmap.height())); 664 if (!success) { 665 callback.Run(false, SkBitmap()); 666 return; 667 } 668 SkCanvas canvas(grayscale_bitmap); 669 SkPaint paint; 670 skia::RefPtr<SkColorFilter> filter = 671 skia::AdoptRef(SkLumaColorFilter::Create()); 672 paint.setColorFilter(filter.get()); 673 canvas.drawBitmap(scaled_bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint); 674 callback.Run(true, grayscale_bitmap); 675 } 676 677 // static 678 void DelegatedFrameHost::ReturnSubscriberTexture( 679 base::WeakPtr<DelegatedFrameHost> dfh, 680 scoped_refptr<OwnedMailbox> subscriber_texture, 681 uint32 sync_point) { 682 if (!subscriber_texture.get()) 683 return; 684 if (!dfh) 685 return; 686 687 subscriber_texture->UpdateSyncPoint(sync_point); 688 689 if (dfh->frame_subscriber_ && subscriber_texture->texture_id()) 690 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture); 691 } 692 693 // static 694 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo( 695 base::WeakPtr<DelegatedFrameHost> dfh, 696 const base::Callback<void(bool)>& callback, 697 scoped_refptr<OwnedMailbox> subscriber_texture, 698 scoped_ptr<cc::SingleReleaseCallback> release_callback, 699 bool result) { 700 callback.Run(result); 701 702 uint32 sync_point = 0; 703 if (result) { 704 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); 705 sync_point = gl_helper->InsertSyncPoint(); 706 } 707 if (release_callback) { 708 // A release callback means the texture came from the compositor, so there 709 // should be no |subscriber_texture|. 710 DCHECK(!subscriber_texture.get()); 711 bool lost_resource = sync_point == 0; 712 release_callback->Run(sync_point, lost_resource); 713 } 714 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point); 715 } 716 717 // static 718 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo( 719 base::WeakPtr<DelegatedFrameHost> dfh, 720 scoped_refptr<OwnedMailbox> subscriber_texture, 721 scoped_refptr<media::VideoFrame> video_frame, 722 const base::Callback<void(bool)>& callback, 723 scoped_ptr<cc::CopyOutputResult> result) { 724 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false)); 725 base::ScopedClosureRunner scoped_return_subscriber_texture( 726 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0)); 727 728 if (!dfh) 729 return; 730 if (result->IsEmpty()) 731 return; 732 if (result->size().IsEmpty()) 733 return; 734 735 // Compute the dest size we want after the letterboxing resize. Make the 736 // coordinates and sizes even because we letterbox in YUV space 737 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to 738 // line up correctly. 739 // The video frame's coded_size() and the result's size() are both physical 740 // pixels. 741 gfx::Rect region_in_frame = 742 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()), 743 result->size()); 744 region_in_frame = gfx::Rect(region_in_frame.x() & ~1, 745 region_in_frame.y() & ~1, 746 region_in_frame.width() & ~1, 747 region_in_frame.height() & ~1); 748 if (region_in_frame.IsEmpty()) 749 return; 750 751 if (!result->HasTexture()) { 752 DCHECK(result->HasBitmap()); 753 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap(); 754 // Scale the bitmap to the required size, if necessary. 755 SkBitmap scaled_bitmap; 756 if (result->size().width() != region_in_frame.width() || 757 result->size().height() != region_in_frame.height()) { 758 skia::ImageOperations::ResizeMethod method = 759 skia::ImageOperations::RESIZE_GOOD; 760 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method, 761 region_in_frame.width(), 762 region_in_frame.height()); 763 } else { 764 scaled_bitmap = *bitmap.get(); 765 } 766 767 { 768 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap); 769 770 media::CopyRGBToVideoFrame( 771 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()), 772 scaled_bitmap.rowBytes(), 773 region_in_frame, 774 video_frame.get()); 775 } 776 ignore_result(scoped_callback_runner.Release()); 777 callback.Run(true); 778 return; 779 } 780 781 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); 782 GLHelper* gl_helper = factory->GetGLHelper(); 783 if (!gl_helper) 784 return; 785 if (subscriber_texture.get() && !subscriber_texture->texture_id()) 786 return; 787 788 cc::TextureMailbox texture_mailbox; 789 scoped_ptr<cc::SingleReleaseCallback> release_callback; 790 result->TakeTexture(&texture_mailbox, &release_callback); 791 DCHECK(texture_mailbox.IsTexture()); 792 793 gfx::Rect result_rect(result->size()); 794 795 content::ReadbackYUVInterface* yuv_readback_pipeline = 796 dfh->yuv_readback_pipeline_.get(); 797 if (yuv_readback_pipeline == NULL || 798 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() || 799 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect || 800 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) { 801 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST; 802 std::string quality_switch = switches::kTabCaptureDownscaleQuality; 803 // If we're scaling up, we can use the "best" quality. 804 if (result_rect.size().width() < region_in_frame.size().width() && 805 result_rect.size().height() < region_in_frame.size().height()) 806 quality_switch = switches::kTabCaptureUpscaleQuality; 807 808 std::string switch_value = 809 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 810 quality_switch); 811 if (switch_value == "fast") 812 quality = GLHelper::SCALER_QUALITY_FAST; 813 else if (switch_value == "good") 814 quality = GLHelper::SCALER_QUALITY_GOOD; 815 else if (switch_value == "best") 816 quality = GLHelper::SCALER_QUALITY_BEST; 817 818 dfh->yuv_readback_pipeline_.reset( 819 gl_helper->CreateReadbackPipelineYUV(quality, 820 result_rect.size(), 821 result_rect, 822 video_frame->coded_size(), 823 region_in_frame, 824 true, 825 true)); 826 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get(); 827 } 828 829 ignore_result(scoped_callback_runner.Release()); 830 ignore_result(scoped_return_subscriber_texture.Release()); 831 base::Callback<void(bool result)> finished_callback = base::Bind( 832 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo, 833 dfh->AsWeakPtr(), 834 callback, 835 subscriber_texture, 836 base::Passed(&release_callback)); 837 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(), 838 texture_mailbox.sync_point(), 839 video_frame.get(), 840 finished_callback); 841 } 842 843 //////////////////////////////////////////////////////////////////////////////// 844 // DelegatedFrameHost, ui::CompositorObserver implementation: 845 846 void DelegatedFrameHost::OnCompositingDidCommit( 847 ui::Compositor* compositor) { 848 RenderWidgetHostImpl* host = client_->GetHost(); 849 if (can_lock_compositor_ == NO_PENDING_COMMIT) { 850 can_lock_compositor_ = YES_CAN_LOCK; 851 if (resize_lock_.get() && resize_lock_->GrabDeferredLock()) 852 can_lock_compositor_ = YES_DID_LOCK; 853 } 854 RunOnCommitCallbacks(); 855 if (resize_lock_ && 856 resize_lock_->expected_size() == current_frame_size_in_dip_) { 857 resize_lock_.reset(); 858 host->WasResized(); 859 // We may have had a resize while we had the lock (e.g. if the lock expired, 860 // or if the UI still gave us some resizes), so make sure we grab a new lock 861 // if necessary. 862 MaybeCreateResizeLock(); 863 } 864 } 865 866 void DelegatedFrameHost::OnCompositingStarted( 867 ui::Compositor* compositor, base::TimeTicks start_time) { 868 last_draw_ended_ = start_time; 869 } 870 871 void DelegatedFrameHost::OnCompositingEnded( 872 ui::Compositor* compositor) { 873 } 874 875 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) { 876 } 877 878 void DelegatedFrameHost::OnCompositingLockStateChanged( 879 ui::Compositor* compositor) { 880 // A compositor lock that is part of a resize lock timed out. We 881 // should display a renderer frame. 882 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) { 883 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME; 884 } 885 } 886 887 void DelegatedFrameHost::OnUpdateVSyncParameters( 888 base::TimeTicks timebase, 889 base::TimeDelta interval) { 890 vsync_timebase_ = timebase; 891 vsync_interval_ = interval; 892 RenderWidgetHostImpl* host = client_->GetHost(); 893 if (client_->IsVisible()) 894 host->UpdateVSyncParameters(timebase, interval); 895 } 896 897 //////////////////////////////////////////////////////////////////////////////// 898 // RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation: 899 900 void DelegatedFrameHost::OnLostResources() { 901 RenderWidgetHostImpl* host = client_->GetHost(); 902 if (frame_provider_.get() || !surface_id_.is_null()) 903 EvictDelegatedFrame(); 904 idle_frame_subscriber_textures_.clear(); 905 yuv_readback_pipeline_.reset(); 906 907 host->ScheduleComposite(); 908 } 909 910 //////////////////////////////////////////////////////////////////////////////// 911 // DelegatedFrameHost, private: 912 913 DelegatedFrameHost::~DelegatedFrameHost() { 914 ImageTransportFactory::GetInstance()->RemoveObserver(this); 915 916 if (!surface_id_.is_null()) 917 surface_factory_->Destroy(surface_id_); 918 if (resource_collection_.get()) 919 resource_collection_->SetClient(NULL); 920 921 DCHECK(!vsync_manager_.get()); 922 } 923 924 void DelegatedFrameHost::RunOnCommitCallbacks() { 925 for (std::vector<base::Closure>::const_iterator 926 it = on_compositing_did_commit_callbacks_.begin(); 927 it != on_compositing_did_commit_callbacks_.end(); ++it) { 928 it->Run(); 929 } 930 on_compositing_did_commit_callbacks_.clear(); 931 } 932 933 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks( 934 const base::Closure& callback) { 935 ui::Compositor* compositor = client_->GetCompositor(); 936 DCHECK(compositor); 937 938 if (!compositor->HasObserver(this)) 939 compositor->AddObserver(this); 940 941 can_lock_compositor_ = NO_PENDING_COMMIT; 942 on_compositing_did_commit_callbacks_.push_back(callback); 943 } 944 945 void DelegatedFrameHost::AddedToWindow() { 946 ui::Compositor* compositor = client_->GetCompositor(); 947 if (compositor) { 948 DCHECK(!vsync_manager_.get()); 949 vsync_manager_ = compositor->vsync_manager(); 950 vsync_manager_->AddObserver(this); 951 } 952 } 953 954 void DelegatedFrameHost::RemovingFromWindow() { 955 RunOnCommitCallbacks(); 956 resize_lock_.reset(); 957 client_->GetHost()->WasResized(); 958 ui::Compositor* compositor = client_->GetCompositor(); 959 if (compositor && compositor->HasObserver(this)) 960 compositor->RemoveObserver(this); 961 962 if (vsync_manager_.get()) { 963 vsync_manager_->RemoveObserver(this); 964 vsync_manager_ = NULL; 965 } 966 } 967 968 void DelegatedFrameHost::LockResources() { 969 DCHECK(frame_provider_.get() || !surface_id_.is_null()); 970 delegated_frame_evictor_->LockFrame(); 971 } 972 973 void DelegatedFrameHost::UnlockResources() { 974 DCHECK(frame_provider_.get() || !surface_id_.is_null()); 975 delegated_frame_evictor_->UnlockFrame(); 976 } 977 978 //////////////////////////////////////////////////////////////////////////////// 979 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation: 980 981 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer, 982 ui::Layer* new_layer) { 983 // The new_layer is the one that will be used by our Window, so that's the one 984 // that should keep our frame. old_layer will be returned to the 985 // RecreateLayer caller, and should have a copy. 986 if (frame_provider_.get()) { 987 new_layer->SetShowDelegatedContent(frame_provider_.get(), 988 current_frame_size_in_dip_); 989 } 990 if (!surface_id_.is_null()) { 991 new_layer->SetShowSurface(surface_id_, current_frame_size_in_dip_); 992 } 993 } 994 995 } // namespace content 996