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
23namespace cc {
24
25SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager,
26                                     ResourceProvider* provider)
27    : manager_(manager), provider_(provider) {
28  DCHECK(manager_);
29}
30
31SurfaceAggregator::~SurfaceAggregator() {}
32
33class 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
58static 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
65RenderPassId 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
76int 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
89static 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
107bool 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
143gfx::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
153void 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
258void 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
279void 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
326void 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
352scoped_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