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