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