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.h"
15 #include "cc/surfaces/surface_factory.h"
16 #include "cc/surfaces/surface_manager.h"
17 #include "content/browser/compositor/resize_lock.h"
18 #include "content/browser/gpu/compositor_util.h"
19 #include "content/common/gpu/client/gl_helper.h"
20 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
21 #include "content/public/common/content_switches.h"
22 #include "media/base/video_frame.h"
23 #include "media/base/video_util.h"
24 #include "skia/ext/image_operations.h"
25 #include "third_party/skia/include/core/SkCanvas.h"
26 #include "third_party/skia/include/core/SkPaint.h"
27 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
28 #include "ui/gfx/frame_time.h"
34 void SatisfyCallback(cc::SurfaceManager* manager,
35 cc::SurfaceSequence sequence) {
36 std::vector<uint32_t> sequences;
37 sequences.push_back(sequence.sequence);
38 manager->DidSatisfySequences(sequence.id_namespace, &sequences);
41 void RequireCallback(cc::SurfaceManager* manager,
43 cc::SurfaceSequence sequence) {
44 cc::Surface* surface = manager->GetSurfaceForId(id);
46 LOG(ERROR) << "Attempting to require callback on nonexistent surface";
49 surface->AddDestructionDependency(sequence);
54 ////////////////////////////////////////////////////////////////////////////////
55 // DelegatedFrameHostClient
57 bool DelegatedFrameHostClient::ShouldCreateResizeLock() {
58 // On Windows and Linux, holding pointer moves will not help throttling
60 // TODO(piman): on Windows we need to block (nested message loop?) the
61 // WM_SIZE event. On Linux we need to throttle at the WM level using
62 // _NET_WM_SYNC_REQUEST.
63 // TODO(ccameron): Mac browser window resizing is incompletely implemented.
64 #if !defined(OS_CHROMEOS)
67 return GetDelegatedFrameHost()->ShouldCreateResizeLock();
71 void DelegatedFrameHostClient::RequestCopyOfOutput(
72 scoped_ptr<cc::CopyOutputRequest> request) {
73 GetDelegatedFrameHost()->RequestCopyOfOutput(request.Pass());
76 ////////////////////////////////////////////////////////////////////////////////
79 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client)
81 use_surfaces_(UseSurfacesEnabled()),
82 last_output_surface_id_(0),
83 pending_delegated_ack_count_(0),
84 skipped_frames_(false),
85 can_lock_compositor_(YES_CAN_LOCK),
86 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) {
87 ImageTransportFactory::GetInstance()->AddObserver(this);
90 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) {
91 delegated_frame_evictor_->SetVisible(true);
93 if (surface_id_.is_null() && !frame_provider_.get() &&
94 !released_front_lock_.get()) {
95 ui::Compositor* compositor = client_->GetCompositor();
97 released_front_lock_ = compositor->GetCompositorLock();
100 ui::Compositor* compositor = client_->GetCompositor();
102 compositor->SetLatencyInfo(latency_info);
106 bool DelegatedFrameHost::HasSavedFrame() {
107 return delegated_frame_evictor_->HasFrame();
110 void DelegatedFrameHost::WasHidden() {
111 delegated_frame_evictor_->SetVisible(false);
112 released_front_lock_ = NULL;
115 void DelegatedFrameHost::MaybeCreateResizeLock() {
116 if (!client_->ShouldCreateResizeLock())
118 DCHECK(client_->GetCompositor());
120 // Listen to changes in the compositor lock state.
121 ui::Compositor* compositor = client_->GetCompositor();
122 if (!compositor->HasObserver(this))
123 compositor->AddObserver(this);
125 bool defer_compositor_lock =
126 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
127 can_lock_compositor_ == NO_PENDING_COMMIT;
129 if (can_lock_compositor_ == YES_CAN_LOCK)
130 can_lock_compositor_ = YES_DID_LOCK;
132 resize_lock_ = client_->CreateResizeLock(defer_compositor_lock);
135 bool DelegatedFrameHost::ShouldCreateResizeLock() {
136 RenderWidgetHostImpl* host = client_->GetHost();
141 if (host->should_auto_resize())
144 gfx::Size desired_size = client_->DesiredFrameSize();
145 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty())
148 ui::Compositor* compositor = client_->GetCompositor();
155 void DelegatedFrameHost::RequestCopyOfOutput(
156 scoped_ptr<cc::CopyOutputRequest> request) {
157 client_->GetLayer()->RequestCopyOfOutput(request.Pass());
160 void DelegatedFrameHost::CopyFromCompositingSurface(
161 const gfx::Rect& src_subrect,
162 const gfx::Size& output_size,
163 CopyFromCompositingSurfaceCallback& callback,
164 const SkColorType color_type) {
165 // Only ARGB888 and RGB565 supported as of now.
166 bool format_support = ((color_type == kAlpha_8_SkColorType) ||
167 (color_type == kRGB_565_SkColorType) ||
168 (color_type == kN32_SkColorType));
169 DCHECK(format_support);
170 if (!CanCopyToBitmap()) {
171 callback.Run(false, SkBitmap());
175 scoped_ptr<cc::CopyOutputRequest> request =
176 cc::CopyOutputRequest::CreateRequest(base::Bind(
177 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
181 request->set_area(src_subrect);
182 client_->RequestCopyOfOutput(request.Pass());
185 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
186 const gfx::Rect& src_subrect,
187 const scoped_refptr<media::VideoFrame>& target,
188 const base::Callback<void(bool)>& callback) {
189 if (!CanCopyToVideoFrame()) {
194 // Try get a texture to reuse.
195 scoped_refptr<OwnedMailbox> subscriber_texture;
196 if (frame_subscriber_) {
197 if (!idle_frame_subscriber_textures_.empty()) {
198 subscriber_texture = idle_frame_subscriber_textures_.back();
199 idle_frame_subscriber_textures_.pop_back();
200 } else if (GLHelper* helper =
201 ImageTransportFactory::GetInstance()->GetGLHelper()) {
202 subscriber_texture = new OwnedMailbox(helper);
206 scoped_ptr<cc::CopyOutputRequest> request =
207 cc::CopyOutputRequest::CreateRequest(base::Bind(
208 &DelegatedFrameHost::
209 CopyFromCompositingSurfaceHasResultForVideo,
210 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
214 request->set_area(src_subrect);
215 if (subscriber_texture.get()) {
216 request->SetTextureMailbox(
217 cc::TextureMailbox(subscriber_texture->mailbox(),
218 subscriber_texture->target(),
219 subscriber_texture->sync_point()));
221 client_->RequestCopyOfOutput(request.Pass());
224 bool DelegatedFrameHost::CanCopyToBitmap() const {
225 return client_->GetCompositor() &&
226 client_->GetLayer()->has_external_content();
229 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
230 return client_->GetCompositor() &&
231 client_->GetLayer()->has_external_content();
234 bool DelegatedFrameHost::CanSubscribeFrame() const {
238 void DelegatedFrameHost::BeginFrameSubscription(
239 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
240 frame_subscriber_ = subscriber.Pass();
243 void DelegatedFrameHost::EndFrameSubscription() {
244 idle_frame_subscriber_textures_.clear();
245 frame_subscriber_.reset();
248 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const {
249 // Should skip a frame only when another frame from the renderer is guaranteed
250 // to replace it. Otherwise may cause hangs when the renderer is waiting for
251 // the completion of latency infos (such as when taking a Snapshot.)
252 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
253 can_lock_compositor_ == NO_PENDING_COMMIT ||
257 return size_in_dip != resize_lock_->expected_size();
260 void DelegatedFrameHost::WasResized() {
261 if (client_->DesiredFrameSize() != current_frame_size_in_dip_ &&
262 client_->GetHost()->is_hidden())
263 EvictDelegatedFrame();
264 MaybeCreateResizeLock();
267 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
269 return resize_lock_->expected_size();
271 return client_->DesiredFrameSize();
274 void DelegatedFrameHost::CheckResizeLock() {
276 resize_lock_->expected_size() != current_frame_size_in_dip_)
279 // Since we got the size we were looking for, unlock the compositor. But delay
280 // the release of the lock until we've kicked a frame with the new texture, to
281 // avoid resizing the UI before we have a chance to draw a "good" frame.
282 resize_lock_->UnlockCompositor();
283 ui::Compositor* compositor = client_->GetCompositor();
285 if (!compositor->HasObserver(this))
286 compositor->AddObserver(this);
290 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
291 const gfx::Rect& damage_rect) {
292 if (!frame_subscriber() || !CanCopyToVideoFrame())
295 const base::TimeTicks now = gfx::FrameTime::Now();
296 base::TimeTicks present_time;
297 if (vsync_timebase_.is_null() || vsync_interval_ <= base::TimeDelta()) {
300 const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_;
301 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_;
304 scoped_refptr<media::VideoFrame> frame;
305 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
306 if (frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time,
307 &frame, &callback)) {
308 CopyFromCompositingSurfaceToVideoFrame(
309 gfx::Rect(current_frame_size_in_dip_),
311 base::Bind(callback, present_time));
315 void DelegatedFrameHost::SwapDelegatedFrame(
316 uint32 output_surface_id,
317 scoped_ptr<cc::DelegatedFrameData> frame_data,
318 float frame_device_scale_factor,
319 const std::vector<ui::LatencyInfo>& latency_info) {
320 RenderWidgetHostImpl* host = client_->GetHost();
321 DCHECK(!frame_data->render_pass_list.empty());
323 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
325 gfx::Size frame_size = root_pass->output_rect.size();
326 gfx::Size frame_size_in_dip =
327 ConvertSizeToDIP(frame_device_scale_factor, frame_size);
329 gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect);
330 damage_rect.Intersect(gfx::Rect(frame_size));
331 gfx::Rect damage_rect_in_dip =
332 ConvertRectToDIP(frame_device_scale_factor, damage_rect);
334 if (ShouldSkipFrame(frame_size_in_dip)) {
335 cc::CompositorFrameAck ack;
336 cc::TransferableResource::ReturnResources(frame_data->resource_list,
339 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(),
340 latency_info.begin(), latency_info.end());
342 RenderWidgetHostImpl::SendSwapCompositorFrameAck(
343 host->GetRoutingID(), output_surface_id,
344 host->GetProcess()->GetID(), ack);
345 skipped_frames_ = true;
349 if (skipped_frames_) {
350 skipped_frames_ = false;
351 damage_rect = gfx::Rect(frame_size);
352 damage_rect_in_dip = gfx::Rect(frame_size_in_dip);
354 // Give the same damage rect to the compositor.
355 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
356 root_pass->damage_rect = damage_rect;
359 if (output_surface_id != last_output_surface_id_) {
360 // Resource ids are scoped by the output surface.
361 // If the originating output surface doesn't match the last one, it
362 // indicates the renderer's output surface may have been recreated, in which
363 // case we should recreate the DelegatedRendererLayer, to avoid matching
364 // resources from the old one with resources from the new one which would
365 // have the same id. Changing the layer to showing painted content destroys
366 // the DelegatedRendererLayer.
367 EvictDelegatedFrame();
369 surface_factory_.reset();
370 if (!surface_returned_resources_.empty())
371 SendReturnedDelegatedResources(last_output_surface_id_);
373 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
374 // any resources from the old output surface with the new output surface id.
375 if (resource_collection_.get()) {
376 resource_collection_->SetClient(NULL);
378 if (resource_collection_->LoseAllResources())
379 SendReturnedDelegatedResources(last_output_surface_id_);
381 resource_collection_ = NULL;
383 last_output_surface_id_ = output_surface_id;
385 ui::Compositor* compositor = client_->GetCompositor();
386 if (frame_size.IsEmpty()) {
387 DCHECK(frame_data->resource_list.empty());
388 EvictDelegatedFrame();
391 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
392 cc::SurfaceManager* manager = factory->GetSurfaceManager();
393 if (!surface_factory_) {
395 factory->GetContextFactory()->CreateSurfaceIdAllocator();
397 make_scoped_ptr(new cc::SurfaceFactory(manager, this));
399 if (surface_id_.is_null() || frame_size != current_surface_size_ ||
400 frame_size_in_dip != current_frame_size_in_dip_) {
401 if (!surface_id_.is_null())
402 surface_factory_->Destroy(surface_id_);
403 surface_id_ = id_allocator_->GenerateId();
404 surface_factory_->Create(surface_id_, frame_size);
405 // manager must outlive compositors using it.
406 client_->GetLayer()->SetShowSurface(
408 base::Bind(&SatisfyCallback, base::Unretained(manager)),
409 base::Bind(&RequireCallback, base::Unretained(manager)), frame_size,
411 current_surface_size_ = frame_size;
413 scoped_ptr<cc::CompositorFrame> compositor_frame =
414 make_scoped_ptr(new cc::CompositorFrame());
415 compositor_frame->delegated_frame_data = frame_data.Pass();
417 compositor_frame->metadata.latency_info.swap(skipped_latency_info_list_);
418 compositor_frame->metadata.latency_info.insert(
419 compositor_frame->metadata.latency_info.end(),
420 latency_info.begin(),
423 base::Closure ack_callback;
425 ack_callback = base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
429 surface_factory_->SubmitFrame(
430 surface_id_, compositor_frame.Pass(), ack_callback);
432 if (!resource_collection_.get()) {
433 resource_collection_ = new cc::DelegatedFrameResourceCollection;
434 resource_collection_->SetClient(this);
436 // If the physical frame size changes, we need a new |frame_provider_|. If
437 // the physical frame size is the same, but the size in DIP changed, we
438 // need to adjust the scale at which the frames will be drawn, and we do
439 // this by making a new |frame_provider_| also to ensure the scale change
440 // is presented in sync with the new frame content.
441 if (!frame_provider_.get() ||
442 frame_size != frame_provider_->frame_size() ||
443 frame_size_in_dip != current_frame_size_in_dip_) {
444 frame_provider_ = new cc::DelegatedFrameProvider(
445 resource_collection_.get(), frame_data.Pass());
446 client_->GetLayer()->SetShowDelegatedContent(frame_provider_.get(),
449 frame_provider_->SetFrameData(frame_data.Pass());
453 released_front_lock_ = NULL;
454 current_frame_size_in_dip_ = frame_size_in_dip;
457 if (!damage_rect_in_dip.IsEmpty())
458 client_->GetLayer()->OnDelegatedFrameDamage(damage_rect_in_dip);
460 pending_delegated_ack_count_++;
463 SendDelegatedFrameAck(output_surface_id);
464 } else if (!use_surfaces_) {
465 std::vector<ui::LatencyInfo>::const_iterator it;
466 for (it = latency_info.begin(); it != latency_info.end(); ++it)
467 compositor->SetLatencyInfo(*it);
468 // If we've previously skipped any latency infos add them.
469 for (it = skipped_latency_info_list_.begin();
470 it != skipped_latency_info_list_.end();
472 compositor->SetLatencyInfo(*it);
473 skipped_latency_info_list_.clear();
474 AddOnCommitCallbackAndDisableLocks(
475 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
479 AddOnCommitCallbackAndDisableLocks(base::Closure());
481 DidReceiveFrameFromRenderer(damage_rect);
482 if (frame_provider_.get() || !surface_id_.is_null())
483 delegated_frame_evictor_->SwappedFrame(!host->is_hidden());
484 // Note: the frame may have been evicted immediately.
487 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) {
488 RenderWidgetHostImpl* host = client_->GetHost();
489 cc::CompositorFrameAck ack;
490 if (!surface_returned_resources_.empty())
491 ack.resources.swap(surface_returned_resources_);
492 if (resource_collection_.get())
493 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
494 RenderWidgetHostImpl::SendSwapCompositorFrameAck(host->GetRoutingID(),
496 host->GetProcess()->GetID(),
498 DCHECK_GT(pending_delegated_ack_count_, 0);
499 pending_delegated_ack_count_--;
502 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
503 if (pending_delegated_ack_count_)
506 SendReturnedDelegatedResources(last_output_surface_id_);
509 void DelegatedFrameHost::SendReturnedDelegatedResources(
510 uint32 output_surface_id) {
511 RenderWidgetHostImpl* host = client_->GetHost();
513 cc::CompositorFrameAck ack;
514 if (!surface_returned_resources_.empty()) {
515 ack.resources.swap(surface_returned_resources_);
517 DCHECK(resource_collection_.get());
518 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
520 DCHECK(!ack.resources.empty());
522 RenderWidgetHostImpl::SendReclaimCompositorResources(
523 host->GetRoutingID(),
525 host->GetProcess()->GetID(),
529 void DelegatedFrameHost::ReturnResources(
530 const cc::ReturnedResourceArray& resources) {
531 if (resources.empty())
533 std::copy(resources.begin(),
535 std::back_inserter(surface_returned_resources_));
536 if (!pending_delegated_ack_count_)
537 SendReturnedDelegatedResources(last_output_surface_id_);
540 void DelegatedFrameHost::EvictDelegatedFrame() {
541 client_->GetLayer()->SetShowSolidColorContent();
542 frame_provider_ = NULL;
543 if (!surface_id_.is_null()) {
544 surface_factory_->Destroy(surface_id_);
545 surface_id_ = cc::SurfaceId();
547 delegated_frame_evictor_->DiscardedFrame();
551 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
552 const gfx::Size& dst_size_in_pixel,
553 const SkColorType color_type,
554 const base::Callback<void(bool, const SkBitmap&)>& callback,
555 scoped_ptr<cc::CopyOutputResult> result) {
556 if (result->IsEmpty() || result->size().IsEmpty()) {
557 callback.Run(false, SkBitmap());
561 if (result->HasTexture()) {
562 // GPU-accelerated path
563 PrepareTextureCopyOutputResult(dst_size_in_pixel, color_type,
569 DCHECK(result->HasBitmap());
571 PrepareBitmapCopyOutputResult(dst_size_in_pixel, color_type, callback,
575 static void CopyFromCompositingSurfaceFinished(
576 const base::Callback<void(bool, const SkBitmap&)>& callback,
577 scoped_ptr<cc::SingleReleaseCallback> release_callback,
578 scoped_ptr<SkBitmap> bitmap,
579 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
581 bitmap_pixels_lock.reset();
583 uint32 sync_point = 0;
585 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
586 sync_point = gl_helper->InsertSyncPoint();
588 bool lost_resource = sync_point == 0;
589 release_callback->Run(sync_point, lost_resource);
591 callback.Run(result, *bitmap);
595 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
596 const gfx::Size& dst_size_in_pixel,
597 const SkColorType color_type,
598 const base::Callback<void(bool, const SkBitmap&)>& callback,
599 scoped_ptr<cc::CopyOutputResult> result) {
600 DCHECK(result->HasTexture());
601 base::ScopedClosureRunner scoped_callback_runner(
602 base::Bind(callback, false, SkBitmap()));
604 // TODO(sikugu): We should be able to validate the format here using
605 // GLHelper::IsReadbackConfigSupported before we processs the result.
606 // See crbug.com/415682.
607 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
608 if (!bitmap->tryAllocPixels(SkImageInfo::Make(dst_size_in_pixel.width(),
609 dst_size_in_pixel.height(),
611 kOpaque_SkAlphaType)))
614 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
615 GLHelper* gl_helper = factory->GetGLHelper();
619 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
620 new SkAutoLockPixels(*bitmap));
621 uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
623 cc::TextureMailbox texture_mailbox;
624 scoped_ptr<cc::SingleReleaseCallback> release_callback;
625 result->TakeTexture(&texture_mailbox, &release_callback);
626 DCHECK(texture_mailbox.IsTexture());
628 ignore_result(scoped_callback_runner.Release());
630 gl_helper->CropScaleReadbackAndCleanMailbox(
631 texture_mailbox.mailbox(),
632 texture_mailbox.sync_point(),
634 gfx::Rect(result->size()),
638 base::Bind(&CopyFromCompositingSurfaceFinished,
640 base::Passed(&release_callback),
641 base::Passed(&bitmap),
642 base::Passed(&bitmap_pixels_lock)),
643 GLHelper::SCALER_QUALITY_FAST);
647 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
648 const gfx::Size& dst_size_in_pixel,
649 const SkColorType color_type,
650 const base::Callback<void(bool, const SkBitmap&)>& callback,
651 scoped_ptr<cc::CopyOutputResult> result) {
652 if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) {
654 callback.Run(false, SkBitmap());
657 DCHECK(result->HasBitmap());
658 scoped_ptr<SkBitmap> source = result->TakeBitmap();
660 SkBitmap scaled_bitmap;
661 if (source->width() != dst_size_in_pixel.width() ||
662 source->height() != dst_size_in_pixel.height()) {
664 skia::ImageOperations::Resize(*source,
665 skia::ImageOperations::RESIZE_BEST,
666 dst_size_in_pixel.width(),
667 dst_size_in_pixel.height());
669 scaled_bitmap = *source;
671 if (color_type == kN32_SkColorType) {
672 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
673 callback.Run(true, scaled_bitmap);
676 DCHECK_EQ(color_type, kAlpha_8_SkColorType);
677 // The software path currently always returns N32 bitmap regardless of the
678 // |color_type| we ask for.
679 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
680 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|.
681 SkBitmap grayscale_bitmap;
682 bool success = grayscale_bitmap.tryAllocPixels(
683 SkImageInfo::MakeA8(scaled_bitmap.width(), scaled_bitmap.height()));
685 callback.Run(false, SkBitmap());
688 SkCanvas canvas(grayscale_bitmap);
690 skia::RefPtr<SkColorFilter> filter =
691 skia::AdoptRef(SkLumaColorFilter::Create());
692 paint.setColorFilter(filter.get());
693 canvas.drawBitmap(scaled_bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint);
694 callback.Run(true, grayscale_bitmap);
698 void DelegatedFrameHost::ReturnSubscriberTexture(
699 base::WeakPtr<DelegatedFrameHost> dfh,
700 scoped_refptr<OwnedMailbox> subscriber_texture,
702 if (!subscriber_texture.get())
707 subscriber_texture->UpdateSyncPoint(sync_point);
709 if (dfh->frame_subscriber_ && subscriber_texture->texture_id())
710 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture);
714 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
715 base::WeakPtr<DelegatedFrameHost> dfh,
716 const base::Callback<void(bool)>& callback,
717 scoped_refptr<OwnedMailbox> subscriber_texture,
718 scoped_ptr<cc::SingleReleaseCallback> release_callback,
720 callback.Run(result);
722 uint32 sync_point = 0;
724 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
725 sync_point = gl_helper->InsertSyncPoint();
727 if (release_callback) {
728 // A release callback means the texture came from the compositor, so there
729 // should be no |subscriber_texture|.
730 DCHECK(!subscriber_texture.get());
731 bool lost_resource = sync_point == 0;
732 release_callback->Run(sync_point, lost_resource);
734 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point);
738 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
739 base::WeakPtr<DelegatedFrameHost> dfh,
740 scoped_refptr<OwnedMailbox> subscriber_texture,
741 scoped_refptr<media::VideoFrame> video_frame,
742 const base::Callback<void(bool)>& callback,
743 scoped_ptr<cc::CopyOutputResult> result) {
744 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
745 base::ScopedClosureRunner scoped_return_subscriber_texture(
746 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0));
750 if (result->IsEmpty())
752 if (result->size().IsEmpty())
755 // Compute the dest size we want after the letterboxing resize. Make the
756 // coordinates and sizes even because we letterbox in YUV space
757 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
758 // line up correctly.
759 // The video frame's coded_size() and the result's size() are both physical
761 gfx::Rect region_in_frame =
762 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()),
764 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
765 region_in_frame.y() & ~1,
766 region_in_frame.width() & ~1,
767 region_in_frame.height() & ~1);
768 if (region_in_frame.IsEmpty())
771 if (!result->HasTexture()) {
772 DCHECK(result->HasBitmap());
773 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap();
774 // Scale the bitmap to the required size, if necessary.
775 SkBitmap scaled_bitmap;
776 if (result->size().width() != region_in_frame.width() ||
777 result->size().height() != region_in_frame.height()) {
778 skia::ImageOperations::ResizeMethod method =
779 skia::ImageOperations::RESIZE_GOOD;
780 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method,
781 region_in_frame.width(),
782 region_in_frame.height());
784 scaled_bitmap = *bitmap.get();
788 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
790 media::CopyRGBToVideoFrame(
791 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
792 scaled_bitmap.rowBytes(),
796 ignore_result(scoped_callback_runner.Release());
801 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
802 GLHelper* gl_helper = factory->GetGLHelper();
805 if (subscriber_texture.get() && !subscriber_texture->texture_id())
808 cc::TextureMailbox texture_mailbox;
809 scoped_ptr<cc::SingleReleaseCallback> release_callback;
810 result->TakeTexture(&texture_mailbox, &release_callback);
811 DCHECK(texture_mailbox.IsTexture());
813 gfx::Rect result_rect(result->size());
815 content::ReadbackYUVInterface* yuv_readback_pipeline =
816 dfh->yuv_readback_pipeline_.get();
817 if (yuv_readback_pipeline == NULL ||
818 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() ||
819 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect ||
820 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) {
821 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST;
822 std::string quality_switch = switches::kTabCaptureDownscaleQuality;
823 // If we're scaling up, we can use the "best" quality.
824 if (result_rect.size().width() < region_in_frame.size().width() &&
825 result_rect.size().height() < region_in_frame.size().height())
826 quality_switch = switches::kTabCaptureUpscaleQuality;
828 std::string switch_value =
829 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
831 if (switch_value == "fast")
832 quality = GLHelper::SCALER_QUALITY_FAST;
833 else if (switch_value == "good")
834 quality = GLHelper::SCALER_QUALITY_GOOD;
835 else if (switch_value == "best")
836 quality = GLHelper::SCALER_QUALITY_BEST;
838 dfh->yuv_readback_pipeline_.reset(
839 gl_helper->CreateReadbackPipelineYUV(quality,
842 video_frame->coded_size(),
846 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get();
849 ignore_result(scoped_callback_runner.Release());
850 ignore_result(scoped_return_subscriber_texture.Release());
851 base::Callback<void(bool result)> finished_callback = base::Bind(
852 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo,
856 base::Passed(&release_callback));
857 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
858 texture_mailbox.sync_point(),
863 ////////////////////////////////////////////////////////////////////////////////
864 // DelegatedFrameHost, ui::CompositorObserver implementation:
866 void DelegatedFrameHost::OnCompositingDidCommit(
867 ui::Compositor* compositor) {
868 RenderWidgetHostImpl* host = client_->GetHost();
869 if (can_lock_compositor_ == NO_PENDING_COMMIT) {
870 can_lock_compositor_ = YES_CAN_LOCK;
871 if (resize_lock_.get() && resize_lock_->GrabDeferredLock())
872 can_lock_compositor_ = YES_DID_LOCK;
874 RunOnCommitCallbacks();
876 resize_lock_->expected_size() == current_frame_size_in_dip_) {
877 resize_lock_.reset();
879 // We may have had a resize while we had the lock (e.g. if the lock expired,
880 // or if the UI still gave us some resizes), so make sure we grab a new lock
882 MaybeCreateResizeLock();
886 void DelegatedFrameHost::OnCompositingStarted(
887 ui::Compositor* compositor, base::TimeTicks start_time) {
888 last_draw_ended_ = start_time;
891 void DelegatedFrameHost::OnCompositingEnded(
892 ui::Compositor* compositor) {
895 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
898 void DelegatedFrameHost::OnCompositingLockStateChanged(
899 ui::Compositor* compositor) {
900 // A compositor lock that is part of a resize lock timed out. We
901 // should display a renderer frame.
902 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) {
903 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME;
907 void DelegatedFrameHost::OnUpdateVSyncParameters(
908 base::TimeTicks timebase,
909 base::TimeDelta interval) {
910 vsync_timebase_ = timebase;
911 vsync_interval_ = interval;
912 RenderWidgetHostImpl* host = client_->GetHost();
913 if (client_->IsVisible())
914 host->UpdateVSyncParameters(timebase, interval);
917 ////////////////////////////////////////////////////////////////////////////////
918 // RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation:
920 void DelegatedFrameHost::OnLostResources() {
921 RenderWidgetHostImpl* host = client_->GetHost();
922 if (frame_provider_.get() || !surface_id_.is_null())
923 EvictDelegatedFrame();
924 idle_frame_subscriber_textures_.clear();
925 yuv_readback_pipeline_.reset();
927 host->ScheduleComposite();
930 ////////////////////////////////////////////////////////////////////////////////
931 // DelegatedFrameHost, private:
933 DelegatedFrameHost::~DelegatedFrameHost() {
934 ImageTransportFactory::GetInstance()->RemoveObserver(this);
936 if (!surface_id_.is_null())
937 surface_factory_->Destroy(surface_id_);
938 if (resource_collection_.get())
939 resource_collection_->SetClient(NULL);
941 DCHECK(!vsync_manager_.get());
944 void DelegatedFrameHost::RunOnCommitCallbacks() {
945 for (std::vector<base::Closure>::const_iterator
946 it = on_compositing_did_commit_callbacks_.begin();
947 it != on_compositing_did_commit_callbacks_.end(); ++it) {
950 on_compositing_did_commit_callbacks_.clear();
953 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
954 const base::Closure& callback) {
955 ui::Compositor* compositor = client_->GetCompositor();
958 if (!compositor->HasObserver(this))
959 compositor->AddObserver(this);
961 can_lock_compositor_ = NO_PENDING_COMMIT;
962 if (!callback.is_null())
963 on_compositing_did_commit_callbacks_.push_back(callback);
966 void DelegatedFrameHost::AddedToWindow() {
967 ui::Compositor* compositor = client_->GetCompositor();
969 DCHECK(!vsync_manager_.get());
970 vsync_manager_ = compositor->vsync_manager();
971 vsync_manager_->AddObserver(this);
975 void DelegatedFrameHost::RemovingFromWindow() {
976 RunOnCommitCallbacks();
977 resize_lock_.reset();
978 client_->GetHost()->WasResized();
979 ui::Compositor* compositor = client_->GetCompositor();
980 if (compositor && compositor->HasObserver(this))
981 compositor->RemoveObserver(this);
983 if (vsync_manager_.get()) {
984 vsync_manager_->RemoveObserver(this);
985 vsync_manager_ = NULL;
989 void DelegatedFrameHost::LockResources() {
990 DCHECK(frame_provider_.get() || !surface_id_.is_null());
991 delegated_frame_evictor_->LockFrame();
994 void DelegatedFrameHost::UnlockResources() {
995 DCHECK(frame_provider_.get() || !surface_id_.is_null());
996 delegated_frame_evictor_->UnlockFrame();
999 ////////////////////////////////////////////////////////////////////////////////
1000 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
1002 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer,
1003 ui::Layer* new_layer) {
1004 // The new_layer is the one that will be used by our Window, so that's the one
1005 // that should keep our frame. old_layer will be returned to the
1006 // RecreateLayer caller, and should have a copy.
1007 if (frame_provider_.get()) {
1008 new_layer->SetShowDelegatedContent(frame_provider_.get(),
1009 current_frame_size_in_dip_);
1011 if (!surface_id_.is_null()) {
1012 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
1013 cc::SurfaceManager* manager = factory->GetSurfaceManager();
1014 new_layer->SetShowSurface(
1015 surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)),
1016 base::Bind(&RequireCallback, base::Unretained(manager)),
1017 current_surface_size_, current_frame_size_in_dip_);
1021 } // namespace content