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.
5 #include "content/browser/compositor/delegated_frame_host.h"
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"
30 ////////////////////////////////////////////////////////////////////////////////
31 // DelegatedFrameHostClient
33 bool DelegatedFrameHostClient::ShouldCreateResizeLock() {
34 // On Windows and Linux, holding pointer moves will not help throttling
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)
43 return GetDelegatedFrameHost()->ShouldCreateResizeLock();
47 void DelegatedFrameHostClient::RequestCopyOfOutput(
48 scoped_ptr<cc::CopyOutputRequest> request) {
49 GetDelegatedFrameHost()->RequestCopyOfOutput(request.Pass());
52 ////////////////////////////////////////////////////////////////////////////////
55 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* 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);
66 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) {
67 delegated_frame_evictor_->SetVisible(true);
69 if (surface_id_.is_null() && !frame_provider_.get() &&
70 !released_front_lock_.get()) {
71 ui::Compositor* compositor = client_->GetCompositor();
73 released_front_lock_ = compositor->GetCompositorLock();
76 ui::Compositor* compositor = client_->GetCompositor();
78 compositor->SetLatencyInfo(latency_info);
82 bool DelegatedFrameHost::HasSavedFrame() {
83 return delegated_frame_evictor_->HasFrame();
86 void DelegatedFrameHost::WasHidden() {
87 delegated_frame_evictor_->SetVisible(false);
88 released_front_lock_ = NULL;
91 void DelegatedFrameHost::MaybeCreateResizeLock() {
92 if (!client_->ShouldCreateResizeLock())
94 DCHECK(client_->GetCompositor());
96 // Listen to changes in the compositor lock state.
97 ui::Compositor* compositor = client_->GetCompositor();
98 if (!compositor->HasObserver(this))
99 compositor->AddObserver(this);
101 bool defer_compositor_lock =
102 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
103 can_lock_compositor_ == NO_PENDING_COMMIT;
105 if (can_lock_compositor_ == YES_CAN_LOCK)
106 can_lock_compositor_ = YES_DID_LOCK;
108 resize_lock_ = client_->CreateResizeLock(defer_compositor_lock);
111 bool DelegatedFrameHost::ShouldCreateResizeLock() {
112 RenderWidgetHostImpl* host = client_->GetHost();
117 if (host->should_auto_resize())
120 gfx::Size desired_size = client_->DesiredFrameSize();
121 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty())
124 ui::Compositor* compositor = client_->GetCompositor();
131 void DelegatedFrameHost::RequestCopyOfOutput(
132 scoped_ptr<cc::CopyOutputRequest> request) {
134 if (surface_factory_ && !surface_id_.is_null())
135 surface_factory_->RequestCopyOfSurface(surface_id_, request.Pass());
137 request->SendEmptyResult();
139 client_->GetLayer()->RequestCopyOfOutput(request.Pass());
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());
158 scoped_ptr<cc::CopyOutputRequest> request =
159 cc::CopyOutputRequest::CreateRequest(base::Bind(
160 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
164 request->set_area(src_subrect);
165 client_->RequestCopyOfOutput(request.Pass());
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()) {
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);
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.
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()));
204 client_->RequestCopyOfOutput(request.Pass());
207 bool DelegatedFrameHost::CanCopyToBitmap() const {
208 return client_->GetCompositor() &&
209 client_->GetLayer()->has_external_content();
212 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
213 return client_->GetCompositor() &&
214 client_->GetLayer()->has_external_content();
217 bool DelegatedFrameHost::CanSubscribeFrame() const {
221 void DelegatedFrameHost::BeginFrameSubscription(
222 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
223 frame_subscriber_ = subscriber.Pass();
226 void DelegatedFrameHost::EndFrameSubscription() {
227 idle_frame_subscriber_textures_.clear();
228 frame_subscriber_.reset();
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 ||
240 return size_in_dip != resize_lock_->expected_size();
243 void DelegatedFrameHost::WasResized() {
244 if (client_->DesiredFrameSize() != current_frame_size_in_dip_ &&
245 client_->GetHost()->is_hidden())
246 EvictDelegatedFrame();
247 MaybeCreateResizeLock();
250 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
252 return resize_lock_->expected_size();
254 return client_->DesiredFrameSize();
257 void DelegatedFrameHost::CheckResizeLock() {
259 resize_lock_->expected_size() != current_frame_size_in_dip_)
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();
268 if (!compositor->HasObserver(this))
269 compositor->AddObserver(this);
273 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
274 const gfx::Rect& damage_rect) {
275 if (!frame_subscriber() || !CanCopyToVideoFrame())
278 const base::TimeTicks now = gfx::FrameTime::Now();
279 base::TimeTicks present_time;
280 if (vsync_timebase_.is_null() || vsync_interval_ <= base::TimeDelta()) {
283 const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_;
284 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_;
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_),
294 base::Bind(callback, present_time));
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());
306 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
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);
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);
317 if (ShouldSkipFrame(frame_size_in_dip)) {
318 cc::CompositorFrameAck ack;
319 cc::TransferableResource::ReturnResources(frame_data->resource_list,
322 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(),
323 latency_info.begin(), latency_info.end());
325 RenderWidgetHostImpl::SendSwapCompositorFrameAck(
326 host->GetRoutingID(), output_surface_id,
327 host->GetProcess()->GetID(), ack);
328 skipped_frames_ = true;
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);
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;
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();
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);
357 if (resource_collection_->LoseAllResources())
358 SendReturnedDelegatedResources(last_output_surface_id_);
360 resource_collection_ = NULL;
362 last_output_surface_id_ = output_surface_id;
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;
372 if (!surface_factory_) {
373 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
374 cc::SurfaceManager* manager = factory->GetSurfaceManager();
375 id_allocator_ = factory->CreateSurfaceIdAllocator();
377 make_scoped_ptr(new cc::SurfaceFactory(manager, this));
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;
391 scoped_ptr<cc::CompositorFrame> compositor_frame =
392 make_scoped_ptr(new cc::CompositorFrame());
393 compositor_frame->delegated_frame_data = frame_data.Pass();
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(),
401 base::Closure ack_callback;
403 ack_callback = base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
407 surface_factory_->SubmitFrame(
408 surface_id_, compositor_frame.Pass(), ack_callback);
410 if (!resource_collection_.get()) {
411 resource_collection_ = new cc::DelegatedFrameResourceCollection;
412 resource_collection_->SetClient(this);
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(),
427 frame_provider_->SetFrameData(frame_data.Pass());
429 modified_layers = true;
432 released_front_lock_ = NULL;
433 current_frame_size_in_dip_ = frame_size_in_dip;
436 if (modified_layers && !damage_rect_in_dip.IsEmpty()) {
437 // TODO(jbauman): Need to always tell the window observer about the
439 client_->GetLayer()->OnDelegatedFrameDamage(damage_rect_in_dip);
442 pending_delegated_ack_count_++;
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();
454 compositor->SetLatencyInfo(*it);
455 skipped_latency_info_list_.clear();
456 AddOnCommitCallbackAndDisableLocks(
457 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
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.
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(),
476 host->GetProcess()->GetID(),
478 DCHECK_GT(pending_delegated_ack_count_, 0);
479 pending_delegated_ack_count_--;
482 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
483 if (pending_delegated_ack_count_)
486 SendReturnedDelegatedResources(last_output_surface_id_);
489 void DelegatedFrameHost::SendReturnedDelegatedResources(
490 uint32 output_surface_id) {
491 RenderWidgetHostImpl* host = client_->GetHost();
493 cc::CompositorFrameAck ack;
494 if (!surface_returned_resources_.empty()) {
495 ack.resources.swap(surface_returned_resources_);
497 DCHECK(resource_collection_.get());
498 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
500 DCHECK(!ack.resources.empty());
502 RenderWidgetHostImpl::SendReclaimCompositorResources(
503 host->GetRoutingID(),
505 host->GetProcess()->GetID(),
509 void DelegatedFrameHost::ReturnResources(
510 const cc::ReturnedResourceArray& resources) {
511 if (resources.empty())
513 std::copy(resources.begin(),
515 std::back_inserter(surface_returned_resources_));
516 if (!pending_delegated_ack_count_)
517 SendReturnedDelegatedResources(last_output_surface_id_);
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();
527 delegated_frame_evictor_->DiscardedFrame();
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());
541 if (result->HasTexture()) {
542 // GPU-accelerated path
543 PrepareTextureCopyOutputResult(dst_size_in_pixel, color_type,
549 DCHECK(result->HasBitmap());
551 PrepareBitmapCopyOutputResult(dst_size_in_pixel, color_type, callback,
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,
561 bitmap_pixels_lock.reset();
563 uint32 sync_point = 0;
565 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
566 sync_point = gl_helper->InsertSyncPoint();
568 bool lost_resource = sync_point == 0;
569 release_callback->Run(sync_point, lost_resource);
571 callback.Run(result, *bitmap);
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()));
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(),
591 kOpaque_SkAlphaType)))
594 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
595 GLHelper* gl_helper = factory->GetGLHelper();
599 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
600 new SkAutoLockPixels(*bitmap));
601 uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
603 cc::TextureMailbox texture_mailbox;
604 scoped_ptr<cc::SingleReleaseCallback> release_callback;
605 result->TakeTexture(&texture_mailbox, &release_callback);
606 DCHECK(texture_mailbox.IsTexture());
608 ignore_result(scoped_callback_runner.Release());
610 gl_helper->CropScaleReadbackAndCleanMailbox(
611 texture_mailbox.mailbox(),
612 texture_mailbox.sync_point(),
614 gfx::Rect(result->size()),
618 base::Bind(&CopyFromCompositingSurfaceFinished,
620 base::Passed(&release_callback),
621 base::Passed(&bitmap),
622 base::Passed(&bitmap_pixels_lock)),
623 GLHelper::SCALER_QUALITY_FAST);
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) {
634 callback.Run(false, SkBitmap());
637 DCHECK(result->HasBitmap());
638 scoped_ptr<SkBitmap> source = result->TakeBitmap();
640 SkBitmap scaled_bitmap;
641 if (source->width() != dst_size_in_pixel.width() ||
642 source->height() != dst_size_in_pixel.height()) {
644 skia::ImageOperations::Resize(*source,
645 skia::ImageOperations::RESIZE_BEST,
646 dst_size_in_pixel.width(),
647 dst_size_in_pixel.height());
649 scaled_bitmap = *source;
651 if (color_type == kN32_SkColorType) {
652 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
653 callback.Run(true, scaled_bitmap);
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()));
665 callback.Run(false, SkBitmap());
668 SkCanvas canvas(grayscale_bitmap);
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);
678 void DelegatedFrameHost::ReturnSubscriberTexture(
679 base::WeakPtr<DelegatedFrameHost> dfh,
680 scoped_refptr<OwnedMailbox> subscriber_texture,
682 if (!subscriber_texture.get())
687 subscriber_texture->UpdateSyncPoint(sync_point);
689 if (dfh->frame_subscriber_ && subscriber_texture->texture_id())
690 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture);
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,
700 callback.Run(result);
702 uint32 sync_point = 0;
704 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
705 sync_point = gl_helper->InsertSyncPoint();
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);
714 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point);
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));
730 if (result->IsEmpty())
732 if (result->size().IsEmpty())
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
741 gfx::Rect region_in_frame =
742 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_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())
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());
764 scaled_bitmap = *bitmap.get();
768 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
770 media::CopyRGBToVideoFrame(
771 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
772 scaled_bitmap.rowBytes(),
776 ignore_result(scoped_callback_runner.Release());
781 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
782 GLHelper* gl_helper = factory->GetGLHelper();
785 if (subscriber_texture.get() && !subscriber_texture->texture_id())
788 cc::TextureMailbox texture_mailbox;
789 scoped_ptr<cc::SingleReleaseCallback> release_callback;
790 result->TakeTexture(&texture_mailbox, &release_callback);
791 DCHECK(texture_mailbox.IsTexture());
793 gfx::Rect result_rect(result->size());
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;
808 std::string switch_value =
809 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
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;
818 dfh->yuv_readback_pipeline_.reset(
819 gl_helper->CreateReadbackPipelineYUV(quality,
822 video_frame->coded_size(),
826 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get();
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,
836 base::Passed(&release_callback));
837 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
838 texture_mailbox.sync_point(),
843 ////////////////////////////////////////////////////////////////////////////////
844 // DelegatedFrameHost, ui::CompositorObserver implementation:
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;
854 RunOnCommitCallbacks();
856 resize_lock_->expected_size() == current_frame_size_in_dip_) {
857 resize_lock_.reset();
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
862 MaybeCreateResizeLock();
866 void DelegatedFrameHost::OnCompositingStarted(
867 ui::Compositor* compositor, base::TimeTicks start_time) {
868 last_draw_ended_ = start_time;
871 void DelegatedFrameHost::OnCompositingEnded(
872 ui::Compositor* compositor) {
875 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
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;
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);
897 ////////////////////////////////////////////////////////////////////////////////
898 // RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation:
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();
907 host->ScheduleComposite();
910 ////////////////////////////////////////////////////////////////////////////////
911 // DelegatedFrameHost, private:
913 DelegatedFrameHost::~DelegatedFrameHost() {
914 ImageTransportFactory::GetInstance()->RemoveObserver(this);
916 if (!surface_id_.is_null())
917 surface_factory_->Destroy(surface_id_);
918 if (resource_collection_.get())
919 resource_collection_->SetClient(NULL);
921 DCHECK(!vsync_manager_.get());
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) {
930 on_compositing_did_commit_callbacks_.clear();
933 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
934 const base::Closure& callback) {
935 ui::Compositor* compositor = client_->GetCompositor();
938 if (!compositor->HasObserver(this))
939 compositor->AddObserver(this);
941 can_lock_compositor_ = NO_PENDING_COMMIT;
942 on_compositing_did_commit_callbacks_.push_back(callback);
945 void DelegatedFrameHost::AddedToWindow() {
946 ui::Compositor* compositor = client_->GetCompositor();
948 DCHECK(!vsync_manager_.get());
949 vsync_manager_ = compositor->vsync_manager();
950 vsync_manager_->AddObserver(this);
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);
962 if (vsync_manager_.get()) {
963 vsync_manager_->RemoveObserver(this);
964 vsync_manager_ = NULL;
968 void DelegatedFrameHost::LockResources() {
969 DCHECK(frame_provider_.get() || !surface_id_.is_null());
970 delegated_frame_evictor_->LockFrame();
973 void DelegatedFrameHost::UnlockResources() {
974 DCHECK(frame_provider_.get() || !surface_id_.is_null());
975 delegated_frame_evictor_->UnlockFrame();
978 ////////////////////////////////////////////////////////////////////////////////
979 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
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_);
990 if (!surface_id_.is_null()) {
991 new_layer->SetShowSurface(surface_id_, current_frame_size_in_dip_);
995 } // namespace content