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/logging.h"
12 #include "base/trace_event/trace_event.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 // Create a clip rect for an aggregated quad from the original clip rect and
52 // the clip rect from the surface it's on.
53 SurfaceAggregator::ClipData
SurfaceAggregator::CalculateClipRect(
54 const ClipData
& surface_clip
,
55 const ClipData
& quad_clip
,
56 const gfx::Transform
& content_to_target_transform
) {
58 if (surface_clip
.is_clipped
)
59 out_clip
= surface_clip
;
61 if (quad_clip
.is_clipped
) {
62 // TODO(jamesr): This only works if content_to_target_transform maps integer
63 // rects to integer rects.
64 gfx::Rect final_clip
= MathUtil::MapEnclosingClippedRect(
65 content_to_target_transform
, quad_clip
.rect
);
66 if (out_clip
.is_clipped
)
67 out_clip
.rect
.Intersect(final_clip
);
69 out_clip
.rect
= final_clip
;
70 out_clip
.is_clipped
= true;
76 class SurfaceAggregator::RenderPassIdAllocator
{
78 explicit RenderPassIdAllocator(int* next_index
) : next_index_(next_index
) {}
79 ~RenderPassIdAllocator() {}
81 void AddKnownPass(RenderPassId id
) {
82 if (id_to_index_map_
.find(id
) != id_to_index_map_
.end())
84 id_to_index_map_
[id
] = (*next_index_
)++;
87 RenderPassId
Remap(RenderPassId id
) {
88 DCHECK(id_to_index_map_
.find(id
) != id_to_index_map_
.end());
89 return RenderPassId(1, id_to_index_map_
[id
]);
93 base::hash_map
<RenderPassId
, int> id_to_index_map_
;
96 DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator
);
99 static void UnrefHelper(base::WeakPtr
<SurfaceFactory
> surface_factory
,
100 const ReturnedResourceArray
& resources
,
101 BlockingTaskRunner
* main_thread_task_runner
) {
103 surface_factory
->UnrefResources(resources
);
106 RenderPassId
SurfaceAggregator::RemapPassId(RenderPassId surface_local_pass_id
,
107 SurfaceId surface_id
) {
108 RenderPassIdAllocator
* allocator
= render_pass_allocator_map_
.get(surface_id
);
110 allocator
= new RenderPassIdAllocator(&next_render_pass_id_
);
111 render_pass_allocator_map_
.set(surface_id
, make_scoped_ptr(allocator
));
113 allocator
->AddKnownPass(surface_local_pass_id
);
114 return allocator
->Remap(surface_local_pass_id
);
117 int SurfaceAggregator::ChildIdForSurface(Surface
* surface
) {
118 SurfaceToResourceChildIdMap::iterator it
=
119 surface_id_to_resource_child_id_
.find(surface
->surface_id());
120 if (it
== surface_id_to_resource_child_id_
.end()) {
122 provider_
->CreateChild(base::Bind(&UnrefHelper
, surface
->factory()));
123 if (surface
->factory()) {
124 provider_
->SetChildNeedsSyncPoints(
125 child_id
, surface
->factory()->needs_sync_points());
127 surface_id_to_resource_child_id_
[surface
->surface_id()] = child_id
;
134 static ResourceProvider::ResourceId
ResourceRemapHelper(
135 const ResourceProvider::ResourceIdMap
& child_to_parent_map
,
136 ResourceProvider::ResourceId id
) {
137 ResourceProvider::ResourceIdMap::const_iterator it
=
138 child_to_parent_map
.find(id
);
139 DCHECK(it
!= child_to_parent_map
.end());
141 DCHECK_EQ(it
->first
, id
);
142 ResourceProvider::ResourceId remapped_id
= it
->second
;
146 static ResourceProvider::ResourceId
ValidateResourceHelper(
148 const ResourceProvider::ResourceIdMap
& child_to_parent_map
,
149 ResourceProvider::ResourceIdSet
* resources_in_frame
,
150 ResourceProvider::ResourceId id
) {
151 ResourceProvider::ResourceIdMap::const_iterator it
=
152 child_to_parent_map
.find(id
);
153 if (it
== child_to_parent_map
.end()) {
154 *invalid_frame
= true;
157 resources_in_frame
->insert(id
);
161 bool SurfaceAggregator::ValidateResources(
163 const DelegatedFrameData
* frame_data
) {
164 if (!provider_
) // TODO(jamesr): hack for unit tests that don't set up rp
167 int child_id
= ChildIdForSurface(surface
);
168 if (surface
->factory())
169 surface
->factory()->RefResources(frame_data
->resource_list
);
170 provider_
->ReceiveFromChild(child_id
, frame_data
->resource_list
);
172 ResourceProvider::ResourceIdSet referenced_resources
;
173 size_t reserve_size
= frame_data
->resource_list
.size();
174 #if defined(COMPILER_MSVC)
175 referenced_resources
.reserve(reserve_size
);
176 #elif defined(COMPILER_GCC)
177 // Pre-standard hash-tables only implement resize, which behaves similarly
178 // to reserve for these keys. Resizing to 0 may also be broken (particularly
180 // TODO(jbauman): Replace with reserve when C++11 is supported everywhere.
182 referenced_resources
.resize(reserve_size
);
185 bool invalid_frame
= false;
186 DrawQuad::ResourceIteratorCallback remap
=
187 base::Bind(&ValidateResourceHelper
, &invalid_frame
,
188 base::ConstRef(provider_
->GetChildToParentMap(child_id
)),
189 &referenced_resources
);
190 for (const auto& render_pass
: frame_data
->render_pass_list
) {
191 for (const auto& quad
: render_pass
->quad_list
)
192 quad
->IterateResources(remap
);
196 provider_
->DeclareUsedResourcesFromChild(child_id
, referenced_resources
);
198 return invalid_frame
;
201 gfx::Rect
SurfaceAggregator::DamageRectForSurface(const Surface
* surface
,
202 const RenderPass
& source
,
203 const gfx::Rect
& full_rect
) {
204 int previous_index
= previous_contained_surfaces_
[surface
->surface_id()];
205 if (previous_index
== surface
->frame_index())
207 else if (previous_index
== surface
->frame_index() - 1)
208 return source
.damage_rect
;
212 void SurfaceAggregator::HandleSurfaceQuad(
213 const SurfaceDrawQuad
* surface_quad
,
214 const gfx::Transform
& content_to_target_transform
,
215 const ClipData
& clip_rect
,
216 RenderPass
* dest_pass
) {
217 SurfaceId surface_id
= surface_quad
->surface_id
;
218 // If this surface's id is already in our referenced set then it creates
219 // a cycle in the graph and should be dropped.
220 if (referenced_surfaces_
.count(surface_id
))
222 Surface
* surface
= manager_
->GetSurfaceForId(surface_id
);
224 contained_surfaces_
[surface_id
] = 0;
227 contained_surfaces_
[surface_id
] = surface
->frame_index();
228 const CompositorFrame
* frame
= surface
->GetEligibleFrame();
231 const DelegatedFrameData
* frame_data
= frame
->delegated_frame_data
.get();
235 std::multimap
<RenderPassId
, CopyOutputRequest
*> copy_requests
;
236 surface
->TakeCopyOutputRequests(©_requests
);
238 const RenderPassList
& render_pass_list
= frame_data
->render_pass_list
;
239 bool invalid_frame
= ValidateResources(surface
, frame_data
);
241 for (auto& request
: copy_requests
) {
242 request
.second
->SendEmptyResult();
243 delete request
.second
;
248 SurfaceSet::iterator it
= referenced_surfaces_
.insert(surface_id
).first
;
249 DrawQuad::ResourceIteratorCallback remap
;
251 int child_id
= ChildIdForSurface(surface
);
253 base::Bind(&ResourceRemapHelper
,
254 base::ConstRef(provider_
->GetChildToParentMap(child_id
)));
257 bool merge_pass
= surface_quad
->opacity() == 1.f
&& copy_requests
.empty();
259 gfx::Rect surface_damage
= DamageRectForSurface(
260 surface
, *render_pass_list
.back(), surface_quad
->visible_rect
);
261 const RenderPassList
& referenced_passes
= render_pass_list
;
262 size_t passes_to_copy
=
263 merge_pass
? referenced_passes
.size() - 1 : referenced_passes
.size();
264 for (size_t j
= 0; j
< passes_to_copy
; ++j
) {
265 const RenderPass
& source
= *referenced_passes
[j
];
267 size_t sqs_size
= source
.shared_quad_state_list
.size();
268 size_t dq_size
= source
.quad_list
.size();
269 scoped_ptr
<RenderPass
> copy_pass(RenderPass::Create(sqs_size
, dq_size
));
271 RenderPassId remapped_pass_id
= RemapPassId(source
.id
, surface_id
);
273 copy_pass
->SetAll(remapped_pass_id
, source
.output_rect
, gfx::Rect(),
274 source
.transform_to_root_target
,
275 source
.has_transparent_background
);
277 MoveMatchingRequests(source
.id
, ©_requests
, ©_pass
->copy_requests
);
279 // Contributing passes aggregated in to the pass list need to take the
280 // transform of the surface quad into account to update their transform to
282 copy_pass
->transform_to_root_target
.ConcatTransform(
283 surface_quad
->quadTransform());
284 copy_pass
->transform_to_root_target
.ConcatTransform(
285 content_to_target_transform
);
286 copy_pass
->transform_to_root_target
.ConcatTransform(
287 dest_pass
->transform_to_root_target
);
289 CopyQuadsToPass(source
.quad_list
, source
.shared_quad_state_list
, remap
,
290 gfx::Transform(), ClipData(), copy_pass
.get(), surface_id
);
292 if (j
== referenced_passes
.size() - 1)
293 surface_damage
= gfx::UnionRects(surface_damage
, copy_pass
->damage_rect
);
295 dest_pass_list_
->push_back(copy_pass
.Pass());
298 const RenderPass
& last_pass
= *render_pass_list
.back();
300 // TODO(jamesr): Clean up last pass special casing.
301 const QuadList
& quads
= last_pass
.quad_list
;
303 gfx::Transform surface_transform
= surface_quad
->quadTransform();
304 surface_transform
.ConcatTransform(content_to_target_transform
);
306 // Intersect the transformed visible rect and the clip rect to create a
307 // smaller cliprect for the quad.
308 ClipData
surface_quad_clip_rect(
309 true, MathUtil::MapEnclosingClippedRect(surface_quad
->quadTransform(),
310 surface_quad
->visible_rect
));
311 if (surface_quad
->isClipped())
312 surface_quad_clip_rect
.rect
.Intersect(surface_quad
->clipRect());
314 ClipData quads_clip
= CalculateClipRect(clip_rect
, surface_quad_clip_rect
,
315 content_to_target_transform
);
317 CopyQuadsToPass(quads
, last_pass
.shared_quad_state_list
, remap
,
318 surface_transform
, quads_clip
, dest_pass
, surface_id
);
320 RenderPassId remapped_pass_id
= RemapPassId(last_pass
.id
, surface_id
);
322 CopySharedQuadState(surface_quad
->shared_quad_state
,
323 content_to_target_transform
, clip_rect
, dest_pass
);
325 SharedQuadState
* shared_quad_state
=
326 dest_pass
->shared_quad_state_list
.back();
327 RenderPassDrawQuad
* quad
=
328 dest_pass
->CreateAndAppendDrawQuad
<RenderPassDrawQuad
>();
329 quad
->SetNew(shared_quad_state
,
331 surface_quad
->visible_rect
,
340 dest_pass
->damage_rect
=
341 gfx::UnionRects(dest_pass
->damage_rect
,
342 MathUtil::MapEnclosingClippedRect(
343 surface_quad
->quadTransform(), surface_damage
));
345 referenced_surfaces_
.erase(it
);
348 void SurfaceAggregator::CopySharedQuadState(
349 const SharedQuadState
* source_sqs
,
350 const gfx::Transform
& content_to_target_transform
,
351 const ClipData
& clip_rect
,
352 RenderPass
* dest_render_pass
) {
353 SharedQuadState
* copy_shared_quad_state
=
354 dest_render_pass
->CreateAndAppendSharedQuadState();
355 copy_shared_quad_state
->CopyFrom(source_sqs
);
356 // content_to_target_transform contains any transformation that may exist
357 // between the context that these quads are being copied from (i.e. the
358 // surface's draw transform when aggregated from within a surface) to the
359 // target space of the pass. This will be identity except when copying the
360 // root draw pass from a surface into a pass when the surface draw quad's
361 // transform is not identity.
362 copy_shared_quad_state
->content_to_target_transform
.ConcatTransform(
363 content_to_target_transform
);
365 ClipData new_clip_rect
= CalculateClipRect(
366 clip_rect
, ClipData(source_sqs
->is_clipped
, source_sqs
->clip_rect
),
367 content_to_target_transform
);
368 copy_shared_quad_state
->is_clipped
= new_clip_rect
.is_clipped
;
369 copy_shared_quad_state
->clip_rect
= new_clip_rect
.rect
;
372 void SurfaceAggregator::CopyQuadsToPass(
373 const QuadList
& source_quad_list
,
374 const SharedQuadStateList
& source_shared_quad_state_list
,
375 const DrawQuad::ResourceIteratorCallback
& remap
,
376 const gfx::Transform
& content_to_target_transform
,
377 const ClipData
& clip_rect
,
378 RenderPass
* dest_pass
,
379 SurfaceId surface_id
) {
380 const SharedQuadState
* last_copied_source_shared_quad_state
= NULL
;
382 SharedQuadStateList::ConstIterator sqs_iter
=
383 source_shared_quad_state_list
.begin();
384 for (const auto& quad
: source_quad_list
) {
385 while (quad
->shared_quad_state
!= *sqs_iter
) {
387 DCHECK(sqs_iter
!= source_shared_quad_state_list
.end());
389 DCHECK_EQ(quad
->shared_quad_state
, *sqs_iter
);
391 if (quad
->material
== DrawQuad::SURFACE_CONTENT
) {
392 const SurfaceDrawQuad
* surface_quad
= SurfaceDrawQuad::MaterialCast(quad
);
393 HandleSurfaceQuad(surface_quad
, content_to_target_transform
, clip_rect
,
396 if (quad
->shared_quad_state
!= last_copied_source_shared_quad_state
) {
397 CopySharedQuadState(quad
->shared_quad_state
,
398 content_to_target_transform
, clip_rect
, dest_pass
);
399 last_copied_source_shared_quad_state
= quad
->shared_quad_state
;
402 if (quad
->material
== DrawQuad::RENDER_PASS
) {
403 const RenderPassDrawQuad
* pass_quad
=
404 RenderPassDrawQuad::MaterialCast(quad
);
405 RenderPassId original_pass_id
= pass_quad
->render_pass_id
;
406 RenderPassId remapped_pass_id
=
407 RemapPassId(original_pass_id
, surface_id
);
409 gfx::Rect pass_damage
;
410 for (const auto* pass
: *dest_pass_list_
) {
411 if (pass
->id
== remapped_pass_id
) {
412 pass_damage
= pass
->damage_rect
;
417 dest_quad
= dest_pass
->CopyFromAndAppendRenderPassDrawQuad(
418 pass_quad
, dest_pass
->shared_quad_state_list
.back(),
420 dest_pass
->damage_rect
=
421 gfx::UnionRects(dest_pass
->damage_rect
,
422 MathUtil::MapEnclosingClippedRect(
423 dest_quad
->quadTransform(), pass_damage
));
425 dest_quad
= dest_pass
->CopyFromAndAppendDrawQuad(
426 quad
, dest_pass
->shared_quad_state_list
.back());
428 if (!remap
.is_null())
429 dest_quad
->IterateResources(remap
);
434 void SurfaceAggregator::CopyPasses(const DelegatedFrameData
* frame_data
,
436 // The root surface is allowed to have copy output requests, so grab them
437 // off its render passes.
438 std::multimap
<RenderPassId
, CopyOutputRequest
*> copy_requests
;
439 surface
->TakeCopyOutputRequests(©_requests
);
441 const RenderPassList
& source_pass_list
= frame_data
->render_pass_list
;
442 bool invalid_frame
= ValidateResources(surface
, frame_data
);
443 DCHECK(!invalid_frame
);
447 DrawQuad::ResourceIteratorCallback remap
;
449 int child_id
= ChildIdForSurface(surface
);
451 base::Bind(&ResourceRemapHelper
,
452 base::ConstRef(provider_
->GetChildToParentMap(child_id
)));
455 for (size_t i
= 0; i
< source_pass_list
.size(); ++i
) {
456 const RenderPass
& source
= *source_pass_list
[i
];
458 size_t sqs_size
= source
.shared_quad_state_list
.size();
459 size_t dq_size
= source
.quad_list
.size();
460 scoped_ptr
<RenderPass
> copy_pass(RenderPass::Create(sqs_size
, dq_size
));
462 MoveMatchingRequests(source
.id
, ©_requests
, ©_pass
->copy_requests
);
464 RenderPassId remapped_pass_id
=
465 RemapPassId(source
.id
, surface
->surface_id());
467 gfx::Rect damage_rect
=
468 (i
< source_pass_list
.size() - 1)
470 : DamageRectForSurface(surface
, source
, source
.output_rect
);
471 copy_pass
->SetAll(remapped_pass_id
, source
.output_rect
, damage_rect
,
472 source
.transform_to_root_target
,
473 source
.has_transparent_background
);
475 CopyQuadsToPass(source
.quad_list
, source
.shared_quad_state_list
, remap
,
476 gfx::Transform(), ClipData(), copy_pass
.get(),
477 surface
->surface_id());
479 dest_pass_list_
->push_back(copy_pass
.Pass());
483 void SurfaceAggregator::RemoveUnreferencedChildren() {
484 for (const auto& surface
: previous_contained_surfaces_
) {
485 if (!contained_surfaces_
.count(surface
.first
)) {
486 SurfaceToResourceChildIdMap::iterator it
=
487 surface_id_to_resource_child_id_
.find(surface
.first
);
488 if (it
!= surface_id_to_resource_child_id_
.end()) {
489 provider_
->DestroyChild(it
->second
);
490 surface_id_to_resource_child_id_
.erase(it
);
493 Surface
* surface_ptr
= manager_
->GetSurfaceForId(surface
.first
);
495 surface_ptr
->RunDrawCallbacks(SurfaceDrawStatus::DRAW_SKIPPED
);
500 scoped_ptr
<CompositorFrame
> SurfaceAggregator::Aggregate(SurfaceId surface_id
) {
501 Surface
* surface
= manager_
->GetSurfaceForId(surface_id
);
503 contained_surfaces_
[surface_id
] = surface
->frame_index();
504 const CompositorFrame
* root_surface_frame
= surface
->GetEligibleFrame();
505 if (!root_surface_frame
)
507 TRACE_EVENT0("cc", "SurfaceAggregator::Aggregate");
509 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
510 frame
->delegated_frame_data
= make_scoped_ptr(new DelegatedFrameData
);
512 DCHECK(root_surface_frame
->delegated_frame_data
);
514 SurfaceSet::iterator it
= referenced_surfaces_
.insert(surface_id
).first
;
516 dest_resource_list_
= &frame
->delegated_frame_data
->resource_list
;
517 dest_pass_list_
= &frame
->delegated_frame_data
->render_pass_list
;
519 CopyPasses(root_surface_frame
->delegated_frame_data
.get(), surface
);
521 referenced_surfaces_
.erase(it
);
522 DCHECK(referenced_surfaces_
.empty());
524 if (dest_pass_list_
->empty())
527 dest_pass_list_
= NULL
;
528 RemoveUnreferencedChildren();
529 contained_surfaces_
.swap(previous_contained_surfaces_
);
530 contained_surfaces_
.clear();
532 for (SurfaceIndexMap::iterator it
= previous_contained_surfaces_
.begin();
533 it
!= previous_contained_surfaces_
.end();
535 Surface
* surface
= manager_
->GetSurfaceForId(it
->first
);
537 surface
->TakeLatencyInfo(&frame
->metadata
.latency_info
);
540 // TODO(jamesr): Aggregate all resource references into the returned frame's
546 void SurfaceAggregator::ReleaseResources(SurfaceId surface_id
) {
547 SurfaceToResourceChildIdMap::iterator it
=
548 surface_id_to_resource_child_id_
.find(surface_id
);
549 if (it
!= surface_id_to_resource_child_id_
.end()) {
550 provider_
->DestroyChild(it
->second
);
551 surface_id_to_resource_child_id_
.erase(it
);
555 void SurfaceAggregator::SetFullDamageForSurface(SurfaceId surface_id
) {
556 auto it
= previous_contained_surfaces_
.find(surface_id
);
557 if (it
== previous_contained_surfaces_
.end())
559 // Set the last drawn index as 0 to ensure full damage next time it's drawn.