ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / content / browser / compositor / delegated_frame_host.cc
blobe6cb16150673df0c4db6cc80b28610e4154792a2
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/compositor/delegated_frame_host.h"
7 #include "base/callback_helpers.h"
8 #include "base/command_line.h"
9 #include "cc/output/compositor_frame.h"
10 #include "cc/output/compositor_frame_ack.h"
11 #include "cc/output/copy_output_request.h"
12 #include "cc/resources/single_release_callback.h"
13 #include "cc/resources/texture_mailbox.h"
14 #include "cc/surfaces/surface.h"
15 #include "cc/surfaces/surface_factory.h"
16 #include "cc/surfaces/surface_manager.h"
17 #include "content/browser/compositor/resize_lock.h"
18 #include "content/browser/gpu/compositor_util.h"
19 #include "content/common/gpu/client/gl_helper.h"
20 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
21 #include "content/public/common/content_switches.h"
22 #include "media/base/video_frame.h"
23 #include "media/base/video_util.h"
24 #include "skia/ext/image_operations.h"
25 #include "third_party/skia/include/core/SkCanvas.h"
26 #include "third_party/skia/include/core/SkPaint.h"
27 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
28 #include "ui/gfx/frame_time.h"
29 #include "ui/gfx/geometry/dip_util.h"
31 namespace content {
33 namespace {
35 void SatisfyCallback(cc::SurfaceManager* manager,
36 cc::SurfaceSequence sequence) {
37 std::vector<uint32_t> sequences;
38 sequences.push_back(sequence.sequence);
39 manager->DidSatisfySequences(sequence.id_namespace, &sequences);
42 void RequireCallback(cc::SurfaceManager* manager,
43 cc::SurfaceId id,
44 cc::SurfaceSequence sequence) {
45 cc::Surface* surface = manager->GetSurfaceForId(id);
46 if (!surface) {
47 LOG(ERROR) << "Attempting to require callback on nonexistent surface";
48 return;
50 surface->AddDestructionDependency(sequence);
53 } // namespace
55 ////////////////////////////////////////////////////////////////////////////////
56 // DelegatedFrameHost
58 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client)
59 : client_(client),
60 compositor_(nullptr),
61 use_surfaces_(UseSurfacesEnabled()),
62 last_output_surface_id_(0),
63 pending_delegated_ack_count_(0),
64 skipped_frames_(false),
65 current_scale_factor_(1.f),
66 can_lock_compositor_(YES_CAN_LOCK),
67 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) {
68 ImageTransportFactory::GetInstance()->AddObserver(this);
71 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) {
72 delegated_frame_evictor_->SetVisible(true);
74 if (surface_id_.is_null() && !frame_provider_.get() &&
75 !released_front_lock_.get()) {
76 if (compositor_)
77 released_front_lock_ = compositor_->GetCompositorLock();
80 if (compositor_) {
81 compositor_->SetLatencyInfo(latency_info);
85 bool DelegatedFrameHost::HasSavedFrame() {
86 return delegated_frame_evictor_->HasFrame();
89 void DelegatedFrameHost::WasHidden() {
90 delegated_frame_evictor_->SetVisible(false);
91 released_front_lock_ = NULL;
94 void DelegatedFrameHost::MaybeCreateResizeLock() {
95 if (!ShouldCreateResizeLock())
96 return;
97 DCHECK(compositor_);
99 bool defer_compositor_lock =
100 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
101 can_lock_compositor_ == NO_PENDING_COMMIT;
103 if (can_lock_compositor_ == YES_CAN_LOCK)
104 can_lock_compositor_ = YES_DID_LOCK;
106 resize_lock_ =
107 client_->DelegatedFrameHostCreateResizeLock(defer_compositor_lock);
110 bool DelegatedFrameHost::ShouldCreateResizeLock() {
111 if (!client_->DelegatedFrameCanCreateResizeLock())
112 return false;
114 if (resize_lock_)
115 return false;
117 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
118 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty())
119 return false;
121 if (!compositor_)
122 return false;
124 return true;
127 void DelegatedFrameHost::CopyFromCompositingSurface(
128 const gfx::Rect& src_subrect,
129 const gfx::Size& output_size,
130 ReadbackRequestCallback& callback,
131 const SkColorType color_type) {
132 // Only ARGB888 and RGB565 supported as of now.
133 bool format_support = ((color_type == kAlpha_8_SkColorType) ||
134 (color_type == kRGB_565_SkColorType) ||
135 (color_type == kN32_SkColorType));
136 DCHECK(format_support);
137 if (!CanCopyToBitmap()) {
138 callback.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE);
139 return;
142 scoped_ptr<cc::CopyOutputRequest> request =
143 cc::CopyOutputRequest::CreateRequest(base::Bind(
144 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
145 output_size,
146 color_type,
147 callback));
148 if (!src_subrect.IsEmpty())
149 request->set_area(src_subrect);
150 RequestCopyOfOutput(request.Pass());
153 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
154 const gfx::Rect& src_subrect,
155 const scoped_refptr<media::VideoFrame>& target,
156 const base::Callback<void(bool)>& callback) {
157 if (!CanCopyToVideoFrame()) {
158 callback.Run(false);
159 return;
162 // Try get a texture to reuse.
163 scoped_refptr<OwnedMailbox> subscriber_texture;
164 if (frame_subscriber_) {
165 if (!idle_frame_subscriber_textures_.empty()) {
166 subscriber_texture = idle_frame_subscriber_textures_.back();
167 idle_frame_subscriber_textures_.pop_back();
168 } else if (GLHelper* helper =
169 ImageTransportFactory::GetInstance()->GetGLHelper()) {
170 subscriber_texture = new OwnedMailbox(helper);
174 scoped_ptr<cc::CopyOutputRequest> request =
175 cc::CopyOutputRequest::CreateRequest(base::Bind(
176 &DelegatedFrameHost::
177 CopyFromCompositingSurfaceHasResultForVideo,
178 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
179 subscriber_texture,
180 target,
181 callback));
182 request->set_area(src_subrect);
183 if (subscriber_texture.get()) {
184 request->SetTextureMailbox(
185 cc::TextureMailbox(subscriber_texture->mailbox(),
186 subscriber_texture->target(),
187 subscriber_texture->sync_point()));
189 RequestCopyOfOutput(request.Pass());
192 bool DelegatedFrameHost::CanCopyToBitmap() const {
193 return compositor_ &&
194 client_->DelegatedFrameHostGetLayer()->has_external_content();
197 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
198 return compositor_ &&
199 client_->DelegatedFrameHostGetLayer()->has_external_content();
202 bool DelegatedFrameHost::CanSubscribeFrame() const {
203 return true;
206 void DelegatedFrameHost::BeginFrameSubscription(
207 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
208 frame_subscriber_ = subscriber.Pass();
211 void DelegatedFrameHost::EndFrameSubscription() {
212 idle_frame_subscriber_textures_.clear();
213 frame_subscriber_.reset();
216 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const {
217 // Should skip a frame only when another frame from the renderer is guaranteed
218 // to replace it. Otherwise may cause hangs when the renderer is waiting for
219 // the completion of latency infos (such as when taking a Snapshot.)
220 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
221 can_lock_compositor_ == NO_PENDING_COMMIT ||
222 !resize_lock_.get())
223 return false;
225 return size_in_dip != resize_lock_->expected_size();
228 void DelegatedFrameHost::WasResized() {
229 if (client_->DelegatedFrameHostDesiredSizeInDIP() !=
230 current_frame_size_in_dip_ &&
231 !client_->DelegatedFrameHostIsVisible())
232 EvictDelegatedFrame();
233 MaybeCreateResizeLock();
236 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
237 if (resize_lock_)
238 return resize_lock_->expected_size();
239 else
240 return client_->DelegatedFrameHostDesiredSizeInDIP();
243 void DelegatedFrameHost::CheckResizeLock() {
244 if (!resize_lock_ ||
245 resize_lock_->expected_size() != current_frame_size_in_dip_)
246 return;
248 // Since we got the size we were looking for, unlock the compositor. But delay
249 // the release of the lock until we've kicked a frame with the new texture, to
250 // avoid resizing the UI before we have a chance to draw a "good" frame.
251 resize_lock_->UnlockCompositor();
254 void DelegatedFrameHost::DidReceiveFrameFromRenderer(
255 const gfx::Rect& damage_rect) {
256 if (!frame_subscriber() || !CanCopyToVideoFrame())
257 return;
259 const base::TimeTicks now = gfx::FrameTime::Now();
260 base::TimeTicks present_time;
261 if (vsync_timebase_.is_null() || vsync_interval_ <= base::TimeDelta()) {
262 present_time = now;
263 } else {
264 const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_;
265 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_;
268 scoped_refptr<media::VideoFrame> frame;
269 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
270 if (frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time,
271 &frame, &callback)) {
272 CopyFromCompositingSurfaceToVideoFrame(
273 gfx::Rect(current_frame_size_in_dip_),
274 frame,
275 base::Bind(callback, present_time));
279 void DelegatedFrameHost::SwapDelegatedFrame(
280 uint32 output_surface_id,
281 scoped_ptr<cc::DelegatedFrameData> frame_data,
282 float frame_device_scale_factor,
283 const std::vector<ui::LatencyInfo>& latency_info) {
284 DCHECK(!frame_data->render_pass_list.empty());
286 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
288 gfx::Size frame_size = root_pass->output_rect.size();
289 gfx::Size frame_size_in_dip =
290 gfx::ConvertSizeToDIP(frame_device_scale_factor, frame_size);
292 gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect);
293 damage_rect.Intersect(gfx::Rect(frame_size));
294 gfx::Rect damage_rect_in_dip =
295 gfx::ConvertRectToDIP(frame_device_scale_factor, damage_rect);
297 if (ShouldSkipFrame(frame_size_in_dip)) {
298 cc::CompositorFrameAck ack;
299 cc::TransferableResource::ReturnResources(frame_data->resource_list,
300 &ack.resources);
302 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(),
303 latency_info.begin(), latency_info.end());
305 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack);
306 skipped_frames_ = true;
307 return;
310 if (skipped_frames_) {
311 skipped_frames_ = false;
312 damage_rect = gfx::Rect(frame_size);
313 damage_rect_in_dip = gfx::Rect(frame_size_in_dip);
315 // Give the same damage rect to the compositor.
316 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
317 root_pass->damage_rect = damage_rect;
320 if (output_surface_id != last_output_surface_id_) {
321 // Resource ids are scoped by the output surface.
322 // If the originating output surface doesn't match the last one, it
323 // indicates the renderer's output surface may have been recreated, in which
324 // case we should recreate the DelegatedRendererLayer, to avoid matching
325 // resources from the old one with resources from the new one which would
326 // have the same id. Changing the layer to showing painted content destroys
327 // the DelegatedRendererLayer.
328 EvictDelegatedFrame();
330 surface_factory_.reset();
331 if (!surface_returned_resources_.empty())
332 SendReturnedDelegatedResources(last_output_surface_id_);
334 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
335 // any resources from the old output surface with the new output surface id.
336 if (resource_collection_.get()) {
337 resource_collection_->SetClient(NULL);
339 if (resource_collection_->LoseAllResources())
340 SendReturnedDelegatedResources(last_output_surface_id_);
342 resource_collection_ = NULL;
344 last_output_surface_id_ = output_surface_id;
346 bool immediate_ack = !compositor_;
347 pending_delegated_ack_count_++;
349 if (frame_size.IsEmpty()) {
350 DCHECK(frame_data->resource_list.empty());
351 EvictDelegatedFrame();
352 } else {
353 if (use_surfaces_) {
354 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
355 cc::SurfaceManager* manager = factory->GetSurfaceManager();
356 if (!surface_factory_) {
357 id_allocator_ =
358 factory->GetContextFactory()->CreateSurfaceIdAllocator();
359 surface_factory_ =
360 make_scoped_ptr(new cc::SurfaceFactory(manager, this));
362 if (surface_id_.is_null() || frame_size != current_surface_size_ ||
363 frame_size_in_dip != current_frame_size_in_dip_) {
364 if (!surface_id_.is_null())
365 surface_factory_->Destroy(surface_id_);
366 surface_id_ = id_allocator_->GenerateId();
367 surface_factory_->Create(surface_id_);
368 // manager must outlive compositors using it.
369 client_->DelegatedFrameHostGetLayer()->SetShowSurface(
370 surface_id_,
371 base::Bind(&SatisfyCallback, base::Unretained(manager)),
372 base::Bind(&RequireCallback, base::Unretained(manager)), frame_size,
373 frame_device_scale_factor, frame_size_in_dip);
374 current_surface_size_ = frame_size;
375 current_scale_factor_ = frame_device_scale_factor;
377 scoped_ptr<cc::CompositorFrame> compositor_frame =
378 make_scoped_ptr(new cc::CompositorFrame());
379 compositor_frame->delegated_frame_data = frame_data.Pass();
381 compositor_frame->metadata.latency_info.swap(skipped_latency_info_list_);
382 compositor_frame->metadata.latency_info.insert(
383 compositor_frame->metadata.latency_info.end(),
384 latency_info.begin(),
385 latency_info.end());
387 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
388 if (desired_size != frame_size_in_dip && !desired_size.IsEmpty())
389 immediate_ack = true;
391 cc::SurfaceFactory::DrawCallback ack_callback;
392 if (compositor_ && !immediate_ack) {
393 ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn,
394 AsWeakPtr(), output_surface_id);
396 surface_factory_->SubmitFrame(
397 surface_id_, compositor_frame.Pass(), ack_callback);
398 } else {
399 if (!resource_collection_.get()) {
400 resource_collection_ = new cc::DelegatedFrameResourceCollection;
401 resource_collection_->SetClient(this);
403 // If the physical frame size changes, we need a new |frame_provider_|. If
404 // the physical frame size is the same, but the size in DIP changed, we
405 // need to adjust the scale at which the frames will be drawn, and we do
406 // this by making a new |frame_provider_| also to ensure the scale change
407 // is presented in sync with the new frame content.
408 if (!frame_provider_.get() ||
409 frame_size != frame_provider_->frame_size() ||
410 frame_size_in_dip != current_frame_size_in_dip_) {
411 frame_provider_ = new cc::DelegatedFrameProvider(
412 resource_collection_.get(), frame_data.Pass());
413 client_->DelegatedFrameHostGetLayer()->SetShowDelegatedContent(
414 frame_provider_.get(), frame_size_in_dip);
415 } else {
416 frame_provider_->SetFrameData(frame_data.Pass());
420 released_front_lock_ = NULL;
421 current_frame_size_in_dip_ = frame_size_in_dip;
422 CheckResizeLock();
424 if (!damage_rect_in_dip.IsEmpty())
425 client_->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage(
426 damage_rect_in_dip);
428 if (immediate_ack) {
429 SendDelegatedFrameAck(output_surface_id);
430 } else if (!use_surfaces_) {
431 std::vector<ui::LatencyInfo>::const_iterator it;
432 for (it = latency_info.begin(); it != latency_info.end(); ++it)
433 compositor_->SetLatencyInfo(*it);
434 // If we've previously skipped any latency infos add them.
435 for (it = skipped_latency_info_list_.begin();
436 it != skipped_latency_info_list_.end();
437 ++it)
438 compositor_->SetLatencyInfo(*it);
439 skipped_latency_info_list_.clear();
440 AddOnCommitCallbackAndDisableLocks(
441 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
442 AsWeakPtr(),
443 output_surface_id));
444 } else {
445 AddOnCommitCallbackAndDisableLocks(base::Closure());
447 DidReceiveFrameFromRenderer(damage_rect);
448 if (frame_provider_.get() || !surface_id_.is_null())
449 delegated_frame_evictor_->SwappedFrame(
450 client_->DelegatedFrameHostIsVisible());
451 // Note: the frame may have been evicted immediately.
454 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) {
455 cc::CompositorFrameAck ack;
456 if (!surface_returned_resources_.empty())
457 ack.resources.swap(surface_returned_resources_);
458 if (resource_collection_.get())
459 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
460 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack);
461 DCHECK_GT(pending_delegated_ack_count_, 0);
462 pending_delegated_ack_count_--;
465 void DelegatedFrameHost::SurfaceDrawn(uint32 output_surface_id,
466 cc::SurfaceDrawStatus drawn) {
467 SendDelegatedFrameAck(output_surface_id);
470 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
471 if (pending_delegated_ack_count_)
472 return;
474 SendReturnedDelegatedResources(last_output_surface_id_);
477 void DelegatedFrameHost::SendReturnedDelegatedResources(
478 uint32 output_surface_id) {
479 cc::CompositorFrameAck ack;
480 if (!surface_returned_resources_.empty()) {
481 ack.resources.swap(surface_returned_resources_);
482 } else {
483 DCHECK(resource_collection_.get());
484 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
486 DCHECK(!ack.resources.empty());
488 client_->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id,
489 ack);
492 void DelegatedFrameHost::ReturnResources(
493 const cc::ReturnedResourceArray& resources) {
494 if (resources.empty())
495 return;
496 std::copy(resources.begin(),
497 resources.end(),
498 std::back_inserter(surface_returned_resources_));
499 if (!pending_delegated_ack_count_)
500 SendReturnedDelegatedResources(last_output_surface_id_);
503 void DelegatedFrameHost::EvictDelegatedFrame() {
504 client_->DelegatedFrameHostGetLayer()->SetShowSolidColorContent();
505 frame_provider_ = NULL;
506 if (!surface_id_.is_null()) {
507 surface_factory_->Destroy(surface_id_);
508 surface_id_ = cc::SurfaceId();
510 delegated_frame_evictor_->DiscardedFrame();
513 // static
514 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
515 const gfx::Size& dst_size_in_pixel,
516 const SkColorType color_type,
517 ReadbackRequestCallback& callback,
518 scoped_ptr<cc::CopyOutputResult> result) {
519 if (result->IsEmpty() || result->size().IsEmpty()) {
520 callback.Run(SkBitmap(), content::READBACK_FAILED);
521 return;
524 gfx::Size output_size_in_pixel;
525 if (dst_size_in_pixel.IsEmpty())
526 output_size_in_pixel = result->size();
527 else
528 output_size_in_pixel = dst_size_in_pixel;
530 if (result->HasTexture()) {
531 // GPU-accelerated path
532 PrepareTextureCopyOutputResult(output_size_in_pixel, color_type,
533 callback,
534 result.Pass());
535 return;
538 DCHECK(result->HasBitmap());
539 // Software path
540 PrepareBitmapCopyOutputResult(output_size_in_pixel, color_type, callback,
541 result.Pass());
544 static void CopyFromCompositingSurfaceFinished(
545 ReadbackRequestCallback& callback,
546 scoped_ptr<cc::SingleReleaseCallback> release_callback,
547 scoped_ptr<SkBitmap> bitmap,
548 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
549 bool result) {
550 bitmap_pixels_lock.reset();
552 uint32 sync_point = 0;
553 if (result) {
554 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
555 if (gl_helper)
556 sync_point = gl_helper->InsertSyncPoint();
558 bool lost_resource = sync_point == 0;
559 release_callback->Run(sync_point, lost_resource);
561 callback.Run(*bitmap,
562 result ? content::READBACK_SUCCESS : content::READBACK_FAILED);
565 // static
566 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
567 const gfx::Size& dst_size_in_pixel,
568 const SkColorType color_type,
569 ReadbackRequestCallback& callback,
570 scoped_ptr<cc::CopyOutputResult> result) {
571 DCHECK(result->HasTexture());
572 base::ScopedClosureRunner scoped_callback_runner(
573 base::Bind(callback, SkBitmap(), content::READBACK_FAILED));
575 // TODO(sikugu): We should be able to validate the format here using
576 // GLHelper::IsReadbackConfigSupported before we processs the result.
577 // See crbug.com/415682.
578 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
579 if (!bitmap->tryAllocPixels(SkImageInfo::Make(dst_size_in_pixel.width(),
580 dst_size_in_pixel.height(),
581 color_type,
582 kOpaque_SkAlphaType)))
583 return;
585 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
586 GLHelper* gl_helper = factory->GetGLHelper();
587 if (!gl_helper)
588 return;
590 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
591 new SkAutoLockPixels(*bitmap));
592 uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
594 cc::TextureMailbox texture_mailbox;
595 scoped_ptr<cc::SingleReleaseCallback> release_callback;
596 result->TakeTexture(&texture_mailbox, &release_callback);
597 DCHECK(texture_mailbox.IsTexture());
599 ignore_result(scoped_callback_runner.Release());
601 gl_helper->CropScaleReadbackAndCleanMailbox(
602 texture_mailbox.mailbox(),
603 texture_mailbox.sync_point(),
604 result->size(),
605 gfx::Rect(result->size()),
606 dst_size_in_pixel,
607 pixels,
608 color_type,
609 base::Bind(&CopyFromCompositingSurfaceFinished,
610 callback,
611 base::Passed(&release_callback),
612 base::Passed(&bitmap),
613 base::Passed(&bitmap_pixels_lock)),
614 GLHelper::SCALER_QUALITY_FAST);
617 // static
618 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
619 const gfx::Size& dst_size_in_pixel,
620 const SkColorType color_type,
621 ReadbackRequestCallback& callback,
622 scoped_ptr<cc::CopyOutputResult> result) {
623 if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) {
624 NOTIMPLEMENTED();
625 callback.Run(SkBitmap(), READBACK_FORMAT_NOT_SUPPORTED);
626 return;
628 DCHECK(result->HasBitmap());
629 scoped_ptr<SkBitmap> source = result->TakeBitmap();
630 DCHECK(source);
631 SkBitmap scaled_bitmap;
632 if (source->width() != dst_size_in_pixel.width() ||
633 source->height() != dst_size_in_pixel.height()) {
634 scaled_bitmap =
635 skia::ImageOperations::Resize(*source,
636 skia::ImageOperations::RESIZE_BEST,
637 dst_size_in_pixel.width(),
638 dst_size_in_pixel.height());
639 } else {
640 scaled_bitmap = *source;
642 if (color_type == kN32_SkColorType) {
643 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
644 callback.Run(scaled_bitmap, READBACK_SUCCESS);
645 return;
647 DCHECK_EQ(color_type, kAlpha_8_SkColorType);
648 // The software path currently always returns N32 bitmap regardless of the
649 // |color_type| we ask for.
650 DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
651 // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|.
652 SkBitmap grayscale_bitmap;
653 bool success = grayscale_bitmap.tryAllocPixels(
654 SkImageInfo::MakeA8(scaled_bitmap.width(), scaled_bitmap.height()));
655 if (!success) {
656 callback.Run(SkBitmap(), content::READBACK_MEMORY_ALLOCATION_FAILURE);
657 return;
659 SkCanvas canvas(grayscale_bitmap);
660 SkPaint paint;
661 skia::RefPtr<SkColorFilter> filter =
662 skia::AdoptRef(SkLumaColorFilter::Create());
663 paint.setColorFilter(filter.get());
664 canvas.drawBitmap(scaled_bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint);
665 callback.Run(grayscale_bitmap, READBACK_SUCCESS);
668 // static
669 void DelegatedFrameHost::ReturnSubscriberTexture(
670 base::WeakPtr<DelegatedFrameHost> dfh,
671 scoped_refptr<OwnedMailbox> subscriber_texture,
672 uint32 sync_point) {
673 if (!subscriber_texture.get())
674 return;
675 if (!dfh)
676 return;
678 subscriber_texture->UpdateSyncPoint(sync_point);
680 if (dfh->frame_subscriber_ && subscriber_texture->texture_id())
681 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture);
684 // static
685 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
686 base::WeakPtr<DelegatedFrameHost> dfh,
687 const base::Callback<void(bool)>& callback,
688 scoped_refptr<OwnedMailbox> subscriber_texture,
689 scoped_ptr<cc::SingleReleaseCallback> release_callback,
690 bool result) {
691 callback.Run(result);
693 uint32 sync_point = 0;
694 if (result) {
695 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
696 sync_point = gl_helper->InsertSyncPoint();
698 if (release_callback) {
699 // A release callback means the texture came from the compositor, so there
700 // should be no |subscriber_texture|.
701 DCHECK(!subscriber_texture.get());
702 bool lost_resource = sync_point == 0;
703 release_callback->Run(sync_point, lost_resource);
705 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point);
708 // static
709 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
710 base::WeakPtr<DelegatedFrameHost> dfh,
711 scoped_refptr<OwnedMailbox> subscriber_texture,
712 scoped_refptr<media::VideoFrame> video_frame,
713 const base::Callback<void(bool)>& callback,
714 scoped_ptr<cc::CopyOutputResult> result) {
715 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
716 base::ScopedClosureRunner scoped_return_subscriber_texture(
717 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0));
719 if (!dfh)
720 return;
721 if (result->IsEmpty())
722 return;
723 if (result->size().IsEmpty())
724 return;
726 // Compute the dest size we want after the letterboxing resize. Make the
727 // coordinates and sizes even because we letterbox in YUV space
728 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
729 // line up correctly.
730 // The video frame's visible_rect() and the result's size() are both physical
731 // pixels.
732 gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
733 video_frame->visible_rect(), result->size());
734 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
735 region_in_frame.y() & ~1,
736 region_in_frame.width() & ~1,
737 region_in_frame.height() & ~1);
738 if (region_in_frame.IsEmpty())
739 return;
741 if (!result->HasTexture()) {
742 DCHECK(result->HasBitmap());
743 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap();
744 // Scale the bitmap to the required size, if necessary.
745 SkBitmap scaled_bitmap;
746 if (result->size() != region_in_frame.size()) {
747 skia::ImageOperations::ResizeMethod method =
748 skia::ImageOperations::RESIZE_GOOD;
749 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method,
750 region_in_frame.width(),
751 region_in_frame.height());
752 } else {
753 scaled_bitmap = *bitmap.get();
757 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
759 media::CopyRGBToVideoFrame(
760 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
761 scaled_bitmap.rowBytes(),
762 region_in_frame,
763 video_frame.get());
765 ignore_result(scoped_callback_runner.Release());
766 callback.Run(true);
767 return;
770 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
771 GLHelper* gl_helper = factory->GetGLHelper();
772 if (!gl_helper)
773 return;
774 if (subscriber_texture.get() && !subscriber_texture->texture_id())
775 return;
777 cc::TextureMailbox texture_mailbox;
778 scoped_ptr<cc::SingleReleaseCallback> release_callback;
779 result->TakeTexture(&texture_mailbox, &release_callback);
780 DCHECK(texture_mailbox.IsTexture());
782 gfx::Rect result_rect(result->size());
784 content::ReadbackYUVInterface* yuv_readback_pipeline =
785 dfh->yuv_readback_pipeline_.get();
786 if (yuv_readback_pipeline == NULL ||
787 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() ||
788 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect ||
789 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) {
790 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST;
791 std::string quality_switch = switches::kTabCaptureDownscaleQuality;
792 // If we're scaling up, we can use the "best" quality.
793 if (result_rect.size().width() < region_in_frame.size().width() &&
794 result_rect.size().height() < region_in_frame.size().height())
795 quality_switch = switches::kTabCaptureUpscaleQuality;
797 std::string switch_value =
798 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
799 quality_switch);
800 if (switch_value == "fast")
801 quality = GLHelper::SCALER_QUALITY_FAST;
802 else if (switch_value == "good")
803 quality = GLHelper::SCALER_QUALITY_GOOD;
804 else if (switch_value == "best")
805 quality = GLHelper::SCALER_QUALITY_BEST;
807 dfh->yuv_readback_pipeline_.reset(
808 gl_helper->CreateReadbackPipelineYUV(quality,
809 result_rect.size(),
810 result_rect,
811 region_in_frame.size(),
812 true,
813 true));
814 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get();
817 ignore_result(scoped_callback_runner.Release());
818 ignore_result(scoped_return_subscriber_texture.Release());
819 base::Callback<void(bool result)> finished_callback = base::Bind(
820 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo,
821 dfh->AsWeakPtr(),
822 callback,
823 subscriber_texture,
824 base::Passed(&release_callback));
825 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
826 texture_mailbox.sync_point(),
827 video_frame.get(),
828 region_in_frame.origin(),
829 finished_callback);
832 ////////////////////////////////////////////////////////////////////////////////
833 // DelegatedFrameHost, ui::CompositorObserver implementation:
835 void DelegatedFrameHost::OnCompositingDidCommit(
836 ui::Compositor* compositor) {
837 if (can_lock_compositor_ == NO_PENDING_COMMIT) {
838 can_lock_compositor_ = YES_CAN_LOCK;
839 if (resize_lock_.get() && resize_lock_->GrabDeferredLock())
840 can_lock_compositor_ = YES_DID_LOCK;
842 RunOnCommitCallbacks();
843 if (resize_lock_ &&
844 resize_lock_->expected_size() == current_frame_size_in_dip_) {
845 resize_lock_.reset();
846 client_->DelegatedFrameHostResizeLockWasReleased();
847 // We may have had a resize while we had the lock (e.g. if the lock expired,
848 // or if the UI still gave us some resizes), so make sure we grab a new lock
849 // if necessary.
850 MaybeCreateResizeLock();
854 void DelegatedFrameHost::OnCompositingStarted(
855 ui::Compositor* compositor, base::TimeTicks start_time) {
856 last_draw_ended_ = start_time;
859 void DelegatedFrameHost::OnCompositingEnded(
860 ui::Compositor* compositor) {
863 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
866 void DelegatedFrameHost::OnCompositingLockStateChanged(
867 ui::Compositor* compositor) {
868 // A compositor lock that is part of a resize lock timed out. We
869 // should display a renderer frame.
870 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) {
871 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME;
875 void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor* compositor) {
876 DCHECK_EQ(compositor, compositor_);
877 ResetCompositor();
878 DCHECK(!compositor_);
881 void DelegatedFrameHost::OnUpdateVSyncParameters(
882 base::TimeTicks timebase,
883 base::TimeDelta interval) {
884 vsync_timebase_ = timebase;
885 vsync_interval_ = interval;
886 if (client_->DelegatedFrameHostIsVisible())
887 client_->DelegatedFrameHostUpdateVSyncParameters(timebase, interval);
890 ////////////////////////////////////////////////////////////////////////////////
891 // DelegatedFrameHost, ImageTransportFactoryObserver implementation:
893 void DelegatedFrameHost::OnLostResources() {
894 if (frame_provider_.get() || !surface_id_.is_null())
895 EvictDelegatedFrame();
896 idle_frame_subscriber_textures_.clear();
897 yuv_readback_pipeline_.reset();
899 client_->DelegatedFrameHostOnLostCompositorResources();
902 ////////////////////////////////////////////////////////////////////////////////
903 // DelegatedFrameHost, private:
905 DelegatedFrameHost::~DelegatedFrameHost() {
906 DCHECK(!compositor_);
907 ImageTransportFactory::GetInstance()->RemoveObserver(this);
909 if (!surface_id_.is_null())
910 surface_factory_->Destroy(surface_id_);
911 if (resource_collection_.get())
912 resource_collection_->SetClient(NULL);
914 DCHECK(!vsync_manager_.get());
917 void DelegatedFrameHost::RunOnCommitCallbacks() {
918 for (std::vector<base::Closure>::const_iterator
919 it = on_compositing_did_commit_callbacks_.begin();
920 it != on_compositing_did_commit_callbacks_.end(); ++it) {
921 it->Run();
923 on_compositing_did_commit_callbacks_.clear();
926 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
927 const base::Closure& callback) {
928 DCHECK(compositor_);
930 can_lock_compositor_ = NO_PENDING_COMMIT;
931 if (!callback.is_null())
932 on_compositing_did_commit_callbacks_.push_back(callback);
935 void DelegatedFrameHost::SetCompositor(ui::Compositor* compositor) {
936 DCHECK(!compositor_);
937 if (!compositor)
938 return;
939 compositor_ = compositor;
940 compositor_->AddObserver(this);
941 DCHECK(!vsync_manager_.get());
942 vsync_manager_ = compositor_->vsync_manager();
943 vsync_manager_->AddObserver(this);
946 void DelegatedFrameHost::ResetCompositor() {
947 if (!compositor_)
948 return;
949 RunOnCommitCallbacks();
950 if (resize_lock_) {
951 resize_lock_.reset();
952 client_->DelegatedFrameHostResizeLockWasReleased();
954 if (compositor_->HasObserver(this))
955 compositor_->RemoveObserver(this);
956 if (vsync_manager_.get()) {
957 vsync_manager_->RemoveObserver(this);
958 vsync_manager_ = NULL;
960 compositor_ = nullptr;
963 void DelegatedFrameHost::LockResources() {
964 DCHECK(frame_provider_.get() || !surface_id_.is_null());
965 delegated_frame_evictor_->LockFrame();
968 void DelegatedFrameHost::RequestCopyOfOutput(
969 scoped_ptr<cc::CopyOutputRequest> request) {
970 if (!request_copy_of_output_callback_for_testing_.is_null())
971 request_copy_of_output_callback_for_testing_.Run(request.Pass());
972 else
973 client_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput(request.Pass());
976 void DelegatedFrameHost::UnlockResources() {
977 DCHECK(frame_provider_.get() || !surface_id_.is_null());
978 delegated_frame_evictor_->UnlockFrame();
981 ////////////////////////////////////////////////////////////////////////////////
982 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
984 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer,
985 ui::Layer* new_layer) {
986 // The new_layer is the one that will be used by our Window, so that's the one
987 // that should keep our frame. old_layer will be returned to the
988 // RecreateLayer caller, and should have a copy.
989 if (frame_provider_.get()) {
990 new_layer->SetShowDelegatedContent(frame_provider_.get(),
991 current_frame_size_in_dip_);
993 if (!surface_id_.is_null()) {
994 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
995 cc::SurfaceManager* manager = factory->GetSurfaceManager();
996 new_layer->SetShowSurface(
997 surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)),
998 base::Bind(&RequireCallback, base::Unretained(manager)),
999 current_surface_size_, current_scale_factor_,
1000 current_frame_size_in_dip_);
1004 } // namespace content