Add ICU message format support
[chromium-blink-merge.git] / content / browser / compositor / delegated_frame_host.cc
blob903f7a092666ec6aa667e10a07378c42bd263513
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/compositor/delegated_frame_host.h"
7 #include "base/callback_helpers.h"
8 #include "base/command_line.h"
9 #include "base/time/default_tick_clock.h"
10 #include "cc/output/compositor_frame.h"
11 #include "cc/output/compositor_frame_ack.h"
12 #include "cc/output/copy_output_request.h"
13 #include "cc/resources/single_release_callback.h"
14 #include "cc/resources/texture_mailbox.h"
15 #include "cc/surfaces/surface.h"
16 #include "cc/surfaces/surface_factory.h"
17 #include "cc/surfaces/surface_manager.h"
18 #include "content/browser/compositor/resize_lock.h"
19 #include "content/browser/gpu/compositor_util.h"
20 #include "content/common/gpu/client/gl_helper.h"
21 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
22 #include "content/public/common/content_switches.h"
23 #include "media/base/video_frame.h"
24 #include "media/base/video_util.h"
25 #include "skia/ext/image_operations.h"
26 #include "third_party/skia/include/core/SkCanvas.h"
27 #include "third_party/skia/include/core/SkPaint.h"
28 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
29 #include "ui/gfx/geometry/dip_util.h"
31 namespace content {
33 namespace {
35 void SatisfyCallback(cc::SurfaceManager* manager,
36 cc::SurfaceSequence sequence) {
37 std::vector<uint32_t> sequences;
38 sequences.push_back(sequence.sequence);
39 manager->DidSatisfySequences(sequence.id_namespace, &sequences);
42 void RequireCallback(cc::SurfaceManager* manager,
43 cc::SurfaceId id,
44 cc::SurfaceSequence sequence) {
45 cc::Surface* surface = manager->GetSurfaceForId(id);
46 if (!surface) {
47 LOG(ERROR) << "Attempting to require callback on nonexistent surface";
48 return;
50 surface->AddDestructionDependency(sequence);
53 } // namespace
55 ////////////////////////////////////////////////////////////////////////////////
56 // DelegatedFrameHost
58 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client)
59 : client_(client),
60 compositor_(nullptr),
61 use_surfaces_(UseSurfacesEnabled()),
62 tick_clock_(new base::DefaultTickClock()),
63 last_output_surface_id_(0),
64 pending_delegated_ack_count_(0),
65 skipped_frames_(false),
66 current_scale_factor_(1.f),
67 can_lock_compositor_(YES_CAN_LOCK),
68 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) {
69 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
70 factory->AddObserver(this);
71 id_allocator_ = factory->GetContextFactory()->CreateSurfaceIdAllocator();
74 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) {
75 delegated_frame_evictor_->SetVisible(true);
77 if (surface_id_.is_null() && !frame_provider_.get() &&
78 !released_front_lock_.get()) {
79 if (compositor_)
80 released_front_lock_ = compositor_->GetCompositorLock();
83 if (compositor_) {
84 compositor_->SetLatencyInfo(latency_info);
88 bool DelegatedFrameHost::HasSavedFrame() {
89 return delegated_frame_evictor_->HasFrame();
92 void DelegatedFrameHost::WasHidden() {
93 delegated_frame_evictor_->SetVisible(false);
94 released_front_lock_ = NULL;
97 void DelegatedFrameHost::MaybeCreateResizeLock() {
98 if (!ShouldCreateResizeLock())
99 return;
100 DCHECK(compositor_);
102 bool defer_compositor_lock =
103 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
104 can_lock_compositor_ == NO_PENDING_COMMIT;
106 if (can_lock_compositor_ == YES_CAN_LOCK)
107 can_lock_compositor_ = YES_DID_LOCK;
109 resize_lock_ =
110 client_->DelegatedFrameHostCreateResizeLock(defer_compositor_lock);
113 bool DelegatedFrameHost::ShouldCreateResizeLock() {
114 if (!client_->DelegatedFrameCanCreateResizeLock())
115 return false;
117 if (resize_lock_)
118 return false;
120 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
121 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty())
122 return false;
124 if (!compositor_)
125 return false;
127 return true;
130 void DelegatedFrameHost::CopyFromCompositingSurface(
131 const gfx::Rect& src_subrect,
132 const gfx::Size& output_size,
133 ReadbackRequestCallback& callback,
134 const SkColorType preferred_color_type) {
135 // Only ARGB888 and RGB565 supported as of now.
136 bool format_support = ((preferred_color_type == kAlpha_8_SkColorType) ||
137 (preferred_color_type == kRGB_565_SkColorType) ||
138 (preferred_color_type == kN32_SkColorType));
139 DCHECK(format_support);
140 if (!CanCopyToBitmap()) {
141 callback.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE);
142 return;
145 scoped_ptr<cc::CopyOutputRequest> request =
146 cc::CopyOutputRequest::CreateRequest(
147 base::Bind(&DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
148 output_size, preferred_color_type, callback));
149 if (!src_subrect.IsEmpty())
150 request->set_area(src_subrect);
151 RequestCopyOfOutput(request.Pass());
154 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
155 const gfx::Rect& src_subrect,
156 const scoped_refptr<media::VideoFrame>& target,
157 const base::Callback<void(bool)>& callback) {
158 if (!CanCopyToVideoFrame()) {
159 callback.Run(false);
160 return;
163 scoped_ptr<cc::CopyOutputRequest> request =
164 cc::CopyOutputRequest::CreateRequest(base::Bind(
165 &DelegatedFrameHost::
166 CopyFromCompositingSurfaceHasResultForVideo,
167 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
168 nullptr,
169 target,
170 callback));
171 request->set_area(src_subrect);
172 RequestCopyOfOutput(request.Pass());
175 bool DelegatedFrameHost::CanCopyToBitmap() const {
176 return compositor_ &&
177 client_->DelegatedFrameHostGetLayer()->has_external_content();
180 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
181 return compositor_ &&
182 client_->DelegatedFrameHostGetLayer()->has_external_content();
185 bool DelegatedFrameHost::CanSubscribeFrame() const {
186 return true;
189 void DelegatedFrameHost::BeginFrameSubscription(
190 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
191 frame_subscriber_ = subscriber.Pass();
194 void DelegatedFrameHost::EndFrameSubscription() {
195 idle_frame_subscriber_textures_.clear();
196 frame_subscriber_.reset();
199 uint32_t DelegatedFrameHost::GetSurfaceIdNamespace() {
200 if (!use_surfaces_)
201 return 0;
203 return id_allocator_->id_namespace();
206 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const {
207 // Should skip a frame only when another frame from the renderer is guaranteed
208 // to replace it. Otherwise may cause hangs when the renderer is waiting for
209 // the completion of latency infos (such as when taking a Snapshot.)
210 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
211 can_lock_compositor_ == NO_PENDING_COMMIT ||
212 !resize_lock_.get())
213 return false;
215 return size_in_dip != resize_lock_->expected_size();
218 void DelegatedFrameHost::WasResized() {
219 if (client_->DelegatedFrameHostDesiredSizeInDIP() !=
220 current_frame_size_in_dip_ &&
221 !client_->DelegatedFrameHostIsVisible())
222 EvictDelegatedFrame();
223 MaybeCreateResizeLock();
226 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
227 if (resize_lock_)
228 return resize_lock_->expected_size();
229 else
230 return client_->DelegatedFrameHostDesiredSizeInDIP();
233 void DelegatedFrameHost::CheckResizeLock() {
234 if (!resize_lock_ ||
235 resize_lock_->expected_size() != current_frame_size_in_dip_)
236 return;
238 // Since we got the size we were looking for, unlock the compositor. But delay
239 // the release of the lock until we've kicked a frame with the new texture, to
240 // avoid resizing the UI before we have a chance to draw a "good" frame.
241 resize_lock_->UnlockCompositor();
244 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
245 const gfx::Rect& damage_rect) {
246 if (!frame_subscriber() || !CanCopyToVideoFrame())
247 return;
249 const base::TimeTicks now = tick_clock_->NowTicks();
250 base::TimeTicks present_time;
251 if (vsync_interval_ <= base::TimeDelta()) {
252 present_time = now;
253 } else {
254 const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_;
255 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_;
258 scoped_refptr<media::VideoFrame> frame;
259 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
260 if (!frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time,
261 &frame, &callback))
262 return;
264 // Get a texture to re-use; else, create a new one.
265 scoped_refptr<OwnedMailbox> subscriber_texture;
266 if (!idle_frame_subscriber_textures_.empty()) {
267 subscriber_texture = idle_frame_subscriber_textures_.back();
268 idle_frame_subscriber_textures_.pop_back();
269 } else if (GLHelper* helper =
270 ImageTransportFactory::GetInstance()->GetGLHelper()) {
271 subscriber_texture = new OwnedMailbox(helper);
274 scoped_ptr<cc::CopyOutputRequest> request =
275 cc::CopyOutputRequest::CreateRequest(base::Bind(
276 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo,
277 AsWeakPtr(),
278 subscriber_texture,
279 frame,
280 base::Bind(callback, present_time)));
281 // Setting the source in this copy request asks that the layer abort any prior
282 // uncommitted copy requests made on behalf of the same frame subscriber.
283 // This will not affect any of the copy requests spawned elsewhere from
284 // DelegatedFrameHost (e.g., a call to CopyFromCompositingSurface() for
285 // screenshots) since those copy requests do not specify |frame_subscriber()|
286 // as a source.
287 request->set_source(frame_subscriber());
288 request->set_area(gfx::Rect(current_frame_size_in_dip_));
289 if (subscriber_texture.get()) {
290 request->SetTextureMailbox(
291 cc::TextureMailbox(subscriber_texture->mailbox(),
292 subscriber_texture->target(),
293 subscriber_texture->sync_point()));
295 RequestCopyOfOutput(request.Pass());
298 void DelegatedFrameHost::SwapDelegatedFrame(
299 uint32 output_surface_id,
300 scoped_ptr<cc::DelegatedFrameData> frame_data,
301 float frame_device_scale_factor,
302 const std::vector<ui::LatencyInfo>& latency_info,
303 std::vector<uint32_t>* satisfies_sequences) {
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 gfx::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 gfx::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 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, 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 surface_factory_.reset();
351 if (!surface_returned_resources_.empty())
352 SendReturnedDelegatedResources(last_output_surface_id_);
354 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
355 // any resources from the old output surface with the new output surface id.
356 if (resource_collection_.get()) {
357 resource_collection_->SetClient(NULL);
359 if (resource_collection_->LoseAllResources())
360 SendReturnedDelegatedResources(last_output_surface_id_);
362 resource_collection_ = NULL;
364 last_output_surface_id_ = output_surface_id;
366 bool immediate_ack = !compositor_;
367 pending_delegated_ack_count_++;
369 if (frame_size.IsEmpty()) {
370 DCHECK(frame_data->resource_list.empty());
371 EvictDelegatedFrame();
372 } else {
373 if (use_surfaces_) {
374 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
375 cc::SurfaceManager* manager = factory->GetSurfaceManager();
376 if (!surface_factory_) {
377 surface_factory_ =
378 make_scoped_ptr(new cc::SurfaceFactory(manager, this));
380 if (surface_id_.is_null() || frame_size != current_surface_size_ ||
381 frame_size_in_dip != current_frame_size_in_dip_) {
382 if (!surface_id_.is_null())
383 surface_factory_->Destroy(surface_id_);
384 surface_id_ = id_allocator_->GenerateId();
385 surface_factory_->Create(surface_id_);
386 // manager must outlive compositors using it.
387 client_->DelegatedFrameHostGetLayer()->SetShowSurface(
388 surface_id_,
389 base::Bind(&SatisfyCallback, base::Unretained(manager)),
390 base::Bind(&RequireCallback, base::Unretained(manager)), frame_size,
391 frame_device_scale_factor, frame_size_in_dip);
392 current_surface_size_ = frame_size;
393 current_scale_factor_ = frame_device_scale_factor;
395 scoped_ptr<cc::CompositorFrame> compositor_frame =
396 make_scoped_ptr(new cc::CompositorFrame());
397 compositor_frame->delegated_frame_data = frame_data.Pass();
399 compositor_frame->metadata.latency_info.swap(skipped_latency_info_list_);
400 compositor_frame->metadata.latency_info.insert(
401 compositor_frame->metadata.latency_info.end(),
402 latency_info.begin(),
403 latency_info.end());
404 compositor_frame->metadata.satisfies_sequences.swap(*satisfies_sequences);
406 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
407 if (desired_size != frame_size_in_dip && !desired_size.IsEmpty())
408 immediate_ack = true;
410 cc::SurfaceFactory::DrawCallback ack_callback;
411 if (compositor_ && !immediate_ack) {
412 ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn,
413 AsWeakPtr(), output_surface_id);
415 surface_factory_->SubmitFrame(
416 surface_id_, compositor_frame.Pass(), ack_callback);
417 } else {
418 if (!resource_collection_.get()) {
419 resource_collection_ = new cc::DelegatedFrameResourceCollection;
420 resource_collection_->SetClient(this);
422 // If the physical frame size changes, we need a new |frame_provider_|. If
423 // the physical frame size is the same, but the size in DIP changed, we
424 // need to adjust the scale at which the frames will be drawn, and we do
425 // this by making a new |frame_provider_| also to ensure the scale change
426 // is presented in sync with the new frame content.
427 if (!frame_provider_.get() ||
428 frame_size != frame_provider_->frame_size() ||
429 frame_size_in_dip != current_frame_size_in_dip_) {
430 frame_provider_ = new cc::DelegatedFrameProvider(
431 resource_collection_.get(), frame_data.Pass());
432 client_->DelegatedFrameHostGetLayer()->SetShowDelegatedContent(
433 frame_provider_.get(), frame_size_in_dip);
434 } else {
435 frame_provider_->SetFrameData(frame_data.Pass());
439 released_front_lock_ = NULL;
440 current_frame_size_in_dip_ = frame_size_in_dip;
441 CheckResizeLock();
443 if (!damage_rect_in_dip.IsEmpty())
444 client_->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage(
445 damage_rect_in_dip);
447 if (immediate_ack) {
448 SendDelegatedFrameAck(output_surface_id);
449 } else if (!use_surfaces_) {
450 std::vector<ui::LatencyInfo>::const_iterator it;
451 for (it = latency_info.begin(); it != latency_info.end(); ++it)
452 compositor_->SetLatencyInfo(*it);
453 // If we've previously skipped any latency infos add them.
454 for (it = skipped_latency_info_list_.begin();
455 it != skipped_latency_info_list_.end();
456 ++it)
457 compositor_->SetLatencyInfo(*it);
458 skipped_latency_info_list_.clear();
459 AddOnCommitCallbackAndDisableLocks(
460 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
461 AsWeakPtr(), output_surface_id));
462 } else {
463 AddOnCommitCallbackAndDisableLocks(base::Closure());
465 DidReceiveFrameFromRenderer(damage_rect);
466 if (frame_provider_.get() || !surface_id_.is_null())
467 delegated_frame_evictor_->SwappedFrame(
468 client_->DelegatedFrameHostIsVisible());
469 // Note: the frame may have been evicted immediately.
472 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) {
473 cc::CompositorFrameAck ack;
474 if (!surface_returned_resources_.empty())
475 ack.resources.swap(surface_returned_resources_);
476 if (resource_collection_.get())
477 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
478 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack);
479 DCHECK_GT(pending_delegated_ack_count_, 0);
480 pending_delegated_ack_count_--;
483 void DelegatedFrameHost::SurfaceDrawn(uint32 output_surface_id,
484 cc::SurfaceDrawStatus drawn) {
485 SendDelegatedFrameAck(output_surface_id);
488 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
489 if (pending_delegated_ack_count_)
490 return;
492 SendReturnedDelegatedResources(last_output_surface_id_);
495 void DelegatedFrameHost::SendReturnedDelegatedResources(
496 uint32 output_surface_id) {
497 cc::CompositorFrameAck ack;
498 if (!surface_returned_resources_.empty()) {
499 ack.resources.swap(surface_returned_resources_);
500 } else {
501 DCHECK(resource_collection_.get());
502 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
504 DCHECK(!ack.resources.empty());
506 client_->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id,
507 ack);
510 void DelegatedFrameHost::ReturnResources(
511 const cc::ReturnedResourceArray& resources) {
512 if (resources.empty())
513 return;
514 std::copy(resources.begin(),
515 resources.end(),
516 std::back_inserter(surface_returned_resources_));
517 if (!pending_delegated_ack_count_)
518 SendReturnedDelegatedResources(last_output_surface_id_);
521 void DelegatedFrameHost::EvictDelegatedFrame() {
522 client_->DelegatedFrameHostGetLayer()->SetShowSolidColorContent();
523 frame_provider_ = NULL;
524 if (!surface_id_.is_null()) {
525 surface_factory_->Destroy(surface_id_);
526 surface_id_ = cc::SurfaceId();
528 delegated_frame_evictor_->DiscardedFrame();
531 // static
532 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
533 const gfx::Size& dst_size_in_pixel,
534 const SkColorType color_type,
535 ReadbackRequestCallback& callback,
536 scoped_ptr<cc::CopyOutputResult> result) {
537 if (result->IsEmpty() || result->size().IsEmpty()) {
538 callback.Run(SkBitmap(), content::READBACK_FAILED);
539 return;
542 gfx::Size output_size_in_pixel;
543 if (dst_size_in_pixel.IsEmpty())
544 output_size_in_pixel = result->size();
545 else
546 output_size_in_pixel = dst_size_in_pixel;
548 if (result->HasTexture()) {
549 // GPU-accelerated path
550 PrepareTextureCopyOutputResult(output_size_in_pixel, color_type,
551 callback,
552 result.Pass());
553 return;
556 DCHECK(result->HasBitmap());
557 // Software path
558 PrepareBitmapCopyOutputResult(output_size_in_pixel, color_type, callback,
559 result.Pass());
562 static void CopyFromCompositingSurfaceFinished(
563 ReadbackRequestCallback& callback,
564 scoped_ptr<cc::SingleReleaseCallback> release_callback,
565 scoped_ptr<SkBitmap> bitmap,
566 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
567 bool result) {
568 bitmap_pixels_lock.reset();
570 uint32 sync_point = 0;
571 if (result) {
572 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
573 if (gl_helper)
574 sync_point = gl_helper->InsertSyncPoint();
576 bool lost_resource = sync_point == 0;
577 release_callback->Run(sync_point, lost_resource);
579 callback.Run(*bitmap,
580 result ? content::READBACK_SUCCESS : content::READBACK_FAILED);
583 // static
584 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
585 const gfx::Size& dst_size_in_pixel,
586 const SkColorType color_type,
587 ReadbackRequestCallback& callback,
588 scoped_ptr<cc::CopyOutputResult> result) {
589 DCHECK(result->HasTexture());
590 base::ScopedClosureRunner scoped_callback_runner(
591 base::Bind(callback, SkBitmap(), content::READBACK_FAILED));
593 // TODO(siva.gunturi): We should be able to validate the format here using
594 // GLHelper::IsReadbackConfigSupported before we processs the result.
595 // See crbug.com/415682 and crbug.com/415131.
596 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
597 if (!bitmap->tryAllocPixels(SkImageInfo::Make(
598 dst_size_in_pixel.width(), dst_size_in_pixel.height(), color_type,
599 kOpaque_SkAlphaType))) {
600 scoped_callback_runner.Reset(base::Bind(
601 callback, SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE));
602 return;
605 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
606 GLHelper* gl_helper = factory->GetGLHelper();
607 if (!gl_helper)
608 return;
610 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
611 new SkAutoLockPixels(*bitmap));
612 uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
614 cc::TextureMailbox texture_mailbox;
615 scoped_ptr<cc::SingleReleaseCallback> release_callback;
616 result->TakeTexture(&texture_mailbox, &release_callback);
617 DCHECK(texture_mailbox.IsTexture());
619 ignore_result(scoped_callback_runner.Release());
621 gl_helper->CropScaleReadbackAndCleanMailbox(
622 texture_mailbox.mailbox(),
623 texture_mailbox.sync_point(),
624 result->size(),
625 gfx::Rect(result->size()),
626 dst_size_in_pixel,
627 pixels,
628 color_type,
629 base::Bind(&CopyFromCompositingSurfaceFinished,
630 callback,
631 base::Passed(&release_callback),
632 base::Passed(&bitmap),
633 base::Passed(&bitmap_pixels_lock)),
634 GLHelper::SCALER_QUALITY_GOOD);
637 // static
638 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
639 const gfx::Size& dst_size_in_pixel,
640 const SkColorType preferred_color_type,
641 ReadbackRequestCallback& callback,
642 scoped_ptr<cc::CopyOutputResult> result) {
643 SkColorType color_type = preferred_color_type;
644 if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) {
645 // Switch back to default colortype if format not supported.
646 color_type = kN32_SkColorType;
648 DCHECK(result->HasBitmap());
649 scoped_ptr<SkBitmap> source = result->TakeBitmap();
650 DCHECK(source);
651 SkBitmap scaled_bitmap;
652 if (source->width() != dst_size_in_pixel.width() ||
653 source->height() != dst_size_in_pixel.height()) {
654 scaled_bitmap =
655 skia::ImageOperations::Resize(*source,
656 skia::ImageOperations::RESIZE_BEST,
657 dst_size_in_pixel.width(),
658 dst_size_in_pixel.height());
659 } else {
660 scaled_bitmap = *source;
662 if (color_type == kN32_SkColorType) {
663 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
664 callback.Run(scaled_bitmap, READBACK_SUCCESS);
665 return;
667 DCHECK_EQ(color_type, kAlpha_8_SkColorType);
668 // The software path currently always returns N32 bitmap regardless of the
669 // |color_type| we ask for.
670 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
671 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|.
672 SkBitmap grayscale_bitmap;
673 bool success = grayscale_bitmap.tryAllocPixels(
674 SkImageInfo::MakeA8(scaled_bitmap.width(), scaled_bitmap.height()));
675 if (!success) {
676 callback.Run(SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE);
677 return;
679 SkCanvas canvas(grayscale_bitmap);
680 SkPaint paint;
681 skia::RefPtr<SkColorFilter> filter =
682 skia::AdoptRef(SkLumaColorFilter::Create());
683 paint.setColorFilter(filter.get());
684 canvas.drawBitmap(scaled_bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint);
685 callback.Run(grayscale_bitmap, READBACK_SUCCESS);
688 // static
689 void DelegatedFrameHost::ReturnSubscriberTexture(
690 base::WeakPtr<DelegatedFrameHost> dfh,
691 scoped_refptr<OwnedMailbox> subscriber_texture,
692 uint32 sync_point) {
693 if (!subscriber_texture.get())
694 return;
695 if (!dfh)
696 return;
698 subscriber_texture->UpdateSyncPoint(sync_point);
700 if (dfh->frame_subscriber_ && subscriber_texture->texture_id())
701 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture);
704 // static
705 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
706 base::WeakPtr<DelegatedFrameHost> dfh,
707 const base::Callback<void(bool)>& callback,
708 scoped_refptr<OwnedMailbox> subscriber_texture,
709 scoped_ptr<cc::SingleReleaseCallback> release_callback,
710 bool result) {
711 callback.Run(result);
713 uint32 sync_point = 0;
714 if (result) {
715 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
716 sync_point = gl_helper->InsertSyncPoint();
718 if (release_callback) {
719 // A release callback means the texture came from the compositor, so there
720 // should be no |subscriber_texture|.
721 DCHECK(!subscriber_texture.get());
722 bool lost_resource = sync_point == 0;
723 release_callback->Run(sync_point, lost_resource);
725 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point);
728 // static
729 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
730 base::WeakPtr<DelegatedFrameHost> dfh,
731 scoped_refptr<OwnedMailbox> subscriber_texture,
732 scoped_refptr<media::VideoFrame> video_frame,
733 const base::Callback<void(bool)>& callback,
734 scoped_ptr<cc::CopyOutputResult> result) {
735 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
736 base::ScopedClosureRunner scoped_return_subscriber_texture(
737 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0));
739 if (!dfh)
740 return;
741 if (result->IsEmpty())
742 return;
743 if (result->size().IsEmpty())
744 return;
746 // Compute the dest size we want after the letterboxing resize. Make the
747 // coordinates and sizes even because we letterbox in YUV space
748 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
749 // line up correctly.
750 // The video frame's visible_rect() and the result's size() are both physical
751 // pixels.
752 gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
753 video_frame->visible_rect(), result->size());
754 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
755 region_in_frame.y() & ~1,
756 region_in_frame.width() & ~1,
757 region_in_frame.height() & ~1);
758 if (region_in_frame.IsEmpty())
759 return;
761 if (!result->HasTexture()) {
762 DCHECK(result->HasBitmap());
763 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap();
764 // Scale the bitmap to the required size, if necessary.
765 SkBitmap scaled_bitmap;
766 if (result->size() != region_in_frame.size()) {
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 region_in_frame.size(),
832 true,
833 true));
834 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get();
837 ignore_result(scoped_callback_runner.Release());
838 ignore_result(scoped_return_subscriber_texture.Release());
839 base::Callback<void(bool result)> finished_callback = base::Bind(
840 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo,
841 dfh->AsWeakPtr(),
842 callback,
843 subscriber_texture,
844 base::Passed(&release_callback));
845 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
846 texture_mailbox.sync_point(),
847 video_frame.get(),
848 region_in_frame.origin(),
849 finished_callback);
852 ////////////////////////////////////////////////////////////////////////////////
853 // DelegatedFrameHost, ui::CompositorObserver implementation:
855 void DelegatedFrameHost::OnCompositingDidCommit(
856 ui::Compositor* compositor) {
857 if (can_lock_compositor_ == NO_PENDING_COMMIT) {
858 can_lock_compositor_ = YES_CAN_LOCK;
859 if (resize_lock_.get() && resize_lock_->GrabDeferredLock())
860 can_lock_compositor_ = YES_DID_LOCK;
862 RunOnCommitCallbacks();
863 if (resize_lock_ &&
864 resize_lock_->expected_size() == current_frame_size_in_dip_) {
865 resize_lock_.reset();
866 client_->DelegatedFrameHostResizeLockWasReleased();
867 // We may have had a resize while we had the lock (e.g. if the lock expired,
868 // or if the UI still gave us some resizes), so make sure we grab a new lock
869 // if necessary.
870 MaybeCreateResizeLock();
874 void DelegatedFrameHost::OnCompositingStarted(
875 ui::Compositor* compositor, base::TimeTicks start_time) {
876 last_draw_ended_ = start_time;
879 void DelegatedFrameHost::OnCompositingEnded(
880 ui::Compositor* compositor) {
883 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
886 void DelegatedFrameHost::OnCompositingLockStateChanged(
887 ui::Compositor* compositor) {
888 // A compositor lock that is part of a resize lock timed out. We
889 // should display a renderer frame.
890 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) {
891 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME;
895 void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor* compositor) {
896 DCHECK_EQ(compositor, compositor_);
897 ResetCompositor();
898 DCHECK(!compositor_);
901 void DelegatedFrameHost::OnUpdateVSyncParameters(
902 base::TimeTicks timebase,
903 base::TimeDelta interval) {
904 SetVSyncParameters(timebase, interval);
905 if (client_->DelegatedFrameHostIsVisible())
906 client_->DelegatedFrameHostUpdateVSyncParameters(timebase, interval);
909 ////////////////////////////////////////////////////////////////////////////////
910 // DelegatedFrameHost, ImageTransportFactoryObserver implementation:
912 void DelegatedFrameHost::OnLostResources() {
913 if (frame_provider_.get() || !surface_id_.is_null())
914 EvictDelegatedFrame();
915 idle_frame_subscriber_textures_.clear();
916 yuv_readback_pipeline_.reset();
918 client_->DelegatedFrameHostOnLostCompositorResources();
921 ////////////////////////////////////////////////////////////////////////////////
922 // DelegatedFrameHost, private:
924 DelegatedFrameHost::~DelegatedFrameHost() {
925 DCHECK(!compositor_);
926 ImageTransportFactory::GetInstance()->RemoveObserver(this);
928 if (!surface_id_.is_null())
929 surface_factory_->Destroy(surface_id_);
930 if (resource_collection_.get())
931 resource_collection_->SetClient(NULL);
933 DCHECK(!vsync_manager_.get());
936 void DelegatedFrameHost::RunOnCommitCallbacks() {
937 for (std::vector<base::Closure>::const_iterator
938 it = on_compositing_did_commit_callbacks_.begin();
939 it != on_compositing_did_commit_callbacks_.end(); ++it) {
940 it->Run();
942 on_compositing_did_commit_callbacks_.clear();
945 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
946 const base::Closure& callback) {
947 DCHECK(compositor_);
949 can_lock_compositor_ = NO_PENDING_COMMIT;
950 if (!callback.is_null())
951 on_compositing_did_commit_callbacks_.push_back(callback);
954 void DelegatedFrameHost::SetCompositor(ui::Compositor* compositor) {
955 DCHECK(!compositor_);
956 if (!compositor)
957 return;
958 compositor_ = compositor;
959 compositor_->AddObserver(this);
960 DCHECK(!vsync_manager_.get());
961 vsync_manager_ = compositor_->vsync_manager();
962 vsync_manager_->AddObserver(this);
965 void DelegatedFrameHost::ResetCompositor() {
966 if (!compositor_)
967 return;
968 RunOnCommitCallbacks();
969 if (resize_lock_) {
970 resize_lock_.reset();
971 client_->DelegatedFrameHostResizeLockWasReleased();
973 if (compositor_->HasObserver(this))
974 compositor_->RemoveObserver(this);
975 if (vsync_manager_.get()) {
976 vsync_manager_->RemoveObserver(this);
977 vsync_manager_ = NULL;
979 compositor_ = nullptr;
982 void DelegatedFrameHost::SetVSyncParameters(const base::TimeTicks& timebase,
983 const base::TimeDelta& interval) {
984 vsync_timebase_ = timebase;
985 vsync_interval_ = interval;
988 void DelegatedFrameHost::LockResources() {
989 DCHECK(frame_provider_.get() || !surface_id_.is_null());
990 delegated_frame_evictor_->LockFrame();
993 void DelegatedFrameHost::RequestCopyOfOutput(
994 scoped_ptr<cc::CopyOutputRequest> request) {
995 if (!request_copy_of_output_callback_for_testing_.is_null())
996 request_copy_of_output_callback_for_testing_.Run(request.Pass());
997 else
998 client_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput(request.Pass());
1001 void DelegatedFrameHost::UnlockResources() {
1002 DCHECK(frame_provider_.get() || !surface_id_.is_null());
1003 delegated_frame_evictor_->UnlockFrame();
1006 ////////////////////////////////////////////////////////////////////////////////
1007 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
1009 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer,
1010 ui::Layer* new_layer) {
1011 // The new_layer is the one that will be used by our Window, so that's the one
1012 // that should keep our frame. old_layer will be returned to the
1013 // RecreateLayer caller, and should have a copy.
1014 if (frame_provider_.get()) {
1015 new_layer->SetShowDelegatedContent(frame_provider_.get(),
1016 current_frame_size_in_dip_);
1018 if (!surface_id_.is_null()) {
1019 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
1020 cc::SurfaceManager* manager = factory->GetSurfaceManager();
1021 new_layer->SetShowSurface(
1022 surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)),
1023 base::Bind(&RequireCallback, base::Unretained(manager)),
1024 current_surface_size_, current_scale_factor_,
1025 current_frame_size_in_dip_);
1029 } // namespace content