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.
5 #include "cc/surfaces/surface_aggregator.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"
28 void MoveMatchingRequests(
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
) {
35 output_requests
->push_back(scoped_ptr
<CopyOutputRequest
>(it
->second
));
38 copy_requests
->erase(request_range
.first
, request_range
.second
);
43 SurfaceAggregator::SurfaceAggregator(SurfaceManager
* manager
,
44 ResourceProvider
* provider
)
45 : manager_(manager
), provider_(provider
), next_render_pass_id_(1) {
49 SurfaceAggregator::~SurfaceAggregator() {}
51 class SurfaceAggregator::RenderPassIdAllocator
{
53 explicit RenderPassIdAllocator(int* next_index
) : next_index_(next_index
) {}
54 ~RenderPassIdAllocator() {}
56 void AddKnownPass(RenderPassId id
) {
57 if (id_to_index_map_
.find(id
) != id_to_index_map_
.end())
59 id_to_index_map_
[id
] = (*next_index_
)++;
62 RenderPassId
Remap(RenderPassId id
) {
63 DCHECK(id_to_index_map_
.find(id
) != id_to_index_map_
.end());
64 return RenderPassId(1, id_to_index_map_
[id
]);
68 base::hash_map
<RenderPassId
, int> id_to_index_map_
;
71 DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator
);
74 static void UnrefHelper(base::WeakPtr
<SurfaceFactory
> surface_factory
,
75 const ReturnedResourceArray
& resources
,
76 BlockingTaskRunner
* main_thread_task_runner
) {
78 surface_factory
->UnrefResources(resources
);
81 RenderPassId
SurfaceAggregator::RemapPassId(RenderPassId surface_local_pass_id
,
82 SurfaceId surface_id
) {
83 RenderPassIdAllocator
* allocator
= render_pass_allocator_map_
.get(surface_id
);
85 allocator
= new RenderPassIdAllocator(&next_render_pass_id_
);
86 render_pass_allocator_map_
.set(surface_id
, make_scoped_ptr(allocator
));
88 allocator
->AddKnownPass(surface_local_pass_id
);
89 return allocator
->Remap(surface_local_pass_id
);
92 int 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()) {
97 provider_
->CreateChild(base::Bind(&UnrefHelper
, surface
->factory()));
98 surface_id_to_resource_child_id_
[surface
->surface_id()] = child_id
;
105 static ResourceProvider::ResourceId
ResourceRemapHelper(
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;
117 DCHECK_EQ(it
->first
, id
);
118 ResourceProvider::ResourceId remapped_id
= it
->second
;
119 resources_in_frame
->push_back(id
);
123 bool 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
130 int child_id
= ChildIdForSurface(surface
);
131 provider_
->ReceiveFromChild(child_id
, frame_data
->resource_list
);
132 if (surface
->factory())
133 surface
->factory()->RefResources(frame_data
->resource_list
);
135 typedef ResourceProvider::ResourceIdArray IdArray
;
136 IdArray referenced_resources
;
138 bool invalid_frame
= false;
139 DrawQuad::ResourceIteratorCallback remap
=
140 base::Bind(&ResourceRemapHelper
,
142 provider_
->GetChildToParentMap(child_id
),
143 &referenced_resources
);
144 for (const auto& render_pass
: *render_pass_list
) {
145 for (const auto& quad
: render_pass
->quad_list
)
146 quad
->IterateResources(remap
);
150 provider_
->DeclareUsedResourcesFromChild(child_id
, referenced_resources
);
152 return invalid_frame
;
155 gfx::Rect
SurfaceAggregator::DamageRectForSurface(const Surface
* surface
,
156 const RenderPass
& source
) {
157 int previous_index
= previous_contained_surfaces_
[surface
->surface_id()];
158 if (previous_index
== surface
->frame_index())
160 else if (previous_index
== surface
->frame_index() - 1)
161 return source
.damage_rect
;
162 return gfx::Rect(surface
->size());
165 void SurfaceAggregator::HandleSurfaceQuad(const SurfaceDrawQuad
* surface_quad
,
166 RenderPass
* dest_pass
) {
167 SurfaceId surface_id
= surface_quad
->surface_id
;
168 // If this surface's id is already in our referenced set then it creates
169 // a cycle in the graph and should be dropped.
170 if (referenced_surfaces_
.count(surface_id
))
172 Surface
* surface
= manager_
->GetSurfaceForId(surface_id
);
174 contained_surfaces_
[surface_id
] = 0;
177 contained_surfaces_
[surface_id
] = surface
->frame_index();
178 const CompositorFrame
* frame
= surface
->GetEligibleFrame();
181 const DelegatedFrameData
* frame_data
= frame
->delegated_frame_data
.get();
185 std::multimap
<RenderPassId
, CopyOutputRequest
*> copy_requests
;
186 surface
->TakeCopyOutputRequests(©_requests
);
188 RenderPassList render_pass_list
;
189 bool invalid_frame
= TakeResources(surface
, frame_data
, &render_pass_list
);
191 for (auto& request
: copy_requests
) {
192 request
.second
->SendEmptyResult();
193 delete request
.second
;
198 SurfaceSet::iterator it
= referenced_surfaces_
.insert(surface_id
).first
;
200 bool merge_pass
= copy_requests
.empty();
202 const RenderPassList
& referenced_passes
= render_pass_list
;
203 size_t passes_to_copy
=
204 merge_pass
? referenced_passes
.size() - 1 : referenced_passes
.size();
205 for (size_t j
= 0; j
< passes_to_copy
; ++j
) {
206 const RenderPass
& source
= *referenced_passes
[j
];
208 size_t sqs_size
= source
.shared_quad_state_list
.size();
209 size_t dq_size
= source
.quad_list
.size();
210 scoped_ptr
<RenderPass
> copy_pass(RenderPass::Create(sqs_size
, dq_size
));
212 RenderPassId remapped_pass_id
= RemapPassId(source
.id
, surface_id
);
214 copy_pass
->SetAll(remapped_pass_id
,
217 source
.transform_to_root_target
,
218 source
.has_transparent_background
);
220 MoveMatchingRequests(source
.id
, ©_requests
, ©_pass
->copy_requests
);
222 // Contributing passes aggregated in to the pass list need to take the
223 // transform of the surface quad into account to update their transform to
225 // TODO(jamesr): Make sure this is sufficient for surfaces nested several
226 // levels deep and add tests.
227 copy_pass
->transform_to_root_target
.ConcatTransform(
228 surface_quad
->quadTransform());
230 CopyQuadsToPass(source
.quad_list
,
231 source
.shared_quad_state_list
,
236 dest_pass_list_
->push_back(copy_pass
.Pass());
239 const RenderPass
& last_pass
= *render_pass_list
.back();
241 // TODO(jamesr): Clean up last pass special casing.
242 const QuadList
& quads
= last_pass
.quad_list
;
244 // TODO(jamesr): Make sure clipping is enforced.
245 CopyQuadsToPass(quads
,
246 last_pass
.shared_quad_state_list
,
247 surface_quad
->quadTransform(),
251 RenderPassId remapped_pass_id
= RemapPassId(last_pass
.id
, surface_id
);
253 SharedQuadState
* shared_quad_state
=
254 dest_pass
->CreateAndAppendSharedQuadState();
255 shared_quad_state
->CopyFrom(surface_quad
->shared_quad_state
);
256 RenderPassDrawQuad
* quad
=
257 dest_pass
->CreateAndAppendDrawQuad
<RenderPassDrawQuad
>();
258 quad
->SetNew(shared_quad_state
,
260 surface_quad
->visible_rect
,
269 dest_pass
->damage_rect
=
270 gfx::UnionRects(dest_pass
->damage_rect
,
271 MathUtil::MapEnclosingClippedRect(
272 surface_quad
->quadTransform(),
273 DamageRectForSurface(surface
, last_pass
)));
275 referenced_surfaces_
.erase(it
);
278 void SurfaceAggregator::CopySharedQuadState(
279 const SharedQuadState
* source_sqs
,
280 const gfx::Transform
& content_to_target_transform
,
281 RenderPass
* dest_render_pass
) {
282 SharedQuadState
* copy_shared_quad_state
=
283 dest_render_pass
->CreateAndAppendSharedQuadState();
284 copy_shared_quad_state
->CopyFrom(source_sqs
);
285 // content_to_target_transform contains any transformation that may exist
286 // between the context that these quads are being copied from (i.e. the
287 // surface's draw transform when aggregated from within a surface) to the
288 // target space of the pass. This will be identity except when copying the
289 // root draw pass from a surface into a pass when the surface draw quad's
290 // transform is not identity.
291 copy_shared_quad_state
->content_to_target_transform
.ConcatTransform(
292 content_to_target_transform
);
293 if (copy_shared_quad_state
->is_clipped
) {
294 copy_shared_quad_state
->clip_rect
= MathUtil::MapEnclosingClippedRect(
295 content_to_target_transform
, copy_shared_quad_state
->clip_rect
);
299 void SurfaceAggregator::CopyQuadsToPass(
300 const QuadList
& source_quad_list
,
301 const SharedQuadStateList
& source_shared_quad_state_list
,
302 const gfx::Transform
& content_to_target_transform
,
303 RenderPass
* dest_pass
,
304 SurfaceId surface_id
) {
305 const SharedQuadState
* last_copied_source_shared_quad_state
= NULL
;
307 SharedQuadStateList::ConstIterator sqs_iter
=
308 source_shared_quad_state_list
.begin();
309 for (const auto& quad
: source_quad_list
) {
310 while (quad
->shared_quad_state
!= *sqs_iter
) {
312 DCHECK(sqs_iter
!= source_shared_quad_state_list
.end());
314 DCHECK_EQ(quad
->shared_quad_state
, *sqs_iter
);
316 if (quad
->material
== DrawQuad::SURFACE_CONTENT
) {
317 const SurfaceDrawQuad
* surface_quad
= SurfaceDrawQuad::MaterialCast(quad
);
318 HandleSurfaceQuad(surface_quad
, dest_pass
);
320 if (quad
->shared_quad_state
!= last_copied_source_shared_quad_state
) {
322 quad
->shared_quad_state
, content_to_target_transform
, dest_pass
);
323 last_copied_source_shared_quad_state
= quad
->shared_quad_state
;
325 if (quad
->material
== DrawQuad::RENDER_PASS
) {
326 const RenderPassDrawQuad
* pass_quad
=
327 RenderPassDrawQuad::MaterialCast(quad
);
328 RenderPassId original_pass_id
= pass_quad
->render_pass_id
;
329 RenderPassId remapped_pass_id
=
330 RemapPassId(original_pass_id
, surface_id
);
332 dest_pass
->CopyFromAndAppendRenderPassDrawQuad(
334 dest_pass
->shared_quad_state_list
.back(),
337 dest_pass
->CopyFromAndAppendDrawQuad(
338 quad
, dest_pass
->shared_quad_state_list
.back());
344 void SurfaceAggregator::CopyPasses(const DelegatedFrameData
* frame_data
,
346 RenderPassList source_pass_list
;
348 // The root surface is allowed to have copy output requests, so grab them
349 // off its render passes.
350 std::multimap
<RenderPassId
, CopyOutputRequest
*> copy_requests
;
351 surface
->TakeCopyOutputRequests(©_requests
);
353 bool invalid_frame
= TakeResources(surface
, frame_data
, &source_pass_list
);
354 DCHECK(!invalid_frame
);
356 for (size_t i
= 0; i
< source_pass_list
.size(); ++i
) {
357 const RenderPass
& source
= *source_pass_list
[i
];
359 size_t sqs_size
= source
.shared_quad_state_list
.size();
360 size_t dq_size
= source
.quad_list
.size();
361 scoped_ptr
<RenderPass
> copy_pass(RenderPass::Create(sqs_size
, dq_size
));
363 MoveMatchingRequests(source
.id
, ©_requests
, ©_pass
->copy_requests
);
365 RenderPassId remapped_pass_id
=
366 RemapPassId(source
.id
, surface
->surface_id());
368 copy_pass
->SetAll(remapped_pass_id
,
370 DamageRectForSurface(surface
, source
),
371 source
.transform_to_root_target
,
372 source
.has_transparent_background
);
374 CopyQuadsToPass(source
.quad_list
,
375 source
.shared_quad_state_list
,
378 surface
->surface_id());
380 dest_pass_list_
->push_back(copy_pass
.Pass());
384 scoped_ptr
<CompositorFrame
> SurfaceAggregator::Aggregate(SurfaceId surface_id
) {
385 Surface
* surface
= manager_
->GetSurfaceForId(surface_id
);
387 contained_surfaces_
[surface_id
] = surface
->frame_index();
388 const CompositorFrame
* root_surface_frame
= surface
->GetEligibleFrame();
389 if (!root_surface_frame
)
391 TRACE_EVENT0("cc", "SurfaceAggregator::Aggregate");
393 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
394 frame
->delegated_frame_data
= make_scoped_ptr(new DelegatedFrameData
);
396 DCHECK(root_surface_frame
->delegated_frame_data
);
398 SurfaceSet::iterator it
= referenced_surfaces_
.insert(surface_id
).first
;
400 dest_resource_list_
= &frame
->delegated_frame_data
->resource_list
;
401 dest_pass_list_
= &frame
->delegated_frame_data
->render_pass_list
;
403 CopyPasses(root_surface_frame
->delegated_frame_data
.get(), surface
);
405 referenced_surfaces_
.erase(it
);
406 DCHECK(referenced_surfaces_
.empty());
408 dest_pass_list_
= NULL
;
409 contained_surfaces_
.swap(previous_contained_surfaces_
);
410 contained_surfaces_
.clear();
412 for (SurfaceIndexMap::iterator it
= previous_contained_surfaces_
.begin();
413 it
!= previous_contained_surfaces_
.end();
415 Surface
* surface
= manager_
->GetSurfaceForId(it
->first
);
417 surface
->TakeLatencyInfo(&frame
->metadata
.latency_info
);
420 // TODO(jamesr): Aggregate all resource references into the returned frame's