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.
4
5 #include "content/browser/compositor/delegated_frame_host.h"
6
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 "content/browser/compositor/resize_lock.h"
15 #include "content/common/gpu/client/gl_helper.h"
16 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
17 #include "content/public/common/content_switches.h"
18 #include "media/base/video_frame.h"
19 #include "media/base/video_util.h"
20 #include "skia/ext/image_operations.h"
21
22 namespace content {
23
24 ////////////////////////////////////////////////////////////////////////////////
25 // DelegatedFrameHostClient
26
ShouldCreateResizeLock()27 bool DelegatedFrameHostClient::ShouldCreateResizeLock() {
28 // On Windows and Linux, holding pointer moves will not help throttling
29 // resizes.
30 // TODO(piman): on Windows we need to block (nested message loop?) the
31 // WM_SIZE event. On Linux we need to throttle at the WM level using
32 // _NET_WM_SYNC_REQUEST.
33 // TODO(ccameron): Mac browser window resizing is incompletely implemented.
34 #if !defined(OS_CHROMEOS)
35 return false;
36 #else
37 return GetDelegatedFrameHost()->ShouldCreateResizeLock();
38 #endif
39 }
40
RequestCopyOfOutput(scoped_ptr<cc::CopyOutputRequest> request)41 void DelegatedFrameHostClient::RequestCopyOfOutput(
42 scoped_ptr<cc::CopyOutputRequest> request) {
43 GetDelegatedFrameHost()->RequestCopyOfOutput(request.Pass());
44 }
45
46 ////////////////////////////////////////////////////////////////////////////////
47 // DelegatedFrameHost
48
DelegatedFrameHost(DelegatedFrameHostClient * client)49 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client)
50 : client_(client),
51 last_output_surface_id_(0),
52 pending_delegated_ack_count_(0),
53 skipped_frames_(false),
54 can_lock_compositor_(YES_CAN_LOCK),
55 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) {
56 ImageTransportFactory::GetInstance()->AddObserver(this);
57 }
58
WasShown()59 void DelegatedFrameHost::WasShown() {
60 delegated_frame_evictor_->SetVisible(true);
61
62 if (!released_front_lock_.get()) {
63 ui::Compositor* compositor = client_->GetCompositor();
64 if (compositor)
65 released_front_lock_ = compositor->GetCompositorLock();
66 }
67 }
68
WasHidden()69 void DelegatedFrameHost::WasHidden() {
70 delegated_frame_evictor_->SetVisible(false);
71 released_front_lock_ = NULL;
72 }
73
MaybeCreateResizeLock()74 void DelegatedFrameHost::MaybeCreateResizeLock() {
75 if (!client_->ShouldCreateResizeLock())
76 return;
77 DCHECK(client_->GetCompositor());
78
79 // Listen to changes in the compositor lock state.
80 ui::Compositor* compositor = client_->GetCompositor();
81 if (!compositor->HasObserver(this))
82 compositor->AddObserver(this);
83
84 bool defer_compositor_lock =
85 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
86 can_lock_compositor_ == NO_PENDING_COMMIT;
87
88 if (can_lock_compositor_ == YES_CAN_LOCK)
89 can_lock_compositor_ = YES_DID_LOCK;
90
91 resize_lock_ = client_->CreateResizeLock(defer_compositor_lock);
92 }
93
ShouldCreateResizeLock()94 bool DelegatedFrameHost::ShouldCreateResizeLock() {
95 RenderWidgetHostImpl* host = client_->GetHost();
96
97 if (resize_lock_)
98 return false;
99
100 if (host->should_auto_resize())
101 return false;
102
103 gfx::Size desired_size = client_->DesiredFrameSize();
104 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty())
105 return false;
106
107 ui::Compositor* compositor = client_->GetCompositor();
108 if (!compositor)
109 return false;
110
111 return true;
112 }
113
RequestCopyOfOutput(scoped_ptr<cc::CopyOutputRequest> request)114 void DelegatedFrameHost::RequestCopyOfOutput(
115 scoped_ptr<cc::CopyOutputRequest> request) {
116 client_->GetLayer()->RequestCopyOfOutput(request.Pass());
117 }
118
CopyFromCompositingSurface(const gfx::Rect & src_subrect,const gfx::Size & dst_size,const base::Callback<void (bool,const SkBitmap &)> & callback,const SkBitmap::Config config)119 void DelegatedFrameHost::CopyFromCompositingSurface(
120 const gfx::Rect& src_subrect,
121 const gfx::Size& dst_size,
122 const base::Callback<void(bool, const SkBitmap&)>& callback,
123 const SkBitmap::Config config) {
124 // Only ARGB888 and RGB565 supported as of now.
125 bool format_support = ((config == SkBitmap::kRGB_565_Config) ||
126 (config == SkBitmap::kARGB_8888_Config));
127 DCHECK(format_support);
128 if (!CanCopyToBitmap()) {
129 callback.Run(false, SkBitmap());
130 return;
131 }
132
133 const gfx::Size& dst_size_in_pixel =
134 client_->ConvertViewSizeToPixel(dst_size);
135 scoped_ptr<cc::CopyOutputRequest> request =
136 cc::CopyOutputRequest::CreateRequest(base::Bind(
137 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
138 dst_size_in_pixel,
139 config,
140 callback));
141 gfx::Rect src_subrect_in_pixel =
142 ConvertRectToPixel(client_->CurrentDeviceScaleFactor(), src_subrect);
143 request->set_area(src_subrect_in_pixel);
144 client_->RequestCopyOfOutput(request.Pass());
145 }
146
CopyFromCompositingSurfaceToVideoFrame(const gfx::Rect & src_subrect,const scoped_refptr<media::VideoFrame> & target,const base::Callback<void (bool)> & callback)147 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
148 const gfx::Rect& src_subrect,
149 const scoped_refptr<media::VideoFrame>& target,
150 const base::Callback<void(bool)>& callback) {
151 if (!CanCopyToVideoFrame()) {
152 callback.Run(false);
153 return;
154 }
155
156 // Try get a texture to reuse.
157 scoped_refptr<OwnedMailbox> subscriber_texture;
158 if (frame_subscriber_) {
159 if (!idle_frame_subscriber_textures_.empty()) {
160 subscriber_texture = idle_frame_subscriber_textures_.back();
161 idle_frame_subscriber_textures_.pop_back();
162 } else if (GLHelper* helper =
163 ImageTransportFactory::GetInstance()->GetGLHelper()) {
164 subscriber_texture = new OwnedMailbox(helper);
165 }
166 }
167
168 scoped_ptr<cc::CopyOutputRequest> request =
169 cc::CopyOutputRequest::CreateRequest(base::Bind(
170 &DelegatedFrameHost::
171 CopyFromCompositingSurfaceHasResultForVideo,
172 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
173 subscriber_texture,
174 target,
175 callback));
176 gfx::Rect src_subrect_in_pixel =
177 ConvertRectToPixel(client_->CurrentDeviceScaleFactor(), src_subrect);
178 request->set_area(src_subrect_in_pixel);
179 if (subscriber_texture.get()) {
180 request->SetTextureMailbox(
181 cc::TextureMailbox(subscriber_texture->mailbox(),
182 subscriber_texture->target(),
183 subscriber_texture->sync_point()));
184 }
185 client_->RequestCopyOfOutput(request.Pass());
186 }
187
CanCopyToBitmap() const188 bool DelegatedFrameHost::CanCopyToBitmap() const {
189 return client_->GetCompositor() &&
190 client_->GetLayer()->has_external_content();
191 }
192
CanCopyToVideoFrame() const193 bool DelegatedFrameHost::CanCopyToVideoFrame() const {
194 return client_->GetCompositor() &&
195 client_->GetLayer()->has_external_content();
196 }
197
CanSubscribeFrame() const198 bool DelegatedFrameHost::CanSubscribeFrame() const {
199 return true;
200 }
201
BeginFrameSubscription(scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber)202 void DelegatedFrameHost::BeginFrameSubscription(
203 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
204 frame_subscriber_ = subscriber.Pass();
205 }
206
EndFrameSubscription()207 void DelegatedFrameHost::EndFrameSubscription() {
208 idle_frame_subscriber_textures_.clear();
209 frame_subscriber_.reset();
210 }
211
ShouldSkipFrame(gfx::Size size_in_dip) const212 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const {
213 // Should skip a frame only when another frame from the renderer is guaranteed
214 // to replace it. Otherwise may cause hangs when the renderer is waiting for
215 // the completion of latency infos (such as when taking a Snapshot.)
216 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
217 can_lock_compositor_ == NO_PENDING_COMMIT ||
218 !resize_lock_.get())
219 return false;
220
221 return size_in_dip != resize_lock_->expected_size();
222 }
223
WasResized()224 void DelegatedFrameHost::WasResized() {
225 MaybeCreateResizeLock();
226 }
227
GetRequestedRendererSize() const228 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
229 if (resize_lock_)
230 return resize_lock_->expected_size();
231 else
232 return client_->DesiredFrameSize();
233 }
234
CheckResizeLock()235 void DelegatedFrameHost::CheckResizeLock() {
236 if (!resize_lock_ ||
237 resize_lock_->expected_size() != current_frame_size_in_dip_)
238 return;
239
240 // Since we got the size we were looking for, unlock the compositor. But delay
241 // the release of the lock until we've kicked a frame with the new texture, to
242 // avoid resizing the UI before we have a chance to draw a "good" frame.
243 resize_lock_->UnlockCompositor();
244 ui::Compositor* compositor = client_->GetCompositor();
245 if (compositor) {
246 if (!compositor->HasObserver(this))
247 compositor->AddObserver(this);
248 }
249 }
250
DidReceiveFrameFromRenderer()251 void DelegatedFrameHost::DidReceiveFrameFromRenderer() {
252 if (frame_subscriber() && CanCopyToVideoFrame()) {
253 const base::TimeTicks present_time = base::TimeTicks::Now();
254 scoped_refptr<media::VideoFrame> frame;
255 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
256 if (frame_subscriber()->ShouldCaptureFrame(present_time,
257 &frame, &callback)) {
258 CopyFromCompositingSurfaceToVideoFrame(
259 gfx::Rect(current_frame_size_in_dip_),
260 frame,
261 base::Bind(callback, present_time));
262 }
263 }
264 }
265
SwapDelegatedFrame(uint32 output_surface_id,scoped_ptr<cc::DelegatedFrameData> frame_data,float frame_device_scale_factor,const std::vector<ui::LatencyInfo> & latency_info)266 void DelegatedFrameHost::SwapDelegatedFrame(
267 uint32 output_surface_id,
268 scoped_ptr<cc::DelegatedFrameData> frame_data,
269 float frame_device_scale_factor,
270 const std::vector<ui::LatencyInfo>& latency_info) {
271 RenderWidgetHostImpl* host = client_->GetHost();
272 DCHECK(!frame_data->render_pass_list.empty());
273
274 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
275
276 gfx::Size frame_size = root_pass->output_rect.size();
277 gfx::Size frame_size_in_dip =
278 ConvertSizeToDIP(frame_device_scale_factor, frame_size);
279
280 gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect);
281 damage_rect.Intersect(gfx::Rect(frame_size));
282 gfx::Rect damage_rect_in_dip =
283 ConvertRectToDIP(frame_device_scale_factor, damage_rect);
284
285 if (ShouldSkipFrame(frame_size_in_dip)) {
286 cc::CompositorFrameAck ack;
287 cc::TransferableResource::ReturnResources(frame_data->resource_list,
288 &ack.resources);
289
290 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(),
291 latency_info.begin(), latency_info.end());
292
293 RenderWidgetHostImpl::SendSwapCompositorFrameAck(
294 host->GetRoutingID(), output_surface_id,
295 host->GetProcess()->GetID(), ack);
296 skipped_frames_ = true;
297 return;
298 }
299
300 if (skipped_frames_) {
301 skipped_frames_ = false;
302 damage_rect = gfx::Rect(frame_size);
303 damage_rect_in_dip = gfx::Rect(frame_size_in_dip);
304
305 // Give the same damage rect to the compositor.
306 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
307 root_pass->damage_rect = damage_rect;
308 }
309
310 if (output_surface_id != last_output_surface_id_) {
311 // Resource ids are scoped by the output surface.
312 // If the originating output surface doesn't match the last one, it
313 // indicates the renderer's output surface may have been recreated, in which
314 // case we should recreate the DelegatedRendererLayer, to avoid matching
315 // resources from the old one with resources from the new one which would
316 // have the same id. Changing the layer to showing painted content destroys
317 // the DelegatedRendererLayer.
318 EvictDelegatedFrame();
319
320 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
321 // any resources from the old output surface with the new output surface id.
322 if (resource_collection_.get()) {
323 resource_collection_->SetClient(NULL);
324
325 if (resource_collection_->LoseAllResources())
326 SendReturnedDelegatedResources(last_output_surface_id_);
327
328 resource_collection_ = NULL;
329 }
330 last_output_surface_id_ = output_surface_id;
331 }
332 if (frame_size.IsEmpty()) {
333 DCHECK(frame_data->resource_list.empty());
334 EvictDelegatedFrame();
335 } else {
336 if (!resource_collection_) {
337 resource_collection_ = new cc::DelegatedFrameResourceCollection;
338 resource_collection_->SetClient(this);
339 }
340 // If the physical frame size changes, we need a new |frame_provider_|. If
341 // the physical frame size is the same, but the size in DIP changed, we
342 // need to adjust the scale at which the frames will be drawn, and we do
343 // this by making a new |frame_provider_| also to ensure the scale change
344 // is presented in sync with the new frame content.
345 if (!frame_provider_.get() || frame_size != frame_provider_->frame_size() ||
346 frame_size_in_dip != current_frame_size_in_dip_) {
347 frame_provider_ = new cc::DelegatedFrameProvider(
348 resource_collection_.get(), frame_data.Pass());
349 client_->GetLayer()->SetShowDelegatedContent(frame_provider_.get(),
350 frame_size_in_dip);
351 } else {
352 frame_provider_->SetFrameData(frame_data.Pass());
353 }
354 }
355 released_front_lock_ = NULL;
356 current_frame_size_in_dip_ = frame_size_in_dip;
357 CheckResizeLock();
358
359 client_->SchedulePaintInRect(damage_rect_in_dip);
360
361 pending_delegated_ack_count_++;
362
363 ui::Compositor* compositor = client_->GetCompositor();
364 if (!compositor) {
365 SendDelegatedFrameAck(output_surface_id);
366 } else {
367 std::vector<ui::LatencyInfo>::const_iterator it;
368 for (it = latency_info.begin(); it != latency_info.end(); ++it)
369 compositor->SetLatencyInfo(*it);
370 // If we've previously skipped any latency infos add them.
371 for (it = skipped_latency_info_list_.begin();
372 it != skipped_latency_info_list_.end();
373 ++it)
374 compositor->SetLatencyInfo(*it);
375 skipped_latency_info_list_.clear();
376 AddOnCommitCallbackAndDisableLocks(
377 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck,
378 AsWeakPtr(),
379 output_surface_id));
380 }
381 DidReceiveFrameFromRenderer();
382 if (frame_provider_.get())
383 delegated_frame_evictor_->SwappedFrame(!host->is_hidden());
384 // Note: the frame may have been evicted immediately.
385 }
386
SendDelegatedFrameAck(uint32 output_surface_id)387 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) {
388 RenderWidgetHostImpl* host = client_->GetHost();
389 cc::CompositorFrameAck ack;
390 if (resource_collection_)
391 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
392 RenderWidgetHostImpl::SendSwapCompositorFrameAck(host->GetRoutingID(),
393 output_surface_id,
394 host->GetProcess()->GetID(),
395 ack);
396 DCHECK_GT(pending_delegated_ack_count_, 0);
397 pending_delegated_ack_count_--;
398 }
399
UnusedResourcesAreAvailable()400 void DelegatedFrameHost::UnusedResourcesAreAvailable() {
401 if (pending_delegated_ack_count_)
402 return;
403
404 SendReturnedDelegatedResources(last_output_surface_id_);
405 }
406
SendReturnedDelegatedResources(uint32 output_surface_id)407 void DelegatedFrameHost::SendReturnedDelegatedResources(
408 uint32 output_surface_id) {
409 RenderWidgetHostImpl* host = client_->GetHost();
410 DCHECK(resource_collection_);
411
412 cc::CompositorFrameAck ack;
413 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
414 DCHECK(!ack.resources.empty());
415
416 RenderWidgetHostImpl::SendReclaimCompositorResources(
417 host->GetRoutingID(),
418 output_surface_id,
419 host->GetProcess()->GetID(),
420 ack);
421 }
422
EvictDelegatedFrame()423 void DelegatedFrameHost::EvictDelegatedFrame() {
424 client_->GetLayer()->SetShowPaintedContent();
425 frame_provider_ = NULL;
426 delegated_frame_evictor_->DiscardedFrame();
427 }
428
429 // static
CopyFromCompositingSurfaceHasResult(const gfx::Size & dst_size_in_pixel,const SkBitmap::Config config,const base::Callback<void (bool,const SkBitmap &)> & callback,scoped_ptr<cc::CopyOutputResult> result)430 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
431 const gfx::Size& dst_size_in_pixel,
432 const SkBitmap::Config config,
433 const base::Callback<void(bool, const SkBitmap&)>& callback,
434 scoped_ptr<cc::CopyOutputResult> result) {
435 if (result->IsEmpty() || result->size().IsEmpty()) {
436 callback.Run(false, SkBitmap());
437 return;
438 }
439
440 if (result->HasTexture()) {
441 PrepareTextureCopyOutputResult(dst_size_in_pixel, config,
442 callback,
443 result.Pass());
444 return;
445 }
446
447 DCHECK(result->HasBitmap());
448 PrepareBitmapCopyOutputResult(dst_size_in_pixel, config, callback,
449 result.Pass());
450 }
451
CopyFromCompositingSurfaceFinished(const base::Callback<void (bool,const SkBitmap &)> & callback,scoped_ptr<cc::SingleReleaseCallback> release_callback,scoped_ptr<SkBitmap> bitmap,scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,bool result)452 static void CopyFromCompositingSurfaceFinished(
453 const base::Callback<void(bool, const SkBitmap&)>& callback,
454 scoped_ptr<cc::SingleReleaseCallback> release_callback,
455 scoped_ptr<SkBitmap> bitmap,
456 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
457 bool result) {
458 bitmap_pixels_lock.reset();
459
460 uint32 sync_point = 0;
461 if (result) {
462 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
463 sync_point = gl_helper->InsertSyncPoint();
464 }
465 bool lost_resource = sync_point == 0;
466 release_callback->Run(sync_point, lost_resource);
467
468 callback.Run(result, *bitmap);
469 }
470
471 // static
PrepareTextureCopyOutputResult(const gfx::Size & dst_size_in_pixel,const SkBitmap::Config config,const base::Callback<void (bool,const SkBitmap &)> & callback,scoped_ptr<cc::CopyOutputResult> result)472 void DelegatedFrameHost::PrepareTextureCopyOutputResult(
473 const gfx::Size& dst_size_in_pixel,
474 const SkBitmap::Config config,
475 const base::Callback<void(bool, const SkBitmap&)>& callback,
476 scoped_ptr<cc::CopyOutputResult> result) {
477 DCHECK(result->HasTexture());
478 base::ScopedClosureRunner scoped_callback_runner(
479 base::Bind(callback, false, SkBitmap()));
480
481 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
482 bitmap->setConfig(config,
483 dst_size_in_pixel.width(), dst_size_in_pixel.height(),
484 0, kOpaque_SkAlphaType);
485 if (!bitmap->allocPixels())
486 return;
487
488 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
489 GLHelper* gl_helper = factory->GetGLHelper();
490 if (!gl_helper)
491 return;
492
493 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
494 new SkAutoLockPixels(*bitmap));
495 uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
496
497 cc::TextureMailbox texture_mailbox;
498 scoped_ptr<cc::SingleReleaseCallback> release_callback;
499 result->TakeTexture(&texture_mailbox, &release_callback);
500 DCHECK(texture_mailbox.IsTexture());
501
502 ignore_result(scoped_callback_runner.Release());
503
504 gl_helper->CropScaleReadbackAndCleanMailbox(
505 texture_mailbox.mailbox(),
506 texture_mailbox.sync_point(),
507 result->size(),
508 gfx::Rect(result->size()),
509 dst_size_in_pixel,
510 pixels,
511 config,
512 base::Bind(&CopyFromCompositingSurfaceFinished,
513 callback,
514 base::Passed(&release_callback),
515 base::Passed(&bitmap),
516 base::Passed(&bitmap_pixels_lock)),
517 GLHelper::SCALER_QUALITY_FAST);
518 }
519
520 // static
PrepareBitmapCopyOutputResult(const gfx::Size & dst_size_in_pixel,const SkBitmap::Config config,const base::Callback<void (bool,const SkBitmap &)> & callback,scoped_ptr<cc::CopyOutputResult> result)521 void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
522 const gfx::Size& dst_size_in_pixel,
523 const SkBitmap::Config config,
524 const base::Callback<void(bool, const SkBitmap&)>& callback,
525 scoped_ptr<cc::CopyOutputResult> result) {
526 if (config != SkBitmap::kARGB_8888_Config) {
527 NOTIMPLEMENTED();
528 callback.Run(false, SkBitmap());
529 return;
530 }
531 DCHECK(result->HasBitmap());
532 scoped_ptr<SkBitmap> source = result->TakeBitmap();
533 DCHECK(source);
534 SkBitmap bitmap = skia::ImageOperations::Resize(
535 *source,
536 skia::ImageOperations::RESIZE_BEST,
537 dst_size_in_pixel.width(),
538 dst_size_in_pixel.height());
539 callback.Run(true, bitmap);
540 }
541
542 // static
ReturnSubscriberTexture(base::WeakPtr<DelegatedFrameHost> dfh,scoped_refptr<OwnedMailbox> subscriber_texture,uint32 sync_point)543 void DelegatedFrameHost::ReturnSubscriberTexture(
544 base::WeakPtr<DelegatedFrameHost> dfh,
545 scoped_refptr<OwnedMailbox> subscriber_texture,
546 uint32 sync_point) {
547 if (!subscriber_texture.get())
548 return;
549 if (!dfh)
550 return;
551
552 subscriber_texture->UpdateSyncPoint(sync_point);
553
554 if (dfh->frame_subscriber_ && subscriber_texture->texture_id())
555 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture);
556 }
557
CopyFromCompositingSurfaceFinishedForVideo(base::WeakPtr<DelegatedFrameHost> dfh,const base::Callback<void (bool)> & callback,scoped_refptr<OwnedMailbox> subscriber_texture,scoped_ptr<cc::SingleReleaseCallback> release_callback,bool result)558 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
559 base::WeakPtr<DelegatedFrameHost> dfh,
560 const base::Callback<void(bool)>& callback,
561 scoped_refptr<OwnedMailbox> subscriber_texture,
562 scoped_ptr<cc::SingleReleaseCallback> release_callback,
563 bool result) {
564 callback.Run(result);
565
566 uint32 sync_point = 0;
567 if (result) {
568 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
569 sync_point = gl_helper->InsertSyncPoint();
570 }
571 if (release_callback) {
572 // A release callback means the texture came from the compositor, so there
573 // should be no |subscriber_texture|.
574 DCHECK(!subscriber_texture);
575 bool lost_resource = sync_point == 0;
576 release_callback->Run(sync_point, lost_resource);
577 }
578 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point);
579 }
580
581 // static
CopyFromCompositingSurfaceHasResultForVideo(base::WeakPtr<DelegatedFrameHost> dfh,scoped_refptr<OwnedMailbox> subscriber_texture,scoped_refptr<media::VideoFrame> video_frame,const base::Callback<void (bool)> & callback,scoped_ptr<cc::CopyOutputResult> result)582 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
583 base::WeakPtr<DelegatedFrameHost> dfh,
584 scoped_refptr<OwnedMailbox> subscriber_texture,
585 scoped_refptr<media::VideoFrame> video_frame,
586 const base::Callback<void(bool)>& callback,
587 scoped_ptr<cc::CopyOutputResult> result) {
588 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
589 base::ScopedClosureRunner scoped_return_subscriber_texture(
590 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0));
591
592 if (!dfh)
593 return;
594 if (result->IsEmpty())
595 return;
596 if (result->size().IsEmpty())
597 return;
598
599 // Compute the dest size we want after the letterboxing resize. Make the
600 // coordinates and sizes even because we letterbox in YUV space
601 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
602 // line up correctly.
603 // The video frame's coded_size() and the result's size() are both physical
604 // pixels.
605 gfx::Rect region_in_frame =
606 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()),
607 result->size());
608 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
609 region_in_frame.y() & ~1,
610 region_in_frame.width() & ~1,
611 region_in_frame.height() & ~1);
612 if (region_in_frame.IsEmpty())
613 return;
614
615 if (!result->HasTexture()) {
616 DCHECK(result->HasBitmap());
617 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap();
618 // Scale the bitmap to the required size, if necessary.
619 SkBitmap scaled_bitmap;
620 if (result->size().width() != region_in_frame.width() ||
621 result->size().height() != region_in_frame.height()) {
622 skia::ImageOperations::ResizeMethod method =
623 skia::ImageOperations::RESIZE_GOOD;
624 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method,
625 region_in_frame.width(),
626 region_in_frame.height());
627 } else {
628 scaled_bitmap = *bitmap.get();
629 }
630
631 {
632 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
633
634 media::CopyRGBToVideoFrame(
635 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
636 scaled_bitmap.rowBytes(),
637 region_in_frame,
638 video_frame.get());
639 }
640 ignore_result(scoped_callback_runner.Release());
641 callback.Run(true);
642 return;
643 }
644
645 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
646 GLHelper* gl_helper = factory->GetGLHelper();
647 if (!gl_helper)
648 return;
649 if (subscriber_texture.get() && !subscriber_texture->texture_id())
650 return;
651
652 cc::TextureMailbox texture_mailbox;
653 scoped_ptr<cc::SingleReleaseCallback> release_callback;
654 result->TakeTexture(&texture_mailbox, &release_callback);
655 DCHECK(texture_mailbox.IsTexture());
656
657 gfx::Rect result_rect(result->size());
658
659 content::ReadbackYUVInterface* yuv_readback_pipeline =
660 dfh->yuv_readback_pipeline_.get();
661 if (yuv_readback_pipeline == NULL ||
662 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() ||
663 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect ||
664 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) {
665 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST;
666 std::string quality_switch = switches::kTabCaptureDownscaleQuality;
667 // If we're scaling up, we can use the "best" quality.
668 if (result_rect.size().width() < region_in_frame.size().width() &&
669 result_rect.size().height() < region_in_frame.size().height())
670 quality_switch = switches::kTabCaptureUpscaleQuality;
671
672 std::string switch_value =
673 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(quality_switch);
674 if (switch_value == "fast")
675 quality = GLHelper::SCALER_QUALITY_FAST;
676 else if (switch_value == "good")
677 quality = GLHelper::SCALER_QUALITY_GOOD;
678 else if (switch_value == "best")
679 quality = GLHelper::SCALER_QUALITY_BEST;
680
681 dfh->yuv_readback_pipeline_.reset(
682 gl_helper->CreateReadbackPipelineYUV(quality,
683 result_rect.size(),
684 result_rect,
685 video_frame->coded_size(),
686 region_in_frame,
687 true,
688 true));
689 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get();
690 }
691
692 ignore_result(scoped_callback_runner.Release());
693 ignore_result(scoped_return_subscriber_texture.Release());
694 base::Callback<void(bool result)> finished_callback = base::Bind(
695 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo,
696 dfh->AsWeakPtr(),
697 callback,
698 subscriber_texture,
699 base::Passed(&release_callback));
700 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
701 texture_mailbox.sync_point(),
702 video_frame.get(),
703 finished_callback);
704 }
705
706 ////////////////////////////////////////////////////////////////////////////////
707 // DelegatedFrameHost, ui::CompositorObserver implementation:
708
OnCompositingDidCommit(ui::Compositor * compositor)709 void DelegatedFrameHost::OnCompositingDidCommit(
710 ui::Compositor* compositor) {
711 RenderWidgetHostImpl* host = client_->GetHost();
712 if (can_lock_compositor_ == NO_PENDING_COMMIT) {
713 can_lock_compositor_ = YES_CAN_LOCK;
714 if (resize_lock_.get() && resize_lock_->GrabDeferredLock())
715 can_lock_compositor_ = YES_DID_LOCK;
716 }
717 RunOnCommitCallbacks();
718 if (resize_lock_ &&
719 resize_lock_->expected_size() == current_frame_size_in_dip_) {
720 resize_lock_.reset();
721 host->WasResized();
722 // We may have had a resize while we had the lock (e.g. if the lock expired,
723 // or if the UI still gave us some resizes), so make sure we grab a new lock
724 // if necessary.
725 MaybeCreateResizeLock();
726 }
727 }
728
OnCompositingStarted(ui::Compositor * compositor,base::TimeTicks start_time)729 void DelegatedFrameHost::OnCompositingStarted(
730 ui::Compositor* compositor, base::TimeTicks start_time) {
731 last_draw_ended_ = start_time;
732 }
733
OnCompositingEnded(ui::Compositor * compositor)734 void DelegatedFrameHost::OnCompositingEnded(
735 ui::Compositor* compositor) {
736 }
737
OnCompositingAborted(ui::Compositor * compositor)738 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
739 }
740
OnCompositingLockStateChanged(ui::Compositor * compositor)741 void DelegatedFrameHost::OnCompositingLockStateChanged(
742 ui::Compositor* compositor) {
743 // A compositor lock that is part of a resize lock timed out. We
744 // should display a renderer frame.
745 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) {
746 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME;
747 }
748 }
749
OnUpdateVSyncParameters(base::TimeTicks timebase,base::TimeDelta interval)750 void DelegatedFrameHost::OnUpdateVSyncParameters(
751 base::TimeTicks timebase,
752 base::TimeDelta interval) {
753 RenderWidgetHostImpl* host = client_->GetHost();
754 if (client_->IsVisible())
755 host->UpdateVSyncParameters(timebase, interval);
756 }
757
758 ////////////////////////////////////////////////////////////////////////////////
759 // RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation:
760
OnLostResources()761 void DelegatedFrameHost::OnLostResources() {
762 RenderWidgetHostImpl* host = client_->GetHost();
763 if (frame_provider_.get())
764 EvictDelegatedFrame();
765 idle_frame_subscriber_textures_.clear();
766 yuv_readback_pipeline_.reset();
767
768 host->ScheduleComposite();
769 }
770
771 ////////////////////////////////////////////////////////////////////////////////
772 // DelegatedFrameHost, private:
773
~DelegatedFrameHost()774 DelegatedFrameHost::~DelegatedFrameHost() {
775 ImageTransportFactory::GetInstance()->RemoveObserver(this);
776
777 if (resource_collection_.get())
778 resource_collection_->SetClient(NULL);
779
780 DCHECK(!vsync_manager_);
781 }
782
RunOnCommitCallbacks()783 void DelegatedFrameHost::RunOnCommitCallbacks() {
784 for (std::vector<base::Closure>::const_iterator
785 it = on_compositing_did_commit_callbacks_.begin();
786 it != on_compositing_did_commit_callbacks_.end(); ++it) {
787 it->Run();
788 }
789 on_compositing_did_commit_callbacks_.clear();
790 }
791
AddOnCommitCallbackAndDisableLocks(const base::Closure & callback)792 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks(
793 const base::Closure& callback) {
794 ui::Compositor* compositor = client_->GetCompositor();
795 DCHECK(compositor);
796
797 if (!compositor->HasObserver(this))
798 compositor->AddObserver(this);
799
800 can_lock_compositor_ = NO_PENDING_COMMIT;
801 on_compositing_did_commit_callbacks_.push_back(callback);
802 }
803
AddedToWindow()804 void DelegatedFrameHost::AddedToWindow() {
805 ui::Compositor* compositor = client_->GetCompositor();
806 if (compositor) {
807 DCHECK(!vsync_manager_);
808 vsync_manager_ = compositor->vsync_manager();
809 vsync_manager_->AddObserver(this);
810 }
811 }
812
RemovingFromWindow()813 void DelegatedFrameHost::RemovingFromWindow() {
814 RunOnCommitCallbacks();
815 resize_lock_.reset();
816 client_->GetHost()->WasResized();
817 ui::Compositor* compositor = client_->GetCompositor();
818 if (compositor && compositor->HasObserver(this))
819 compositor->RemoveObserver(this);
820
821 if (vsync_manager_) {
822 vsync_manager_->RemoveObserver(this);
823 vsync_manager_ = NULL;
824 }
825 }
826
LockResources()827 void DelegatedFrameHost::LockResources() {
828 DCHECK(frame_provider_);
829 delegated_frame_evictor_->LockFrame();
830 }
831
UnlockResources()832 void DelegatedFrameHost::UnlockResources() {
833 DCHECK(frame_provider_);
834 delegated_frame_evictor_->UnlockFrame();
835 }
836
837 ////////////////////////////////////////////////////////////////////////////////
838 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
839
OnLayerRecreated(ui::Layer * old_layer,ui::Layer * new_layer)840 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer,
841 ui::Layer* new_layer) {
842 // The new_layer is the one that will be used by our Window, so that's the one
843 // that should keep our frame. old_layer will be returned to the
844 // RecreateLayer caller, and should have a copy.
845 if (frame_provider_.get()) {
846 new_layer->SetShowDelegatedContent(frame_provider_.get(),
847 current_frame_size_in_dip_);
848 }
849 }
850
851 } // namespace content
852
853