[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / browser / compositor / delegated_frame_host.cc
blob7f614c6fde3e7449159327c06325e89ebb38cd62
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/compositor/delegated_frame_host.h"
7 #include "base/callback_helpers.h"
8 #include "base/command_line.h"
9 #include "base/time/default_tick_clock.h"
10 #include "cc/output/compositor_frame.h"
11 #include "cc/output/compositor_frame_ack.h"
12 #include "cc/output/copy_output_request.h"
13 #include "cc/resources/single_release_callback.h"
14 #include "cc/resources/texture_mailbox.h"
15 #include "cc/surfaces/surface.h"
16 #include "cc/surfaces/surface_factory.h"
17 #include "cc/surfaces/surface_hittest.h"
18 #include "cc/surfaces/surface_manager.h"
19 #include "content/browser/compositor/resize_lock.h"
20 #include "content/browser/compositor/surface_utils.h"
21 #include "content/browser/gpu/compositor_util.h"
22 #include "content/common/gpu/client/gl_helper.h"
23 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
24 #include "content/public/common/content_switches.h"
25 #include "media/base/video_frame.h"
26 #include "media/base/video_util.h"
27 #include "skia/ext/image_operations.h"
28 #include "third_party/skia/include/core/SkCanvas.h"
29 #include "third_party/skia/include/core/SkPaint.h"
30 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
31 #include "ui/gfx/geometry/dip_util.h"
33 namespace content {
35 namespace {
37 void SatisfyCallback(cc::SurfaceManager* manager,
38 cc::SurfaceSequence sequence) {
39 std::vector<uint32_t> sequences;
40 sequences.push_back(sequence.sequence);
41 manager->DidSatisfySequences(sequence.id_namespace, &sequences);
44 void RequireCallback(cc::SurfaceManager* manager,
45 cc::SurfaceId id,
46 cc::SurfaceSequence sequence) {
47 cc::Surface* surface = manager->GetSurfaceForId(id);
48 if (!surface) {
49 LOG(ERROR) << "Attempting to require callback on nonexistent surface";
50 return;
52 surface->AddDestructionDependency(sequence);
55 } // namespace
57 ////////////////////////////////////////////////////////////////////////////////
58 // DelegatedFrameHost
60 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client)
61 : client_(client),
62 compositor_(nullptr),
63 use_surfaces_(UseSurfacesEnabled()),
64 tick_clock_(new base::DefaultTickClock()),
65 last_output_surface_id_(0),
66 pending_delegated_ack_count_(0),
67 skipped_frames_(false),
68 current_scale_factor_(1.f),
69 can_lock_compositor_(YES_CAN_LOCK),
70 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) {
71 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
72 factory->AddObserver(this);
73 id_allocator_ = factory->GetContextFactory()->CreateSurfaceIdAllocator();
76 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) {
77 delegated_frame_evictor_->SetVisible(true);
79 if (surface_id_.is_null() && !frame_provider_.get() &&
80 !released_front_lock_.get()) {
81 if (compositor_)
82 released_front_lock_ = compositor_->GetCompositorLock();
85 if (compositor_) {
86 compositor_->SetLatencyInfo(latency_info);
90 bool DelegatedFrameHost::HasSavedFrame() {
91 return delegated_frame_evictor_->HasFrame();
94 void DelegatedFrameHost::WasHidden() {
95 delegated_frame_evictor_->SetVisible(false);
96 released_front_lock_ = NULL;
99 void DelegatedFrameHost::MaybeCreateResizeLock() {
100 if (!ShouldCreateResizeLock())
101 return;
102 DCHECK(compositor_);
104 bool defer_compositor_lock =
105 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
106 can_lock_compositor_ == NO_PENDING_COMMIT;
108 if (can_lock_compositor_ == YES_CAN_LOCK)
109 can_lock_compositor_ = YES_DID_LOCK;
111 resize_lock_ =
112 client_->DelegatedFrameHostCreateResizeLock(defer_compositor_lock);
115 bool DelegatedFrameHost::ShouldCreateResizeLock() {
116 if (!client_->DelegatedFrameCanCreateResizeLock())
117 return false;
119 if (resize_lock_)
120 return false;
122 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
123 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty())
124 return false;
126 if (!compositor_)
127 return false;
129 return true;
132 void DelegatedFrameHost::CopyFromCompositingSurface(
133 const gfx::Rect& src_subrect,
134 const gfx::Size& output_size,
135 ReadbackRequestCallback& callback,
136 const SkColorType preferred_color_type) {
137 // Only ARGB888 and RGB565 supported as of now.
138 bool format_support = ((preferred_color_type == kAlpha_8_SkColorType) ||
139 (preferred_color_type == kRGB_565_SkColorType) ||
140 (preferred_color_type == kN32_SkColorType));
141 DCHECK(format_support);
142 if (!CanCopyToBitmap()) {
143 callback.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE);
144 return;
147 scoped_ptr<cc::CopyOutputRequest> request =
148 cc::CopyOutputRequest::CreateRequest(
149 base::Bind(&DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
150 output_size, preferred_color_type, callback));
151 if (!src_subrect.IsEmpty())
152 request->set_area(src_subrect);
153 RequestCopyOfOutput(request.Pass());
156 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
157 const gfx::Rect& src_subrect,
158 const scoped_refptr<media::VideoFrame>& target,
159 const base::Callback<void(bool)>& callback) {
160 if (!CanCopyToVideoFrame()) {
161 callback.Run(false);
162 return;
165 scoped_ptr<cc::CopyOutputRequest> request =
166 cc::CopyOutputRequest::CreateRequest(base::Bind(
167 &DelegatedFrameHost::
168 CopyFromCompositingSurfaceHasResultForVideo,
169 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
170 nullptr,
171 target,
172 callback));
173 request->set_area(src_subrect);
174 RequestCopyOfOutput(request.Pass());
177 bool DelegatedFrameHost::CanCopyToBitmap() const {
178 return compositor_ &&
179 client_->DelegatedFrameHostGetLayer()->has_external_content();
182 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
183 return compositor_ &&
184 client_->DelegatedFrameHostGetLayer()->has_external_content();
187 bool DelegatedFrameHost::CanSubscribeFrame() const {
188 return true;
191 void DelegatedFrameHost::BeginFrameSubscription(
192 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
193 frame_subscriber_ = subscriber.Pass();
196 void DelegatedFrameHost::EndFrameSubscription() {
197 idle_frame_subscriber_textures_.clear();
198 frame_subscriber_.reset();
201 uint32_t DelegatedFrameHost::GetSurfaceIdNamespace() {
202 if (!use_surfaces_)
203 return 0;
205 return id_allocator_->id_namespace();
208 cc::SurfaceId DelegatedFrameHost::SurfaceIdAtPoint(
209 const gfx::Point& point,
210 gfx::Point* transformed_point) {
211 if (surface_id_.is_null())
212 return surface_id_;
213 cc::SurfaceHittest hittest(GetSurfaceManager());
214 return hittest.GetTargetSurfaceAtPoint(surface_id_, point, transformed_point);
217 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const {
218 // Should skip a frame only when another frame from the renderer is guaranteed
219 // to replace it. Otherwise may cause hangs when the renderer is waiting for
220 // the completion of latency infos (such as when taking a Snapshot.)
221 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
222 can_lock_compositor_ == NO_PENDING_COMMIT ||
223 !resize_lock_.get())
224 return false;
226 return size_in_dip != resize_lock_->expected_size();
229 void DelegatedFrameHost::WasResized() {
230 if (client_->DelegatedFrameHostDesiredSizeInDIP() !=
231 current_frame_size_in_dip_ &&
232 !client_->DelegatedFrameHostIsVisible())
233 EvictDelegatedFrame();
234 MaybeCreateResizeLock();
237 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
238 if (resize_lock_)
239 return resize_lock_->expected_size();
240 else
241 return client_->DelegatedFrameHostDesiredSizeInDIP();
244 void DelegatedFrameHost::CheckResizeLock() {
245 if (!resize_lock_ ||
246 resize_lock_->expected_size() != current_frame_size_in_dip_)
247 return;
249 // Since we got the size we were looking for, unlock the compositor. But delay
250 // the release of the lock until we've kicked a frame with the new texture, to
251 // avoid resizing the UI before we have a chance to draw a "good" frame.
252 resize_lock_->UnlockCompositor();
255 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
256 const gfx::Rect& damage_rect) {
257 if (!frame_subscriber() || !CanCopyToVideoFrame())
258 return;
260 const base::TimeTicks now = tick_clock_->NowTicks();
261 base::TimeTicks present_time;
262 if (vsync_interval_ <= base::TimeDelta()) {
263 present_time = now;
264 } else {
265 const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_;
266 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_;
269 scoped_refptr<media::VideoFrame> frame;
270 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
271 if (!frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time,
272 &frame, &callback))
273 return;
275 // Get a texture to re-use; else, create a new one.
276 scoped_refptr<OwnedMailbox> subscriber_texture;
277 if (!idle_frame_subscriber_textures_.empty()) {
278 subscriber_texture = idle_frame_subscriber_textures_.back();
279 idle_frame_subscriber_textures_.pop_back();
280 } else if (GLHelper* helper =
281 ImageTransportFactory::GetInstance()->GetGLHelper()) {
282 subscriber_texture = new OwnedMailbox(helper);
285 scoped_ptr<cc::CopyOutputRequest> request =
286 cc::CopyOutputRequest::CreateRequest(base::Bind(
287 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo,
288 AsWeakPtr(),
289 subscriber_texture,
290 frame,
291 base::Bind(callback, present_time)));
292 // Setting the source in this copy request asks that the layer abort any prior
293 // uncommitted copy requests made on behalf of the same frame subscriber.
294 // This will not affect any of the copy requests spawned elsewhere from
295 // DelegatedFrameHost (e.g., a call to CopyFromCompositingSurface() for
296 // screenshots) since those copy requests do not specify |frame_subscriber()|
297 // as a source.
298 request->set_source(frame_subscriber());
299 request->set_area(gfx::Rect(current_frame_size_in_dip_));
300 if (subscriber_texture.get()) {
301 request->SetTextureMailbox(
302 cc::TextureMailbox(subscriber_texture->mailbox(),
303 subscriber_texture->target(),
304 subscriber_texture->sync_point()));
306 RequestCopyOfOutput(request.Pass());
309 void DelegatedFrameHost::SwapDelegatedFrame(
310 uint32 output_surface_id,
311 scoped_ptr<cc::DelegatedFrameData> frame_data,
312 float frame_device_scale_factor,
313 const std::vector<ui::LatencyInfo>& latency_info,
314 std::vector<uint32_t>* satisfies_sequences) {
315 DCHECK(!frame_data->render_pass_list.empty());
317 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
319 gfx::Size frame_size = root_pass->output_rect.size();
320 gfx::Size frame_size_in_dip =
321 gfx::ConvertSizeToDIP(frame_device_scale_factor, frame_size);
323 gfx::Rect damage_rect = root_pass->damage_rect;
324 damage_rect.Intersect(gfx::Rect(frame_size));
325 gfx::Rect damage_rect_in_dip =
326 gfx::ConvertRectToDIP(frame_device_scale_factor, damage_rect);
328 if (ShouldSkipFrame(frame_size_in_dip)) {
329 cc::CompositorFrameAck ack;
330 cc::TransferableResource::ReturnResources(frame_data->resource_list,
331 &ack.resources);
333 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(),
334 latency_info.begin(), latency_info.end());
336 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack);
337 skipped_frames_ = true;
338 return;
341 if (skipped_frames_) {
342 skipped_frames_ = false;
343 damage_rect = gfx::Rect(frame_size);
344 damage_rect_in_dip = gfx::Rect(frame_size_in_dip);
346 // Give the same damage rect to the compositor.
347 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
348 root_pass->damage_rect = damage_rect;
351 if (output_surface_id != last_output_surface_id_) {
352 // Resource ids are scoped by the output surface.
353 // If the originating output surface doesn't match the last one, it
354 // indicates the renderer's output surface may have been recreated, in which
355 // case we should recreate the DelegatedRendererLayer, to avoid matching
356 // resources from the old one with resources from the new one which would
357 // have the same id. Changing the layer to showing painted content destroys
358 // the DelegatedRendererLayer.
359 EvictDelegatedFrame();
361 surface_factory_.reset();
362 if (!surface_returned_resources_.empty())
363 SendReturnedDelegatedResources(last_output_surface_id_);
365 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
366 // any resources from the old output surface with the new output surface id.
367 if (resource_collection_.get()) {
368 resource_collection_->SetClient(NULL);
370 if (resource_collection_->LoseAllResources())
371 SendReturnedDelegatedResources(last_output_surface_id_);
373 resource_collection_ = NULL;
375 last_output_surface_id_ = output_surface_id;
377 bool immediate_ack = !compositor_;
378 pending_delegated_ack_count_++;
380 if (frame_size.IsEmpty()) {
381 DCHECK(frame_data->resource_list.empty());
382 EvictDelegatedFrame();
383 } else {
384 if (use_surfaces_) {
385 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
386 cc::SurfaceManager* manager = factory->GetSurfaceManager();
387 if (!surface_factory_) {
388 surface_factory_ =
389 make_scoped_ptr(new cc::SurfaceFactory(manager, this));
391 if (surface_id_.is_null() || frame_size != current_surface_size_ ||
392 frame_size_in_dip != current_frame_size_in_dip_) {
393 if (!surface_id_.is_null())
394 surface_factory_->Destroy(surface_id_);
395 surface_id_ = id_allocator_->GenerateId();
396 surface_factory_->Create(surface_id_);
397 // manager must outlive compositors using it.
398 client_->DelegatedFrameHostGetLayer()->SetShowSurface(
399 surface_id_,
400 base::Bind(&SatisfyCallback, base::Unretained(manager)),
401 base::Bind(&RequireCallback, base::Unretained(manager)), frame_size,
402 frame_device_scale_factor, frame_size_in_dip);
403 current_surface_size_ = frame_size;
404 current_scale_factor_ = frame_device_scale_factor;
406 scoped_ptr<cc::CompositorFrame> compositor_frame =
407 make_scoped_ptr(new cc::CompositorFrame());
408 compositor_frame->delegated_frame_data = frame_data.Pass();
410 compositor_frame->metadata.latency_info.swap(skipped_latency_info_list_);
411 compositor_frame->metadata.latency_info.insert(
412 compositor_frame->metadata.latency_info.end(),
413 latency_info.begin(),
414 latency_info.end());
415 compositor_frame->metadata.satisfies_sequences.swap(*satisfies_sequences);
417 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
418 if (desired_size != frame_size_in_dip && !desired_size.IsEmpty())
419 immediate_ack = true;
421 cc::SurfaceFactory::DrawCallback ack_callback;
422 if (compositor_ && !immediate_ack) {
423 ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn,
424 AsWeakPtr(), output_surface_id);
426 surface_factory_->SubmitCompositorFrame(
427 surface_id_, compositor_frame.Pass(), ack_callback);
428 } else {
429 if (!resource_collection_.get()) {
430 resource_collection_ = new cc::DelegatedFrameResourceCollection;
431 resource_collection_->SetClient(this);
433 // If the physical frame size changes, we need a new |frame_provider_|. If
434 // the physical frame size is the same, but the size in DIP changed, we
435 // need to adjust the scale at which the frames will be drawn, and we do
436 // this by making a new |frame_provider_| also to ensure the scale change
437 // is presented in sync with the new frame content.
438 if (!frame_provider_.get() ||
439 frame_size != frame_provider_->frame_size() ||
440 frame_size_in_dip != current_frame_size_in_dip_) {
441 frame_provider_ = new cc::DelegatedFrameProvider(
442 resource_collection_.get(), frame_data.Pass());
443 client_->DelegatedFrameHostGetLayer()->SetShowDelegatedContent(
444 frame_provider_.get(), frame_size_in_dip);
445 } else {
446 frame_provider_->SetFrameData(frame_data.Pass());
450 released_front_lock_ = NULL;
451 current_frame_size_in_dip_ = frame_size_in_dip;
452 CheckResizeLock();
454 if (!damage_rect_in_dip.IsEmpty())
455 client_->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage(
456 damage_rect_in_dip);
458 if (immediate_ack) {
459 SendDelegatedFrameAck(output_surface_id);
460 } else if (!use_surfaces_) {
461 std::vector<ui::LatencyInfo>::const_iterator it;
462 for (it = latency_info.begin(); it != latency_info.end(); ++it)
463 compositor_->SetLatencyInfo(*it);
464 // If we've previously skipped any latency infos add them.
465 for (it = skipped_latency_info_list_.begin();
466 it != skipped_latency_info_list_.end();
467 ++it)
468 compositor_->SetLatencyInfo(*it);
469 skipped_latency_info_list_.clear();
470 AddOnCommitCallbackAndDisableLocks(
471 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
472 AsWeakPtr(), output_surface_id));
473 } else {
474 AddOnCommitCallbackAndDisableLocks(base::Closure());
476 DidReceiveFrameFromRenderer(damage_rect);
477 if (frame_provider_.get() || !surface_id_.is_null())
478 delegated_frame_evictor_->SwappedFrame(
479 client_->DelegatedFrameHostIsVisible());
480 // Note: the frame may have been evicted immediately.
483 void DelegatedFrameHost::ClearDelegatedFrame() {
484 if (frame_provider_.get() || !surface_id_.is_null())
485 EvictDelegatedFrame();
488 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) {
489 cc::CompositorFrameAck ack;
490 if (!surface_returned_resources_.empty())
491 ack.resources.swap(surface_returned_resources_);
492 if (resource_collection_.get())
493 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
494 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack);
495 DCHECK_GT(pending_delegated_ack_count_, 0);
496 pending_delegated_ack_count_--;
499 void DelegatedFrameHost::SurfaceDrawn(uint32 output_surface_id,
500 cc::SurfaceDrawStatus drawn) {
501 SendDelegatedFrameAck(output_surface_id);
504 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
505 if (pending_delegated_ack_count_)
506 return;
508 SendReturnedDelegatedResources(last_output_surface_id_);
511 void DelegatedFrameHost::SendReturnedDelegatedResources(
512 uint32 output_surface_id) {
513 cc::CompositorFrameAck ack;
514 if (!surface_returned_resources_.empty()) {
515 ack.resources.swap(surface_returned_resources_);
516 } else {
517 DCHECK(resource_collection_.get());
518 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
520 DCHECK(!ack.resources.empty());
522 client_->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id,
523 ack);
526 void DelegatedFrameHost::ReturnResources(
527 const cc::ReturnedResourceArray& resources) {
528 if (resources.empty())
529 return;
530 std::copy(resources.begin(),
531 resources.end(),
532 std::back_inserter(surface_returned_resources_));
533 if (!pending_delegated_ack_count_)
534 SendReturnedDelegatedResources(last_output_surface_id_);
537 void DelegatedFrameHost::EvictDelegatedFrame() {
538 client_->DelegatedFrameHostGetLayer()->SetShowSolidColorContent();
539 frame_provider_ = NULL;
540 if (!surface_id_.is_null()) {
541 surface_factory_->Destroy(surface_id_);
542 surface_id_ = cc::SurfaceId();
544 delegated_frame_evictor_->DiscardedFrame();
547 // static
548 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
549 const gfx::Size& dst_size_in_pixel,
550 const SkColorType color_type,
551 ReadbackRequestCallback& callback,
552 scoped_ptr<cc::CopyOutputResult> result) {
553 if (result->IsEmpty() || result->size().IsEmpty()) {
554 callback.Run(SkBitmap(), content::READBACK_FAILED);
555 return;
558 gfx::Size output_size_in_pixel;
559 if (dst_size_in_pixel.IsEmpty())
560 output_size_in_pixel = result->size();
561 else
562 output_size_in_pixel = dst_size_in_pixel;
564 if (result->HasTexture()) {
565 // GPU-accelerated path
566 PrepareTextureCopyOutputResult(output_size_in_pixel, color_type,
567 callback,
568 result.Pass());
569 return;
572 DCHECK(result->HasBitmap());
573 // Software path
574 PrepareBitmapCopyOutputResult(output_size_in_pixel, color_type, callback,
575 result.Pass());
578 static void CopyFromCompositingSurfaceFinished(
579 ReadbackRequestCallback& callback,
580 scoped_ptr<cc::SingleReleaseCallback> release_callback,
581 scoped_ptr<SkBitmap> bitmap,
582 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
583 bool result) {
584 bitmap_pixels_lock.reset();
586 uint32 sync_point = 0;
587 if (result) {
588 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
589 if (gl_helper)
590 sync_point = gl_helper->InsertSyncPoint();
592 bool lost_resource = sync_point == 0;
593 release_callback->Run(sync_point, lost_resource);
595 callback.Run(*bitmap,
596 result ? content::READBACK_SUCCESS : content::READBACK_FAILED);
599 // static
600 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
601 const gfx::Size& dst_size_in_pixel,
602 const SkColorType color_type,
603 ReadbackRequestCallback& callback,
604 scoped_ptr<cc::CopyOutputResult> result) {
605 DCHECK(result->HasTexture());
606 base::ScopedClosureRunner scoped_callback_runner(
607 base::Bind(callback, SkBitmap(), content::READBACK_FAILED));
609 // TODO(siva.gunturi): We should be able to validate the format here using
610 // GLHelper::IsReadbackConfigSupported before we processs the result.
611 // See crbug.com/415682 and crbug.com/415131.
612 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
613 if (!bitmap->tryAllocPixels(SkImageInfo::Make(
614 dst_size_in_pixel.width(), dst_size_in_pixel.height(), color_type,
615 kOpaque_SkAlphaType))) {
616 scoped_callback_runner.Reset(base::Bind(
617 callback, SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE));
618 return;
621 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
622 GLHelper* gl_helper = factory->GetGLHelper();
623 if (!gl_helper)
624 return;
626 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
627 new SkAutoLockPixels(*bitmap));
628 uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
630 cc::TextureMailbox texture_mailbox;
631 scoped_ptr<cc::SingleReleaseCallback> release_callback;
632 result->TakeTexture(&texture_mailbox, &release_callback);
633 DCHECK(texture_mailbox.IsTexture());
635 ignore_result(scoped_callback_runner.Release());
637 gl_helper->CropScaleReadbackAndCleanMailbox(
638 texture_mailbox.mailbox(),
639 texture_mailbox.sync_point(),
640 result->size(),
641 gfx::Rect(result->size()),
642 dst_size_in_pixel,
643 pixels,
644 color_type,
645 base::Bind(&CopyFromCompositingSurfaceFinished,
646 callback,
647 base::Passed(&release_callback),
648 base::Passed(&bitmap),
649 base::Passed(&bitmap_pixels_lock)),
650 GLHelper::SCALER_QUALITY_GOOD);
653 // static
654 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
655 const gfx::Size& dst_size_in_pixel,
656 const SkColorType preferred_color_type,
657 ReadbackRequestCallback& callback,
658 scoped_ptr<cc::CopyOutputResult> result) {
659 SkColorType color_type = preferred_color_type;
660 if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) {
661 // Switch back to default colortype if format not supported.
662 color_type = kN32_SkColorType;
664 DCHECK(result->HasBitmap());
665 scoped_ptr<SkBitmap> source = result->TakeBitmap();
666 DCHECK(source);
667 SkBitmap scaled_bitmap;
668 if (source->width() != dst_size_in_pixel.width() ||
669 source->height() != dst_size_in_pixel.height()) {
670 scaled_bitmap =
671 skia::ImageOperations::Resize(*source,
672 skia::ImageOperations::RESIZE_BEST,
673 dst_size_in_pixel.width(),
674 dst_size_in_pixel.height());
675 } else {
676 scaled_bitmap = *source;
678 if (color_type == kN32_SkColorType) {
679 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
680 callback.Run(scaled_bitmap, READBACK_SUCCESS);
681 return;
683 DCHECK_EQ(color_type, kAlpha_8_SkColorType);
684 // The software path currently always returns N32 bitmap regardless of the
685 // |color_type| we ask for.
686 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
687 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|.
688 SkBitmap grayscale_bitmap;
689 bool success = grayscale_bitmap.tryAllocPixels(
690 SkImageInfo::MakeA8(scaled_bitmap.width(), scaled_bitmap.height()));
691 if (!success) {
692 callback.Run(SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE);
693 return;
695 SkCanvas canvas(grayscale_bitmap);
696 SkPaint paint;
697 skia::RefPtr<SkColorFilter> filter =
698 skia::AdoptRef(SkLumaColorFilter::Create());
699 paint.setColorFilter(filter.get());
700 canvas.drawBitmap(scaled_bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint);
701 callback.Run(grayscale_bitmap, READBACK_SUCCESS);
704 // static
705 void DelegatedFrameHost::ReturnSubscriberTexture(
706 base::WeakPtr<DelegatedFrameHost> dfh,
707 scoped_refptr<OwnedMailbox> subscriber_texture,
708 uint32 sync_point) {
709 if (!subscriber_texture.get())
710 return;
711 if (!dfh)
712 return;
714 subscriber_texture->UpdateSyncPoint(sync_point);
716 if (dfh->frame_subscriber_ && subscriber_texture->texture_id())
717 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture);
720 // static
721 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
722 base::WeakPtr<DelegatedFrameHost> dfh,
723 const base::Callback<void(bool)>& callback,
724 scoped_refptr<OwnedMailbox> subscriber_texture,
725 scoped_ptr<cc::SingleReleaseCallback> release_callback,
726 bool result) {
727 callback.Run(result);
729 uint32 sync_point = 0;
730 if (result) {
731 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
732 sync_point = gl_helper->InsertSyncPoint();
734 if (release_callback) {
735 // A release callback means the texture came from the compositor, so there
736 // should be no |subscriber_texture|.
737 DCHECK(!subscriber_texture.get());
738 bool lost_resource = sync_point == 0;
739 release_callback->Run(sync_point, lost_resource);
741 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point);
744 // static
745 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
746 base::WeakPtr<DelegatedFrameHost> dfh,
747 scoped_refptr<OwnedMailbox> subscriber_texture,
748 scoped_refptr<media::VideoFrame> video_frame,
749 const base::Callback<void(bool)>& callback,
750 scoped_ptr<cc::CopyOutputResult> result) {
751 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
752 base::ScopedClosureRunner scoped_return_subscriber_texture(
753 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0));
755 if (!dfh)
756 return;
757 if (result->IsEmpty())
758 return;
759 if (result->size().IsEmpty())
760 return;
762 // Compute the dest size we want after the letterboxing resize. Make the
763 // coordinates and sizes even because we letterbox in YUV space
764 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
765 // line up correctly.
766 // The video frame's visible_rect() and the result's size() are both physical
767 // pixels.
768 gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
769 video_frame->visible_rect(), result->size());
770 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
771 region_in_frame.y() & ~1,
772 region_in_frame.width() & ~1,
773 region_in_frame.height() & ~1);
774 if (region_in_frame.IsEmpty())
775 return;
777 if (!result->HasTexture()) {
778 DCHECK(result->HasBitmap());
779 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap();
780 // Scale the bitmap to the required size, if necessary.
781 SkBitmap scaled_bitmap;
782 if (result->size() != region_in_frame.size()) {
783 skia::ImageOperations::ResizeMethod method =
784 skia::ImageOperations::RESIZE_GOOD;
785 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method,
786 region_in_frame.width(),
787 region_in_frame.height());
788 } else {
789 scaled_bitmap = *bitmap.get();
793 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
795 media::CopyRGBToVideoFrame(
796 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
797 scaled_bitmap.rowBytes(),
798 region_in_frame,
799 video_frame.get());
801 ignore_result(scoped_callback_runner.Release());
802 callback.Run(true);
803 return;
806 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
807 GLHelper* gl_helper = factory->GetGLHelper();
808 if (!gl_helper)
809 return;
810 if (subscriber_texture.get() && !subscriber_texture->texture_id())
811 return;
813 cc::TextureMailbox texture_mailbox;
814 scoped_ptr<cc::SingleReleaseCallback> release_callback;
815 result->TakeTexture(&texture_mailbox, &release_callback);
816 DCHECK(texture_mailbox.IsTexture());
818 gfx::Rect result_rect(result->size());
820 content::ReadbackYUVInterface* yuv_readback_pipeline =
821 dfh->yuv_readback_pipeline_.get();
822 if (yuv_readback_pipeline == NULL ||
823 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() ||
824 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect ||
825 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) {
826 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST;
827 std::string quality_switch = switches::kTabCaptureDownscaleQuality;
828 // If we're scaling up, we can use the "best" quality.
829 if (result_rect.size().width() < region_in_frame.size().width() &&
830 result_rect.size().height() < region_in_frame.size().height())
831 quality_switch = switches::kTabCaptureUpscaleQuality;
833 std::string switch_value =
834 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
835 quality_switch);
836 if (switch_value == "fast")
837 quality = GLHelper::SCALER_QUALITY_FAST;
838 else if (switch_value == "good")
839 quality = GLHelper::SCALER_QUALITY_GOOD;
840 else if (switch_value == "best")
841 quality = GLHelper::SCALER_QUALITY_BEST;
843 dfh->yuv_readback_pipeline_.reset(
844 gl_helper->CreateReadbackPipelineYUV(quality,
845 result_rect.size(),
846 result_rect,
847 region_in_frame.size(),
848 true,
849 true));
850 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get();
853 ignore_result(scoped_callback_runner.Release());
854 ignore_result(scoped_return_subscriber_texture.Release());
855 base::Callback<void(bool result)> finished_callback = base::Bind(
856 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo,
857 dfh->AsWeakPtr(),
858 callback,
859 subscriber_texture,
860 base::Passed(&release_callback));
861 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
862 texture_mailbox.sync_point(),
863 video_frame.get(),
864 region_in_frame.origin(),
865 finished_callback);
868 ////////////////////////////////////////////////////////////////////////////////
869 // DelegatedFrameHost, ui::CompositorObserver implementation:
871 void DelegatedFrameHost::OnCompositingDidCommit(
872 ui::Compositor* compositor) {
873 if (can_lock_compositor_ == NO_PENDING_COMMIT) {
874 can_lock_compositor_ = YES_CAN_LOCK;
875 if (resize_lock_.get() && resize_lock_->GrabDeferredLock())
876 can_lock_compositor_ = YES_DID_LOCK;
878 RunOnCommitCallbacks();
879 if (resize_lock_ &&
880 resize_lock_->expected_size() == current_frame_size_in_dip_) {
881 resize_lock_.reset();
882 client_->DelegatedFrameHostResizeLockWasReleased();
883 // We may have had a resize while we had the lock (e.g. if the lock expired,
884 // or if the UI still gave us some resizes), so make sure we grab a new lock
885 // if necessary.
886 MaybeCreateResizeLock();
890 void DelegatedFrameHost::OnCompositingStarted(
891 ui::Compositor* compositor, base::TimeTicks start_time) {
892 last_draw_ended_ = start_time;
895 void DelegatedFrameHost::OnCompositingEnded(
896 ui::Compositor* compositor) {
899 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
902 void DelegatedFrameHost::OnCompositingLockStateChanged(
903 ui::Compositor* compositor) {
904 // A compositor lock that is part of a resize lock timed out. We
905 // should display a renderer frame.
906 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) {
907 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME;
911 void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor* compositor) {
912 DCHECK_EQ(compositor, compositor_);
913 ResetCompositor();
914 DCHECK(!compositor_);
917 void DelegatedFrameHost::OnUpdateVSyncParameters(
918 base::TimeTicks timebase,
919 base::TimeDelta interval) {
920 SetVSyncParameters(timebase, interval);
921 if (client_->DelegatedFrameHostIsVisible())
922 client_->DelegatedFrameHostUpdateVSyncParameters(timebase, interval);
925 ////////////////////////////////////////////////////////////////////////////////
926 // DelegatedFrameHost, ImageTransportFactoryObserver implementation:
928 void DelegatedFrameHost::OnLostResources() {
929 if (frame_provider_.get() || !surface_id_.is_null())
930 EvictDelegatedFrame();
931 idle_frame_subscriber_textures_.clear();
932 yuv_readback_pipeline_.reset();
934 client_->DelegatedFrameHostOnLostCompositorResources();
937 ////////////////////////////////////////////////////////////////////////////////
938 // DelegatedFrameHost, private:
940 DelegatedFrameHost::~DelegatedFrameHost() {
941 DCHECK(!compositor_);
942 ImageTransportFactory::GetInstance()->RemoveObserver(this);
944 if (!surface_id_.is_null())
945 surface_factory_->Destroy(surface_id_);
946 if (resource_collection_.get())
947 resource_collection_->SetClient(NULL);
949 DCHECK(!vsync_manager_.get());
952 void DelegatedFrameHost::RunOnCommitCallbacks() {
953 for (std::vector<base::Closure>::const_iterator
954 it = on_compositing_did_commit_callbacks_.begin();
955 it != on_compositing_did_commit_callbacks_.end(); ++it) {
956 it->Run();
958 on_compositing_did_commit_callbacks_.clear();
961 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
962 const base::Closure& callback) {
963 DCHECK(compositor_);
965 can_lock_compositor_ = NO_PENDING_COMMIT;
966 if (!callback.is_null())
967 on_compositing_did_commit_callbacks_.push_back(callback);
970 void DelegatedFrameHost::SetCompositor(ui::Compositor* compositor) {
971 DCHECK(!compositor_);
972 if (!compositor)
973 return;
974 compositor_ = compositor;
975 compositor_->AddObserver(this);
976 DCHECK(!vsync_manager_.get());
977 vsync_manager_ = compositor_->vsync_manager();
978 vsync_manager_->AddObserver(this);
981 void DelegatedFrameHost::ResetCompositor() {
982 if (!compositor_)
983 return;
984 RunOnCommitCallbacks();
985 if (resize_lock_) {
986 resize_lock_.reset();
987 client_->DelegatedFrameHostResizeLockWasReleased();
989 if (compositor_->HasObserver(this))
990 compositor_->RemoveObserver(this);
991 if (vsync_manager_.get()) {
992 vsync_manager_->RemoveObserver(this);
993 vsync_manager_ = NULL;
995 compositor_ = nullptr;
998 void DelegatedFrameHost::SetVSyncParameters(const base::TimeTicks& timebase,
999 const base::TimeDelta& interval) {
1000 vsync_timebase_ = timebase;
1001 vsync_interval_ = interval;
1004 void DelegatedFrameHost::LockResources() {
1005 DCHECK(frame_provider_.get() || !surface_id_.is_null());
1006 delegated_frame_evictor_->LockFrame();
1009 void DelegatedFrameHost::RequestCopyOfOutput(
1010 scoped_ptr<cc::CopyOutputRequest> request) {
1011 if (!request_copy_of_output_callback_for_testing_.is_null())
1012 request_copy_of_output_callback_for_testing_.Run(request.Pass());
1013 else
1014 client_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput(request.Pass());
1017 void DelegatedFrameHost::UnlockResources() {
1018 DCHECK(frame_provider_.get() || !surface_id_.is_null());
1019 delegated_frame_evictor_->UnlockFrame();
1022 ////////////////////////////////////////////////////////////////////////////////
1023 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
1025 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer,
1026 ui::Layer* new_layer) {
1027 // The new_layer is the one that will be used by our Window, so that's the one
1028 // that should keep our frame. old_layer will be returned to the
1029 // RecreateLayer caller, and should have a copy.
1030 if (frame_provider_.get()) {
1031 new_layer->SetShowDelegatedContent(frame_provider_.get(),
1032 current_frame_size_in_dip_);
1034 if (!surface_id_.is_null()) {
1035 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
1036 cc::SurfaceManager* manager = factory->GetSurfaceManager();
1037 new_layer->SetShowSurface(
1038 surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)),
1039 base::Bind(&RequireCallback, base::Unretained(manager)),
1040 current_surface_size_, current_scale_factor_,
1041 current_frame_size_in_dip_);
1045 } // namespace content