Apply _RELATIVE relocations ahead of others.
[chromium-blink-merge.git] / content / browser / compositor / delegated_frame_host.cc
blobccf962abd6644b36eb1d6baa1428a75910b148c8
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/browser/gpu/compositor_util.h"
17 #include "content/common/gpu/client/gl_helper.h"
18 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
19 #include "content/public/common/content_switches.h"
20 #include "media/base/video_frame.h"
21 #include "media/base/video_util.h"
22 #include "skia/ext/image_operations.h"
23 #include "third_party/skia/include/core/SkCanvas.h"
24 #include "third_party/skia/include/core/SkPaint.h"
25 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
26 #include "ui/gfx/frame_time.h"
28 namespace content {
30 ////////////////////////////////////////////////////////////////////////////////
31 // DelegatedFrameHostClient
33 bool DelegatedFrameHostClient::ShouldCreateResizeLock() {
34 // On Windows and Linux, holding pointer moves will not help throttling
35 // resizes.
36 // TODO(piman): on Windows we need to block (nested message loop?) the
37 // WM_SIZE event. On Linux we need to throttle at the WM level using
38 // _NET_WM_SYNC_REQUEST.
39 // TODO(ccameron): Mac browser window resizing is incompletely implemented.
40 #if !defined(OS_CHROMEOS)
41 return false;
42 #else
43 return GetDelegatedFrameHost()->ShouldCreateResizeLock();
44 #endif
47 void DelegatedFrameHostClient::RequestCopyOfOutput(
48 scoped_ptr<cc::CopyOutputRequest> request) {
49 GetDelegatedFrameHost()->RequestCopyOfOutput(request.Pass());
52 ////////////////////////////////////////////////////////////////////////////////
53 // DelegatedFrameHost
55 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client)
56 : client_(client),
57 use_surfaces_(UseSurfacesEnabled()),
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 if (surface_factory_ && !surface_id_.is_null())
135 surface_factory_->RequestCopyOfSurface(surface_id_, request.Pass());
136 else
137 request->SendEmptyResult();
138 } else {
139 client_->GetLayer()->RequestCopyOfOutput(request.Pass());
143 void DelegatedFrameHost::CopyFromCompositingSurface(
144 const gfx::Rect& src_subrect,
145 const gfx::Size& output_size,
146 CopyFromCompositingSurfaceCallback& callback,
147 const SkColorType color_type) {
148 // Only ARGB888 and RGB565 supported as of now.
149 bool format_support = ((color_type == kAlpha_8_SkColorType) ||
150 (color_type == kRGB_565_SkColorType) ||
151 (color_type == kN32_SkColorType));
152 DCHECK(format_support);
153 if (!CanCopyToBitmap()) {
154 callback.Run(false, SkBitmap());
155 return;
158 scoped_ptr<cc::CopyOutputRequest> request =
159 cc::CopyOutputRequest::CreateRequest(base::Bind(
160 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
161 output_size,
162 color_type,
163 callback));
164 request->set_area(src_subrect);
165 client_->RequestCopyOfOutput(request.Pass());
168 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
169 const gfx::Rect& src_subrect,
170 const scoped_refptr<media::VideoFrame>& target,
171 const base::Callback<void(bool)>& callback) {
172 if (!CanCopyToVideoFrame()) {
173 callback.Run(false);
174 return;
177 // Try get a texture to reuse.
178 scoped_refptr<OwnedMailbox> subscriber_texture;
179 if (frame_subscriber_) {
180 if (!idle_frame_subscriber_textures_.empty()) {
181 subscriber_texture = idle_frame_subscriber_textures_.back();
182 idle_frame_subscriber_textures_.pop_back();
183 } else if (GLHelper* helper =
184 ImageTransportFactory::GetInstance()->GetGLHelper()) {
185 subscriber_texture = new OwnedMailbox(helper);
189 scoped_ptr<cc::CopyOutputRequest> request =
190 cc::CopyOutputRequest::CreateRequest(base::Bind(
191 &DelegatedFrameHost::
192 CopyFromCompositingSurfaceHasResultForVideo,
193 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
194 subscriber_texture,
195 target,
196 callback));
197 request->set_area(src_subrect);
198 if (subscriber_texture.get()) {
199 request->SetTextureMailbox(
200 cc::TextureMailbox(subscriber_texture->mailbox(),
201 subscriber_texture->target(),
202 subscriber_texture->sync_point()));
204 client_->RequestCopyOfOutput(request.Pass());
207 bool DelegatedFrameHost::CanCopyToBitmap() const {
208 return client_->GetCompositor() &&
209 client_->GetLayer()->has_external_content();
212 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
213 return client_->GetCompositor() &&
214 client_->GetLayer()->has_external_content();
217 bool DelegatedFrameHost::CanSubscribeFrame() const {
218 return true;
221 void DelegatedFrameHost::BeginFrameSubscription(
222 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
223 frame_subscriber_ = subscriber.Pass();
226 void DelegatedFrameHost::EndFrameSubscription() {
227 idle_frame_subscriber_textures_.clear();
228 frame_subscriber_.reset();
231 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const {
232 // Should skip a frame only when another frame from the renderer is guaranteed
233 // to replace it. Otherwise may cause hangs when the renderer is waiting for
234 // the completion of latency infos (such as when taking a Snapshot.)
235 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
236 can_lock_compositor_ == NO_PENDING_COMMIT ||
237 !resize_lock_.get())
238 return false;
240 return size_in_dip != resize_lock_->expected_size();
243 void DelegatedFrameHost::WasResized() {
244 if (client_->DesiredFrameSize() != current_frame_size_in_dip_ &&
245 client_->GetHost()->is_hidden())
246 EvictDelegatedFrame();
247 MaybeCreateResizeLock();
250 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
251 if (resize_lock_)
252 return resize_lock_->expected_size();
253 else
254 return client_->DesiredFrameSize();
257 void DelegatedFrameHost::CheckResizeLock() {
258 if (!resize_lock_ ||
259 resize_lock_->expected_size() != current_frame_size_in_dip_)
260 return;
262 // Since we got the size we were looking for, unlock the compositor. But delay
263 // the release of the lock until we've kicked a frame with the new texture, to
264 // avoid resizing the UI before we have a chance to draw a "good" frame.
265 resize_lock_->UnlockCompositor();
266 ui::Compositor* compositor = client_->GetCompositor();
267 if (compositor) {
268 if (!compositor->HasObserver(this))
269 compositor->AddObserver(this);
273 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
274 const gfx::Rect& damage_rect) {
275 if (!frame_subscriber() || !CanCopyToVideoFrame())
276 return;
278 const base::TimeTicks now = gfx::FrameTime::Now();
279 base::TimeTicks present_time;
280 if (vsync_timebase_.is_null() || vsync_interval_ <= base::TimeDelta()) {
281 present_time = now;
282 } else {
283 const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_;
284 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_;
287 scoped_refptr<media::VideoFrame> frame;
288 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
289 if (frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time,
290 &frame, &callback)) {
291 CopyFromCompositingSurfaceToVideoFrame(
292 gfx::Rect(current_frame_size_in_dip_),
293 frame,
294 base::Bind(callback, present_time));
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 RenderWidgetHostImpl* host = client_->GetHost();
304 DCHECK(!frame_data->render_pass_list.empty());
306 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
308 gfx::Size frame_size = root_pass->output_rect.size();
309 gfx::Size frame_size_in_dip =
310 ConvertSizeToDIP(frame_device_scale_factor, frame_size);
312 gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect);
313 damage_rect.Intersect(gfx::Rect(frame_size));
314 gfx::Rect damage_rect_in_dip =
315 ConvertRectToDIP(frame_device_scale_factor, damage_rect);
317 if (ShouldSkipFrame(frame_size_in_dip)) {
318 cc::CompositorFrameAck ack;
319 cc::TransferableResource::ReturnResources(frame_data->resource_list,
320 &ack.resources);
322 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(),
323 latency_info.begin(), latency_info.end());
325 RenderWidgetHostImpl::SendSwapCompositorFrameAck(
326 host->GetRoutingID(), output_surface_id,
327 host->GetProcess()->GetID(), ack);
328 skipped_frames_ = true;
329 return;
332 if (skipped_frames_) {
333 skipped_frames_ = false;
334 damage_rect = gfx::Rect(frame_size);
335 damage_rect_in_dip = gfx::Rect(frame_size_in_dip);
337 // Give the same damage rect to the compositor.
338 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
339 root_pass->damage_rect = damage_rect;
342 if (output_surface_id != last_output_surface_id_) {
343 // Resource ids are scoped by the output surface.
344 // If the originating output surface doesn't match the last one, it
345 // indicates the renderer's output surface may have been recreated, in which
346 // case we should recreate the DelegatedRendererLayer, to avoid matching
347 // resources from the old one with resources from the new one which would
348 // have the same id. Changing the layer to showing painted content destroys
349 // the DelegatedRendererLayer.
350 EvictDelegatedFrame();
352 surface_factory_.reset();
353 if (!surface_returned_resources_.empty())
354 SendReturnedDelegatedResources(last_output_surface_id_);
356 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
357 // any resources from the old output surface with the new output surface id.
358 if (resource_collection_.get()) {
359 resource_collection_->SetClient(NULL);
361 if (resource_collection_->LoseAllResources())
362 SendReturnedDelegatedResources(last_output_surface_id_);
364 resource_collection_ = NULL;
366 last_output_surface_id_ = output_surface_id;
368 ui::Compositor* compositor = client_->GetCompositor();
369 if (frame_size.IsEmpty()) {
370 DCHECK(frame_data->resource_list.empty());
371 EvictDelegatedFrame();
372 } else {
373 if (use_surfaces_) {
374 if (!surface_factory_) {
375 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
376 cc::SurfaceManager* manager = factory->GetSurfaceManager();
377 id_allocator_ =
378 factory->GetContextFactory()->CreateSurfaceIdAllocator();
379 surface_factory_ =
380 make_scoped_ptr(new cc::SurfaceFactory(manager, this));
382 if (surface_id_.is_null() || frame_size != current_surface_size_ ||
383 frame_size_in_dip != current_frame_size_in_dip_) {
384 if (!surface_id_.is_null()) {
385 if (compositor) {
386 std::set<cc::SurfaceSequence> seq;
387 seq.insert(compositor->InsertSurfaceSequenceForNextFrame());
388 // Destruction of this surface needs to wait for compositors that
389 // have drawn using it to swap frames that don't reference it.
390 // TODO(jbauman): Handle cases where the compositor has been
391 // changed since the last draw.
392 surface_factory_->DestroyOnSequence(surface_id_, seq);
393 } else {
394 surface_factory_->Destroy(surface_id_);
397 surface_id_ = id_allocator_->GenerateId();
398 surface_factory_->Create(surface_id_, frame_size);
399 client_->GetLayer()->SetShowSurface(surface_id_, frame_size_in_dip);
400 current_surface_size_ = frame_size;
402 scoped_ptr<cc::CompositorFrame> compositor_frame =
403 make_scoped_ptr(new cc::CompositorFrame());
404 compositor_frame->delegated_frame_data = frame_data.Pass();
406 compositor_frame->metadata.latency_info.swap(skipped_latency_info_list_);
407 compositor_frame->metadata.latency_info.insert(
408 compositor_frame->metadata.latency_info.end(),
409 latency_info.begin(),
410 latency_info.end());
412 base::Closure ack_callback;
413 if (compositor) {
414 ack_callback = base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
415 AsWeakPtr(),
416 output_surface_id);
418 surface_factory_->SubmitFrame(
419 surface_id_, compositor_frame.Pass(), ack_callback);
420 } else {
421 if (!resource_collection_.get()) {
422 resource_collection_ = new cc::DelegatedFrameResourceCollection;
423 resource_collection_->SetClient(this);
425 // If the physical frame size changes, we need a new |frame_provider_|. If
426 // the physical frame size is the same, but the size in DIP changed, we
427 // need to adjust the scale at which the frames will be drawn, and we do
428 // this by making a new |frame_provider_| also to ensure the scale change
429 // is presented in sync with the new frame content.
430 if (!frame_provider_.get() ||
431 frame_size != frame_provider_->frame_size() ||
432 frame_size_in_dip != current_frame_size_in_dip_) {
433 frame_provider_ = new cc::DelegatedFrameProvider(
434 resource_collection_.get(), frame_data.Pass());
435 client_->GetLayer()->SetShowDelegatedContent(frame_provider_.get(),
436 frame_size_in_dip);
437 } else {
438 frame_provider_->SetFrameData(frame_data.Pass());
442 released_front_lock_ = NULL;
443 current_frame_size_in_dip_ = frame_size_in_dip;
444 CheckResizeLock();
446 if (!damage_rect_in_dip.IsEmpty())
447 client_->GetLayer()->OnDelegatedFrameDamage(damage_rect_in_dip);
449 pending_delegated_ack_count_++;
451 if (!compositor) {
452 SendDelegatedFrameAck(output_surface_id);
453 } else if (!use_surfaces_) {
454 std::vector<ui::LatencyInfo>::const_iterator it;
455 for (it = latency_info.begin(); it != latency_info.end(); ++it)
456 compositor->SetLatencyInfo(*it);
457 // If we've previously skipped any latency infos add them.
458 for (it = skipped_latency_info_list_.begin();
459 it != skipped_latency_info_list_.end();
460 ++it)
461 compositor->SetLatencyInfo(*it);
462 skipped_latency_info_list_.clear();
463 AddOnCommitCallbackAndDisableLocks(
464 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
465 AsWeakPtr(),
466 output_surface_id));
467 } else {
468 AddOnCommitCallbackAndDisableLocks(base::Closure());
470 DidReceiveFrameFromRenderer(damage_rect);
471 if (frame_provider_.get() || !surface_id_.is_null())
472 delegated_frame_evictor_->SwappedFrame(!host->is_hidden());
473 // Note: the frame may have been evicted immediately.
476 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) {
477 RenderWidgetHostImpl* host = client_->GetHost();
478 cc::CompositorFrameAck ack;
479 if (!surface_returned_resources_.empty())
480 ack.resources.swap(surface_returned_resources_);
481 if (resource_collection_.get())
482 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
483 RenderWidgetHostImpl::SendSwapCompositorFrameAck(host->GetRoutingID(),
484 output_surface_id,
485 host->GetProcess()->GetID(),
486 ack);
487 DCHECK_GT(pending_delegated_ack_count_, 0);
488 pending_delegated_ack_count_--;
491 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
492 if (pending_delegated_ack_count_)
493 return;
495 SendReturnedDelegatedResources(last_output_surface_id_);
498 void DelegatedFrameHost::SendReturnedDelegatedResources(
499 uint32 output_surface_id) {
500 RenderWidgetHostImpl* host = client_->GetHost();
502 cc::CompositorFrameAck ack;
503 if (!surface_returned_resources_.empty()) {
504 ack.resources.swap(surface_returned_resources_);
505 } else {
506 DCHECK(resource_collection_.get());
507 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
509 DCHECK(!ack.resources.empty());
511 RenderWidgetHostImpl::SendReclaimCompositorResources(
512 host->GetRoutingID(),
513 output_surface_id,
514 host->GetProcess()->GetID(),
515 ack);
518 void DelegatedFrameHost::ReturnResources(
519 const cc::ReturnedResourceArray& resources) {
520 if (resources.empty())
521 return;
522 std::copy(resources.begin(),
523 resources.end(),
524 std::back_inserter(surface_returned_resources_));
525 if (!pending_delegated_ack_count_)
526 SendReturnedDelegatedResources(last_output_surface_id_);
529 void DelegatedFrameHost::EvictDelegatedFrame() {
530 client_->GetLayer()->SetShowSolidColorContent();
531 frame_provider_ = NULL;
532 if (!surface_id_.is_null()) {
533 surface_factory_->Destroy(surface_id_);
534 surface_id_ = cc::SurfaceId();
536 delegated_frame_evictor_->DiscardedFrame();
539 // static
540 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
541 const gfx::Size& dst_size_in_pixel,
542 const SkColorType color_type,
543 const base::Callback<void(bool, const SkBitmap&)>& callback,
544 scoped_ptr<cc::CopyOutputResult> result) {
545 if (result->IsEmpty() || result->size().IsEmpty()) {
546 callback.Run(false, SkBitmap());
547 return;
550 if (result->HasTexture()) {
551 // GPU-accelerated path
552 PrepareTextureCopyOutputResult(dst_size_in_pixel, color_type,
553 callback,
554 result.Pass());
555 return;
558 DCHECK(result->HasBitmap());
559 // Software path
560 PrepareBitmapCopyOutputResult(dst_size_in_pixel, color_type, callback,
561 result.Pass());
564 static void CopyFromCompositingSurfaceFinished(
565 const base::Callback<void(bool, const SkBitmap&)>& callback,
566 scoped_ptr<cc::SingleReleaseCallback> release_callback,
567 scoped_ptr<SkBitmap> bitmap,
568 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
569 bool result) {
570 bitmap_pixels_lock.reset();
572 uint32 sync_point = 0;
573 if (result) {
574 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
575 sync_point = gl_helper->InsertSyncPoint();
577 bool lost_resource = sync_point == 0;
578 release_callback->Run(sync_point, lost_resource);
580 callback.Run(result, *bitmap);
583 // static
584 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
585 const gfx::Size& dst_size_in_pixel,
586 const SkColorType color_type,
587 const base::Callback<void(bool, const SkBitmap&)>& callback,
588 scoped_ptr<cc::CopyOutputResult> result) {
589 DCHECK(result->HasTexture());
590 base::ScopedClosureRunner scoped_callback_runner(
591 base::Bind(callback, false, SkBitmap()));
593 // TODO(sikugu): We should be able to validate the format here using
594 // GLHelper::IsReadbackConfigSupported before we processs the result.
595 // See crbug.com/415682.
596 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
597 if (!bitmap->tryAllocPixels(SkImageInfo::Make(dst_size_in_pixel.width(),
598 dst_size_in_pixel.height(),
599 color_type,
600 kOpaque_SkAlphaType)))
601 return;
603 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
604 GLHelper* gl_helper = factory->GetGLHelper();
605 if (!gl_helper)
606 return;
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(),
622 result->size(),
623 gfx::Rect(result->size()),
624 dst_size_in_pixel,
625 pixels,
626 color_type,
627 base::Bind(&CopyFromCompositingSurfaceFinished,
628 callback,
629 base::Passed(&release_callback),
630 base::Passed(&bitmap),
631 base::Passed(&bitmap_pixels_lock)),
632 GLHelper::SCALER_QUALITY_FAST);
635 // static
636 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
637 const gfx::Size& dst_size_in_pixel,
638 const SkColorType color_type,
639 const base::Callback<void(bool, const SkBitmap&)>& callback,
640 scoped_ptr<cc::CopyOutputResult> result) {
641 if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) {
642 NOTIMPLEMENTED();
643 callback.Run(false, SkBitmap());
644 return;
646 DCHECK(result->HasBitmap());
647 scoped_ptr<SkBitmap> source = result->TakeBitmap();
648 DCHECK(source);
649 SkBitmap scaled_bitmap;
650 if (source->width() != dst_size_in_pixel.width() ||
651 source->height() != dst_size_in_pixel.height()) {
652 scaled_bitmap =
653 skia::ImageOperations::Resize(*source,
654 skia::ImageOperations::RESIZE_BEST,
655 dst_size_in_pixel.width(),
656 dst_size_in_pixel.height());
657 } else {
658 scaled_bitmap = *source;
660 if (color_type == kN32_SkColorType) {
661 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
662 callback.Run(true, scaled_bitmap);
663 return;
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()));
673 if (!success) {
674 callback.Run(false, SkBitmap());
675 return;
677 SkCanvas canvas(grayscale_bitmap);
678 SkPaint paint;
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(true, grayscale_bitmap);
686 // static
687 void DelegatedFrameHost::ReturnSubscriberTexture(
688 base::WeakPtr<DelegatedFrameHost> dfh,
689 scoped_refptr<OwnedMailbox> subscriber_texture,
690 uint32 sync_point) {
691 if (!subscriber_texture.get())
692 return;
693 if (!dfh)
694 return;
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);
702 // static
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,
708 bool result) {
709 callback.Run(result);
711 uint32 sync_point = 0;
712 if (result) {
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);
726 // static
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));
737 if (!dfh)
738 return;
739 if (result->IsEmpty())
740 return;
741 if (result->size().IsEmpty())
742 return;
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 coded_size() and the result's size() are both physical
749 // pixels.
750 gfx::Rect region_in_frame =
751 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()),
752 result->size());
753 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
754 region_in_frame.y() & ~1,
755 region_in_frame.width() & ~1,
756 region_in_frame.height() & ~1);
757 if (region_in_frame.IsEmpty())
758 return;
760 if (!result->HasTexture()) {
761 DCHECK(result->HasBitmap());
762 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap();
763 // Scale the bitmap to the required size, if necessary.
764 SkBitmap scaled_bitmap;
765 if (result->size().width() != region_in_frame.width() ||
766 result->size().height() != region_in_frame.height()) {
767 skia::ImageOperations::ResizeMethod method =
768 skia::ImageOperations::RESIZE_GOOD;
769 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method,
770 region_in_frame.width(),
771 region_in_frame.height());
772 } else {
773 scaled_bitmap = *bitmap.get();
777 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
779 media::CopyRGBToVideoFrame(
780 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
781 scaled_bitmap.rowBytes(),
782 region_in_frame,
783 video_frame.get());
785 ignore_result(scoped_callback_runner.Release());
786 callback.Run(true);
787 return;
790 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
791 GLHelper* gl_helper = factory->GetGLHelper();
792 if (!gl_helper)
793 return;
794 if (subscriber_texture.get() && !subscriber_texture->texture_id())
795 return;
797 cc::TextureMailbox texture_mailbox;
798 scoped_ptr<cc::SingleReleaseCallback> release_callback;
799 result->TakeTexture(&texture_mailbox, &release_callback);
800 DCHECK(texture_mailbox.IsTexture());
802 gfx::Rect result_rect(result->size());
804 content::ReadbackYUVInterface* yuv_readback_pipeline =
805 dfh->yuv_readback_pipeline_.get();
806 if (yuv_readback_pipeline == NULL ||
807 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() ||
808 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect ||
809 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) {
810 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST;
811 std::string quality_switch = switches::kTabCaptureDownscaleQuality;
812 // If we're scaling up, we can use the "best" quality.
813 if (result_rect.size().width() < region_in_frame.size().width() &&
814 result_rect.size().height() < region_in_frame.size().height())
815 quality_switch = switches::kTabCaptureUpscaleQuality;
817 std::string switch_value =
818 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
819 quality_switch);
820 if (switch_value == "fast")
821 quality = GLHelper::SCALER_QUALITY_FAST;
822 else if (switch_value == "good")
823 quality = GLHelper::SCALER_QUALITY_GOOD;
824 else if (switch_value == "best")
825 quality = GLHelper::SCALER_QUALITY_BEST;
827 dfh->yuv_readback_pipeline_.reset(
828 gl_helper->CreateReadbackPipelineYUV(quality,
829 result_rect.size(),
830 result_rect,
831 video_frame->coded_size(),
832 region_in_frame,
833 true,
834 true));
835 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get();
838 ignore_result(scoped_callback_runner.Release());
839 ignore_result(scoped_return_subscriber_texture.Release());
840 base::Callback<void(bool result)> finished_callback = base::Bind(
841 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo,
842 dfh->AsWeakPtr(),
843 callback,
844 subscriber_texture,
845 base::Passed(&release_callback));
846 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
847 texture_mailbox.sync_point(),
848 video_frame.get(),
849 finished_callback);
852 ////////////////////////////////////////////////////////////////////////////////
853 // DelegatedFrameHost, ui::CompositorObserver implementation:
855 void DelegatedFrameHost::OnCompositingDidCommit(
856 ui::Compositor* compositor) {
857 RenderWidgetHostImpl* host = client_->GetHost();
858 if (can_lock_compositor_ == NO_PENDING_COMMIT) {
859 can_lock_compositor_ = YES_CAN_LOCK;
860 if (resize_lock_.get() && resize_lock_->GrabDeferredLock())
861 can_lock_compositor_ = YES_DID_LOCK;
863 RunOnCommitCallbacks();
864 if (resize_lock_ &&
865 resize_lock_->expected_size() == current_frame_size_in_dip_) {
866 resize_lock_.reset();
867 host->WasResized();
868 // We may have had a resize while we had the lock (e.g. if the lock expired,
869 // or if the UI still gave us some resizes), so make sure we grab a new lock
870 // if necessary.
871 MaybeCreateResizeLock();
875 void DelegatedFrameHost::OnCompositingStarted(
876 ui::Compositor* compositor, base::TimeTicks start_time) {
877 last_draw_ended_ = start_time;
880 void DelegatedFrameHost::OnCompositingEnded(
881 ui::Compositor* compositor) {
884 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
887 void DelegatedFrameHost::OnCompositingLockStateChanged(
888 ui::Compositor* compositor) {
889 // A compositor lock that is part of a resize lock timed out. We
890 // should display a renderer frame.
891 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) {
892 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME;
896 void DelegatedFrameHost::OnUpdateVSyncParameters(
897 base::TimeTicks timebase,
898 base::TimeDelta interval) {
899 vsync_timebase_ = timebase;
900 vsync_interval_ = interval;
901 RenderWidgetHostImpl* host = client_->GetHost();
902 if (client_->IsVisible())
903 host->UpdateVSyncParameters(timebase, interval);
906 ////////////////////////////////////////////////////////////////////////////////
907 // RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation:
909 void DelegatedFrameHost::OnLostResources() {
910 RenderWidgetHostImpl* host = client_->GetHost();
911 if (frame_provider_.get() || !surface_id_.is_null())
912 EvictDelegatedFrame();
913 idle_frame_subscriber_textures_.clear();
914 yuv_readback_pipeline_.reset();
916 host->ScheduleComposite();
919 ////////////////////////////////////////////////////////////////////////////////
920 // DelegatedFrameHost, private:
922 DelegatedFrameHost::~DelegatedFrameHost() {
923 ImageTransportFactory::GetInstance()->RemoveObserver(this);
925 if (!surface_id_.is_null())
926 surface_factory_->Destroy(surface_id_);
927 if (resource_collection_.get())
928 resource_collection_->SetClient(NULL);
930 DCHECK(!vsync_manager_.get());
933 void DelegatedFrameHost::RunOnCommitCallbacks() {
934 for (std::vector<base::Closure>::const_iterator
935 it = on_compositing_did_commit_callbacks_.begin();
936 it != on_compositing_did_commit_callbacks_.end(); ++it) {
937 it->Run();
939 on_compositing_did_commit_callbacks_.clear();
942 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
943 const base::Closure& callback) {
944 ui::Compositor* compositor = client_->GetCompositor();
945 DCHECK(compositor);
947 if (!compositor->HasObserver(this))
948 compositor->AddObserver(this);
950 can_lock_compositor_ = NO_PENDING_COMMIT;
951 if (!callback.is_null())
952 on_compositing_did_commit_callbacks_.push_back(callback);
955 void DelegatedFrameHost::AddedToWindow() {
956 ui::Compositor* compositor = client_->GetCompositor();
957 if (compositor) {
958 DCHECK(!vsync_manager_.get());
959 vsync_manager_ = compositor->vsync_manager();
960 vsync_manager_->AddObserver(this);
964 void DelegatedFrameHost::RemovingFromWindow() {
965 RunOnCommitCallbacks();
966 resize_lock_.reset();
967 client_->GetHost()->WasResized();
968 ui::Compositor* compositor = client_->GetCompositor();
969 if (compositor && compositor->HasObserver(this))
970 compositor->RemoveObserver(this);
972 if (vsync_manager_.get()) {
973 vsync_manager_->RemoveObserver(this);
974 vsync_manager_ = NULL;
978 void DelegatedFrameHost::LockResources() {
979 DCHECK(frame_provider_.get() || !surface_id_.is_null());
980 delegated_frame_evictor_->LockFrame();
983 void DelegatedFrameHost::UnlockResources() {
984 DCHECK(frame_provider_.get() || !surface_id_.is_null());
985 delegated_frame_evictor_->UnlockFrame();
988 ////////////////////////////////////////////////////////////////////////////////
989 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
991 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer,
992 ui::Layer* new_layer) {
993 // The new_layer is the one that will be used by our Window, so that's the one
994 // that should keep our frame. old_layer will be returned to the
995 // RecreateLayer caller, and should have a copy.
996 if (frame_provider_.get()) {
997 new_layer->SetShowDelegatedContent(frame_provider_.get(),
998 current_frame_size_in_dip_);
1000 if (!surface_id_.is_null()) {
1001 new_layer->SetShowSurface(surface_id_, current_frame_size_in_dip_);
1005 } // namespace content