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