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 "base/time/default_tick_clock.h"
10 #include "cc/output/compositor_frame.h"
11 #include "cc/output/compositor_frame_ack.h"
12 #include "cc/output/copy_output_request.h"
13 #include "cc/resources/single_release_callback.h"
14 #include "cc/resources/texture_mailbox.h"
15 #include "cc/surfaces/surface.h"
16 #include "cc/surfaces/surface_factory.h"
17 #include "cc/surfaces/surface_manager.h"
18 #include "content/browser/compositor/resize_lock.h"
19 #include "content/browser/gpu/compositor_util.h"
20 #include "content/common/gpu/client/gl_helper.h"
21 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
22 #include "content/public/common/content_switches.h"
23 #include "media/base/video_frame.h"
24 #include "media/base/video_util.h"
25 #include "skia/ext/image_operations.h"
26 #include "third_party/skia/include/core/SkCanvas.h"
27 #include "third_party/skia/include/core/SkPaint.h"
28 #include "third_party/skia/include/effects/SkLumaColorFilter.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 tick_clock_(new base::DefaultTickClock()),
63 last_output_surface_id_(0),
64 pending_delegated_ack_count_(0),
65 skipped_frames_(false),
66 current_scale_factor_(1.f
),
67 can_lock_compositor_(YES_CAN_LOCK
),
68 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) {
69 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
70 factory
->AddObserver(this);
71 id_allocator_
= factory
->GetContextFactory()->CreateSurfaceIdAllocator();
74 void DelegatedFrameHost::WasShown(const ui::LatencyInfo
& latency_info
) {
75 delegated_frame_evictor_
->SetVisible(true);
77 if (surface_id_
.is_null() && !frame_provider_
.get() &&
78 !released_front_lock_
.get()) {
80 released_front_lock_
= compositor_
->GetCompositorLock();
84 compositor_
->SetLatencyInfo(latency_info
);
88 bool DelegatedFrameHost::HasSavedFrame() {
89 return delegated_frame_evictor_
->HasFrame();
92 void DelegatedFrameHost::WasHidden() {
93 delegated_frame_evictor_
->SetVisible(false);
94 released_front_lock_
= NULL
;
97 void DelegatedFrameHost::MaybeCreateResizeLock() {
98 if (!ShouldCreateResizeLock())
102 bool defer_compositor_lock
=
103 can_lock_compositor_
== NO_PENDING_RENDERER_FRAME
||
104 can_lock_compositor_
== NO_PENDING_COMMIT
;
106 if (can_lock_compositor_
== YES_CAN_LOCK
)
107 can_lock_compositor_
= YES_DID_LOCK
;
110 client_
->DelegatedFrameHostCreateResizeLock(defer_compositor_lock
);
113 bool DelegatedFrameHost::ShouldCreateResizeLock() {
114 if (!client_
->DelegatedFrameCanCreateResizeLock())
120 gfx::Size desired_size
= client_
->DelegatedFrameHostDesiredSizeInDIP();
121 if (desired_size
== current_frame_size_in_dip_
|| desired_size
.IsEmpty())
130 void DelegatedFrameHost::CopyFromCompositingSurface(
131 const gfx::Rect
& src_subrect
,
132 const gfx::Size
& output_size
,
133 ReadbackRequestCallback
& callback
,
134 const SkColorType preferred_color_type
) {
135 // Only ARGB888 and RGB565 supported as of now.
136 bool format_support
= ((preferred_color_type
== kAlpha_8_SkColorType
) ||
137 (preferred_color_type
== kRGB_565_SkColorType
) ||
138 (preferred_color_type
== kN32_SkColorType
));
139 DCHECK(format_support
);
140 if (!CanCopyToBitmap()) {
141 callback
.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE
);
145 scoped_ptr
<cc::CopyOutputRequest
> request
=
146 cc::CopyOutputRequest::CreateRequest(
147 base::Bind(&DelegatedFrameHost::CopyFromCompositingSurfaceHasResult
,
148 output_size
, preferred_color_type
, callback
));
149 if (!src_subrect
.IsEmpty())
150 request
->set_area(src_subrect
);
151 RequestCopyOfOutput(request
.Pass());
154 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
155 const gfx::Rect
& src_subrect
,
156 const scoped_refptr
<media::VideoFrame
>& target
,
157 const base::Callback
<void(bool)>& callback
) {
158 if (!CanCopyToVideoFrame()) {
163 scoped_ptr
<cc::CopyOutputRequest
> request
=
164 cc::CopyOutputRequest::CreateRequest(base::Bind(
165 &DelegatedFrameHost::
166 CopyFromCompositingSurfaceHasResultForVideo
,
167 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
171 request
->set_area(src_subrect
);
172 RequestCopyOfOutput(request
.Pass());
175 bool DelegatedFrameHost::CanCopyToBitmap() const {
176 return compositor_
&&
177 client_
->DelegatedFrameHostGetLayer()->has_external_content();
180 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
181 return compositor_
&&
182 client_
->DelegatedFrameHostGetLayer()->has_external_content();
185 bool DelegatedFrameHost::CanSubscribeFrame() const {
189 void DelegatedFrameHost::BeginFrameSubscription(
190 scoped_ptr
<RenderWidgetHostViewFrameSubscriber
> subscriber
) {
191 frame_subscriber_
= subscriber
.Pass();
194 void DelegatedFrameHost::EndFrameSubscription() {
195 idle_frame_subscriber_textures_
.clear();
196 frame_subscriber_
.reset();
199 uint32_t DelegatedFrameHost::GetSurfaceIdNamespace() {
203 return id_allocator_
->id_namespace();
206 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip
) const {
207 // Should skip a frame only when another frame from the renderer is guaranteed
208 // to replace it. Otherwise may cause hangs when the renderer is waiting for
209 // the completion of latency infos (such as when taking a Snapshot.)
210 if (can_lock_compositor_
== NO_PENDING_RENDERER_FRAME
||
211 can_lock_compositor_
== NO_PENDING_COMMIT
||
215 return size_in_dip
!= resize_lock_
->expected_size();
218 void DelegatedFrameHost::WasResized() {
219 if (client_
->DelegatedFrameHostDesiredSizeInDIP() !=
220 current_frame_size_in_dip_
&&
221 !client_
->DelegatedFrameHostIsVisible())
222 EvictDelegatedFrame();
223 MaybeCreateResizeLock();
226 gfx::Size
DelegatedFrameHost::GetRequestedRendererSize() const {
228 return resize_lock_
->expected_size();
230 return client_
->DelegatedFrameHostDesiredSizeInDIP();
233 void DelegatedFrameHost::CheckResizeLock() {
235 resize_lock_
->expected_size() != current_frame_size_in_dip_
)
238 // Since we got the size we were looking for, unlock the compositor. But delay
239 // the release of the lock until we've kicked a frame with the new texture, to
240 // avoid resizing the UI before we have a chance to draw a "good" frame.
241 resize_lock_
->UnlockCompositor();
244 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
245 const gfx::Rect
& damage_rect
) {
246 if (!frame_subscriber() || !CanCopyToVideoFrame())
249 const base::TimeTicks now
= tick_clock_
->NowTicks();
250 base::TimeTicks present_time
;
251 if (vsync_interval_
<= base::TimeDelta()) {
254 const int64 intervals_elapsed
= (now
- vsync_timebase_
) / vsync_interval_
;
255 present_time
= vsync_timebase_
+ (intervals_elapsed
+ 1) * vsync_interval_
;
258 scoped_refptr
<media::VideoFrame
> frame
;
259 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback
;
260 if (!frame_subscriber()->ShouldCaptureFrame(damage_rect
, present_time
,
264 // Get a texture to re-use; else, create a new one.
265 scoped_refptr
<OwnedMailbox
> subscriber_texture
;
266 if (!idle_frame_subscriber_textures_
.empty()) {
267 subscriber_texture
= idle_frame_subscriber_textures_
.back();
268 idle_frame_subscriber_textures_
.pop_back();
269 } else if (GLHelper
* helper
=
270 ImageTransportFactory::GetInstance()->GetGLHelper()) {
271 subscriber_texture
= new OwnedMailbox(helper
);
274 scoped_ptr
<cc::CopyOutputRequest
> request
=
275 cc::CopyOutputRequest::CreateRequest(base::Bind(
276 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo
,
280 base::Bind(callback
, present_time
)));
281 // Setting the source in this copy request asks that the layer abort any prior
282 // uncommitted copy requests made on behalf of the same frame subscriber.
283 // This will not affect any of the copy requests spawned elsewhere from
284 // DelegatedFrameHost (e.g., a call to CopyFromCompositingSurface() for
285 // screenshots) since those copy requests do not specify |frame_subscriber()|
287 request
->set_source(frame_subscriber());
288 request
->set_area(gfx::Rect(current_frame_size_in_dip_
));
289 if (subscriber_texture
.get()) {
290 request
->SetTextureMailbox(
291 cc::TextureMailbox(subscriber_texture
->mailbox(),
292 subscriber_texture
->target(),
293 subscriber_texture
->sync_point()));
295 RequestCopyOfOutput(request
.Pass());
298 void DelegatedFrameHost::SwapDelegatedFrame(
299 uint32 output_surface_id
,
300 scoped_ptr
<cc::DelegatedFrameData
> frame_data
,
301 float frame_device_scale_factor
,
302 const std::vector
<ui::LatencyInfo
>& latency_info
) {
303 DCHECK(!frame_data
->render_pass_list
.empty());
305 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
307 gfx::Size frame_size
= root_pass
->output_rect
.size();
308 gfx::Size frame_size_in_dip
=
309 gfx::ConvertSizeToDIP(frame_device_scale_factor
, frame_size
);
311 gfx::Rect damage_rect
= gfx::ToEnclosingRect(root_pass
->damage_rect
);
312 damage_rect
.Intersect(gfx::Rect(frame_size
));
313 gfx::Rect damage_rect_in_dip
=
314 gfx::ConvertRectToDIP(frame_device_scale_factor
, damage_rect
);
316 if (ShouldSkipFrame(frame_size_in_dip
)) {
317 cc::CompositorFrameAck ack
;
318 cc::TransferableResource::ReturnResources(frame_data
->resource_list
,
321 skipped_latency_info_list_
.insert(skipped_latency_info_list_
.end(),
322 latency_info
.begin(), latency_info
.end());
324 client_
->DelegatedFrameHostSendCompositorSwapAck(output_surface_id
, ack
);
325 skipped_frames_
= true;
329 if (skipped_frames_
) {
330 skipped_frames_
= false;
331 damage_rect
= gfx::Rect(frame_size
);
332 damage_rect_in_dip
= gfx::Rect(frame_size_in_dip
);
334 // Give the same damage rect to the compositor.
335 cc::RenderPass
* root_pass
= frame_data
->render_pass_list
.back();
336 root_pass
->damage_rect
= damage_rect
;
339 if (output_surface_id
!= last_output_surface_id_
) {
340 // Resource ids are scoped by the output surface.
341 // If the originating output surface doesn't match the last one, it
342 // indicates the renderer's output surface may have been recreated, in which
343 // case we should recreate the DelegatedRendererLayer, to avoid matching
344 // resources from the old one with resources from the new one which would
345 // have the same id. Changing the layer to showing painted content destroys
346 // the DelegatedRendererLayer.
347 EvictDelegatedFrame();
349 surface_factory_
.reset();
350 if (!surface_returned_resources_
.empty())
351 SendReturnedDelegatedResources(last_output_surface_id_
);
353 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
354 // any resources from the old output surface with the new output surface id.
355 if (resource_collection_
.get()) {
356 resource_collection_
->SetClient(NULL
);
358 if (resource_collection_
->LoseAllResources())
359 SendReturnedDelegatedResources(last_output_surface_id_
);
361 resource_collection_
= NULL
;
363 last_output_surface_id_
= output_surface_id
;
365 bool immediate_ack
= !compositor_
;
366 pending_delegated_ack_count_
++;
368 if (frame_size
.IsEmpty()) {
369 DCHECK(frame_data
->resource_list
.empty());
370 EvictDelegatedFrame();
373 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
374 cc::SurfaceManager
* manager
= factory
->GetSurfaceManager();
375 if (!surface_factory_
) {
377 make_scoped_ptr(new cc::SurfaceFactory(manager
, this));
379 if (surface_id_
.is_null() || frame_size
!= current_surface_size_
||
380 frame_size_in_dip
!= current_frame_size_in_dip_
) {
381 if (!surface_id_
.is_null())
382 surface_factory_
->Destroy(surface_id_
);
383 surface_id_
= id_allocator_
->GenerateId();
384 surface_factory_
->Create(surface_id_
);
385 // manager must outlive compositors using it.
386 client_
->DelegatedFrameHostGetLayer()->SetShowSurface(
388 base::Bind(&SatisfyCallback
, base::Unretained(manager
)),
389 base::Bind(&RequireCallback
, base::Unretained(manager
)), frame_size
,
390 frame_device_scale_factor
, frame_size_in_dip
);
391 current_surface_size_
= frame_size
;
392 current_scale_factor_
= frame_device_scale_factor
;
394 scoped_ptr
<cc::CompositorFrame
> compositor_frame
=
395 make_scoped_ptr(new cc::CompositorFrame());
396 compositor_frame
->delegated_frame_data
= frame_data
.Pass();
398 compositor_frame
->metadata
.latency_info
.swap(skipped_latency_info_list_
);
399 compositor_frame
->metadata
.latency_info
.insert(
400 compositor_frame
->metadata
.latency_info
.end(),
401 latency_info
.begin(),
404 gfx::Size desired_size
= client_
->DelegatedFrameHostDesiredSizeInDIP();
405 if (desired_size
!= frame_size_in_dip
&& !desired_size
.IsEmpty())
406 immediate_ack
= true;
408 cc::SurfaceFactory::DrawCallback ack_callback
;
409 if (compositor_
&& !immediate_ack
) {
410 ack_callback
= base::Bind(&DelegatedFrameHost::SurfaceDrawn
,
411 AsWeakPtr(), output_surface_id
);
413 surface_factory_
->SubmitFrame(
414 surface_id_
, compositor_frame
.Pass(), ack_callback
);
416 if (!resource_collection_
.get()) {
417 resource_collection_
= new cc::DelegatedFrameResourceCollection
;
418 resource_collection_
->SetClient(this);
420 // If the physical frame size changes, we need a new |frame_provider_|. If
421 // the physical frame size is the same, but the size in DIP changed, we
422 // need to adjust the scale at which the frames will be drawn, and we do
423 // this by making a new |frame_provider_| also to ensure the scale change
424 // is presented in sync with the new frame content.
425 if (!frame_provider_
.get() ||
426 frame_size
!= frame_provider_
->frame_size() ||
427 frame_size_in_dip
!= current_frame_size_in_dip_
) {
428 frame_provider_
= new cc::DelegatedFrameProvider(
429 resource_collection_
.get(), frame_data
.Pass());
430 client_
->DelegatedFrameHostGetLayer()->SetShowDelegatedContent(
431 frame_provider_
.get(), frame_size_in_dip
);
433 frame_provider_
->SetFrameData(frame_data
.Pass());
437 released_front_lock_
= NULL
;
438 current_frame_size_in_dip_
= frame_size_in_dip
;
441 if (!damage_rect_in_dip
.IsEmpty())
442 client_
->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage(
446 SendDelegatedFrameAck(output_surface_id
);
447 } else if (!use_surfaces_
) {
448 std::vector
<ui::LatencyInfo
>::const_iterator it
;
449 for (it
= latency_info
.begin(); it
!= latency_info
.end(); ++it
)
450 compositor_
->SetLatencyInfo(*it
);
451 // If we've previously skipped any latency infos add them.
452 for (it
= skipped_latency_info_list_
.begin();
453 it
!= skipped_latency_info_list_
.end();
455 compositor_
->SetLatencyInfo(*it
);
456 skipped_latency_info_list_
.clear();
457 AddOnCommitCallbackAndDisableLocks(
458 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck
,
459 AsWeakPtr(), output_surface_id
));
461 AddOnCommitCallbackAndDisableLocks(base::Closure());
463 DidReceiveFrameFromRenderer(damage_rect
);
464 if (frame_provider_
.get() || !surface_id_
.is_null())
465 delegated_frame_evictor_
->SwappedFrame(
466 client_
->DelegatedFrameHostIsVisible());
467 // Note: the frame may have been evicted immediately.
470 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id
) {
471 cc::CompositorFrameAck ack
;
472 if (!surface_returned_resources_
.empty())
473 ack
.resources
.swap(surface_returned_resources_
);
474 if (resource_collection_
.get())
475 resource_collection_
->TakeUnusedResourcesForChildCompositor(&ack
.resources
);
476 client_
->DelegatedFrameHostSendCompositorSwapAck(output_surface_id
, ack
);
477 DCHECK_GT(pending_delegated_ack_count_
, 0);
478 pending_delegated_ack_count_
--;
481 void DelegatedFrameHost::SurfaceDrawn(uint32 output_surface_id
,
482 cc::SurfaceDrawStatus drawn
) {
483 SendDelegatedFrameAck(output_surface_id
);
486 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
487 if (pending_delegated_ack_count_
)
490 SendReturnedDelegatedResources(last_output_surface_id_
);
493 void DelegatedFrameHost::SendReturnedDelegatedResources(
494 uint32 output_surface_id
) {
495 cc::CompositorFrameAck ack
;
496 if (!surface_returned_resources_
.empty()) {
497 ack
.resources
.swap(surface_returned_resources_
);
499 DCHECK(resource_collection_
.get());
500 resource_collection_
->TakeUnusedResourcesForChildCompositor(&ack
.resources
);
502 DCHECK(!ack
.resources
.empty());
504 client_
->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id
,
508 void DelegatedFrameHost::ReturnResources(
509 const cc::ReturnedResourceArray
& resources
) {
510 if (resources
.empty())
512 std::copy(resources
.begin(),
514 std::back_inserter(surface_returned_resources_
));
515 if (!pending_delegated_ack_count_
)
516 SendReturnedDelegatedResources(last_output_surface_id_
);
519 void DelegatedFrameHost::EvictDelegatedFrame() {
520 client_
->DelegatedFrameHostGetLayer()->SetShowSolidColorContent();
521 frame_provider_
= NULL
;
522 if (!surface_id_
.is_null()) {
523 surface_factory_
->Destroy(surface_id_
);
524 surface_id_
= cc::SurfaceId();
526 delegated_frame_evictor_
->DiscardedFrame();
530 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
531 const gfx::Size
& dst_size_in_pixel
,
532 const SkColorType color_type
,
533 ReadbackRequestCallback
& callback
,
534 scoped_ptr
<cc::CopyOutputResult
> result
) {
535 if (result
->IsEmpty() || result
->size().IsEmpty()) {
536 callback
.Run(SkBitmap(), content::READBACK_FAILED
);
540 gfx::Size output_size_in_pixel
;
541 if (dst_size_in_pixel
.IsEmpty())
542 output_size_in_pixel
= result
->size();
544 output_size_in_pixel
= dst_size_in_pixel
;
546 if (result
->HasTexture()) {
547 // GPU-accelerated path
548 PrepareTextureCopyOutputResult(output_size_in_pixel
, color_type
,
554 DCHECK(result
->HasBitmap());
556 PrepareBitmapCopyOutputResult(output_size_in_pixel
, color_type
, callback
,
560 static void CopyFromCompositingSurfaceFinished(
561 ReadbackRequestCallback
& callback
,
562 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
,
563 scoped_ptr
<SkBitmap
> bitmap
,
564 scoped_ptr
<SkAutoLockPixels
> bitmap_pixels_lock
,
566 bitmap_pixels_lock
.reset();
568 uint32 sync_point
= 0;
570 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
572 sync_point
= gl_helper
->InsertSyncPoint();
574 bool lost_resource
= sync_point
== 0;
575 release_callback
->Run(sync_point
, lost_resource
);
577 callback
.Run(*bitmap
,
578 result
? content::READBACK_SUCCESS
: content::READBACK_FAILED
);
582 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
583 const gfx::Size
& dst_size_in_pixel
,
584 const SkColorType color_type
,
585 ReadbackRequestCallback
& callback
,
586 scoped_ptr
<cc::CopyOutputResult
> result
) {
587 DCHECK(result
->HasTexture());
588 base::ScopedClosureRunner
scoped_callback_runner(
589 base::Bind(callback
, SkBitmap(), content::READBACK_FAILED
));
591 // TODO(siva.gunturi): We should be able to validate the format here using
592 // GLHelper::IsReadbackConfigSupported before we processs the result.
593 // See crbug.com/415682 and crbug.com/415131.
594 scoped_ptr
<SkBitmap
> bitmap(new SkBitmap
);
595 if (!bitmap
->tryAllocPixels(SkImageInfo::Make(
596 dst_size_in_pixel
.width(), dst_size_in_pixel
.height(), color_type
,
597 kOpaque_SkAlphaType
))) {
598 scoped_callback_runner
.Reset(base::Bind(
599 callback
, SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE
));
603 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
604 GLHelper
* gl_helper
= factory
->GetGLHelper();
608 scoped_ptr
<SkAutoLockPixels
> bitmap_pixels_lock(
609 new SkAutoLockPixels(*bitmap
));
610 uint8
* pixels
= static_cast<uint8
*>(bitmap
->getPixels());
612 cc::TextureMailbox texture_mailbox
;
613 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
614 result
->TakeTexture(&texture_mailbox
, &release_callback
);
615 DCHECK(texture_mailbox
.IsTexture());
617 ignore_result(scoped_callback_runner
.Release());
619 gl_helper
->CropScaleReadbackAndCleanMailbox(
620 texture_mailbox
.mailbox(),
621 texture_mailbox
.sync_point(),
623 gfx::Rect(result
->size()),
627 base::Bind(&CopyFromCompositingSurfaceFinished
,
629 base::Passed(&release_callback
),
630 base::Passed(&bitmap
),
631 base::Passed(&bitmap_pixels_lock
)),
632 GLHelper::SCALER_QUALITY_GOOD
);
636 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
637 const gfx::Size
& dst_size_in_pixel
,
638 const SkColorType preferred_color_type
,
639 ReadbackRequestCallback
& callback
,
640 scoped_ptr
<cc::CopyOutputResult
> result
) {
641 SkColorType color_type
= preferred_color_type
;
642 if (color_type
!= kN32_SkColorType
&& color_type
!= kAlpha_8_SkColorType
) {
643 // Switch back to default colortype if format not supported.
644 color_type
= kN32_SkColorType
;
646 DCHECK(result
->HasBitmap());
647 scoped_ptr
<SkBitmap
> source
= result
->TakeBitmap();
649 SkBitmap scaled_bitmap
;
650 if (source
->width() != dst_size_in_pixel
.width() ||
651 source
->height() != dst_size_in_pixel
.height()) {
653 skia::ImageOperations::Resize(*source
,
654 skia::ImageOperations::RESIZE_BEST
,
655 dst_size_in_pixel
.width(),
656 dst_size_in_pixel
.height());
658 scaled_bitmap
= *source
;
660 if (color_type
== kN32_SkColorType
) {
661 DCHECK_EQ(scaled_bitmap
.colorType(), kN32_SkColorType
);
662 callback
.Run(scaled_bitmap
, READBACK_SUCCESS
);
665 DCHECK_EQ(color_type
, kAlpha_8_SkColorType
);
666 // The software path currently always returns N32 bitmap regardless of the
667 // |color_type| we ask for.
668 DCHECK_EQ(scaled_bitmap
.colorType(), kN32_SkColorType
);
669 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|.
670 SkBitmap grayscale_bitmap
;
671 bool success
= grayscale_bitmap
.tryAllocPixels(
672 SkImageInfo::MakeA8(scaled_bitmap
.width(), scaled_bitmap
.height()));
674 callback
.Run(SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE
);
677 SkCanvas
canvas(grayscale_bitmap
);
679 skia::RefPtr
<SkColorFilter
> filter
=
680 skia::AdoptRef(SkLumaColorFilter::Create());
681 paint
.setColorFilter(filter
.get());
682 canvas
.drawBitmap(scaled_bitmap
, SkIntToScalar(0), SkIntToScalar(0), &paint
);
683 callback
.Run(grayscale_bitmap
, READBACK_SUCCESS
);
687 void DelegatedFrameHost::ReturnSubscriberTexture(
688 base::WeakPtr
<DelegatedFrameHost
> dfh
,
689 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
691 if (!subscriber_texture
.get())
696 subscriber_texture
->UpdateSyncPoint(sync_point
);
698 if (dfh
->frame_subscriber_
&& subscriber_texture
->texture_id())
699 dfh
->idle_frame_subscriber_textures_
.push_back(subscriber_texture
);
703 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
704 base::WeakPtr
<DelegatedFrameHost
> dfh
,
705 const base::Callback
<void(bool)>& callback
,
706 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
707 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
,
709 callback
.Run(result
);
711 uint32 sync_point
= 0;
713 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
714 sync_point
= gl_helper
->InsertSyncPoint();
716 if (release_callback
) {
717 // A release callback means the texture came from the compositor, so there
718 // should be no |subscriber_texture|.
719 DCHECK(!subscriber_texture
.get());
720 bool lost_resource
= sync_point
== 0;
721 release_callback
->Run(sync_point
, lost_resource
);
723 ReturnSubscriberTexture(dfh
, subscriber_texture
, sync_point
);
727 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
728 base::WeakPtr
<DelegatedFrameHost
> dfh
,
729 scoped_refptr
<OwnedMailbox
> subscriber_texture
,
730 scoped_refptr
<media::VideoFrame
> video_frame
,
731 const base::Callback
<void(bool)>& callback
,
732 scoped_ptr
<cc::CopyOutputResult
> result
) {
733 base::ScopedClosureRunner
scoped_callback_runner(base::Bind(callback
, false));
734 base::ScopedClosureRunner
scoped_return_subscriber_texture(
735 base::Bind(&ReturnSubscriberTexture
, dfh
, subscriber_texture
, 0));
739 if (result
->IsEmpty())
741 if (result
->size().IsEmpty())
744 // Compute the dest size we want after the letterboxing resize. Make the
745 // coordinates and sizes even because we letterbox in YUV space
746 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
747 // line up correctly.
748 // The video frame's visible_rect() and the result's size() are both physical
750 gfx::Rect region_in_frame
= media::ComputeLetterboxRegion(
751 video_frame
->visible_rect(), result
->size());
752 region_in_frame
= gfx::Rect(region_in_frame
.x() & ~1,
753 region_in_frame
.y() & ~1,
754 region_in_frame
.width() & ~1,
755 region_in_frame
.height() & ~1);
756 if (region_in_frame
.IsEmpty())
759 if (!result
->HasTexture()) {
760 DCHECK(result
->HasBitmap());
761 scoped_ptr
<SkBitmap
> bitmap
= result
->TakeBitmap();
762 // Scale the bitmap to the required size, if necessary.
763 SkBitmap scaled_bitmap
;
764 if (result
->size() != region_in_frame
.size()) {
765 skia::ImageOperations::ResizeMethod method
=
766 skia::ImageOperations::RESIZE_GOOD
;
767 scaled_bitmap
= skia::ImageOperations::Resize(*bitmap
.get(), method
,
768 region_in_frame
.width(),
769 region_in_frame
.height());
771 scaled_bitmap
= *bitmap
.get();
775 SkAutoLockPixels
scaled_bitmap_locker(scaled_bitmap
);
777 media::CopyRGBToVideoFrame(
778 reinterpret_cast<uint8
*>(scaled_bitmap
.getPixels()),
779 scaled_bitmap
.rowBytes(),
783 ignore_result(scoped_callback_runner
.Release());
788 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
789 GLHelper
* gl_helper
= factory
->GetGLHelper();
792 if (subscriber_texture
.get() && !subscriber_texture
->texture_id())
795 cc::TextureMailbox texture_mailbox
;
796 scoped_ptr
<cc::SingleReleaseCallback
> release_callback
;
797 result
->TakeTexture(&texture_mailbox
, &release_callback
);
798 DCHECK(texture_mailbox
.IsTexture());
800 gfx::Rect
result_rect(result
->size());
802 content::ReadbackYUVInterface
* yuv_readback_pipeline
=
803 dfh
->yuv_readback_pipeline_
.get();
804 if (yuv_readback_pipeline
== NULL
||
805 yuv_readback_pipeline
->scaler()->SrcSize() != result_rect
.size() ||
806 yuv_readback_pipeline
->scaler()->SrcSubrect() != result_rect
||
807 yuv_readback_pipeline
->scaler()->DstSize() != region_in_frame
.size()) {
808 GLHelper::ScalerQuality quality
= GLHelper::SCALER_QUALITY_FAST
;
809 std::string quality_switch
= switches::kTabCaptureDownscaleQuality
;
810 // If we're scaling up, we can use the "best" quality.
811 if (result_rect
.size().width() < region_in_frame
.size().width() &&
812 result_rect
.size().height() < region_in_frame
.size().height())
813 quality_switch
= switches::kTabCaptureUpscaleQuality
;
815 std::string switch_value
=
816 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
818 if (switch_value
== "fast")
819 quality
= GLHelper::SCALER_QUALITY_FAST
;
820 else if (switch_value
== "good")
821 quality
= GLHelper::SCALER_QUALITY_GOOD
;
822 else if (switch_value
== "best")
823 quality
= GLHelper::SCALER_QUALITY_BEST
;
825 dfh
->yuv_readback_pipeline_
.reset(
826 gl_helper
->CreateReadbackPipelineYUV(quality
,
829 region_in_frame
.size(),
832 yuv_readback_pipeline
= dfh
->yuv_readback_pipeline_
.get();
835 ignore_result(scoped_callback_runner
.Release());
836 ignore_result(scoped_return_subscriber_texture
.Release());
837 base::Callback
<void(bool result
)> finished_callback
= base::Bind(
838 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo
,
842 base::Passed(&release_callback
));
843 yuv_readback_pipeline
->ReadbackYUV(texture_mailbox
.mailbox(),
844 texture_mailbox
.sync_point(),
846 region_in_frame
.origin(),
850 ////////////////////////////////////////////////////////////////////////////////
851 // DelegatedFrameHost, ui::CompositorObserver implementation:
853 void DelegatedFrameHost::OnCompositingDidCommit(
854 ui::Compositor
* compositor
) {
855 if (can_lock_compositor_
== NO_PENDING_COMMIT
) {
856 can_lock_compositor_
= YES_CAN_LOCK
;
857 if (resize_lock_
.get() && resize_lock_
->GrabDeferredLock())
858 can_lock_compositor_
= YES_DID_LOCK
;
860 RunOnCommitCallbacks();
862 resize_lock_
->expected_size() == current_frame_size_in_dip_
) {
863 resize_lock_
.reset();
864 client_
->DelegatedFrameHostResizeLockWasReleased();
865 // We may have had a resize while we had the lock (e.g. if the lock expired,
866 // or if the UI still gave us some resizes), so make sure we grab a new lock
868 MaybeCreateResizeLock();
872 void DelegatedFrameHost::OnCompositingStarted(
873 ui::Compositor
* compositor
, base::TimeTicks start_time
) {
874 last_draw_ended_
= start_time
;
877 void DelegatedFrameHost::OnCompositingEnded(
878 ui::Compositor
* compositor
) {
881 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor
* compositor
) {
884 void DelegatedFrameHost::OnCompositingLockStateChanged(
885 ui::Compositor
* compositor
) {
886 // A compositor lock that is part of a resize lock timed out. We
887 // should display a renderer frame.
888 if (!compositor
->IsLocked() && can_lock_compositor_
== YES_DID_LOCK
) {
889 can_lock_compositor_
= NO_PENDING_RENDERER_FRAME
;
893 void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor
* compositor
) {
894 DCHECK_EQ(compositor
, compositor_
);
896 DCHECK(!compositor_
);
899 void DelegatedFrameHost::OnUpdateVSyncParameters(
900 base::TimeTicks timebase
,
901 base::TimeDelta interval
) {
902 SetVSyncParameters(timebase
, interval
);
903 if (client_
->DelegatedFrameHostIsVisible())
904 client_
->DelegatedFrameHostUpdateVSyncParameters(timebase
, interval
);
907 ////////////////////////////////////////////////////////////////////////////////
908 // DelegatedFrameHost, ImageTransportFactoryObserver implementation:
910 void DelegatedFrameHost::OnLostResources() {
911 if (frame_provider_
.get() || !surface_id_
.is_null())
912 EvictDelegatedFrame();
913 idle_frame_subscriber_textures_
.clear();
914 yuv_readback_pipeline_
.reset();
916 client_
->DelegatedFrameHostOnLostCompositorResources();
919 ////////////////////////////////////////////////////////////////////////////////
920 // DelegatedFrameHost, private:
922 DelegatedFrameHost::~DelegatedFrameHost() {
923 DCHECK(!compositor_
);
924 ImageTransportFactory::GetInstance()->RemoveObserver(this);
926 if (!surface_id_
.is_null())
927 surface_factory_
->Destroy(surface_id_
);
928 if (resource_collection_
.get())
929 resource_collection_
->SetClient(NULL
);
931 DCHECK(!vsync_manager_
.get());
934 void DelegatedFrameHost::RunOnCommitCallbacks() {
935 for (std::vector
<base::Closure
>::const_iterator
936 it
= on_compositing_did_commit_callbacks_
.begin();
937 it
!= on_compositing_did_commit_callbacks_
.end(); ++it
) {
940 on_compositing_did_commit_callbacks_
.clear();
943 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
944 const base::Closure
& callback
) {
947 can_lock_compositor_
= NO_PENDING_COMMIT
;
948 if (!callback
.is_null())
949 on_compositing_did_commit_callbacks_
.push_back(callback
);
952 void DelegatedFrameHost::SetCompositor(ui::Compositor
* compositor
) {
953 DCHECK(!compositor_
);
956 compositor_
= compositor
;
957 compositor_
->AddObserver(this);
958 DCHECK(!vsync_manager_
.get());
959 vsync_manager_
= compositor_
->vsync_manager();
960 vsync_manager_
->AddObserver(this);
963 void DelegatedFrameHost::ResetCompositor() {
966 RunOnCommitCallbacks();
968 resize_lock_
.reset();
969 client_
->DelegatedFrameHostResizeLockWasReleased();
971 if (compositor_
->HasObserver(this))
972 compositor_
->RemoveObserver(this);
973 if (vsync_manager_
.get()) {
974 vsync_manager_
->RemoveObserver(this);
975 vsync_manager_
= NULL
;
977 compositor_
= nullptr;
980 void DelegatedFrameHost::SetVSyncParameters(const base::TimeTicks
& timebase
,
981 const base::TimeDelta
& interval
) {
982 vsync_timebase_
= timebase
;
983 vsync_interval_
= interval
;
986 void DelegatedFrameHost::LockResources() {
987 DCHECK(frame_provider_
.get() || !surface_id_
.is_null());
988 delegated_frame_evictor_
->LockFrame();
991 void DelegatedFrameHost::RequestCopyOfOutput(
992 scoped_ptr
<cc::CopyOutputRequest
> request
) {
993 if (!request_copy_of_output_callback_for_testing_
.is_null())
994 request_copy_of_output_callback_for_testing_
.Run(request
.Pass());
996 client_
->DelegatedFrameHostGetLayer()->RequestCopyOfOutput(request
.Pass());
999 void DelegatedFrameHost::UnlockResources() {
1000 DCHECK(frame_provider_
.get() || !surface_id_
.is_null());
1001 delegated_frame_evictor_
->UnlockFrame();
1004 ////////////////////////////////////////////////////////////////////////////////
1005 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
1007 void DelegatedFrameHost::OnLayerRecreated(ui::Layer
* old_layer
,
1008 ui::Layer
* new_layer
) {
1009 // The new_layer is the one that will be used by our Window, so that's the one
1010 // that should keep our frame. old_layer will be returned to the
1011 // RecreateLayer caller, and should have a copy.
1012 if (frame_provider_
.get()) {
1013 new_layer
->SetShowDelegatedContent(frame_provider_
.get(),
1014 current_frame_size_in_dip_
);
1016 if (!surface_id_
.is_null()) {
1017 ImageTransportFactory
* factory
= ImageTransportFactory::GetInstance();
1018 cc::SurfaceManager
* manager
= factory
->GetSurfaceManager();
1019 new_layer
->SetShowSurface(
1020 surface_id_
, base::Bind(&SatisfyCallback
, base::Unretained(manager
)),
1021 base::Bind(&RequireCallback
, base::Unretained(manager
)),
1022 current_surface_size_
, current_scale_factor_
,
1023 current_frame_size_in_dip_
);
1027 } // namespace content