• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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