Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / compositor / delegated_frame_host.cc
blob2aa746dfc9e2a0ccc597e4b6e48b1ba2ee941746
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"
31 namespace content {
33 namespace {
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,
43 cc::SurfaceId id,
44 cc::SurfaceSequence sequence) {
45 cc::Surface* surface = manager->GetSurfaceForId(id);
46 if (!surface) {
47 LOG(ERROR) << "Attempting to require callback on nonexistent surface";
48 return;
50 surface->AddDestructionDependency(sequence);
53 } // namespace
55 ////////////////////////////////////////////////////////////////////////////////
56 // DelegatedFrameHost
58 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client)
59 : client_(client),
60 compositor_(nullptr),
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()) {
76 if (compositor_)
77 released_front_lock_ = compositor_->GetCompositorLock();
80 if (compositor_) {
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())
96 return;
97 DCHECK(compositor_);
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;
106 resize_lock_ =
107 client_->DelegatedFrameHostCreateResizeLock(defer_compositor_lock);
110 bool DelegatedFrameHost::ShouldCreateResizeLock() {
111 if (!client_->DelegatedFrameCanCreateResizeLock())
112 return false;
114 if (resize_lock_)
115 return false;
117 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
118 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty())
119 return false;
121 if (!compositor_)
122 return false;
124 return true;
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);
139 return;
142 scoped_ptr<cc::CopyOutputRequest> request =
143 cc::CopyOutputRequest::CreateRequest(base::Bind(
144 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
145 output_size,
146 color_type,
147 callback));
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()) {
158 callback.Run(false);
159 return;
162 scoped_ptr<cc::CopyOutputRequest> request =
163 cc::CopyOutputRequest::CreateRequest(base::Bind(
164 &DelegatedFrameHost::
165 CopyFromCompositingSurfaceHasResultForVideo,
166 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
167 nullptr,
168 target,
169 callback));
170 request->set_area(src_subrect);
171 RequestCopyOfOutput(request.Pass());
174 bool DelegatedFrameHost::CanCopyToBitmap() const {
175 return compositor_ &&
176 client_->DelegatedFrameHostGetLayer()->has_external_content();
179 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
180 return compositor_ &&
181 client_->DelegatedFrameHostGetLayer()->has_external_content();
184 bool DelegatedFrameHost::CanSubscribeFrame() const {
185 return true;
188 void DelegatedFrameHost::BeginFrameSubscription(
189 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
190 frame_subscriber_ = subscriber.Pass();
193 void DelegatedFrameHost::EndFrameSubscription() {
194 idle_frame_subscriber_textures_.clear();
195 frame_subscriber_.reset();
198 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const {
199 // Should skip a frame only when another frame from the renderer is guaranteed
200 // to replace it. Otherwise may cause hangs when the renderer is waiting for
201 // the completion of latency infos (such as when taking a Snapshot.)
202 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
203 can_lock_compositor_ == NO_PENDING_COMMIT ||
204 !resize_lock_.get())
205 return false;
207 return size_in_dip != resize_lock_->expected_size();
210 void DelegatedFrameHost::WasResized() {
211 if (client_->DelegatedFrameHostDesiredSizeInDIP() !=
212 current_frame_size_in_dip_ &&
213 !client_->DelegatedFrameHostIsVisible())
214 EvictDelegatedFrame();
215 MaybeCreateResizeLock();
218 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
219 if (resize_lock_)
220 return resize_lock_->expected_size();
221 else
222 return client_->DelegatedFrameHostDesiredSizeInDIP();
225 void DelegatedFrameHost::CheckResizeLock() {
226 if (!resize_lock_ ||
227 resize_lock_->expected_size() != current_frame_size_in_dip_)
228 return;
230 // Since we got the size we were looking for, unlock the compositor. But delay
231 // the release of the lock until we've kicked a frame with the new texture, to
232 // avoid resizing the UI before we have a chance to draw a "good" frame.
233 resize_lock_->UnlockCompositor();
236 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
237 const gfx::Rect& damage_rect) {
238 if (!frame_subscriber() || !CanCopyToVideoFrame())
239 return;
241 const base::TimeTicks now = gfx::FrameTime::Now();
242 base::TimeTicks present_time;
243 if (vsync_timebase_.is_null() || vsync_interval_ <= base::TimeDelta()) {
244 present_time = now;
245 } else {
246 const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_;
247 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_;
250 scoped_refptr<media::VideoFrame> frame;
251 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
252 if (!frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time,
253 &frame, &callback))
254 return;
256 // Get a texture to re-use; else, create a new one.
257 scoped_refptr<OwnedMailbox> subscriber_texture;
258 if (!idle_frame_subscriber_textures_.empty()) {
259 subscriber_texture = idle_frame_subscriber_textures_.back();
260 idle_frame_subscriber_textures_.pop_back();
261 } else if (GLHelper* helper =
262 ImageTransportFactory::GetInstance()->GetGLHelper()) {
263 subscriber_texture = new OwnedMailbox(helper);
266 scoped_ptr<cc::CopyOutputRequest> request =
267 cc::CopyOutputRequest::CreateRequest(base::Bind(
268 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo,
269 AsWeakPtr(),
270 subscriber_texture,
271 frame,
272 base::Bind(callback, present_time)));
273 // Setting the source in this copy request asks that the layer abort any prior
274 // uncommitted copy requests made on behalf of the same frame subscriber.
275 // This will not affect any of the copy requests spawned elsewhere from
276 // DelegatedFrameHost (e.g., a call to CopyFromCompositingSurface() for
277 // screenshots) since those copy requests do not specify |frame_subscriber()|
278 // as a source.
279 request->set_source(frame_subscriber());
280 request->set_area(gfx::Rect(current_frame_size_in_dip_));
281 if (subscriber_texture.get()) {
282 request->SetTextureMailbox(
283 cc::TextureMailbox(subscriber_texture->mailbox(),
284 subscriber_texture->target(),
285 subscriber_texture->sync_point()));
287 RequestCopyOfOutput(request.Pass());
290 void DelegatedFrameHost::SwapDelegatedFrame(
291 uint32 output_surface_id,
292 scoped_ptr<cc::DelegatedFrameData> frame_data,
293 float frame_device_scale_factor,
294 const std::vector<ui::LatencyInfo>& latency_info) {
295 DCHECK(!frame_data->render_pass_list.empty());
297 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
299 gfx::Size frame_size = root_pass->output_rect.size();
300 gfx::Size frame_size_in_dip =
301 gfx::ConvertSizeToDIP(frame_device_scale_factor, frame_size);
303 gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect);
304 damage_rect.Intersect(gfx::Rect(frame_size));
305 gfx::Rect damage_rect_in_dip =
306 gfx::ConvertRectToDIP(frame_device_scale_factor, damage_rect);
308 if (ShouldSkipFrame(frame_size_in_dip)) {
309 cc::CompositorFrameAck ack;
310 cc::TransferableResource::ReturnResources(frame_data->resource_list,
311 &ack.resources);
313 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(),
314 latency_info.begin(), latency_info.end());
316 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack);
317 skipped_frames_ = true;
318 return;
321 if (skipped_frames_) {
322 skipped_frames_ = false;
323 damage_rect = gfx::Rect(frame_size);
324 damage_rect_in_dip = gfx::Rect(frame_size_in_dip);
326 // Give the same damage rect to the compositor.
327 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
328 root_pass->damage_rect = damage_rect;
331 if (output_surface_id != last_output_surface_id_) {
332 // Resource ids are scoped by the output surface.
333 // If the originating output surface doesn't match the last one, it
334 // indicates the renderer's output surface may have been recreated, in which
335 // case we should recreate the DelegatedRendererLayer, to avoid matching
336 // resources from the old one with resources from the new one which would
337 // have the same id. Changing the layer to showing painted content destroys
338 // the DelegatedRendererLayer.
339 EvictDelegatedFrame();
341 surface_factory_.reset();
342 if (!surface_returned_resources_.empty())
343 SendReturnedDelegatedResources(last_output_surface_id_);
345 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
346 // any resources from the old output surface with the new output surface id.
347 if (resource_collection_.get()) {
348 resource_collection_->SetClient(NULL);
350 if (resource_collection_->LoseAllResources())
351 SendReturnedDelegatedResources(last_output_surface_id_);
353 resource_collection_ = NULL;
355 last_output_surface_id_ = output_surface_id;
357 bool immediate_ack = !compositor_;
358 pending_delegated_ack_count_++;
360 if (frame_size.IsEmpty()) {
361 DCHECK(frame_data->resource_list.empty());
362 EvictDelegatedFrame();
363 } else {
364 if (use_surfaces_) {
365 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
366 cc::SurfaceManager* manager = factory->GetSurfaceManager();
367 if (!surface_factory_) {
368 id_allocator_ =
369 factory->GetContextFactory()->CreateSurfaceIdAllocator();
370 surface_factory_ =
371 make_scoped_ptr(new cc::SurfaceFactory(manager, this));
373 if (surface_id_.is_null() || frame_size != current_surface_size_ ||
374 frame_size_in_dip != current_frame_size_in_dip_) {
375 if (!surface_id_.is_null())
376 surface_factory_->Destroy(surface_id_);
377 surface_id_ = id_allocator_->GenerateId();
378 surface_factory_->Create(surface_id_);
379 // manager must outlive compositors using it.
380 client_->DelegatedFrameHostGetLayer()->SetShowSurface(
381 surface_id_,
382 base::Bind(&SatisfyCallback, base::Unretained(manager)),
383 base::Bind(&RequireCallback, base::Unretained(manager)), frame_size,
384 frame_device_scale_factor, frame_size_in_dip);
385 current_surface_size_ = frame_size;
386 current_scale_factor_ = frame_device_scale_factor;
388 scoped_ptr<cc::CompositorFrame> compositor_frame =
389 make_scoped_ptr(new cc::CompositorFrame());
390 compositor_frame->delegated_frame_data = frame_data.Pass();
392 compositor_frame->metadata.latency_info.swap(skipped_latency_info_list_);
393 compositor_frame->metadata.latency_info.insert(
394 compositor_frame->metadata.latency_info.end(),
395 latency_info.begin(),
396 latency_info.end());
398 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
399 if (desired_size != frame_size_in_dip && !desired_size.IsEmpty())
400 immediate_ack = true;
402 cc::SurfaceFactory::DrawCallback ack_callback;
403 if (compositor_ && !immediate_ack) {
404 ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn,
405 AsWeakPtr(), output_surface_id);
407 surface_factory_->SubmitFrame(
408 surface_id_, compositor_frame.Pass(), ack_callback);
409 } else {
410 if (!resource_collection_.get()) {
411 resource_collection_ = new cc::DelegatedFrameResourceCollection;
412 resource_collection_->SetClient(this);
414 // If the physical frame size changes, we need a new |frame_provider_|. If
415 // the physical frame size is the same, but the size in DIP changed, we
416 // need to adjust the scale at which the frames will be drawn, and we do
417 // this by making a new |frame_provider_| also to ensure the scale change
418 // is presented in sync with the new frame content.
419 if (!frame_provider_.get() ||
420 frame_size != frame_provider_->frame_size() ||
421 frame_size_in_dip != current_frame_size_in_dip_) {
422 frame_provider_ = new cc::DelegatedFrameProvider(
423 resource_collection_.get(), frame_data.Pass());
424 client_->DelegatedFrameHostGetLayer()->SetShowDelegatedContent(
425 frame_provider_.get(), frame_size_in_dip);
426 } else {
427 frame_provider_->SetFrameData(frame_data.Pass());
431 released_front_lock_ = NULL;
432 current_frame_size_in_dip_ = frame_size_in_dip;
433 CheckResizeLock();
435 if (!damage_rect_in_dip.IsEmpty())
436 client_->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage(
437 damage_rect_in_dip);
439 if (immediate_ack) {
440 SendDelegatedFrameAck(output_surface_id);
441 } else if (!use_surfaces_) {
442 std::vector<ui::LatencyInfo>::const_iterator it;
443 for (it = latency_info.begin(); it != latency_info.end(); ++it)
444 compositor_->SetLatencyInfo(*it);
445 // If we've previously skipped any latency infos add them.
446 for (it = skipped_latency_info_list_.begin();
447 it != skipped_latency_info_list_.end();
448 ++it)
449 compositor_->SetLatencyInfo(*it);
450 skipped_latency_info_list_.clear();
451 AddOnCommitCallbackAndDisableLocks(
452 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
453 AsWeakPtr(), output_surface_id));
454 } else {
455 AddOnCommitCallbackAndDisableLocks(base::Closure());
457 DidReceiveFrameFromRenderer(damage_rect);
458 if (frame_provider_.get() || !surface_id_.is_null())
459 delegated_frame_evictor_->SwappedFrame(
460 client_->DelegatedFrameHostIsVisible());
461 // Note: the frame may have been evicted immediately.
464 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) {
465 cc::CompositorFrameAck ack;
466 if (!surface_returned_resources_.empty())
467 ack.resources.swap(surface_returned_resources_);
468 if (resource_collection_.get())
469 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
470 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack);
471 DCHECK_GT(pending_delegated_ack_count_, 0);
472 pending_delegated_ack_count_--;
475 void DelegatedFrameHost::SurfaceDrawn(uint32 output_surface_id,
476 cc::SurfaceDrawStatus drawn) {
477 SendDelegatedFrameAck(output_surface_id);
480 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
481 if (pending_delegated_ack_count_)
482 return;
484 SendReturnedDelegatedResources(last_output_surface_id_);
487 void DelegatedFrameHost::SendReturnedDelegatedResources(
488 uint32 output_surface_id) {
489 cc::CompositorFrameAck ack;
490 if (!surface_returned_resources_.empty()) {
491 ack.resources.swap(surface_returned_resources_);
492 } else {
493 DCHECK(resource_collection_.get());
494 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
496 DCHECK(!ack.resources.empty());
498 client_->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id,
499 ack);
502 void DelegatedFrameHost::ReturnResources(
503 const cc::ReturnedResourceArray& resources) {
504 if (resources.empty())
505 return;
506 std::copy(resources.begin(),
507 resources.end(),
508 std::back_inserter(surface_returned_resources_));
509 if (!pending_delegated_ack_count_)
510 SendReturnedDelegatedResources(last_output_surface_id_);
513 void DelegatedFrameHost::EvictDelegatedFrame() {
514 client_->DelegatedFrameHostGetLayer()->SetShowSolidColorContent();
515 frame_provider_ = NULL;
516 if (!surface_id_.is_null()) {
517 surface_factory_->Destroy(surface_id_);
518 surface_id_ = cc::SurfaceId();
520 delegated_frame_evictor_->DiscardedFrame();
523 // static
524 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
525 const gfx::Size& dst_size_in_pixel,
526 const SkColorType color_type,
527 ReadbackRequestCallback& callback,
528 scoped_ptr<cc::CopyOutputResult> result) {
529 if (result->IsEmpty() || result->size().IsEmpty()) {
530 callback.Run(SkBitmap(), content::READBACK_FAILED);
531 return;
534 gfx::Size output_size_in_pixel;
535 if (dst_size_in_pixel.IsEmpty())
536 output_size_in_pixel = result->size();
537 else
538 output_size_in_pixel = dst_size_in_pixel;
540 if (result->HasTexture()) {
541 // GPU-accelerated path
542 PrepareTextureCopyOutputResult(output_size_in_pixel, color_type,
543 callback,
544 result.Pass());
545 return;
548 DCHECK(result->HasBitmap());
549 // Software path
550 PrepareBitmapCopyOutputResult(output_size_in_pixel, color_type, callback,
551 result.Pass());
554 static void CopyFromCompositingSurfaceFinished(
555 ReadbackRequestCallback& callback,
556 scoped_ptr<cc::SingleReleaseCallback> release_callback,
557 scoped_ptr<SkBitmap> bitmap,
558 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
559 bool result) {
560 bitmap_pixels_lock.reset();
562 uint32 sync_point = 0;
563 if (result) {
564 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
565 if (gl_helper)
566 sync_point = gl_helper->InsertSyncPoint();
568 bool lost_resource = sync_point == 0;
569 release_callback->Run(sync_point, lost_resource);
571 callback.Run(*bitmap,
572 result ? content::READBACK_SUCCESS : content::READBACK_FAILED);
575 // static
576 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
577 const gfx::Size& dst_size_in_pixel,
578 const SkColorType color_type,
579 ReadbackRequestCallback& callback,
580 scoped_ptr<cc::CopyOutputResult> result) {
581 DCHECK(result->HasTexture());
582 base::ScopedClosureRunner scoped_callback_runner(
583 base::Bind(callback, SkBitmap(), content::READBACK_FAILED));
585 // TODO(sikugu): We should be able to validate the format here using
586 // GLHelper::IsReadbackConfigSupported before we processs the result.
587 // See crbug.com/415682.
588 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
589 if (!bitmap->tryAllocPixels(SkImageInfo::Make(dst_size_in_pixel.width(),
590 dst_size_in_pixel.height(),
591 color_type,
592 kOpaque_SkAlphaType)))
593 return;
595 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
596 GLHelper* gl_helper = factory->GetGLHelper();
597 if (!gl_helper)
598 return;
600 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
601 new SkAutoLockPixels(*bitmap));
602 uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
604 cc::TextureMailbox texture_mailbox;
605 scoped_ptr<cc::SingleReleaseCallback> release_callback;
606 result->TakeTexture(&texture_mailbox, &release_callback);
607 DCHECK(texture_mailbox.IsTexture());
609 ignore_result(scoped_callback_runner.Release());
611 gl_helper->CropScaleReadbackAndCleanMailbox(
612 texture_mailbox.mailbox(),
613 texture_mailbox.sync_point(),
614 result->size(),
615 gfx::Rect(result->size()),
616 dst_size_in_pixel,
617 pixels,
618 color_type,
619 base::Bind(&CopyFromCompositingSurfaceFinished,
620 callback,
621 base::Passed(&release_callback),
622 base::Passed(&bitmap),
623 base::Passed(&bitmap_pixels_lock)),
624 GLHelper::SCALER_QUALITY_FAST);
627 // static
628 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
629 const gfx::Size& dst_size_in_pixel,
630 const SkColorType color_type,
631 ReadbackRequestCallback& callback,
632 scoped_ptr<cc::CopyOutputResult> result) {
633 if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) {
634 NOTIMPLEMENTED();
635 callback.Run(SkBitmap(), READBACK_FORMAT_NOT_SUPPORTED);
636 return;
638 DCHECK(result->HasBitmap());
639 scoped_ptr<SkBitmap> source = result->TakeBitmap();
640 DCHECK(source);
641 SkBitmap scaled_bitmap;
642 if (source->width() != dst_size_in_pixel.width() ||
643 source->height() != dst_size_in_pixel.height()) {
644 scaled_bitmap =
645 skia::ImageOperations::Resize(*source,
646 skia::ImageOperations::RESIZE_BEST,
647 dst_size_in_pixel.width(),
648 dst_size_in_pixel.height());
649 } else {
650 scaled_bitmap = *source;
652 if (color_type == kN32_SkColorType) {
653 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
654 callback.Run(scaled_bitmap, READBACK_SUCCESS);
655 return;
657 DCHECK_EQ(color_type, kAlpha_8_SkColorType);
658 // The software path currently always returns N32 bitmap regardless of the
659 // |color_type| we ask for.
660 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
661 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|.
662 SkBitmap grayscale_bitmap;
663 bool success = grayscale_bitmap.tryAllocPixels(
664 SkImageInfo::MakeA8(scaled_bitmap.width(), scaled_bitmap.height()));
665 if (!success) {
666 callback.Run(SkBitmap(), content::READBACK_MEMORY_ALLOCATION_FAILURE);
667 return;
669 SkCanvas canvas(grayscale_bitmap);
670 SkPaint paint;
671 skia::RefPtr<SkColorFilter> filter =
672 skia::AdoptRef(SkLumaColorFilter::Create());
673 paint.setColorFilter(filter.get());
674 canvas.drawBitmap(scaled_bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint);
675 callback.Run(grayscale_bitmap, READBACK_SUCCESS);
678 // static
679 void DelegatedFrameHost::ReturnSubscriberTexture(
680 base::WeakPtr<DelegatedFrameHost> dfh,
681 scoped_refptr<OwnedMailbox> subscriber_texture,
682 uint32 sync_point) {
683 if (!subscriber_texture.get())
684 return;
685 if (!dfh)
686 return;
688 subscriber_texture->UpdateSyncPoint(sync_point);
690 if (dfh->frame_subscriber_ && subscriber_texture->texture_id())
691 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture);
694 // static
695 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
696 base::WeakPtr<DelegatedFrameHost> dfh,
697 const base::Callback<void(bool)>& callback,
698 scoped_refptr<OwnedMailbox> subscriber_texture,
699 scoped_ptr<cc::SingleReleaseCallback> release_callback,
700 bool result) {
701 callback.Run(result);
703 uint32 sync_point = 0;
704 if (result) {
705 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
706 sync_point = gl_helper->InsertSyncPoint();
708 if (release_callback) {
709 // A release callback means the texture came from the compositor, so there
710 // should be no |subscriber_texture|.
711 DCHECK(!subscriber_texture.get());
712 bool lost_resource = sync_point == 0;
713 release_callback->Run(sync_point, lost_resource);
715 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point);
718 // static
719 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
720 base::WeakPtr<DelegatedFrameHost> dfh,
721 scoped_refptr<OwnedMailbox> subscriber_texture,
722 scoped_refptr<media::VideoFrame> video_frame,
723 const base::Callback<void(bool)>& callback,
724 scoped_ptr<cc::CopyOutputResult> result) {
725 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
726 base::ScopedClosureRunner scoped_return_subscriber_texture(
727 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0));
729 if (!dfh)
730 return;
731 if (result->IsEmpty())
732 return;
733 if (result->size().IsEmpty())
734 return;
736 // Compute the dest size we want after the letterboxing resize. Make the
737 // coordinates and sizes even because we letterbox in YUV space
738 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
739 // line up correctly.
740 // The video frame's visible_rect() and the result's size() are both physical
741 // pixels.
742 gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
743 video_frame->visible_rect(), result->size());
744 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
745 region_in_frame.y() & ~1,
746 region_in_frame.width() & ~1,
747 region_in_frame.height() & ~1);
748 if (region_in_frame.IsEmpty())
749 return;
751 if (!result->HasTexture()) {
752 DCHECK(result->HasBitmap());
753 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap();
754 // Scale the bitmap to the required size, if necessary.
755 SkBitmap scaled_bitmap;
756 if (result->size() != region_in_frame.size()) {
757 skia::ImageOperations::ResizeMethod method =
758 skia::ImageOperations::RESIZE_GOOD;
759 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method,
760 region_in_frame.width(),
761 region_in_frame.height());
762 } else {
763 scaled_bitmap = *bitmap.get();
767 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
769 media::CopyRGBToVideoFrame(
770 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
771 scaled_bitmap.rowBytes(),
772 region_in_frame,
773 video_frame.get());
775 ignore_result(scoped_callback_runner.Release());
776 callback.Run(true);
777 return;
780 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
781 GLHelper* gl_helper = factory->GetGLHelper();
782 if (!gl_helper)
783 return;
784 if (subscriber_texture.get() && !subscriber_texture->texture_id())
785 return;
787 cc::TextureMailbox texture_mailbox;
788 scoped_ptr<cc::SingleReleaseCallback> release_callback;
789 result->TakeTexture(&texture_mailbox, &release_callback);
790 DCHECK(texture_mailbox.IsTexture());
792 gfx::Rect result_rect(result->size());
794 content::ReadbackYUVInterface* yuv_readback_pipeline =
795 dfh->yuv_readback_pipeline_.get();
796 if (yuv_readback_pipeline == NULL ||
797 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() ||
798 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect ||
799 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) {
800 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST;
801 std::string quality_switch = switches::kTabCaptureDownscaleQuality;
802 // If we're scaling up, we can use the "best" quality.
803 if (result_rect.size().width() < region_in_frame.size().width() &&
804 result_rect.size().height() < region_in_frame.size().height())
805 quality_switch = switches::kTabCaptureUpscaleQuality;
807 std::string switch_value =
808 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
809 quality_switch);
810 if (switch_value == "fast")
811 quality = GLHelper::SCALER_QUALITY_FAST;
812 else if (switch_value == "good")
813 quality = GLHelper::SCALER_QUALITY_GOOD;
814 else if (switch_value == "best")
815 quality = GLHelper::SCALER_QUALITY_BEST;
817 dfh->yuv_readback_pipeline_.reset(
818 gl_helper->CreateReadbackPipelineYUV(quality,
819 result_rect.size(),
820 result_rect,
821 region_in_frame.size(),
822 true,
823 true));
824 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get();
827 ignore_result(scoped_callback_runner.Release());
828 ignore_result(scoped_return_subscriber_texture.Release());
829 base::Callback<void(bool result)> finished_callback = base::Bind(
830 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo,
831 dfh->AsWeakPtr(),
832 callback,
833 subscriber_texture,
834 base::Passed(&release_callback));
835 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
836 texture_mailbox.sync_point(),
837 video_frame.get(),
838 region_in_frame.origin(),
839 finished_callback);
842 ////////////////////////////////////////////////////////////////////////////////
843 // DelegatedFrameHost, ui::CompositorObserver implementation:
845 void DelegatedFrameHost::OnCompositingDidCommit(
846 ui::Compositor* compositor) {
847 if (can_lock_compositor_ == NO_PENDING_COMMIT) {
848 can_lock_compositor_ = YES_CAN_LOCK;
849 if (resize_lock_.get() && resize_lock_->GrabDeferredLock())
850 can_lock_compositor_ = YES_DID_LOCK;
852 RunOnCommitCallbacks();
853 if (resize_lock_ &&
854 resize_lock_->expected_size() == current_frame_size_in_dip_) {
855 resize_lock_.reset();
856 client_->DelegatedFrameHostResizeLockWasReleased();
857 // We may have had a resize while we had the lock (e.g. if the lock expired,
858 // or if the UI still gave us some resizes), so make sure we grab a new lock
859 // if necessary.
860 MaybeCreateResizeLock();
864 void DelegatedFrameHost::OnCompositingStarted(
865 ui::Compositor* compositor, base::TimeTicks start_time) {
866 last_draw_ended_ = start_time;
869 void DelegatedFrameHost::OnCompositingEnded(
870 ui::Compositor* compositor) {
873 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
876 void DelegatedFrameHost::OnCompositingLockStateChanged(
877 ui::Compositor* compositor) {
878 // A compositor lock that is part of a resize lock timed out. We
879 // should display a renderer frame.
880 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) {
881 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME;
885 void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor* compositor) {
886 DCHECK_EQ(compositor, compositor_);
887 ResetCompositor();
888 DCHECK(!compositor_);
891 void DelegatedFrameHost::OnUpdateVSyncParameters(
892 base::TimeTicks timebase,
893 base::TimeDelta interval) {
894 vsync_timebase_ = timebase;
895 vsync_interval_ = interval;
896 if (client_->DelegatedFrameHostIsVisible())
897 client_->DelegatedFrameHostUpdateVSyncParameters(timebase, interval);
900 ////////////////////////////////////////////////////////////////////////////////
901 // DelegatedFrameHost, ImageTransportFactoryObserver implementation:
903 void DelegatedFrameHost::OnLostResources() {
904 if (frame_provider_.get() || !surface_id_.is_null())
905 EvictDelegatedFrame();
906 idle_frame_subscriber_textures_.clear();
907 yuv_readback_pipeline_.reset();
909 client_->DelegatedFrameHostOnLostCompositorResources();
912 ////////////////////////////////////////////////////////////////////////////////
913 // DelegatedFrameHost, private:
915 DelegatedFrameHost::~DelegatedFrameHost() {
916 DCHECK(!compositor_);
917 ImageTransportFactory::GetInstance()->RemoveObserver(this);
919 if (!surface_id_.is_null())
920 surface_factory_->Destroy(surface_id_);
921 if (resource_collection_.get())
922 resource_collection_->SetClient(NULL);
924 DCHECK(!vsync_manager_.get());
927 void DelegatedFrameHost::RunOnCommitCallbacks() {
928 for (std::vector<base::Closure>::const_iterator
929 it = on_compositing_did_commit_callbacks_.begin();
930 it != on_compositing_did_commit_callbacks_.end(); ++it) {
931 it->Run();
933 on_compositing_did_commit_callbacks_.clear();
936 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
937 const base::Closure& callback) {
938 DCHECK(compositor_);
940 can_lock_compositor_ = NO_PENDING_COMMIT;
941 if (!callback.is_null())
942 on_compositing_did_commit_callbacks_.push_back(callback);
945 void DelegatedFrameHost::SetCompositor(ui::Compositor* compositor) {
946 DCHECK(!compositor_);
947 if (!compositor)
948 return;
949 compositor_ = compositor;
950 compositor_->AddObserver(this);
951 DCHECK(!vsync_manager_.get());
952 vsync_manager_ = compositor_->vsync_manager();
953 vsync_manager_->AddObserver(this);
956 void DelegatedFrameHost::ResetCompositor() {
957 if (!compositor_)
958 return;
959 RunOnCommitCallbacks();
960 if (resize_lock_) {
961 resize_lock_.reset();
962 client_->DelegatedFrameHostResizeLockWasReleased();
964 if (compositor_->HasObserver(this))
965 compositor_->RemoveObserver(this);
966 if (vsync_manager_.get()) {
967 vsync_manager_->RemoveObserver(this);
968 vsync_manager_ = NULL;
970 compositor_ = nullptr;
973 void DelegatedFrameHost::LockResources() {
974 DCHECK(frame_provider_.get() || !surface_id_.is_null());
975 delegated_frame_evictor_->LockFrame();
978 void DelegatedFrameHost::RequestCopyOfOutput(
979 scoped_ptr<cc::CopyOutputRequest> request) {
980 if (!request_copy_of_output_callback_for_testing_.is_null())
981 request_copy_of_output_callback_for_testing_.Run(request.Pass());
982 else
983 client_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput(request.Pass());
986 void DelegatedFrameHost::UnlockResources() {
987 DCHECK(frame_provider_.get() || !surface_id_.is_null());
988 delegated_frame_evictor_->UnlockFrame();
991 ////////////////////////////////////////////////////////////////////////////////
992 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
994 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer,
995 ui::Layer* new_layer) {
996 // The new_layer is the one that will be used by our Window, so that's the one
997 // that should keep our frame. old_layer will be returned to the
998 // RecreateLayer caller, and should have a copy.
999 if (frame_provider_.get()) {
1000 new_layer->SetShowDelegatedContent(frame_provider_.get(),
1001 current_frame_size_in_dip_);
1003 if (!surface_id_.is_null()) {
1004 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
1005 cc::SurfaceManager* manager = factory->GetSurfaceManager();
1006 new_layer->SetShowSurface(
1007 surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)),
1008 base::Bind(&RequireCallback, base::Unretained(manager)),
1009 current_surface_size_, current_scale_factor_,
1010 current_frame_size_in_dip_);
1014 } // namespace content