Home | History | Annotate | Download | only in surfaces
      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 "cc/surfaces/surface_aggregator.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/containers/hash_tables.h"
      9 #include "base/debug/trace_event.h"
     10 #include "base/logging.h"
     11 #include "cc/base/math_util.h"
     12 #include "cc/output/compositor_frame.h"
     13 #include "cc/output/delegated_frame_data.h"
     14 #include "cc/quads/draw_quad.h"
     15 #include "cc/quads/render_pass_draw_quad.h"
     16 #include "cc/quads/shared_quad_state.h"
     17 #include "cc/quads/surface_draw_quad.h"
     18 #include "cc/surfaces/surface.h"
     19 #include "cc/surfaces/surface_factory.h"
     20 #include "cc/surfaces/surface_manager.h"
     21 #include "cc/trees/blocking_task_runner.h"
     22 
     23 namespace cc {
     24 
     25 SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager,
     26                                      ResourceProvider* provider)
     27     : manager_(manager), provider_(provider) {
     28   DCHECK(manager_);
     29 }
     30 
     31 SurfaceAggregator::~SurfaceAggregator() {}
     32 
     33 class SurfaceAggregator::RenderPassIdAllocator {
     34  public:
     35   explicit RenderPassIdAllocator(SurfaceId surface_id)
     36       : surface_id_(surface_id), next_index_(1) {}
     37   ~RenderPassIdAllocator() {}
     38 
     39   void AddKnownPass(RenderPassId id) {
     40     if (id_to_index_map_.find(id) != id_to_index_map_.end())
     41       return;
     42     id_to_index_map_[id] = next_index_++;
     43   }
     44 
     45   RenderPassId Remap(RenderPassId id) {
     46     DCHECK(id_to_index_map_.find(id) != id_to_index_map_.end());
     47     return RenderPassId(surface_id_.id, id_to_index_map_[id]);
     48   }
     49 
     50  private:
     51   base::hash_map<RenderPassId, int> id_to_index_map_;
     52   SurfaceId surface_id_;
     53   int next_index_;
     54 
     55   DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator);
     56 };
     57 
     58 static void UnrefHelper(base::WeakPtr<SurfaceFactory> surface_factory,
     59                         const ReturnedResourceArray& resources,
     60                         BlockingTaskRunner* main_thread_task_runner) {
     61   if (surface_factory)
     62     surface_factory->UnrefResources(resources);
     63 }
     64 
     65 RenderPassId SurfaceAggregator::RemapPassId(RenderPassId surface_local_pass_id,
     66                                             SurfaceId surface_id) {
     67   RenderPassIdAllocator* allocator = render_pass_allocator_map_.get(surface_id);
     68   if (!allocator) {
     69     allocator = new RenderPassIdAllocator(surface_id);
     70     render_pass_allocator_map_.set(surface_id, make_scoped_ptr(allocator));
     71   }
     72   allocator->AddKnownPass(surface_local_pass_id);
     73   return allocator->Remap(surface_local_pass_id);
     74 }
     75 
     76 int SurfaceAggregator::ChildIdForSurface(Surface* surface) {
     77   SurfaceToResourceChildIdMap::iterator it =
     78       surface_id_to_resource_child_id_.find(surface->surface_id());
     79   if (it == surface_id_to_resource_child_id_.end()) {
     80     int child_id = provider_->CreateChild(
     81         base::Bind(&UnrefHelper, surface->factory()->AsWeakPtr()));
     82     surface_id_to_resource_child_id_[surface->surface_id()] = child_id;
     83     return child_id;
     84   } else {
     85     return it->second;
     86   }
     87 }
     88 
     89 static ResourceProvider::ResourceId ResourceRemapHelper(
     90     bool* invalid_frame,
     91     const ResourceProvider::ResourceIdMap& child_to_parent_map,
     92     ResourceProvider::ResourceIdArray* resources_in_frame,
     93     ResourceProvider::ResourceId id) {
     94   ResourceProvider::ResourceIdMap::const_iterator it =
     95       child_to_parent_map.find(id);
     96   if (it == child_to_parent_map.end()) {
     97     *invalid_frame = true;
     98     return 0;
     99   }
    100 
    101   DCHECK_EQ(it->first, id);
    102   ResourceProvider::ResourceId remapped_id = it->second;
    103   resources_in_frame->push_back(id);
    104   return remapped_id;
    105 }
    106 
    107 bool SurfaceAggregator::TakeResources(Surface* surface,
    108                                       const DelegatedFrameData* frame_data,
    109                                       RenderPassList* render_pass_list) {
    110   RenderPass::CopyAll(frame_data->render_pass_list, render_pass_list);
    111   if (!provider_)  // TODO(jamesr): hack for unit tests that don't set up rp
    112     return false;
    113 
    114   int child_id = ChildIdForSurface(surface);
    115   provider_->ReceiveFromChild(child_id, frame_data->resource_list);
    116   surface->factory()->RefResources(frame_data->resource_list);
    117 
    118   typedef ResourceProvider::ResourceIdArray IdArray;
    119   IdArray referenced_resources;
    120 
    121   bool invalid_frame = false;
    122   DrawQuad::ResourceIteratorCallback remap =
    123       base::Bind(&ResourceRemapHelper,
    124                  &invalid_frame,
    125                  provider_->GetChildToParentMap(child_id),
    126                  &referenced_resources);
    127   for (RenderPassList::iterator it = render_pass_list->begin();
    128        it != render_pass_list->end();
    129        ++it) {
    130     QuadList& quad_list = (*it)->quad_list;
    131     for (QuadList::Iterator quad_it = quad_list.begin();
    132          quad_it != quad_list.end();
    133          ++quad_it) {
    134       quad_it->IterateResources(remap);
    135     }
    136   }
    137   if (!invalid_frame)
    138     provider_->DeclareUsedResourcesFromChild(child_id, referenced_resources);
    139 
    140   return invalid_frame;
    141 }
    142 
    143 gfx::Rect SurfaceAggregator::DamageRectForSurface(const Surface* surface,
    144                                                   const RenderPass& source) {
    145   int previous_index = previous_contained_surfaces_[surface->surface_id()];
    146   if (previous_index == surface->frame_index())
    147     return gfx::Rect();
    148   else if (previous_index == surface->frame_index() - 1)
    149     return source.damage_rect;
    150   return gfx::Rect(surface->size());
    151 }
    152 
    153 void SurfaceAggregator::HandleSurfaceQuad(const SurfaceDrawQuad* surface_quad,
    154                                           RenderPass* dest_pass) {
    155   SurfaceId surface_id = surface_quad->surface_id;
    156   // If this surface's id is already in our referenced set then it creates
    157   // a cycle in the graph and should be dropped.
    158   if (referenced_surfaces_.count(surface_id))
    159     return;
    160   Surface* surface = manager_->GetSurfaceForId(surface_id);
    161   if (!surface) {
    162     contained_surfaces_[surface_id] = 0;
    163     return;
    164   }
    165   contained_surfaces_[surface_id] = surface->frame_index();
    166   const CompositorFrame* frame = surface->GetEligibleFrame();
    167   if (!frame)
    168     return;
    169   const DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
    170   if (!frame_data)
    171     return;
    172 
    173   RenderPassList render_pass_list;
    174   bool invalid_frame = TakeResources(surface, frame_data, &render_pass_list);
    175   if (invalid_frame)
    176     return;
    177 
    178   SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first;
    179 
    180   ScopedPtrVector<CopyOutputRequest> copy_requests;
    181   surface->TakeCopyOutputRequests(&copy_requests);
    182 
    183   bool merge_pass = copy_requests.empty();
    184 
    185   const RenderPassList& referenced_passes = render_pass_list;
    186   size_t passes_to_copy =
    187       merge_pass ? referenced_passes.size() - 1 : referenced_passes.size();
    188   for (size_t j = 0; j < passes_to_copy; ++j) {
    189     const RenderPass& source = *referenced_passes[j];
    190 
    191     scoped_ptr<RenderPass> copy_pass(RenderPass::Create());
    192 
    193     RenderPassId remapped_pass_id = RemapPassId(source.id, surface_id);
    194 
    195     copy_pass->SetAll(remapped_pass_id,
    196                       source.output_rect,
    197                       source.damage_rect,
    198                       source.transform_to_root_target,
    199                       source.has_transparent_background);
    200 
    201     // Contributing passes aggregated in to the pass list need to take the
    202     // transform of the surface quad into account to update their transform to
    203     // the root surface.
    204     // TODO(jamesr): Make sure this is sufficient for surfaces nested several
    205     // levels deep and add tests.
    206     copy_pass->transform_to_root_target.ConcatTransform(
    207         surface_quad->quadTransform());
    208 
    209     CopyQuadsToPass(source.quad_list,
    210                     source.shared_quad_state_list,
    211                     gfx::Transform(),
    212                     copy_pass.get(),
    213                     surface_id);
    214 
    215     dest_pass_list_->push_back(copy_pass.Pass());
    216   }
    217 
    218   const RenderPass& last_pass = *render_pass_list.back();
    219   if (merge_pass) {
    220     // TODO(jamesr): Clean up last pass special casing.
    221     const QuadList& quads = last_pass.quad_list;
    222 
    223     // TODO(jamesr): Make sure clipping is enforced.
    224     CopyQuadsToPass(quads,
    225                     last_pass.shared_quad_state_list,
    226                     surface_quad->quadTransform(),
    227                     dest_pass,
    228                     surface_id);
    229   } else {
    230     RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id);
    231 
    232     dest_pass_list_->back()->copy_requests.swap(copy_requests);
    233 
    234     SharedQuadState* shared_quad_state =
    235         dest_pass->CreateAndAppendSharedQuadState();
    236     shared_quad_state->CopyFrom(surface_quad->shared_quad_state);
    237     RenderPassDrawQuad* quad =
    238         dest_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
    239     quad->SetNew(shared_quad_state,
    240                  surface_quad->rect,
    241                  surface_quad->visible_rect,
    242                  remapped_pass_id,
    243                  0,
    244                  gfx::RectF(),
    245                  FilterOperations(),
    246                  gfx::Vector2dF(),
    247                  FilterOperations());
    248   }
    249   dest_pass->damage_rect =
    250       gfx::UnionRects(dest_pass->damage_rect,
    251                       MathUtil::MapEnclosingClippedRect(
    252                           surface_quad->quadTransform(),
    253                           DamageRectForSurface(surface, last_pass)));
    254 
    255   referenced_surfaces_.erase(it);
    256 }
    257 
    258 void SurfaceAggregator::CopySharedQuadState(
    259     const SharedQuadState* source_sqs,
    260     const gfx::Transform& content_to_target_transform,
    261     RenderPass* dest_render_pass) {
    262   SharedQuadState* copy_shared_quad_state =
    263       dest_render_pass->CreateAndAppendSharedQuadState();
    264   copy_shared_quad_state->CopyFrom(source_sqs);
    265   // content_to_target_transform contains any transformation that may exist
    266   // between the context that these quads are being copied from (i.e. the
    267   // surface's draw transform when aggregated from within a surface) to the
    268   // target space of the pass. This will be identity except when copying the
    269   // root draw pass from a surface into a pass when the surface draw quad's
    270   // transform is not identity.
    271   copy_shared_quad_state->content_to_target_transform.ConcatTransform(
    272       content_to_target_transform);
    273   if (copy_shared_quad_state->is_clipped) {
    274     copy_shared_quad_state->clip_rect = MathUtil::MapEnclosingClippedRect(
    275         content_to_target_transform, copy_shared_quad_state->clip_rect);
    276   }
    277 }
    278 
    279 void SurfaceAggregator::CopyQuadsToPass(
    280     const QuadList& source_quad_list,
    281     const SharedQuadStateList& source_shared_quad_state_list,
    282     const gfx::Transform& content_to_target_transform,
    283     RenderPass* dest_pass,
    284     SurfaceId surface_id) {
    285   const SharedQuadState* last_copied_source_shared_quad_state = NULL;
    286 
    287   size_t sqs_i = 0;
    288   for (QuadList::ConstIterator iter = source_quad_list.begin();
    289        iter != source_quad_list.end();
    290        ++iter) {
    291     const DrawQuad* quad = &*iter;
    292     while (quad->shared_quad_state != source_shared_quad_state_list[sqs_i]) {
    293       ++sqs_i;
    294       DCHECK_LT(sqs_i, source_shared_quad_state_list.size());
    295     }
    296     DCHECK_EQ(quad->shared_quad_state, source_shared_quad_state_list[sqs_i]);
    297 
    298     if (quad->material == DrawQuad::SURFACE_CONTENT) {
    299       const SurfaceDrawQuad* surface_quad = SurfaceDrawQuad::MaterialCast(quad);
    300       HandleSurfaceQuad(surface_quad, dest_pass);
    301     } else {
    302       if (quad->shared_quad_state != last_copied_source_shared_quad_state) {
    303         CopySharedQuadState(
    304             quad->shared_quad_state, content_to_target_transform, dest_pass);
    305         last_copied_source_shared_quad_state = quad->shared_quad_state;
    306       }
    307       if (quad->material == DrawQuad::RENDER_PASS) {
    308         const RenderPassDrawQuad* pass_quad =
    309             RenderPassDrawQuad::MaterialCast(quad);
    310         RenderPassId original_pass_id = pass_quad->render_pass_id;
    311         RenderPassId remapped_pass_id =
    312             RemapPassId(original_pass_id, surface_id);
    313 
    314         dest_pass->CopyFromAndAppendRenderPassDrawQuad(
    315             pass_quad,
    316             dest_pass->shared_quad_state_list.back(),
    317             remapped_pass_id);
    318       } else {
    319         dest_pass->CopyFromAndAppendDrawQuad(
    320             quad, dest_pass->shared_quad_state_list.back());
    321       }
    322     }
    323   }
    324 }
    325 
    326 void SurfaceAggregator::CopyPasses(const RenderPassList& source_pass_list,
    327                                    const Surface* surface) {
    328   for (size_t i = 0; i < source_pass_list.size(); ++i) {
    329     const RenderPass& source = *source_pass_list[i];
    330 
    331     scoped_ptr<RenderPass> copy_pass(RenderPass::Create());
    332 
    333     RenderPassId remapped_pass_id =
    334         RemapPassId(source.id, surface->surface_id());
    335 
    336     copy_pass->SetAll(remapped_pass_id,
    337                       source.output_rect,
    338                       DamageRectForSurface(surface, source),
    339                       source.transform_to_root_target,
    340                       source.has_transparent_background);
    341 
    342     CopyQuadsToPass(source.quad_list,
    343                     source.shared_quad_state_list,
    344                     gfx::Transform(),
    345                     copy_pass.get(),
    346                     surface->surface_id());
    347 
    348     dest_pass_list_->push_back(copy_pass.Pass());
    349   }
    350 }
    351 
    352 scoped_ptr<CompositorFrame> SurfaceAggregator::Aggregate(SurfaceId surface_id) {
    353   Surface* surface = manager_->GetSurfaceForId(surface_id);
    354   DCHECK(surface);
    355   contained_surfaces_[surface_id] = surface->frame_index();
    356   const CompositorFrame* root_surface_frame = surface->GetEligibleFrame();
    357   if (!root_surface_frame)
    358     return scoped_ptr<CompositorFrame>();
    359   TRACE_EVENT0("cc", "SurfaceAggregator::Aggregate");
    360 
    361   scoped_ptr<CompositorFrame> frame(new CompositorFrame);
    362   frame->delegated_frame_data = make_scoped_ptr(new DelegatedFrameData);
    363 
    364   DCHECK(root_surface_frame->delegated_frame_data);
    365 
    366   RenderPassList source_pass_list;
    367 
    368   SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first;
    369 
    370   dest_resource_list_ = &frame->delegated_frame_data->resource_list;
    371   dest_pass_list_ = &frame->delegated_frame_data->render_pass_list;
    372 
    373   bool invalid_frame =
    374       TakeResources(surface,
    375                     root_surface_frame->delegated_frame_data.get(),
    376                     &source_pass_list);
    377   DCHECK(!invalid_frame);
    378 
    379   CopyPasses(source_pass_list, surface);
    380 
    381   referenced_surfaces_.erase(it);
    382   DCHECK(referenced_surfaces_.empty());
    383 
    384   dest_pass_list_ = NULL;
    385   contained_surfaces_.swap(previous_contained_surfaces_);
    386   contained_surfaces_.clear();
    387 
    388   for (SurfaceIndexMap::iterator it = previous_contained_surfaces_.begin();
    389        it != previous_contained_surfaces_.end();
    390        ++it) {
    391     Surface* surface = manager_->GetSurfaceForId(it->first);
    392     if (surface)
    393       surface->TakeLatencyInfo(&frame->metadata.latency_info);
    394   }
    395 
    396   // TODO(jamesr): Aggregate all resource references into the returned frame's
    397   // resource list.
    398 
    399   return frame.Pass();
    400 }
    401 
    402 }  // namespace cc
    403