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"
29 #include "ui/gfx/geometry/dip_util.h"
35 void SatisfyCallback(cc::SurfaceManager
* manager
,
36 cc::SurfaceSequence sequence
) {
37 std::vector
<uint32_t> sequences
;
38 sequences
.push_back(sequence
.sequence
);
39 manager
->DidSatisfySequences(sequence
.id_namespace
, &sequences
);
42 void RequireCallback(cc::SurfaceManager
* manager
,
44 cc::SurfaceSequence sequence
) {
45 cc::Surface
* surface
= manager
->GetSurfaceForId(id
);
47 LOG(ERROR
) << "Attempting to require callback on nonexistent surface";
50 surface
->AddDestructionDependency(sequence
);
55 ////////////////////////////////////////////////////////////////////////////////
58 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient
* client
)
61 use_surfaces_(UseSurfacesEnabled()),
62 last_output_surface_id_(0),
63 pending_delegated_ack_count_(0),
64 skipped_frames_(false),
65 current_scale_factor_(1.f
),
66 can_lock_compositor_(YES_CAN_LOCK
),
67 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) {
68 ImageTransportFactory::GetInstance()->AddObserver(this);
71 void DelegatedFrameHost::WasShown(const ui::LatencyInfo
& latency_info
) {
72 delegated_frame_evictor_
->SetVisible(true);
74 if (surface_id_
.is_null() && !frame_provider_
.get() &&
75 !released_front_lock_
.get()) {
77 released_front_lock_
= compositor_
->GetCompositorLock();
81 compositor_
->SetLatencyInfo(latency_info
);
85 bool DelegatedFrameHost::HasSavedFrame() {
86 return delegated_frame_evictor_
->HasFrame();
89 void DelegatedFrameHost::WasHidden() {
90 delegated_frame_evictor_
->SetVisible(false);
91 released_front_lock_
= NULL
;
94 void DelegatedFrameHost::MaybeCreateResizeLock() {
95 if (!ShouldCreateResizeLock())
99 bool defer_compositor_lock
=
100 can_lock_compositor_
== NO_PENDING_RENDERER_FRAME
||
101 can_lock_compositor_
== NO_PENDING_COMMIT
;
103 if (can_lock_compositor_
== YES_CAN_LOCK
)
104 can_lock_compositor_
= YES_DID_LOCK
;
107 client_
->DelegatedFrameHostCreateResizeLock(defer_compositor_lock
);
110 bool DelegatedFrameHost::ShouldCreateResizeLock() {
111 if (!client_
->DelegatedFrameCanCreateResizeLock())
117 gfx::Size desired_size
= client_
->DelegatedFrameHostDesiredSizeInDIP();
118 if (desired_size
== current_frame_size_in_dip_
|| desired_size
.IsEmpty())
127 void DelegatedFrameHost::CopyFromCompositingSurface(
128 const gfx::Rect
& src_subrect
,
129 const gfx::Size
& output_size
,
130 ReadbackRequestCallback
& callback
,
131 const SkColorType color_type
) {
132 // Only ARGB888 and RGB565 supported as of now.
133 bool format_support
= ((color_type
== kAlpha_8_SkColorType
) ||
134 (color_type
== kRGB_565_SkColorType
) ||
135 (color_type
== kN32_SkColorType
));
136 DCHECK(format_support
);
137 if (!CanCopyToBitmap()) {
138 callback
.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE
);
142 scoped_ptr
<cc::CopyOutputRequest
> request
=
143 cc::CopyOutputRequest::CreateRequest(base::Bind(
144 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult
,
148 if (!src_subrect
.IsEmpty())
149 request
->set_area(src_subrect
);
150 RequestCopyOfOutput(request
.Pass());
153 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
154 const gfx::Rect
& src_subrect
,
155 const scoped_refptr
<media::VideoFrame
>& target
,
156 const base::Callback
<void(bool)>& callback
) {
157 if (!CanCopyToVideoFrame()) {
162 // Try get a texture to reuse.
163 scoped_refptr
<OwnedMailbox
> subscriber_texture
;
164 if (frame_subscriber_
) {
165 if (!idle_frame_subscriber_textures_
.empty()) {
166 subscriber_texture
= idle_frame_subscriber_textures_
.back();
167 idle_frame_subscriber_textures_
.pop_back();
168 } else if (GLHelper
* helper
=
169 ImageTransportFactory::GetInstance()->GetGLHelper()) {
170 subscriber_texture
= new OwnedMailbox(helper
);
174 scoped_ptr
<cc::CopyOutputRequest
> request
=
175 cc::CopyOutputRequest::CreateRequest(base::Bind(
176 &DelegatedFrameHost::
177 CopyFromCompositingSurfaceHasResultForVideo
,
178 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
182 request
->set_area(src_subrect
);
183 if (subscriber_texture
.get()) {
184 request
->SetTextureMailbox(
185 cc::TextureMailbox(subscriber_texture
->mailbox(),
186 subscriber_texture
->target(),
187 subscriber_texture
->sync_point()));
189 RequestCopyOfOutput(request
.Pass());
192 bool DelegatedFrameHost::CanCopyToBitmap() const {
193 return compositor_
&&
194 client_
->DelegatedFrameHostGetLayer()->has_external_content();
197 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
198 return compositor_
&&
199 client_
->DelegatedFrameHostGetLayer()->has_external_content();
202 bool DelegatedFrameHost::CanSubscribeFrame() const {
206 void DelegatedFrameHost::BeginFrameSubscription(
207 scoped_ptr
<RenderWidgetHostViewFrameSubscriber
> subscriber
) {
208 frame_subscriber_
= subscriber
.Pass();
211 void DelegatedFrameHost::EndFrameSubscription() {
212 idle_frame_subscriber_textures_
.clear();
213 frame_subscriber_
.reset();
216 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip
) const {
217 // Should skip a frame only when another frame from the renderer is guaranteed
218 // to replace it. Otherwise may cause hangs when the renderer is waiting for
219 // the completion of latency infos (such as when taking a Snapshot.)
220 if (can_lock_compositor_
== NO_PENDING_RENDERER_FRAME
||
221 can_lock_compositor_
== NO_PENDING_COMMIT
||
225 return size_in_dip
!= resize_lock_
->expected_size();
228 void DelegatedFrameHost::WasResized() {
229 if (client_
->DelegatedFrameHostDesiredSizeInDIP() !=
230 current_frame_size_in_dip_
&&
231 !client_
->DelegatedFrameHostIsVisible())
232 EvictDelegatedFrame();
233 MaybeCreateResizeLock();
236 gfx::Size
DelegatedFrameHost::GetRequestedRendererSize() const {
238 return resize_lock_
->expected_size();
240 return client_
->DelegatedFrameHostDesiredSizeInDIP();
243 void DelegatedFrameHost::CheckResizeLock() {
245 resize_lock_
->expected_size() != current_frame_size_in_dip_
)
248 // Since we got the size we were looking for, unlock the compositor. But delay
249 // the release of the lock until we've kicked a frame with the new texture, to
250 // avoid resizing the UI before we have a chance to draw a "good" frame.
251 resize_lock_
->UnlockCompositor();
254 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
255 const gfx::Rect
& damage_rect
) {
256 if (!frame_subscriber() || !CanCopyToVideoFrame())
259 const base::TimeTicks now
= gfx::FrameTime::Now();
260 base::TimeTicks present_time
;
261 if (vsync_timebase_
.is_null() || vsync_interval_
<= base::TimeDelta()) {
264 const int64 intervals_elapsed
= (now
- vsync_timebase_
) / vsync_interval_
;
265 present_time
= vsync_timebase_
+ (intervals_elapsed
+ 1) * vsync_interval_
;
268 scoped_refptr
<media::VideoFrame
> frame
;
269 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback
;
270 if (frame_subscriber()->ShouldCaptureFrame(damage_rect
, present_time
,
271 &frame
, &callback
)) {
272 CopyFromCompositingSurfaceToVideoFrame(
273 gfx::Rect(current_frame_size_in_dip_
),
275 base::Bind(callback
, present_time
));
279 void DelegatedFrameHost::SwapDelegatedFrame(
280 uint32 output_surface_id
,
281 scoped_ptr
<cc::DelegatedFrameData
> frame_data
,
282 float frame_device_scale_factor
,
283 const std::vector
<ui::LatencyInfo
>& latency_info
) {
284 DCHECK(!frame_data
->render_pass_list
.empty());
286 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
288 gfx::Size frame_size
= root_pass
->output_rect
.size();
289 gfx::Size frame_size_in_dip
=
290 gfx::ConvertSizeToDIP(frame_device_scale_factor
, frame_size
);
292 gfx::Rect damage_rect
= gfx::ToEnclosingRect(root_pass
->damage_rect
);
293 damage_rect
.Intersect(gfx::Rect(frame_size
));
294 gfx::Rect damage_rect_in_dip
=
295 gfx::ConvertRectToDIP(frame_device_scale_factor
, damage_rect
);
297 if (ShouldSkipFrame(frame_size_in_dip
)) {
298 cc::CompositorFrameAck ack
;
299 cc::TransferableResource::ReturnResources(frame_data
->resource_list
,
302 skipped_latency_info_list_
.insert(skipped_latency_info_list_
.end(),
303 latency_info
.begin(), latency_info
.end());
305 client_
->DelegatedFrameHostSendCompositorSwapAck(output_surface_id
, ack
);
306 skipped_frames_
= true;
310 if (skipped_frames_
) {
311 skipped_frames_
= false;
312 damage_rect
= gfx::Rect(frame_size
);
313 damage_rect_in_dip
= gfx::Rect(frame_size_in_dip
);
315 // Give the same damage rect to the compositor.
316 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
317 root_pass
->damage_rect
= damage_rect
;
320 if (output_surface_id
!= last_output_surface_id_
) {
321 // Resource ids are scoped by the output surface.
322 // If the originating output surface doesn't match the last one, it
323 // indicates the renderer's output surface may have been recreated, in which
324 // case we should recreate the DelegatedRendererLayer, to avoid matching
325 // resources from the old one with resources from the new one which would
326 // have the same id. Changing the layer to showing painted content destroys
327 // the DelegatedRendererLayer.
328 EvictDelegatedFrame();
330 surface_factory_
.reset();
331 if (!surface_returned_resources_
.empty())
332 SendReturnedDelegatedResources(last_output_surface_id_
);
334 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
335 // any resources from the old output surface with the new output surface id.
336 if (resource_collection_
.get()) {
337 resource_collection_
->SetClient(NULL
);
339 if (resource_collection_
->LoseAllResources())
340 SendReturnedDelegatedResources(last_output_surface_id_
);
342 resource_collection_
= NULL
;
344 last_output_surface_id_
= output_surface_id
;
346 bool immediate_ack
= !compositor_
;
347 pending_delegated_ack_count_
++;
349 if (frame_size
.IsEmpty()) {
350 DCHECK(frame_data
->resource_list
.empty());
351 EvictDelegatedFrame();
354 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
355 cc::SurfaceManager
* manager
= factory
->GetSurfaceManager();
356 if (!surface_factory_
) {
358 factory
->GetContextFactory()->CreateSurfaceIdAllocator();
360 make_scoped_ptr(new cc::SurfaceFactory(manager
, this));
362 if (surface_id_
.is_null() || frame_size
!= current_surface_size_
||
363 frame_size_in_dip
!= current_frame_size_in_dip_
) {
364 if (!surface_id_
.is_null())
365 surface_factory_
->Destroy(surface_id_
);
366 surface_id_
= id_allocator_
->GenerateId();
367 surface_factory_
->Create(surface_id_
);
368 // manager must outlive compositors using it.
369 client_
->DelegatedFrameHostGetLayer()->SetShowSurface(
371 base::Bind(&SatisfyCallback
, base::Unretained(manager
)),
372 base::Bind(&RequireCallback
, base::Unretained(manager
)), frame_size
,
373 frame_device_scale_factor
, frame_size_in_dip
);
374 current_surface_size_
= frame_size
;
375 current_scale_factor_
= frame_device_scale_factor
;
377 scoped_ptr
<cc::CompositorFrame
> compositor_frame
=
378 make_scoped_ptr(new cc::CompositorFrame());
379 compositor_frame
->delegated_frame_data
= frame_data
.Pass();
381 compositor_frame
->metadata
.latency_info
.swap(skipped_latency_info_list_
);
382 compositor_frame
->metadata
.latency_info
.insert(
383 compositor_frame
->metadata
.latency_info
.end(),
384 latency_info
.begin(),
387 gfx::Size desired_size
= client_
->DelegatedFrameHostDesiredSizeInDIP();
388 if (desired_size
!= frame_size_in_dip
&& !desired_size
.IsEmpty())
389 immediate_ack
= true;
391 cc::SurfaceFactory::DrawCallback ack_callback
;
392 if (compositor_
&& !immediate_ack
) {
393 ack_callback
= base::Bind(&DelegatedFrameHost::SurfaceDrawn
,
394 AsWeakPtr(), output_surface_id
);
396 surface_factory_
->SubmitFrame(
397 surface_id_
, compositor_frame
.Pass(), ack_callback
);
399 if (!resource_collection_
.get()) {
400 resource_collection_
= new cc::DelegatedFrameResourceCollection
;
401 resource_collection_
->SetClient(this);
403 // If the physical frame size changes, we need a new |frame_provider_|. If
404 // the physical frame size is the same, but the size in DIP changed, we
405 // need to adjust the scale at which the frames will be drawn, and we do
406 // this by making a new |frame_provider_| also to ensure the scale change
407 // is presented in sync with the new frame content.
408 if (!frame_provider_
.get() ||
409 frame_size
!= frame_provider_
->frame_size() ||
410 frame_size_in_dip
!= current_frame_size_in_dip_
) {
411 frame_provider_
= new cc::DelegatedFrameProvider(
412 resource_collection_
.get(), frame_data
.Pass());
413 client_
->DelegatedFrameHostGetLayer()->SetShowDelegatedContent(
414 frame_provider_
.get(), frame_size_in_dip
);
416 frame_provider_
->SetFrameData(frame_data
.Pass());
420 released_front_lock_
= NULL
;
421 current_frame_size_in_dip_
= frame_size_in_dip
;
424 if (!damage_rect_in_dip
.IsEmpty())
425 client_
->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage(
429 SendDelegatedFrameAck(output_surface_id
);
430 } else if (!use_surfaces_
) {
431 std::vector
<ui::LatencyInfo
>::const_iterator it
;
432 for (it
= latency_info
.begin(); it
!= latency_info
.end(); ++it
)
433 compositor_
->SetLatencyInfo(*it
);
434 // If we've previously skipped any latency infos add them.
435 for (it
= skipped_latency_info_list_
.begin();
436 it
!= skipped_latency_info_list_
.end();
438 compositor_
->SetLatencyInfo(*it
);
439 skipped_latency_info_list_
.clear();
440 AddOnCommitCallbackAndDisableLocks(
441 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck
,
445 AddOnCommitCallbackAndDisableLocks(base::Closure());
447 DidReceiveFrameFromRenderer(damage_rect
);
448 if (frame_provider_
.get() || !surface_id_
.is_null())
449 delegated_frame_evictor_
->SwappedFrame(
450 client_
->DelegatedFrameHostIsVisible());
451 // Note: the frame may have been evicted immediately.
454 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id
) {
455 cc::CompositorFrameAck ack
;
456 if (!surface_returned_resources_
.empty())
457 ack
.resources
.swap(surface_returned_resources_
);
458 if (resource_collection_
.get())
459 resource_collection_
->TakeUnusedResourcesForChildCompositor(&ack
.resources
);
460 client_
->DelegatedFrameHostSendCompositorSwapAck(output_surface_id
, ack
);
461 DCHECK_GT(pending_delegated_ack_count_
, 0);
462 pending_delegated_ack_count_
--;
465 void DelegatedFrameHost::SurfaceDrawn(uint32 output_surface_id
,
466 cc::SurfaceDrawStatus drawn
) {
467 SendDelegatedFrameAck(output_surface_id
);
470 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
471 if (pending_delegated_ack_count_
)
474 SendReturnedDelegatedResources(last_output_surface_id_
);
477 void DelegatedFrameHost::SendReturnedDelegatedResources(
478 uint32 output_surface_id
) {
479 cc::CompositorFrameAck ack
;
480 if (!surface_returned_resources_
.empty()) {
481 ack
.resources
.swap(surface_returned_resources_
);
483 DCHECK(resource_collection_
.get());
484 resource_collection_
->TakeUnusedResourcesForChildCompositor(&ack
.resources
);
486 DCHECK(!ack
.resources
.empty());
488 client_
->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id
,
492 void DelegatedFrameHost::ReturnResources(
493 const cc::ReturnedResourceArray
& resources
) {
494 if (resources
.empty())
496 std::copy(resources
.begin(),
498 std::back_inserter(surface_returned_resources_
));
499 if (!pending_delegated_ack_count_
)
500 SendReturnedDelegatedResources(last_output_surface_id_
);
503 void DelegatedFrameHost::EvictDelegatedFrame() {
504 client_
->DelegatedFrameHostGetLayer()->SetShowSolidColorContent();
505 frame_provider_
= NULL
;
506 if (!surface_id_
.is_null()) {
507 surface_factory_
->Destroy(surface_id_
);
508 surface_id_
= cc::SurfaceId();
510 delegated_frame_evictor_
->DiscardedFrame();
514 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
515 const gfx::Size
& dst_size_in_pixel
,
516 const SkColorType color_type
,
517 ReadbackRequestCallback
& callback
,
518 scoped_ptr
<cc::CopyOutputResult
> result
) {
519 if (result
->IsEmpty() || result
->size().IsEmpty()) {
520 callback
.Run(SkBitmap(), content::READBACK_FAILED
);
524 gfx::Size output_size_in_pixel
;
525 if (dst_size_in_pixel
.IsEmpty())
526 output_size_in_pixel
= result
->size();
528 output_size_in_pixel
= dst_size_in_pixel
;
530 if (result
->HasTexture()) {
531 // GPU-accelerated path
532 PrepareTextureCopyOutputResult(output_size_in_pixel
, color_type
,
538 DCHECK(result
->HasBitmap());
540 PrepareBitmapCopyOutputResult(output_size_in_pixel
, color_type
, callback
,
544 static void CopyFromCompositingSurfaceFinished(
545 ReadbackRequestCallback
& 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();
556 sync_point
= gl_helper
->InsertSyncPoint();
558 bool lost_resource
= sync_point
== 0;
559 release_callback
->Run(sync_point
, lost_resource
);
561 callback
.Run(*bitmap
,
562 result
? content::READBACK_SUCCESS
: content::READBACK_FAILED
);
566 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
567 const gfx::Size
& dst_size_in_pixel
,
568 const SkColorType color_type
,
569 ReadbackRequestCallback
& callback
,
570 scoped_ptr
<cc::CopyOutputResult
> result
) {
571 DCHECK(result
->HasTexture());
572 base::ScopedClosureRunner
scoped_callback_runner(
573 base::Bind(callback
, SkBitmap(), content::READBACK_FAILED
));
575 // TODO(sikugu): We should be able to validate the format here using
576 // GLHelper::IsReadbackConfigSupported before we processs the result.
577 // See crbug.com/415682.
578 scoped_ptr
<SkBitmap
> bitmap(new SkBitmap
);
579 if (!bitmap
->tryAllocPixels(SkImageInfo::Make(dst_size_in_pixel
.width(),
580 dst_size_in_pixel
.height(),
582 kOpaque_SkAlphaType
)))
585 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
586 GLHelper
* gl_helper
= factory
->GetGLHelper();
590 scoped_ptr
<SkAutoLockPixels
> bitmap_pixels_lock(
591 new SkAutoLockPixels(*bitmap
));
592 uint8
* pixels
= static_cast<uint8
*>(bitmap
->getPixels());
594 cc::TextureMailbox texture_mailbox
;
595 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
596 result
->TakeTexture(&texture_mailbox
, &release_callback
);
597 DCHECK(texture_mailbox
.IsTexture());
599 ignore_result(scoped_callback_runner
.Release());
601 gl_helper
->CropScaleReadbackAndCleanMailbox(
602 texture_mailbox
.mailbox(),
603 texture_mailbox
.sync_point(),
605 gfx::Rect(result
->size()),
609 base::Bind(&CopyFromCompositingSurfaceFinished
,
611 base::Passed(&release_callback
),
612 base::Passed(&bitmap
),
613 base::Passed(&bitmap_pixels_lock
)),
614 GLHelper::SCALER_QUALITY_FAST
);
618 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
619 const gfx::Size
& dst_size_in_pixel
,
620 const SkColorType color_type
,
621 ReadbackRequestCallback
& callback
,
622 scoped_ptr
<cc::CopyOutputResult
> result
) {
623 if (color_type
!= kN32_SkColorType
&& color_type
!= kAlpha_8_SkColorType
) {
625 callback
.Run(SkBitmap(), READBACK_FORMAT_NOT_SUPPORTED
);
628 DCHECK(result
->HasBitmap());
629 scoped_ptr
<SkBitmap
> source
= result
->TakeBitmap();
631 SkBitmap scaled_bitmap
;
632 if (source
->width() != dst_size_in_pixel
.width() ||
633 source
->height() != dst_size_in_pixel
.height()) {
635 skia::ImageOperations::Resize(*source
,
636 skia::ImageOperations::RESIZE_BEST
,
637 dst_size_in_pixel
.width(),
638 dst_size_in_pixel
.height());
640 scaled_bitmap
= *source
;
642 if (color_type
== kN32_SkColorType
) {
643 DCHECK_EQ(scaled_bitmap
.colorType(), kN32_SkColorType
);
644 callback
.Run(scaled_bitmap
, READBACK_SUCCESS
);
647 DCHECK_EQ(color_type
, kAlpha_8_SkColorType
);
648 // The software path currently always returns N32 bitmap regardless of the
649 // |color_type| we ask for.
650 DCHECK_EQ(scaled_bitmap
.colorType(), kN32_SkColorType
);
651 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|.
652 SkBitmap grayscale_bitmap
;
653 bool success
= grayscale_bitmap
.tryAllocPixels(
654 SkImageInfo::MakeA8(scaled_bitmap
.width(), scaled_bitmap
.height()));
656 callback
.Run(SkBitmap(), content::READBACK_MEMORY_ALLOCATION_FAILURE
);
659 SkCanvas
canvas(grayscale_bitmap
);
661 skia::RefPtr
<SkColorFilter
> filter
=
662 skia::AdoptRef(SkLumaColorFilter::Create());
663 paint
.setColorFilter(filter
.get());
664 canvas
.drawBitmap(scaled_bitmap
, SkIntToScalar(0), SkIntToScalar(0), &paint
);
665 callback
.Run(grayscale_bitmap
, READBACK_SUCCESS
);
669 void DelegatedFrameHost::ReturnSubscriberTexture(
670 base::WeakPtr
<DelegatedFrameHost
> dfh
,
671 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
673 if (!subscriber_texture
.get())
678 subscriber_texture
->UpdateSyncPoint(sync_point
);
680 if (dfh
->frame_subscriber_
&& subscriber_texture
->texture_id())
681 dfh
->idle_frame_subscriber_textures_
.push_back(subscriber_texture
);
685 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
686 base::WeakPtr
<DelegatedFrameHost
> dfh
,
687 const base::Callback
<void(bool)>& callback
,
688 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
689 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
,
691 callback
.Run(result
);
693 uint32 sync_point
= 0;
695 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
696 sync_point
= gl_helper
->InsertSyncPoint();
698 if (release_callback
) {
699 // A release callback means the texture came from the compositor, so there
700 // should be no |subscriber_texture|.
701 DCHECK(!subscriber_texture
.get());
702 bool lost_resource
= sync_point
== 0;
703 release_callback
->Run(sync_point
, lost_resource
);
705 ReturnSubscriberTexture(dfh
, subscriber_texture
, sync_point
);
709 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
710 base::WeakPtr
<DelegatedFrameHost
> dfh
,
711 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
712 scoped_refptr
<media::VideoFrame
> video_frame
,
713 const base::Callback
<void(bool)>& callback
,
714 scoped_ptr
<cc::CopyOutputResult
> result
) {
715 base::ScopedClosureRunner
scoped_callback_runner(base::Bind(callback
, false));
716 base::ScopedClosureRunner
scoped_return_subscriber_texture(
717 base::Bind(&ReturnSubscriberTexture
, dfh
, subscriber_texture
, 0));
721 if (result
->IsEmpty())
723 if (result
->size().IsEmpty())
726 // Compute the dest size we want after the letterboxing resize. Make the
727 // coordinates and sizes even because we letterbox in YUV space
728 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
729 // line up correctly.
730 // The video frame's visible_rect() and the result's size() are both physical
732 gfx::Rect region_in_frame
= media::ComputeLetterboxRegion(
733 video_frame
->visible_rect(), result
->size());
734 region_in_frame
= gfx::Rect(region_in_frame
.x() & ~1,
735 region_in_frame
.y() & ~1,
736 region_in_frame
.width() & ~1,
737 region_in_frame
.height() & ~1);
738 if (region_in_frame
.IsEmpty())
741 if (!result
->HasTexture()) {
742 DCHECK(result
->HasBitmap());
743 scoped_ptr
<SkBitmap
> bitmap
= result
->TakeBitmap();
744 // Scale the bitmap to the required size, if necessary.
745 SkBitmap scaled_bitmap
;
746 if (result
->size() != region_in_frame
.size()) {
747 skia::ImageOperations::ResizeMethod method
=
748 skia::ImageOperations::RESIZE_GOOD
;
749 scaled_bitmap
= skia::ImageOperations::Resize(*bitmap
.get(), method
,
750 region_in_frame
.width(),
751 region_in_frame
.height());
753 scaled_bitmap
= *bitmap
.get();
757 SkAutoLockPixels
scaled_bitmap_locker(scaled_bitmap
);
759 media::CopyRGBToVideoFrame(
760 reinterpret_cast<uint8
*>(scaled_bitmap
.getPixels()),
761 scaled_bitmap
.rowBytes(),
765 ignore_result(scoped_callback_runner
.Release());
770 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
771 GLHelper
* gl_helper
= factory
->GetGLHelper();
774 if (subscriber_texture
.get() && !subscriber_texture
->texture_id())
777 cc::TextureMailbox texture_mailbox
;
778 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
779 result
->TakeTexture(&texture_mailbox
, &release_callback
);
780 DCHECK(texture_mailbox
.IsTexture());
782 gfx::Rect
result_rect(result
->size());
784 content::ReadbackYUVInterface
* yuv_readback_pipeline
=
785 dfh
->yuv_readback_pipeline_
.get();
786 if (yuv_readback_pipeline
== NULL
||
787 yuv_readback_pipeline
->scaler()->SrcSize() != result_rect
.size() ||
788 yuv_readback_pipeline
->scaler()->SrcSubrect() != result_rect
||
789 yuv_readback_pipeline
->scaler()->DstSize() != region_in_frame
.size()) {
790 GLHelper::ScalerQuality quality
= GLHelper::SCALER_QUALITY_FAST
;
791 std::string quality_switch
= switches::kTabCaptureDownscaleQuality
;
792 // If we're scaling up, we can use the "best" quality.
793 if (result_rect
.size().width() < region_in_frame
.size().width() &&
794 result_rect
.size().height() < region_in_frame
.size().height())
795 quality_switch
= switches::kTabCaptureUpscaleQuality
;
797 std::string switch_value
=
798 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
800 if (switch_value
== "fast")
801 quality
= GLHelper::SCALER_QUALITY_FAST
;
802 else if (switch_value
== "good")
803 quality
= GLHelper::SCALER_QUALITY_GOOD
;
804 else if (switch_value
== "best")
805 quality
= GLHelper::SCALER_QUALITY_BEST
;
807 dfh
->yuv_readback_pipeline_
.reset(
808 gl_helper
->CreateReadbackPipelineYUV(quality
,
811 region_in_frame
.size(),
814 yuv_readback_pipeline
= dfh
->yuv_readback_pipeline_
.get();
817 ignore_result(scoped_callback_runner
.Release());
818 ignore_result(scoped_return_subscriber_texture
.Release());
819 base::Callback
<void(bool result
)> finished_callback
= base::Bind(
820 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo
,
824 base::Passed(&release_callback
));
825 yuv_readback_pipeline
->ReadbackYUV(texture_mailbox
.mailbox(),
826 texture_mailbox
.sync_point(),
828 region_in_frame
.origin(),
832 ////////////////////////////////////////////////////////////////////////////////
833 // DelegatedFrameHost, ui::CompositorObserver implementation:
835 void DelegatedFrameHost::OnCompositingDidCommit(
836 ui::Compositor
* compositor
) {
837 if (can_lock_compositor_
== NO_PENDING_COMMIT
) {
838 can_lock_compositor_
= YES_CAN_LOCK
;
839 if (resize_lock_
.get() && resize_lock_
->GrabDeferredLock())
840 can_lock_compositor_
= YES_DID_LOCK
;
842 RunOnCommitCallbacks();
844 resize_lock_
->expected_size() == current_frame_size_in_dip_
) {
845 resize_lock_
.reset();
846 client_
->DelegatedFrameHostResizeLockWasReleased();
847 // We may have had a resize while we had the lock (e.g. if the lock expired,
848 // or if the UI still gave us some resizes), so make sure we grab a new lock
850 MaybeCreateResizeLock();
854 void DelegatedFrameHost::OnCompositingStarted(
855 ui::Compositor
* compositor
, base::TimeTicks start_time
) {
856 last_draw_ended_
= start_time
;
859 void DelegatedFrameHost::OnCompositingEnded(
860 ui::Compositor
* compositor
) {
863 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor
* compositor
) {
866 void DelegatedFrameHost::OnCompositingLockStateChanged(
867 ui::Compositor
* compositor
) {
868 // A compositor lock that is part of a resize lock timed out. We
869 // should display a renderer frame.
870 if (!compositor
->IsLocked() && can_lock_compositor_
== YES_DID_LOCK
) {
871 can_lock_compositor_
= NO_PENDING_RENDERER_FRAME
;
875 void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor
* compositor
) {
876 DCHECK_EQ(compositor
, compositor_
);
878 DCHECK(!compositor_
);
881 void DelegatedFrameHost::OnUpdateVSyncParameters(
882 base::TimeTicks timebase
,
883 base::TimeDelta interval
) {
884 vsync_timebase_
= timebase
;
885 vsync_interval_
= interval
;
886 if (client_
->DelegatedFrameHostIsVisible())
887 client_
->DelegatedFrameHostUpdateVSyncParameters(timebase
, interval
);
890 ////////////////////////////////////////////////////////////////////////////////
891 // DelegatedFrameHost, ImageTransportFactoryObserver implementation:
893 void DelegatedFrameHost::OnLostResources() {
894 if (frame_provider_
.get() || !surface_id_
.is_null())
895 EvictDelegatedFrame();
896 idle_frame_subscriber_textures_
.clear();
897 yuv_readback_pipeline_
.reset();
899 client_
->DelegatedFrameHostOnLostCompositorResources();
902 ////////////////////////////////////////////////////////////////////////////////
903 // DelegatedFrameHost, private:
905 DelegatedFrameHost::~DelegatedFrameHost() {
906 DCHECK(!compositor_
);
907 ImageTransportFactory::GetInstance()->RemoveObserver(this);
909 if (!surface_id_
.is_null())
910 surface_factory_
->Destroy(surface_id_
);
911 if (resource_collection_
.get())
912 resource_collection_
->SetClient(NULL
);
914 DCHECK(!vsync_manager_
.get());
917 void DelegatedFrameHost::RunOnCommitCallbacks() {
918 for (std::vector
<base::Closure
>::const_iterator
919 it
= on_compositing_did_commit_callbacks_
.begin();
920 it
!= on_compositing_did_commit_callbacks_
.end(); ++it
) {
923 on_compositing_did_commit_callbacks_
.clear();
926 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
927 const base::Closure
& callback
) {
930 can_lock_compositor_
= NO_PENDING_COMMIT
;
931 if (!callback
.is_null())
932 on_compositing_did_commit_callbacks_
.push_back(callback
);
935 void DelegatedFrameHost::SetCompositor(ui::Compositor
* compositor
) {
936 DCHECK(!compositor_
);
939 compositor_
= compositor
;
940 compositor_
->AddObserver(this);
941 DCHECK(!vsync_manager_
.get());
942 vsync_manager_
= compositor_
->vsync_manager();
943 vsync_manager_
->AddObserver(this);
946 void DelegatedFrameHost::ResetCompositor() {
949 RunOnCommitCallbacks();
951 resize_lock_
.reset();
952 client_
->DelegatedFrameHostResizeLockWasReleased();
954 if (compositor_
->HasObserver(this))
955 compositor_
->RemoveObserver(this);
956 if (vsync_manager_
.get()) {
957 vsync_manager_
->RemoveObserver(this);
958 vsync_manager_
= NULL
;
960 compositor_
= nullptr;
963 void DelegatedFrameHost::LockResources() {
964 DCHECK(frame_provider_
.get() || !surface_id_
.is_null());
965 delegated_frame_evictor_
->LockFrame();
968 void DelegatedFrameHost::RequestCopyOfOutput(
969 scoped_ptr
<cc::CopyOutputRequest
> request
) {
970 if (!request_copy_of_output_callback_for_testing_
.is_null())
971 request_copy_of_output_callback_for_testing_
.Run(request
.Pass());
973 client_
->DelegatedFrameHostGetLayer()->RequestCopyOfOutput(request
.Pass());
976 void DelegatedFrameHost::UnlockResources() {
977 DCHECK(frame_provider_
.get() || !surface_id_
.is_null());
978 delegated_frame_evictor_
->UnlockFrame();
981 ////////////////////////////////////////////////////////////////////////////////
982 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
984 void DelegatedFrameHost::OnLayerRecreated(ui::Layer
* old_layer
,
985 ui::Layer
* new_layer
) {
986 // The new_layer is the one that will be used by our Window, so that's the one
987 // that should keep our frame. old_layer will be returned to the
988 // RecreateLayer caller, and should have a copy.
989 if (frame_provider_
.get()) {
990 new_layer
->SetShowDelegatedContent(frame_provider_
.get(),
991 current_frame_size_in_dip_
);
993 if (!surface_id_
.is_null()) {
994 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
995 cc::SurfaceManager
* manager
= factory
->GetSurfaceManager();
996 new_layer
->SetShowSurface(
997 surface_id_
, base::Bind(&SatisfyCallback
, base::Unretained(manager
)),
998 base::Bind(&RequireCallback
, base::Unretained(manager
)),
999 current_surface_size_
, current_scale_factor_
,
1000 current_frame_size_in_dip_
);
1004 } // namespace content