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  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
147void 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
188bool DelegatedFrameHost::CanCopyToBitmap() const {
189  return client_->GetCompositor() &&
190         client_->GetLayer()->has_external_content();
191}
192
193bool DelegatedFrameHost::CanCopyToVideoFrame() const {
194  return client_->GetCompositor() &&
195         client_->GetLayer()->has_external_content();
196}
197
198bool DelegatedFrameHost::CanSubscribeFrame() const {
199  return true;
200}
201
202void DelegatedFrameHost::BeginFrameSubscription(
203    scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
204  frame_subscriber_ = subscriber.Pass();
205}
206
207void DelegatedFrameHost::EndFrameSubscription() {
208  idle_frame_subscriber_textures_.clear();
209  frame_subscriber_.reset();
210}
211
212bool 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
224void DelegatedFrameHost::WasResized() {
225  MaybeCreateResizeLock();
226}
227
228gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
229  if (resize_lock_)
230    return resize_lock_->expected_size();
231  else
232    return client_->DesiredFrameSize();
233}
234
235void 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
251void 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
266void 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
387void 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
400void DelegatedFrameHost::UnusedResourcesAreAvailable() {
401  if (pending_delegated_ack_count_)
402    return;
403
404  SendReturnedDelegatedResources(last_output_surface_id_);
405}
406
407void 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
423void DelegatedFrameHost::EvictDelegatedFrame() {
424  client_->GetLayer()->SetShowPaintedContent();
425  frame_provider_ = NULL;
426  delegated_frame_evictor_->DiscardedFrame();
427}
428
429// static
430void 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
452static 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
472void 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
521void 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
543void 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
558void 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
582void 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
709void 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
729void DelegatedFrameHost::OnCompositingStarted(
730    ui::Compositor* compositor, base::TimeTicks start_time) {
731  last_draw_ended_ = start_time;
732}
733
734void DelegatedFrameHost::OnCompositingEnded(
735    ui::Compositor* compositor) {
736}
737
738void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) {
739}
740
741void 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
750void 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
761void 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
774DelegatedFrameHost::~DelegatedFrameHost() {
775  ImageTransportFactory::GetInstance()->RemoveObserver(this);
776
777  if (resource_collection_.get())
778    resource_collection_->SetClient(NULL);
779
780  DCHECK(!vsync_manager_);
781}
782
783void 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
792void 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
804void 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
813void 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
827void DelegatedFrameHost::LockResources() {
828  DCHECK(frame_provider_);
829  delegated_frame_evictor_->LockFrame();
830}
831
832void DelegatedFrameHost::UnlockResources() {
833  DCHECK(frame_provider_);
834  delegated_frame_evictor_->UnlockFrame();
835}
836
837////////////////////////////////////////////////////////////////////////////////
838// DelegatedFrameHost, ui::LayerOwnerDelegate implementation:
839
840void 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