blob: 4c4408484e254c3009c05fe15ed33ba4e9b43210 [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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
James Robinson7f480212014-10-31 10:28:08 -07007#include <map>
8
James Robinson646469d2014-10-03 15:33:28 -07009#include "base/bind.h"
10#include "base/containers/hash_tables.h"
11#include "base/debug/trace_event.h"
12#include "base/logging.h"
13#include "cc/base/math_util.h"
14#include "cc/output/compositor_frame.h"
15#include "cc/output/delegated_frame_data.h"
16#include "cc/quads/draw_quad.h"
17#include "cc/quads/render_pass_draw_quad.h"
18#include "cc/quads/shared_quad_state.h"
19#include "cc/quads/surface_draw_quad.h"
20#include "cc/surfaces/surface.h"
21#include "cc/surfaces/surface_factory.h"
22#include "cc/surfaces/surface_manager.h"
23#include "cc/trees/blocking_task_runner.h"
24
25namespace cc {
James Robinson80d418c2014-10-16 16:00:02 -070026namespace {
27
28void MoveMatchingRequests(
29 RenderPassId id,
30 std::multimap<RenderPassId, CopyOutputRequest*>* copy_requests,
31 ScopedPtrVector<CopyOutputRequest>* output_requests) {
32 auto request_range = copy_requests->equal_range(id);
33 for (auto it = request_range.first; it != request_range.second; ++it) {
34 DCHECK(it->second);
35 output_requests->push_back(scoped_ptr<CopyOutputRequest>(it->second));
36 it->second = nullptr;
37 }
38 copy_requests->erase(request_range.first, request_range.second);
39}
40
41} // namespace
James Robinson646469d2014-10-03 15:33:28 -070042
43SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager,
44 ResourceProvider* provider)
James Robinson30d547e2014-10-23 18:20:06 -070045 : manager_(manager), provider_(provider), next_render_pass_id_(1) {
James Robinson646469d2014-10-03 15:33:28 -070046 DCHECK(manager_);
47}
48
49SurfaceAggregator::~SurfaceAggregator() {}
50
51class SurfaceAggregator::RenderPassIdAllocator {
52 public:
James Robinson30d547e2014-10-23 18:20:06 -070053 explicit RenderPassIdAllocator(int* next_index) : next_index_(next_index) {}
James Robinson646469d2014-10-03 15:33:28 -070054 ~RenderPassIdAllocator() {}
55
56 void AddKnownPass(RenderPassId id) {
57 if (id_to_index_map_.find(id) != id_to_index_map_.end())
58 return;
James Robinson30d547e2014-10-23 18:20:06 -070059 id_to_index_map_[id] = (*next_index_)++;
James Robinson646469d2014-10-03 15:33:28 -070060 }
61
62 RenderPassId Remap(RenderPassId id) {
63 DCHECK(id_to_index_map_.find(id) != id_to_index_map_.end());
James Robinson30d547e2014-10-23 18:20:06 -070064 return RenderPassId(1, id_to_index_map_[id]);
James Robinson646469d2014-10-03 15:33:28 -070065 }
66
67 private:
68 base::hash_map<RenderPassId, int> id_to_index_map_;
James Robinson30d547e2014-10-23 18:20:06 -070069 int* next_index_;
James Robinson646469d2014-10-03 15:33:28 -070070
71 DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator);
72};
73
74static void UnrefHelper(base::WeakPtr<SurfaceFactory> surface_factory,
75 const ReturnedResourceArray& resources,
76 BlockingTaskRunner* main_thread_task_runner) {
77 if (surface_factory)
78 surface_factory->UnrefResources(resources);
79}
80
81RenderPassId SurfaceAggregator::RemapPassId(RenderPassId surface_local_pass_id,
82 SurfaceId surface_id) {
83 RenderPassIdAllocator* allocator = render_pass_allocator_map_.get(surface_id);
84 if (!allocator) {
James Robinson30d547e2014-10-23 18:20:06 -070085 allocator = new RenderPassIdAllocator(&next_render_pass_id_);
James Robinson646469d2014-10-03 15:33:28 -070086 render_pass_allocator_map_.set(surface_id, make_scoped_ptr(allocator));
87 }
88 allocator->AddKnownPass(surface_local_pass_id);
89 return allocator->Remap(surface_local_pass_id);
90}
91
92int SurfaceAggregator::ChildIdForSurface(Surface* surface) {
93 SurfaceToResourceChildIdMap::iterator it =
94 surface_id_to_resource_child_id_.find(surface->surface_id());
95 if (it == surface_id_to_resource_child_id_.end()) {
James Robinson53b77582014-10-28 17:00:48 -070096 int child_id =
97 provider_->CreateChild(base::Bind(&UnrefHelper, surface->factory()));
James Robinson646469d2014-10-03 15:33:28 -070098 surface_id_to_resource_child_id_[surface->surface_id()] = child_id;
99 return child_id;
100 } else {
101 return it->second;
102 }
103}
104
105static ResourceProvider::ResourceId ResourceRemapHelper(
106 bool* invalid_frame,
107 const ResourceProvider::ResourceIdMap& child_to_parent_map,
108 ResourceProvider::ResourceIdArray* resources_in_frame,
109 ResourceProvider::ResourceId id) {
110 ResourceProvider::ResourceIdMap::const_iterator it =
111 child_to_parent_map.find(id);
112 if (it == child_to_parent_map.end()) {
113 *invalid_frame = true;
114 return 0;
115 }
116
117 DCHECK_EQ(it->first, id);
118 ResourceProvider::ResourceId remapped_id = it->second;
119 resources_in_frame->push_back(id);
120 return remapped_id;
121}
122
123bool SurfaceAggregator::TakeResources(Surface* surface,
124 const DelegatedFrameData* frame_data,
125 RenderPassList* render_pass_list) {
126 RenderPass::CopyAll(frame_data->render_pass_list, render_pass_list);
127 if (!provider_) // TODO(jamesr): hack for unit tests that don't set up rp
128 return false;
129
130 int child_id = ChildIdForSurface(surface);
131 provider_->ReceiveFromChild(child_id, frame_data->resource_list);
James Robinson53b77582014-10-28 17:00:48 -0700132 if (surface->factory())
133 surface->factory()->RefResources(frame_data->resource_list);
James Robinson646469d2014-10-03 15:33:28 -0700134
135 typedef ResourceProvider::ResourceIdArray IdArray;
136 IdArray referenced_resources;
137
138 bool invalid_frame = false;
139 DrawQuad::ResourceIteratorCallback remap =
140 base::Bind(&ResourceRemapHelper,
141 &invalid_frame,
142 provider_->GetChildToParentMap(child_id),
143 &referenced_resources);
James Robinson7f480212014-10-31 10:28:08 -0700144 for (const auto& render_pass : *render_pass_list) {
145 for (const auto& quad : render_pass->quad_list)
146 quad->IterateResources(remap);
James Robinson646469d2014-10-03 15:33:28 -0700147 }
James Robinsonbaf71d32014-10-08 13:00:20 -0700148
James Robinson646469d2014-10-03 15:33:28 -0700149 if (!invalid_frame)
150 provider_->DeclareUsedResourcesFromChild(child_id, referenced_resources);
151
152 return invalid_frame;
153}
154
155gfx::Rect SurfaceAggregator::DamageRectForSurface(const Surface* surface,
James Robinson6a64b812014-12-03 13:38:42 -0800156 const RenderPass& source,
157 const gfx::Rect& full_rect) {
James Robinson646469d2014-10-03 15:33:28 -0700158 int previous_index = previous_contained_surfaces_[surface->surface_id()];
159 if (previous_index == surface->frame_index())
160 return gfx::Rect();
161 else if (previous_index == surface->frame_index() - 1)
162 return source.damage_rect;
James Robinson6a64b812014-12-03 13:38:42 -0800163 return full_rect;
James Robinson646469d2014-10-03 15:33:28 -0700164}
165
166void SurfaceAggregator::HandleSurfaceQuad(const SurfaceDrawQuad* surface_quad,
James Robinsonc4c1c592014-11-21 18:27:04 -0800167 float opacity,
James Robinson646469d2014-10-03 15:33:28 -0700168 RenderPass* dest_pass) {
169 SurfaceId surface_id = surface_quad->surface_id;
170 // If this surface's id is already in our referenced set then it creates
171 // a cycle in the graph and should be dropped.
172 if (referenced_surfaces_.count(surface_id))
173 return;
174 Surface* surface = manager_->GetSurfaceForId(surface_id);
175 if (!surface) {
176 contained_surfaces_[surface_id] = 0;
177 return;
178 }
179 contained_surfaces_[surface_id] = surface->frame_index();
180 const CompositorFrame* frame = surface->GetEligibleFrame();
181 if (!frame)
182 return;
183 const DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
184 if (!frame_data)
185 return;
186
James Robinson80d418c2014-10-16 16:00:02 -0700187 std::multimap<RenderPassId, CopyOutputRequest*> copy_requests;
188 surface->TakeCopyOutputRequests(&copy_requests);
189
James Robinson646469d2014-10-03 15:33:28 -0700190 RenderPassList render_pass_list;
191 bool invalid_frame = TakeResources(surface, frame_data, &render_pass_list);
James Robinson80d418c2014-10-16 16:00:02 -0700192 if (invalid_frame) {
193 for (auto& request : copy_requests) {
194 request.second->SendEmptyResult();
195 delete request.second;
196 }
James Robinson646469d2014-10-03 15:33:28 -0700197 return;
James Robinson80d418c2014-10-16 16:00:02 -0700198 }
James Robinson646469d2014-10-03 15:33:28 -0700199
200 SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first;
201
James Robinson646469d2014-10-03 15:33:28 -0700202 bool merge_pass = copy_requests.empty();
203
204 const RenderPassList& referenced_passes = render_pass_list;
205 size_t passes_to_copy =
206 merge_pass ? referenced_passes.size() - 1 : referenced_passes.size();
207 for (size_t j = 0; j < passes_to_copy; ++j) {
208 const RenderPass& source = *referenced_passes[j];
209
James Robinsona9763132014-10-06 11:18:13 -0700210 size_t sqs_size = source.shared_quad_state_list.size();
211 size_t dq_size = source.quad_list.size();
212 scoped_ptr<RenderPass> copy_pass(RenderPass::Create(sqs_size, dq_size));
James Robinson646469d2014-10-03 15:33:28 -0700213
214 RenderPassId remapped_pass_id = RemapPassId(source.id, surface_id);
215
216 copy_pass->SetAll(remapped_pass_id,
217 source.output_rect,
218 source.damage_rect,
219 source.transform_to_root_target,
220 source.has_transparent_background);
221
James Robinson80d418c2014-10-16 16:00:02 -0700222 MoveMatchingRequests(source.id, &copy_requests, &copy_pass->copy_requests);
223
James Robinson646469d2014-10-03 15:33:28 -0700224 // Contributing passes aggregated in to the pass list need to take the
225 // transform of the surface quad into account to update their transform to
226 // the root surface.
227 // TODO(jamesr): Make sure this is sufficient for surfaces nested several
228 // levels deep and add tests.
229 copy_pass->transform_to_root_target.ConcatTransform(
230 surface_quad->quadTransform());
231
James Robinsonc4c1c592014-11-21 18:27:04 -0800232 CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
233 gfx::Transform(), 1.f, copy_pass.get(), surface_id);
James Robinson646469d2014-10-03 15:33:28 -0700234
235 dest_pass_list_->push_back(copy_pass.Pass());
236 }
237
238 const RenderPass& last_pass = *render_pass_list.back();
239 if (merge_pass) {
240 // TODO(jamesr): Clean up last pass special casing.
241 const QuadList& quads = last_pass.quad_list;
242
243 // TODO(jamesr): Make sure clipping is enforced.
James Robinsonc4c1c592014-11-21 18:27:04 -0800244 CopyQuadsToPass(quads, last_pass.shared_quad_state_list,
James Robinson646469d2014-10-03 15:33:28 -0700245 surface_quad->quadTransform(),
James Robinsonc4c1c592014-11-21 18:27:04 -0800246 surface_quad->opacity() * opacity, dest_pass, surface_id);
James Robinson646469d2014-10-03 15:33:28 -0700247 } else {
248 RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id);
249
James Robinson646469d2014-10-03 15:33:28 -0700250 SharedQuadState* shared_quad_state =
251 dest_pass->CreateAndAppendSharedQuadState();
252 shared_quad_state->CopyFrom(surface_quad->shared_quad_state);
James Robinsonc4c1c592014-11-21 18:27:04 -0800253 shared_quad_state->opacity *= opacity;
James Robinson646469d2014-10-03 15:33:28 -0700254 RenderPassDrawQuad* quad =
255 dest_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
256 quad->SetNew(shared_quad_state,
257 surface_quad->rect,
258 surface_quad->visible_rect,
259 remapped_pass_id,
260 0,
James Robinson80d418c2014-10-16 16:00:02 -0700261 gfx::Vector2dF(),
262 gfx::Size(),
James Robinson646469d2014-10-03 15:33:28 -0700263 FilterOperations(),
264 gfx::Vector2dF(),
265 FilterOperations());
266 }
267 dest_pass->damage_rect =
268 gfx::UnionRects(dest_pass->damage_rect,
269 MathUtil::MapEnclosingClippedRect(
270 surface_quad->quadTransform(),
James Robinson6a64b812014-12-03 13:38:42 -0800271 DamageRectForSurface(surface, last_pass,
272 surface_quad->visible_rect)));
James Robinson646469d2014-10-03 15:33:28 -0700273
274 referenced_surfaces_.erase(it);
275}
276
277void SurfaceAggregator::CopySharedQuadState(
278 const SharedQuadState* source_sqs,
279 const gfx::Transform& content_to_target_transform,
280 RenderPass* dest_render_pass) {
281 SharedQuadState* copy_shared_quad_state =
282 dest_render_pass->CreateAndAppendSharedQuadState();
283 copy_shared_quad_state->CopyFrom(source_sqs);
284 // content_to_target_transform contains any transformation that may exist
285 // between the context that these quads are being copied from (i.e. the
286 // surface's draw transform when aggregated from within a surface) to the
287 // target space of the pass. This will be identity except when copying the
288 // root draw pass from a surface into a pass when the surface draw quad's
289 // transform is not identity.
290 copy_shared_quad_state->content_to_target_transform.ConcatTransform(
291 content_to_target_transform);
292 if (copy_shared_quad_state->is_clipped) {
293 copy_shared_quad_state->clip_rect = MathUtil::MapEnclosingClippedRect(
294 content_to_target_transform, copy_shared_quad_state->clip_rect);
295 }
296}
297
298void SurfaceAggregator::CopyQuadsToPass(
299 const QuadList& source_quad_list,
300 const SharedQuadStateList& source_shared_quad_state_list,
301 const gfx::Transform& content_to_target_transform,
James Robinsonc4c1c592014-11-21 18:27:04 -0800302 float opacity,
James Robinson646469d2014-10-03 15:33:28 -0700303 RenderPass* dest_pass,
304 SurfaceId surface_id) {
305 const SharedQuadState* last_copied_source_shared_quad_state = NULL;
306
James Robinsona9763132014-10-06 11:18:13 -0700307 SharedQuadStateList::ConstIterator sqs_iter =
308 source_shared_quad_state_list.begin();
309 for (const auto& quad : source_quad_list) {
James Robinson7f480212014-10-31 10:28:08 -0700310 while (quad->shared_quad_state != *sqs_iter) {
James Robinsona9763132014-10-06 11:18:13 -0700311 ++sqs_iter;
312 DCHECK(sqs_iter != source_shared_quad_state_list.end());
James Robinson646469d2014-10-03 15:33:28 -0700313 }
James Robinson7f480212014-10-31 10:28:08 -0700314 DCHECK_EQ(quad->shared_quad_state, *sqs_iter);
James Robinson646469d2014-10-03 15:33:28 -0700315
James Robinson7f480212014-10-31 10:28:08 -0700316 if (quad->material == DrawQuad::SURFACE_CONTENT) {
317 const SurfaceDrawQuad* surface_quad = SurfaceDrawQuad::MaterialCast(quad);
James Robinsonc4c1c592014-11-21 18:27:04 -0800318 HandleSurfaceQuad(surface_quad, opacity, dest_pass);
James Robinson646469d2014-10-03 15:33:28 -0700319 } else {
James Robinson7f480212014-10-31 10:28:08 -0700320 if (quad->shared_quad_state != last_copied_source_shared_quad_state) {
James Robinson646469d2014-10-03 15:33:28 -0700321 CopySharedQuadState(
James Robinson7f480212014-10-31 10:28:08 -0700322 quad->shared_quad_state, content_to_target_transform, dest_pass);
James Robinsonc4c1c592014-11-21 18:27:04 -0800323 dest_pass->shared_quad_state_list.back()->opacity *= opacity;
James Robinson7f480212014-10-31 10:28:08 -0700324 last_copied_source_shared_quad_state = quad->shared_quad_state;
James Robinson646469d2014-10-03 15:33:28 -0700325 }
James Robinson7f480212014-10-31 10:28:08 -0700326 if (quad->material == DrawQuad::RENDER_PASS) {
James Robinson646469d2014-10-03 15:33:28 -0700327 const RenderPassDrawQuad* pass_quad =
James Robinson7f480212014-10-31 10:28:08 -0700328 RenderPassDrawQuad::MaterialCast(quad);
James Robinson646469d2014-10-03 15:33:28 -0700329 RenderPassId original_pass_id = pass_quad->render_pass_id;
330 RenderPassId remapped_pass_id =
331 RemapPassId(original_pass_id, surface_id);
332
333 dest_pass->CopyFromAndAppendRenderPassDrawQuad(
334 pass_quad,
335 dest_pass->shared_quad_state_list.back(),
336 remapped_pass_id);
337 } else {
338 dest_pass->CopyFromAndAppendDrawQuad(
James Robinson7f480212014-10-31 10:28:08 -0700339 quad, dest_pass->shared_quad_state_list.back());
James Robinson646469d2014-10-03 15:33:28 -0700340 }
341 }
342 }
343}
344
James Robinson80d418c2014-10-16 16:00:02 -0700345void SurfaceAggregator::CopyPasses(const DelegatedFrameData* frame_data,
346 Surface* surface) {
347 RenderPassList source_pass_list;
348
349 // The root surface is allowed to have copy output requests, so grab them
350 // off its render passes.
351 std::multimap<RenderPassId, CopyOutputRequest*> copy_requests;
352 surface->TakeCopyOutputRequests(&copy_requests);
353
354 bool invalid_frame = TakeResources(surface, frame_data, &source_pass_list);
355 DCHECK(!invalid_frame);
356
James Robinson646469d2014-10-03 15:33:28 -0700357 for (size_t i = 0; i < source_pass_list.size(); ++i) {
358 const RenderPass& source = *source_pass_list[i];
359
James Robinsona9763132014-10-06 11:18:13 -0700360 size_t sqs_size = source.shared_quad_state_list.size();
361 size_t dq_size = source.quad_list.size();
362 scoped_ptr<RenderPass> copy_pass(RenderPass::Create(sqs_size, dq_size));
James Robinson646469d2014-10-03 15:33:28 -0700363
James Robinson80d418c2014-10-16 16:00:02 -0700364 MoveMatchingRequests(source.id, &copy_requests, &copy_pass->copy_requests);
365
James Robinson646469d2014-10-03 15:33:28 -0700366 RenderPassId remapped_pass_id =
367 RemapPassId(source.id, surface->surface_id());
368
James Robinson6a64b812014-12-03 13:38:42 -0800369 copy_pass->SetAll(remapped_pass_id, source.output_rect,
370 DamageRectForSurface(surface, source, source.output_rect),
James Robinson646469d2014-10-03 15:33:28 -0700371 source.transform_to_root_target,
372 source.has_transparent_background);
373
James Robinsonc4c1c592014-11-21 18:27:04 -0800374 CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
375 gfx::Transform(), 1.f, copy_pass.get(),
James Robinson646469d2014-10-03 15:33:28 -0700376 surface->surface_id());
377
378 dest_pass_list_->push_back(copy_pass.Pass());
379 }
380}
381
Benjamin Lerman57998902014-11-18 16:06:02 +0100382void SurfaceAggregator::RemoveUnreferencedChildren() {
383 for (const auto& surface : previous_contained_surfaces_) {
384 if (!contained_surfaces_.count(surface.first)) {
385 SurfaceToResourceChildIdMap::iterator it =
386 surface_id_to_resource_child_id_.find(surface.first);
387 if (it != surface_id_to_resource_child_id_.end()) {
388 provider_->DestroyChild(it->second);
389 surface_id_to_resource_child_id_.erase(it);
390 }
391 }
392 }
393}
394
James Robinson646469d2014-10-03 15:33:28 -0700395scoped_ptr<CompositorFrame> SurfaceAggregator::Aggregate(SurfaceId surface_id) {
396 Surface* surface = manager_->GetSurfaceForId(surface_id);
397 DCHECK(surface);
398 contained_surfaces_[surface_id] = surface->frame_index();
399 const CompositorFrame* root_surface_frame = surface->GetEligibleFrame();
400 if (!root_surface_frame)
401 return nullptr;
402 TRACE_EVENT0("cc", "SurfaceAggregator::Aggregate");
403
404 scoped_ptr<CompositorFrame> frame(new CompositorFrame);
405 frame->delegated_frame_data = make_scoped_ptr(new DelegatedFrameData);
406
407 DCHECK(root_surface_frame->delegated_frame_data);
408
James Robinson646469d2014-10-03 15:33:28 -0700409 SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first;
410
411 dest_resource_list_ = &frame->delegated_frame_data->resource_list;
412 dest_pass_list_ = &frame->delegated_frame_data->render_pass_list;
413
James Robinson80d418c2014-10-16 16:00:02 -0700414 CopyPasses(root_surface_frame->delegated_frame_data.get(), surface);
James Robinson646469d2014-10-03 15:33:28 -0700415
416 referenced_surfaces_.erase(it);
417 DCHECK(referenced_surfaces_.empty());
418
James Robinson6a64b812014-12-03 13:38:42 -0800419 if (dest_pass_list_->empty())
420 return nullptr;
421
James Robinson646469d2014-10-03 15:33:28 -0700422 dest_pass_list_ = NULL;
Benjamin Lerman57998902014-11-18 16:06:02 +0100423 RemoveUnreferencedChildren();
James Robinson646469d2014-10-03 15:33:28 -0700424 contained_surfaces_.swap(previous_contained_surfaces_);
425 contained_surfaces_.clear();
426
427 for (SurfaceIndexMap::iterator it = previous_contained_surfaces_.begin();
428 it != previous_contained_surfaces_.end();
429 ++it) {
430 Surface* surface = manager_->GetSurfaceForId(it->first);
431 if (surface)
432 surface->TakeLatencyInfo(&frame->metadata.latency_info);
433 }
434
435 // TODO(jamesr): Aggregate all resource references into the returned frame's
436 // resource list.
437
438 return frame.Pass();
439}
440
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100441void SurfaceAggregator::ReleaseResources(SurfaceId surface_id) {
442 SurfaceToResourceChildIdMap::iterator it =
443 surface_id_to_resource_child_id_.find(surface_id);
444 if (it != surface_id_to_resource_child_id_.end()) {
445 provider_->DestroyChild(it->second);
446 surface_id_to_resource_child_id_.erase(it);
447 }
448}
449
James Robinson646469d2014-10-03 15:33:28 -0700450} // namespace cc