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