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/common/gpu/client/gl_helper.h"
17 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
18 #include "content/public/common/content_switches.h"
19 #include "media/base/video_frame.h"
20 #include "media/base/video_util.h"
21 #include "skia/ext/image_operations.h"
22 #include "third_party/skia/include/core/SkCanvas.h"
23 #include "third_party/skia/include/core/SkPaint.h"
24 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
25 #include "ui/gfx/frame_time.h"
29 ////////////////////////////////////////////////////////////////////////////////
30 // DelegatedFrameHostClient
32 bool DelegatedFrameHostClient::ShouldCreateResizeLock() {
33 // On Windows and Linux, holding pointer moves will not help throttling
35 // TODO(piman): on Windows we need to block (nested message loop?) the
36 // WM_SIZE event. On Linux we need to throttle at the WM level using
37 // _NET_WM_SYNC_REQUEST.
38 // TODO(ccameron): Mac browser window resizing is incompletely implemented.
39 #if !defined(OS_CHROMEOS)
42 return GetDelegatedFrameHost()->ShouldCreateResizeLock();
46 void DelegatedFrameHostClient::RequestCopyOfOutput(
47 scoped_ptr
<cc::CopyOutputRequest
> request
) {
48 GetDelegatedFrameHost()->RequestCopyOfOutput(request
.Pass());
51 ////////////////////////////////////////////////////////////////////////////////
54 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient
* client
)
56 use_surfaces_(base::CommandLine::ForCurrentProcess()->HasSwitch(
57 switches::kUseSurfaces
)),
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 // TODO(jbauman): Make this work with surfaces.
135 request
->SendEmptyResult();
137 client_
->GetLayer()->RequestCopyOfOutput(request
.Pass());
141 void DelegatedFrameHost::CopyFromCompositingSurface(
142 const gfx::Rect
& src_subrect
,
143 const gfx::Size
& output_size
,
144 const base::Callback
<void(bool, const SkBitmap
&)>& callback
,
145 const SkColorType color_type
) {
146 // Only ARGB888 and RGB565 supported as of now.
147 bool format_support
= ((color_type
== kAlpha_8_SkColorType
) ||
148 (color_type
== kRGB_565_SkColorType
) ||
149 (color_type
== kN32_SkColorType
));
150 DCHECK(format_support
);
151 if (!CanCopyToBitmap()) {
152 callback
.Run(false, SkBitmap());
156 scoped_ptr
<cc::CopyOutputRequest
> request
=
157 cc::CopyOutputRequest::CreateRequest(base::Bind(
158 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult
,
162 request
->set_area(src_subrect
);
163 client_
->RequestCopyOfOutput(request
.Pass());
166 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
167 const gfx::Rect
& src_subrect
,
168 const scoped_refptr
<media::VideoFrame
>& target
,
169 const base::Callback
<void(bool)>& callback
) {
170 if (!CanCopyToVideoFrame()) {
175 // Try get a texture to reuse.
176 scoped_refptr
<OwnedMailbox
> subscriber_texture
;
177 if (frame_subscriber_
) {
178 if (!idle_frame_subscriber_textures_
.empty()) {
179 subscriber_texture
= idle_frame_subscriber_textures_
.back();
180 idle_frame_subscriber_textures_
.pop_back();
181 } else if (GLHelper
* helper
=
182 ImageTransportFactory::GetInstance()->GetGLHelper()) {
183 subscriber_texture
= new OwnedMailbox(helper
);
187 scoped_ptr
<cc::CopyOutputRequest
> request
=
188 cc::CopyOutputRequest::CreateRequest(base::Bind(
189 &DelegatedFrameHost::
190 CopyFromCompositingSurfaceHasResultForVideo
,
191 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
195 request
->set_area(src_subrect
);
196 if (subscriber_texture
.get()) {
197 request
->SetTextureMailbox(
198 cc::TextureMailbox(subscriber_texture
->mailbox(),
199 subscriber_texture
->target(),
200 subscriber_texture
->sync_point()));
202 client_
->RequestCopyOfOutput(request
.Pass());
205 bool DelegatedFrameHost::CanCopyToBitmap() const {
206 return client_
->GetCompositor() &&
207 client_
->GetLayer()->has_external_content();
210 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
211 return client_
->GetCompositor() &&
212 client_
->GetLayer()->has_external_content();
215 bool DelegatedFrameHost::CanSubscribeFrame() const {
219 void DelegatedFrameHost::BeginFrameSubscription(
220 scoped_ptr
<RenderWidgetHostViewFrameSubscriber
> subscriber
) {
221 frame_subscriber_
= subscriber
.Pass();
224 void DelegatedFrameHost::EndFrameSubscription() {
225 idle_frame_subscriber_textures_
.clear();
226 frame_subscriber_
.reset();
229 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip
) const {
230 // Should skip a frame only when another frame from the renderer is guaranteed
231 // to replace it. Otherwise may cause hangs when the renderer is waiting for
232 // the completion of latency infos (such as when taking a Snapshot.)
233 if (can_lock_compositor_
== NO_PENDING_RENDERER_FRAME
||
234 can_lock_compositor_
== NO_PENDING_COMMIT
||
238 return size_in_dip
!= resize_lock_
->expected_size();
241 void DelegatedFrameHost::WasResized() {
242 if (client_
->DesiredFrameSize() != current_frame_size_in_dip_
&&
243 client_
->GetHost()->is_hidden())
244 EvictDelegatedFrame();
245 MaybeCreateResizeLock();
248 gfx::Size
DelegatedFrameHost::GetRequestedRendererSize() const {
250 return resize_lock_
->expected_size();
252 return client_
->DesiredFrameSize();
255 void DelegatedFrameHost::CheckResizeLock() {
257 resize_lock_
->expected_size() != current_frame_size_in_dip_
)
260 // Since we got the size we were looking for, unlock the compositor. But delay
261 // the release of the lock until we've kicked a frame with the new texture, to
262 // avoid resizing the UI before we have a chance to draw a "good" frame.
263 resize_lock_
->UnlockCompositor();
264 ui::Compositor
* compositor
= client_
->GetCompositor();
266 if (!compositor
->HasObserver(this))
267 compositor
->AddObserver(this);
271 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
272 const gfx::Rect
& damage_rect
) {
273 if (!frame_subscriber() || !CanCopyToVideoFrame())
276 const base::TimeTicks now
= gfx::FrameTime::Now();
277 base::TimeTicks present_time
;
278 if (vsync_timebase_
.is_null() || vsync_interval_
<= base::TimeDelta()) {
281 const int64 intervals_elapsed
= (now
- vsync_timebase_
) / vsync_interval_
;
282 present_time
= vsync_timebase_
+ (intervals_elapsed
+ 1) * vsync_interval_
;
285 scoped_refptr
<media::VideoFrame
> frame
;
286 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback
;
287 if (frame_subscriber()->ShouldCaptureFrame(damage_rect
, present_time
,
288 &frame
, &callback
)) {
289 CopyFromCompositingSurfaceToVideoFrame(
290 gfx::Rect(current_frame_size_in_dip_
),
292 base::Bind(callback
, present_time
));
296 void DelegatedFrameHost::SwapDelegatedFrame(
297 uint32 output_surface_id
,
298 scoped_ptr
<cc::DelegatedFrameData
> frame_data
,
299 float frame_device_scale_factor
,
300 const std::vector
<ui::LatencyInfo
>& latency_info
) {
301 RenderWidgetHostImpl
* host
= client_
->GetHost();
302 DCHECK(!frame_data
->render_pass_list
.empty());
304 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
306 gfx::Size frame_size
= root_pass
->output_rect
.size();
307 gfx::Size frame_size_in_dip
=
308 ConvertSizeToDIP(frame_device_scale_factor
, frame_size
);
310 gfx::Rect damage_rect
= gfx::ToEnclosingRect(root_pass
->damage_rect
);
311 damage_rect
.Intersect(gfx::Rect(frame_size
));
312 gfx::Rect damage_rect_in_dip
=
313 ConvertRectToDIP(frame_device_scale_factor
, damage_rect
);
315 if (ShouldSkipFrame(frame_size_in_dip
)) {
316 cc::CompositorFrameAck ack
;
317 cc::TransferableResource::ReturnResources(frame_data
->resource_list
,
320 skipped_latency_info_list_
.insert(skipped_latency_info_list_
.end(),
321 latency_info
.begin(), latency_info
.end());
323 RenderWidgetHostImpl::SendSwapCompositorFrameAck(
324 host
->GetRoutingID(), output_surface_id
,
325 host
->GetProcess()->GetID(), ack
);
326 skipped_frames_
= true;
330 if (skipped_frames_
) {
331 skipped_frames_
= false;
332 damage_rect
= gfx::Rect(frame_size
);
333 damage_rect_in_dip
= gfx::Rect(frame_size_in_dip
);
335 // Give the same damage rect to the compositor.
336 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
337 root_pass
->damage_rect
= damage_rect
;
340 if (output_surface_id
!= last_output_surface_id_
) {
341 // Resource ids are scoped by the output surface.
342 // If the originating output surface doesn't match the last one, it
343 // indicates the renderer's output surface may have been recreated, in which
344 // case we should recreate the DelegatedRendererLayer, to avoid matching
345 // resources from the old one with resources from the new one which would
346 // have the same id. Changing the layer to showing painted content destroys
347 // the DelegatedRendererLayer.
348 EvictDelegatedFrame();
350 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
351 // any resources from the old output surface with the new output surface id.
352 if (resource_collection_
.get()) {
353 resource_collection_
->SetClient(NULL
);
355 if (resource_collection_
->LoseAllResources())
356 SendReturnedDelegatedResources(last_output_surface_id_
);
358 resource_collection_
= NULL
;
360 last_output_surface_id_
= output_surface_id
;
362 bool modified_layers
= false;
363 if (frame_size
.IsEmpty()) {
364 DCHECK(frame_data
->resource_list
.empty());
365 EvictDelegatedFrame();
366 modified_layers
= true;
369 if (!surface_factory_
) {
370 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
371 cc::SurfaceManager
* manager
= factory
->GetSurfaceManager();
372 id_allocator_
= factory
->CreateSurfaceIdAllocator();
374 make_scoped_ptr(new cc::SurfaceFactory(manager
, this));
376 if (surface_id_
.is_null() || frame_size
!= current_surface_size_
||
377 frame_size_in_dip
!= current_frame_size_in_dip_
) {
378 // TODO(jbauman): Wait to destroy this surface until the parent has
379 // finished using it.
380 if (!surface_id_
.is_null())
381 surface_factory_
->Destroy(surface_id_
);
382 surface_id_
= id_allocator_
->GenerateId();
383 surface_factory_
->Create(surface_id_
, frame_size
);
384 client_
->GetLayer()->SetShowSurface(surface_id_
, frame_size_in_dip
);
385 current_surface_size_
= frame_size
;
386 modified_layers
= true;
388 scoped_ptr
<cc::CompositorFrame
> compositor_frame
=
389 make_scoped_ptr(new cc::CompositorFrame());
390 compositor_frame
->delegated_frame_data
= frame_data
.Pass();
391 surface_factory_
->SubmitFrame(
393 compositor_frame
.Pass(),
394 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck
,
398 if (!resource_collection_
.get()) {
399 resource_collection_
= new cc::DelegatedFrameResourceCollection
;
400 resource_collection_
->SetClient(this);
402 // If the physical frame size changes, we need a new |frame_provider_|. If
403 // the physical frame size is the same, but the size in DIP changed, we
404 // need to adjust the scale at which the frames will be drawn, and we do
405 // this by making a new |frame_provider_| also to ensure the scale change
406 // is presented in sync with the new frame content.
407 if (!frame_provider_
.get() ||
408 frame_size
!= frame_provider_
->frame_size() ||
409 frame_size_in_dip
!= current_frame_size_in_dip_
) {
410 frame_provider_
= new cc::DelegatedFrameProvider(
411 resource_collection_
.get(), frame_data
.Pass());
412 client_
->GetLayer()->SetShowDelegatedContent(frame_provider_
.get(),
415 frame_provider_
->SetFrameData(frame_data
.Pass());
417 modified_layers
= true;
420 released_front_lock_
= NULL
;
421 current_frame_size_in_dip_
= frame_size_in_dip
;
424 if (modified_layers
&& !damage_rect_in_dip
.IsEmpty()) {
425 // TODO(jbauman): Need to always tell the window observer about the
427 client_
->GetLayer()->OnDelegatedFrameDamage(damage_rect_in_dip
);
430 pending_delegated_ack_count_
++;
432 ui::Compositor
* compositor
= client_
->GetCompositor();
434 SendDelegatedFrameAck(output_surface_id
);
435 } else if (!use_surfaces_
) {
436 std::vector
<ui::LatencyInfo
>::const_iterator it
;
437 for (it
= latency_info
.begin(); it
!= latency_info
.end(); ++it
)
438 compositor
->SetLatencyInfo(*it
);
439 // If we've previously skipped any latency infos add them.
440 for (it
= skipped_latency_info_list_
.begin();
441 it
!= skipped_latency_info_list_
.end();
443 compositor
->SetLatencyInfo(*it
);
444 skipped_latency_info_list_
.clear();
445 AddOnCommitCallbackAndDisableLocks(
446 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck
,
450 DidReceiveFrameFromRenderer(damage_rect
);
451 if (frame_provider_
.get() || !surface_id_
.is_null())
452 delegated_frame_evictor_
->SwappedFrame(!host
->is_hidden());
453 // Note: the frame may have been evicted immediately.
456 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id
) {
457 RenderWidgetHostImpl
* host
= client_
->GetHost();
458 cc::CompositorFrameAck ack
;
459 if (!surface_returned_resources_
.empty())
460 ack
.resources
.swap(surface_returned_resources_
);
461 if (resource_collection_
.get())
462 resource_collection_
->TakeUnusedResourcesForChildCompositor(&ack
.resources
);
463 RenderWidgetHostImpl::SendSwapCompositorFrameAck(host
->GetRoutingID(),
465 host
->GetProcess()->GetID(),
467 DCHECK_GT(pending_delegated_ack_count_
, 0);
468 pending_delegated_ack_count_
--;
471 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
472 if (pending_delegated_ack_count_
)
475 SendReturnedDelegatedResources(last_output_surface_id_
);
478 void DelegatedFrameHost::SendReturnedDelegatedResources(
479 uint32 output_surface_id
) {
480 RenderWidgetHostImpl
* host
= client_
->GetHost();
482 cc::CompositorFrameAck ack
;
483 if (!surface_returned_resources_
.empty()) {
484 ack
.resources
.swap(surface_returned_resources_
);
486 DCHECK(resource_collection_
.get());
487 resource_collection_
->TakeUnusedResourcesForChildCompositor(&ack
.resources
);
489 DCHECK(!ack
.resources
.empty());
491 RenderWidgetHostImpl::SendReclaimCompositorResources(
492 host
->GetRoutingID(),
494 host
->GetProcess()->GetID(),
498 void DelegatedFrameHost::ReturnResources(
499 const cc::ReturnedResourceArray
& resources
) {
500 if (resources
.empty())
502 std::copy(resources
.begin(),
504 std::back_inserter(surface_returned_resources_
));
505 if (!pending_delegated_ack_count_
)
506 SendReturnedDelegatedResources(last_output_surface_id_
);
509 void DelegatedFrameHost::EvictDelegatedFrame() {
510 client_
->GetLayer()->SetShowPaintedContent();
511 frame_provider_
= NULL
;
512 if (!surface_id_
.is_null()) {
513 surface_factory_
->Destroy(surface_id_
);
514 surface_id_
= cc::SurfaceId();
516 delegated_frame_evictor_
->DiscardedFrame();
520 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
521 const gfx::Size
& dst_size_in_pixel
,
522 const SkColorType color_type
,
523 const base::Callback
<void(bool, const SkBitmap
&)>& callback
,
524 scoped_ptr
<cc::CopyOutputResult
> result
) {
525 if (result
->IsEmpty() || result
->size().IsEmpty()) {
526 callback
.Run(false, SkBitmap());
530 if (result
->HasTexture()) {
531 // GPU-accelerated path
532 PrepareTextureCopyOutputResult(dst_size_in_pixel
, color_type
,
538 DCHECK(result
->HasBitmap());
540 PrepareBitmapCopyOutputResult(dst_size_in_pixel
, color_type
, callback
,
544 static void CopyFromCompositingSurfaceFinished(
545 const base::Callback
<void(bool, const SkBitmap
&)>& callback
,
546 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
,
547 scoped_ptr
<SkBitmap
> bitmap
,
548 scoped_ptr
<SkAutoLockPixels
> bitmap_pixels_lock
,
550 bitmap_pixels_lock
.reset();
552 uint32 sync_point
= 0;
554 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
555 sync_point
= gl_helper
->InsertSyncPoint();
557 bool lost_resource
= sync_point
== 0;
558 release_callback
->Run(sync_point
, lost_resource
);
560 callback
.Run(result
, *bitmap
);
564 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
565 const gfx::Size
& dst_size_in_pixel
,
566 const SkColorType color_type
,
567 const base::Callback
<void(bool, const SkBitmap
&)>& callback
,
568 scoped_ptr
<cc::CopyOutputResult
> result
) {
569 DCHECK(result
->HasTexture());
570 base::ScopedClosureRunner
scoped_callback_runner(
571 base::Bind(callback
, false, SkBitmap()));
573 scoped_ptr
<SkBitmap
> bitmap(new SkBitmap
);
574 if (!bitmap
->allocPixels(SkImageInfo::Make(dst_size_in_pixel
.width(),
575 dst_size_in_pixel
.height(),
577 kOpaque_SkAlphaType
)))
580 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
581 GLHelper
* gl_helper
= factory
->GetGLHelper();
585 scoped_ptr
<SkAutoLockPixels
> bitmap_pixels_lock(
586 new SkAutoLockPixels(*bitmap
));
587 uint8
* pixels
= static_cast<uint8
*>(bitmap
->getPixels());
589 cc::TextureMailbox texture_mailbox
;
590 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
591 result
->TakeTexture(&texture_mailbox
, &release_callback
);
592 DCHECK(texture_mailbox
.IsTexture());
594 ignore_result(scoped_callback_runner
.Release());
596 gl_helper
->CropScaleReadbackAndCleanMailbox(
597 texture_mailbox
.mailbox(),
598 texture_mailbox
.sync_point(),
600 gfx::Rect(result
->size()),
604 base::Bind(&CopyFromCompositingSurfaceFinished
,
606 base::Passed(&release_callback
),
607 base::Passed(&bitmap
),
608 base::Passed(&bitmap_pixels_lock
)),
609 GLHelper::SCALER_QUALITY_FAST
);
613 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
614 const gfx::Size
& dst_size_in_pixel
,
615 const SkColorType color_type
,
616 const base::Callback
<void(bool, const SkBitmap
&)>& callback
,
617 scoped_ptr
<cc::CopyOutputResult
> result
) {
618 if (color_type
!= kN32_SkColorType
&& color_type
!= kAlpha_8_SkColorType
) {
620 callback
.Run(false, SkBitmap());
623 DCHECK(result
->HasBitmap());
624 scoped_ptr
<SkBitmap
> source
= result
->TakeBitmap();
626 SkBitmap scaled_bitmap
;
627 if (source
->width() != dst_size_in_pixel
.width() ||
628 source
->height() != dst_size_in_pixel
.height()) {
630 skia::ImageOperations::Resize(*source
,
631 skia::ImageOperations::RESIZE_BEST
,
632 dst_size_in_pixel
.width(),
633 dst_size_in_pixel
.height());
635 scaled_bitmap
= *source
;
637 if (color_type
== kN32_SkColorType
) {
638 DCHECK_EQ(scaled_bitmap
.colorType(), kN32_SkColorType
);
639 callback
.Run(true, scaled_bitmap
);
642 DCHECK_EQ(color_type
, kAlpha_8_SkColorType
);
643 // The software path currently always returns N32 bitmap regardless of the
644 // |color_type| we ask for.
645 DCHECK_EQ(scaled_bitmap
.colorType(), kN32_SkColorType
);
646 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|.
647 SkBitmap grayscale_bitmap
;
648 bool success
= grayscale_bitmap
.allocPixels(
649 SkImageInfo::MakeA8(scaled_bitmap
.width(), scaled_bitmap
.height()));
651 callback
.Run(false, SkBitmap());
654 SkCanvas
canvas(grayscale_bitmap
);
656 skia::RefPtr
<SkColorFilter
> filter
=
657 skia::AdoptRef(SkLumaColorFilter::Create());
658 paint
.setColorFilter(filter
.get());
659 canvas
.drawBitmap(scaled_bitmap
, SkIntToScalar(0), SkIntToScalar(0), &paint
);
660 callback
.Run(true, grayscale_bitmap
);
664 void DelegatedFrameHost::ReturnSubscriberTexture(
665 base::WeakPtr
<DelegatedFrameHost
> dfh
,
666 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
668 if (!subscriber_texture
.get())
673 subscriber_texture
->UpdateSyncPoint(sync_point
);
675 if (dfh
->frame_subscriber_
&& subscriber_texture
->texture_id())
676 dfh
->idle_frame_subscriber_textures_
.push_back(subscriber_texture
);
680 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
681 base::WeakPtr
<DelegatedFrameHost
> dfh
,
682 const base::Callback
<void(bool)>& callback
,
683 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
684 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
,
686 callback
.Run(result
);
688 uint32 sync_point
= 0;
690 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
691 sync_point
= gl_helper
->InsertSyncPoint();
693 if (release_callback
) {
694 // A release callback means the texture came from the compositor, so there
695 // should be no |subscriber_texture|.
696 DCHECK(!subscriber_texture
.get());
697 bool lost_resource
= sync_point
== 0;
698 release_callback
->Run(sync_point
, lost_resource
);
700 ReturnSubscriberTexture(dfh
, subscriber_texture
, sync_point
);
704 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
705 base::WeakPtr
<DelegatedFrameHost
> dfh
,
706 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
707 scoped_refptr
<media::VideoFrame
> video_frame
,
708 const base::Callback
<void(bool)>& callback
,
709 scoped_ptr
<cc::CopyOutputResult
> result
) {
710 base::ScopedClosureRunner
scoped_callback_runner(base::Bind(callback
, false));
711 base::ScopedClosureRunner
scoped_return_subscriber_texture(
712 base::Bind(&ReturnSubscriberTexture
, dfh
, subscriber_texture
, 0));
716 if (result
->IsEmpty())
718 if (result
->size().IsEmpty())
721 // Compute the dest size we want after the letterboxing resize. Make the
722 // coordinates and sizes even because we letterbox in YUV space
723 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
724 // line up correctly.
725 // The video frame's coded_size() and the result's size() are both physical
727 gfx::Rect region_in_frame
=
728 media::ComputeLetterboxRegion(gfx::Rect(video_frame
->coded_size()),
730 region_in_frame
= gfx::Rect(region_in_frame
.x() & ~1,
731 region_in_frame
.y() & ~1,
732 region_in_frame
.width() & ~1,
733 region_in_frame
.height() & ~1);
734 if (region_in_frame
.IsEmpty())
737 if (!result
->HasTexture()) {
738 DCHECK(result
->HasBitmap());
739 scoped_ptr
<SkBitmap
> bitmap
= result
->TakeBitmap();
740 // Scale the bitmap to the required size, if necessary.
741 SkBitmap scaled_bitmap
;
742 if (result
->size().width() != region_in_frame
.width() ||
743 result
->size().height() != region_in_frame
.height()) {
744 skia::ImageOperations::ResizeMethod method
=
745 skia::ImageOperations::RESIZE_GOOD
;
746 scaled_bitmap
= skia::ImageOperations::Resize(*bitmap
.get(), method
,
747 region_in_frame
.width(),
748 region_in_frame
.height());
750 scaled_bitmap
= *bitmap
.get();
754 SkAutoLockPixels
scaled_bitmap_locker(scaled_bitmap
);
756 media::CopyRGBToVideoFrame(
757 reinterpret_cast<uint8
*>(scaled_bitmap
.getPixels()),
758 scaled_bitmap
.rowBytes(),
762 ignore_result(scoped_callback_runner
.Release());
767 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
768 GLHelper
* gl_helper
= factory
->GetGLHelper();
771 if (subscriber_texture
.get() && !subscriber_texture
->texture_id())
774 cc::TextureMailbox texture_mailbox
;
775 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
776 result
->TakeTexture(&texture_mailbox
, &release_callback
);
777 DCHECK(texture_mailbox
.IsTexture());
779 gfx::Rect
result_rect(result
->size());
781 content::ReadbackYUVInterface
* yuv_readback_pipeline
=
782 dfh
->yuv_readback_pipeline_
.get();
783 if (yuv_readback_pipeline
== NULL
||
784 yuv_readback_pipeline
->scaler()->SrcSize() != result_rect
.size() ||
785 yuv_readback_pipeline
->scaler()->SrcSubrect() != result_rect
||
786 yuv_readback_pipeline
->scaler()->DstSize() != region_in_frame
.size()) {
787 GLHelper::ScalerQuality quality
= GLHelper::SCALER_QUALITY_FAST
;
788 std::string quality_switch
= switches::kTabCaptureDownscaleQuality
;
789 // If we're scaling up, we can use the "best" quality.
790 if (result_rect
.size().width() < region_in_frame
.size().width() &&
791 result_rect
.size().height() < region_in_frame
.size().height())
792 quality_switch
= switches::kTabCaptureUpscaleQuality
;
794 std::string switch_value
=
795 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
797 if (switch_value
== "fast")
798 quality
= GLHelper::SCALER_QUALITY_FAST
;
799 else if (switch_value
== "good")
800 quality
= GLHelper::SCALER_QUALITY_GOOD
;
801 else if (switch_value
== "best")
802 quality
= GLHelper::SCALER_QUALITY_BEST
;
804 dfh
->yuv_readback_pipeline_
.reset(
805 gl_helper
->CreateReadbackPipelineYUV(quality
,
808 video_frame
->coded_size(),
812 yuv_readback_pipeline
= dfh
->yuv_readback_pipeline_
.get();
815 ignore_result(scoped_callback_runner
.Release());
816 ignore_result(scoped_return_subscriber_texture
.Release());
817 base::Callback
<void(bool result
)> finished_callback
= base::Bind(
818 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo
,
822 base::Passed(&release_callback
));
823 yuv_readback_pipeline
->ReadbackYUV(texture_mailbox
.mailbox(),
824 texture_mailbox
.sync_point(),
829 ////////////////////////////////////////////////////////////////////////////////
830 // DelegatedFrameHost, ui::CompositorObserver implementation:
832 void DelegatedFrameHost::OnCompositingDidCommit(
833 ui::Compositor
* compositor
) {
834 RenderWidgetHostImpl
* host
= client_
->GetHost();
835 if (can_lock_compositor_
== NO_PENDING_COMMIT
) {
836 can_lock_compositor_
= YES_CAN_LOCK
;
837 if (resize_lock_
.get() && resize_lock_
->GrabDeferredLock())
838 can_lock_compositor_
= YES_DID_LOCK
;
840 RunOnCommitCallbacks();
842 resize_lock_
->expected_size() == current_frame_size_in_dip_
) {
843 resize_lock_
.reset();
845 // We may have had a resize while we had the lock (e.g. if the lock expired,
846 // or if the UI still gave us some resizes), so make sure we grab a new lock
848 MaybeCreateResizeLock();
852 void DelegatedFrameHost::OnCompositingStarted(
853 ui::Compositor
* compositor
, base::TimeTicks start_time
) {
854 last_draw_ended_
= start_time
;
857 void DelegatedFrameHost::OnCompositingEnded(
858 ui::Compositor
* compositor
) {
861 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor
* compositor
) {
864 void DelegatedFrameHost::OnCompositingLockStateChanged(
865 ui::Compositor
* compositor
) {
866 // A compositor lock that is part of a resize lock timed out. We
867 // should display a renderer frame.
868 if (!compositor
->IsLocked() && can_lock_compositor_
== YES_DID_LOCK
) {
869 can_lock_compositor_
= NO_PENDING_RENDERER_FRAME
;
873 void DelegatedFrameHost::OnUpdateVSyncParameters(
874 base::TimeTicks timebase
,
875 base::TimeDelta interval
) {
876 vsync_timebase_
= timebase
;
877 vsync_interval_
= interval
;
878 RenderWidgetHostImpl
* host
= client_
->GetHost();
879 if (client_
->IsVisible())
880 host
->UpdateVSyncParameters(timebase
, interval
);
883 ////////////////////////////////////////////////////////////////////////////////
884 // RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation:
886 void DelegatedFrameHost::OnLostResources() {
887 RenderWidgetHostImpl
* host
= client_
->GetHost();
888 if (frame_provider_
.get() || !surface_id_
.is_null())
889 EvictDelegatedFrame();
890 idle_frame_subscriber_textures_
.clear();
891 yuv_readback_pipeline_
.reset();
893 host
->ScheduleComposite();
896 ////////////////////////////////////////////////////////////////////////////////
897 // DelegatedFrameHost, private:
899 DelegatedFrameHost::~DelegatedFrameHost() {
900 ImageTransportFactory::GetInstance()->RemoveObserver(this);
902 if (!surface_id_
.is_null())
903 surface_factory_
->Destroy(surface_id_
);
904 if (resource_collection_
.get())
905 resource_collection_
->SetClient(NULL
);
907 DCHECK(!vsync_manager_
.get());
910 void DelegatedFrameHost::RunOnCommitCallbacks() {
911 for (std::vector
<base::Closure
>::const_iterator
912 it
= on_compositing_did_commit_callbacks_
.begin();
913 it
!= on_compositing_did_commit_callbacks_
.end(); ++it
) {
916 on_compositing_did_commit_callbacks_
.clear();
919 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
920 const base::Closure
& callback
) {
921 ui::Compositor
* compositor
= client_
->GetCompositor();
924 if (!compositor
->HasObserver(this))
925 compositor
->AddObserver(this);
927 can_lock_compositor_
= NO_PENDING_COMMIT
;
928 on_compositing_did_commit_callbacks_
.push_back(callback
);
931 void DelegatedFrameHost::AddedToWindow() {
932 ui::Compositor
* compositor
= client_
->GetCompositor();
934 DCHECK(!vsync_manager_
.get());
935 vsync_manager_
= compositor
->vsync_manager();
936 vsync_manager_
->AddObserver(this);
940 void DelegatedFrameHost::RemovingFromWindow() {
941 RunOnCommitCallbacks();
942 resize_lock_
.reset();
943 client_
->GetHost()->WasResized();
944 ui::Compositor
* compositor
= client_
->GetCompositor();
945 if (compositor
&& compositor
->HasObserver(this))
946 compositor
->RemoveObserver(this);
948 if (vsync_manager_
.get()) {
949 vsync_manager_
->RemoveObserver(this);
950 vsync_manager_
= NULL
;
954 void DelegatedFrameHost::LockResources() {
955 DCHECK(frame_provider_
.get() || !surface_id_
.is_null());
956 delegated_frame_evictor_
->LockFrame();
959 void DelegatedFrameHost::UnlockResources() {
960 DCHECK(frame_provider_
.get() || !surface_id_
.is_null());
961 delegated_frame_evictor_
->UnlockFrame();
964 ////////////////////////////////////////////////////////////////////////////////
965 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
967 void DelegatedFrameHost::OnLayerRecreated(ui::Layer
* old_layer
,
968 ui::Layer
* new_layer
) {
969 // The new_layer is the one that will be used by our Window, so that's the one
970 // that should keep our frame. old_layer will be returned to the
971 // RecreateLayer caller, and should have a copy.
972 if (frame_provider_
.get()) {
973 new_layer
->SetShowDelegatedContent(frame_provider_
.get(),
974 current_frame_size_in_dip_
);
976 if (!surface_id_
.is_null()) {
977 new_layer
->SetShowSurface(surface_id_
, current_frame_size_in_dip_
);
981 } // namespace content