OLD | NEW |
| (Empty) |
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 <map> | |
8 | |
9 #include "base/bind.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 | |
24 namespace cc { | |
25 namespace { | |
26 | |
27 void MoveMatchingRequests( | |
28 RenderPassId id, | |
29 std::multimap<RenderPassId, CopyOutputRequest*>* copy_requests, | |
30 ScopedPtrVector<CopyOutputRequest>* output_requests) { | |
31 auto request_range = copy_requests->equal_range(id); | |
32 for (auto it = request_range.first; it != request_range.second; ++it) { | |
33 DCHECK(it->second); | |
34 output_requests->push_back(scoped_ptr<CopyOutputRequest>(it->second)); | |
35 it->second = nullptr; | |
36 } | |
37 copy_requests->erase(request_range.first, request_range.second); | |
38 } | |
39 | |
40 } // namespace | |
41 | |
42 SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager, | |
43 ResourceProvider* provider) | |
44 : manager_(manager), provider_(provider), next_render_pass_id_(1) { | |
45 DCHECK(manager_); | |
46 } | |
47 | |
48 SurfaceAggregator::~SurfaceAggregator() {} | |
49 | |
50 // Create a clip rect for an aggregated quad from the original clip rect and | |
51 // the clip rect from the surface it's on. | |
52 SurfaceAggregator::ClipData SurfaceAggregator::CalculateClipRect( | |
53 const ClipData& surface_clip, | |
54 const ClipData& quad_clip, | |
55 const gfx::Transform& content_to_target_transform) { | |
56 ClipData out_clip; | |
57 if (surface_clip.is_clipped) | |
58 out_clip = surface_clip; | |
59 | |
60 if (quad_clip.is_clipped) { | |
61 // TODO(jamesr): This only works if content_to_target_transform maps integer | |
62 // rects to integer rects. | |
63 gfx::Rect final_clip = MathUtil::MapEnclosingClippedRect( | |
64 content_to_target_transform, quad_clip.rect); | |
65 if (out_clip.is_clipped) | |
66 out_clip.rect.Intersect(final_clip); | |
67 else | |
68 out_clip.rect = final_clip; | |
69 out_clip.is_clipped = true; | |
70 } | |
71 | |
72 return out_clip; | |
73 } | |
74 | |
75 class SurfaceAggregator::RenderPassIdAllocator { | |
76 public: | |
77 explicit RenderPassIdAllocator(int* next_index) : next_index_(next_index) {} | |
78 ~RenderPassIdAllocator() {} | |
79 | |
80 void AddKnownPass(RenderPassId id) { | |
81 if (id_to_index_map_.find(id) != id_to_index_map_.end()) | |
82 return; | |
83 id_to_index_map_[id] = (*next_index_)++; | |
84 } | |
85 | |
86 RenderPassId Remap(RenderPassId id) { | |
87 DCHECK(id_to_index_map_.find(id) != id_to_index_map_.end()); | |
88 return RenderPassId(1, id_to_index_map_[id]); | |
89 } | |
90 | |
91 private: | |
92 base::hash_map<RenderPassId, int> id_to_index_map_; | |
93 int* next_index_; | |
94 | |
95 DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator); | |
96 }; | |
97 | |
98 static void UnrefHelper(base::WeakPtr<SurfaceFactory> surface_factory, | |
99 const ReturnedResourceArray& resources, | |
100 BlockingTaskRunner* main_thread_task_runner) { | |
101 if (surface_factory) | |
102 surface_factory->UnrefResources(resources); | |
103 } | |
104 | |
105 RenderPassId SurfaceAggregator::RemapPassId(RenderPassId surface_local_pass_id, | |
106 SurfaceId surface_id) { | |
107 RenderPassIdAllocator* allocator = render_pass_allocator_map_.get(surface_id); | |
108 if (!allocator) { | |
109 allocator = new RenderPassIdAllocator(&next_render_pass_id_); | |
110 render_pass_allocator_map_.set(surface_id, make_scoped_ptr(allocator)); | |
111 } | |
112 allocator->AddKnownPass(surface_local_pass_id); | |
113 return allocator->Remap(surface_local_pass_id); | |
114 } | |
115 | |
116 int SurfaceAggregator::ChildIdForSurface(Surface* surface) { | |
117 SurfaceToResourceChildIdMap::iterator it = | |
118 surface_id_to_resource_child_id_.find(surface->surface_id()); | |
119 if (it == surface_id_to_resource_child_id_.end()) { | |
120 int child_id = | |
121 provider_->CreateChild(base::Bind(&UnrefHelper, surface->factory())); | |
122 surface_id_to_resource_child_id_[surface->surface_id()] = child_id; | |
123 return child_id; | |
124 } else { | |
125 return it->second; | |
126 } | |
127 } | |
128 | |
129 static ResourceProvider::ResourceId ResourceRemapHelper( | |
130 bool* invalid_frame, | |
131 const ResourceProvider::ResourceIdMap& child_to_parent_map, | |
132 ResourceProvider::ResourceIdArray* resources_in_frame, | |
133 ResourceProvider::ResourceId id) { | |
134 ResourceProvider::ResourceIdMap::const_iterator it = | |
135 child_to_parent_map.find(id); | |
136 if (it == child_to_parent_map.end()) { | |
137 *invalid_frame = true; | |
138 return 0; | |
139 } | |
140 | |
141 DCHECK_EQ(it->first, id); | |
142 ResourceProvider::ResourceId remapped_id = it->second; | |
143 resources_in_frame->push_back(id); | |
144 return remapped_id; | |
145 } | |
146 | |
147 bool SurfaceAggregator::TakeResources(Surface* surface, | |
148 const DelegatedFrameData* frame_data, | |
149 RenderPassList* render_pass_list) { | |
150 RenderPass::CopyAll(frame_data->render_pass_list, render_pass_list); | |
151 if (!provider_) // TODO(jamesr): hack for unit tests that don't set up rp | |
152 return false; | |
153 | |
154 int child_id = ChildIdForSurface(surface); | |
155 if (surface->factory()) | |
156 surface->factory()->RefResources(frame_data->resource_list); | |
157 provider_->ReceiveFromChild(child_id, frame_data->resource_list); | |
158 | |
159 typedef ResourceProvider::ResourceIdArray IdArray; | |
160 IdArray referenced_resources; | |
161 | |
162 bool invalid_frame = false; | |
163 DrawQuad::ResourceIteratorCallback remap = | |
164 base::Bind(&ResourceRemapHelper, | |
165 &invalid_frame, | |
166 provider_->GetChildToParentMap(child_id), | |
167 &referenced_resources); | |
168 for (const auto& render_pass : *render_pass_list) { | |
169 for (const auto& quad : render_pass->quad_list) | |
170 quad->IterateResources(remap); | |
171 } | |
172 | |
173 if (!invalid_frame) | |
174 provider_->DeclareUsedResourcesFromChild(child_id, referenced_resources); | |
175 | |
176 return invalid_frame; | |
177 } | |
178 | |
179 gfx::Rect SurfaceAggregator::DamageRectForSurface(const Surface* surface, | |
180 const RenderPass& source, | |
181 const gfx::Rect& full_rect) { | |
182 int previous_index = previous_contained_surfaces_[surface->surface_id()]; | |
183 if (previous_index == surface->frame_index()) | |
184 return gfx::Rect(); | |
185 else if (previous_index == surface->frame_index() - 1) | |
186 return source.damage_rect; | |
187 return full_rect; | |
188 } | |
189 | |
190 void SurfaceAggregator::HandleSurfaceQuad( | |
191 const SurfaceDrawQuad* surface_quad, | |
192 const gfx::Transform& content_to_target_transform, | |
193 const ClipData& clip_rect, | |
194 RenderPass* dest_pass) { | |
195 SurfaceId surface_id = surface_quad->surface_id; | |
196 // If this surface's id is already in our referenced set then it creates | |
197 // a cycle in the graph and should be dropped. | |
198 if (referenced_surfaces_.count(surface_id)) | |
199 return; | |
200 Surface* surface = manager_->GetSurfaceForId(surface_id); | |
201 if (!surface) { | |
202 contained_surfaces_[surface_id] = 0; | |
203 return; | |
204 } | |
205 contained_surfaces_[surface_id] = surface->frame_index(); | |
206 const CompositorFrame* frame = surface->GetEligibleFrame(); | |
207 if (!frame) | |
208 return; | |
209 const DelegatedFrameData* frame_data = frame->delegated_frame_data.get(); | |
210 if (!frame_data) | |
211 return; | |
212 | |
213 std::multimap<RenderPassId, CopyOutputRequest*> copy_requests; | |
214 surface->TakeCopyOutputRequests(©_requests); | |
215 | |
216 RenderPassList render_pass_list; | |
217 bool invalid_frame = TakeResources(surface, frame_data, &render_pass_list); | |
218 if (invalid_frame) { | |
219 for (auto& request : copy_requests) { | |
220 request.second->SendEmptyResult(); | |
221 delete request.second; | |
222 } | |
223 return; | |
224 } | |
225 | |
226 SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first; | |
227 | |
228 bool merge_pass = surface_quad->opacity() == 1.f && copy_requests.empty(); | |
229 | |
230 gfx::Rect surface_damage = DamageRectForSurface( | |
231 surface, *render_pass_list.back(), surface_quad->visible_rect); | |
232 const RenderPassList& referenced_passes = render_pass_list; | |
233 size_t passes_to_copy = | |
234 merge_pass ? referenced_passes.size() - 1 : referenced_passes.size(); | |
235 for (size_t j = 0; j < passes_to_copy; ++j) { | |
236 const RenderPass& source = *referenced_passes[j]; | |
237 | |
238 size_t sqs_size = source.shared_quad_state_list.size(); | |
239 size_t dq_size = source.quad_list.size(); | |
240 scoped_ptr<RenderPass> copy_pass(RenderPass::Create(sqs_size, dq_size)); | |
241 | |
242 RenderPassId remapped_pass_id = RemapPassId(source.id, surface_id); | |
243 | |
244 copy_pass->SetAll(remapped_pass_id, source.output_rect, gfx::Rect(), | |
245 source.transform_to_root_target, | |
246 source.has_transparent_background); | |
247 | |
248 MoveMatchingRequests(source.id, ©_requests, ©_pass->copy_requests); | |
249 | |
250 // Contributing passes aggregated in to the pass list need to take the | |
251 // transform of the surface quad into account to update their transform to | |
252 // the root surface. | |
253 copy_pass->transform_to_root_target.ConcatTransform( | |
254 surface_quad->quadTransform()); | |
255 copy_pass->transform_to_root_target.ConcatTransform( | |
256 content_to_target_transform); | |
257 copy_pass->transform_to_root_target.ConcatTransform( | |
258 dest_pass->transform_to_root_target); | |
259 | |
260 CopyQuadsToPass(source.quad_list, source.shared_quad_state_list, | |
261 gfx::Transform(), ClipData(), copy_pass.get(), surface_id); | |
262 | |
263 if (j == referenced_passes.size() - 1) | |
264 surface_damage = gfx::UnionRects(surface_damage, copy_pass->damage_rect); | |
265 | |
266 dest_pass_list_->push_back(copy_pass.Pass()); | |
267 } | |
268 | |
269 const RenderPass& last_pass = *render_pass_list.back(); | |
270 if (merge_pass) { | |
271 // TODO(jamesr): Clean up last pass special casing. | |
272 const QuadList& quads = last_pass.quad_list; | |
273 | |
274 gfx::Transform surface_transform = surface_quad->quadTransform(); | |
275 surface_transform.ConcatTransform(content_to_target_transform); | |
276 | |
277 // Intersect the transformed visible rect and the clip rect to create a | |
278 // smaller cliprect for the quad. | |
279 ClipData surface_quad_clip_rect( | |
280 true, MathUtil::MapEnclosingClippedRect(surface_quad->quadTransform(), | |
281 surface_quad->visible_rect)); | |
282 if (surface_quad->isClipped()) | |
283 surface_quad_clip_rect.rect.Intersect(surface_quad->clipRect()); | |
284 | |
285 ClipData quads_clip = CalculateClipRect(clip_rect, surface_quad_clip_rect, | |
286 content_to_target_transform); | |
287 | |
288 CopyQuadsToPass(quads, last_pass.shared_quad_state_list, surface_transform, | |
289 quads_clip, dest_pass, surface_id); | |
290 } else { | |
291 RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id); | |
292 | |
293 CopySharedQuadState(surface_quad->shared_quad_state, | |
294 content_to_target_transform, clip_rect, dest_pass); | |
295 | |
296 SharedQuadState* shared_quad_state = | |
297 dest_pass->shared_quad_state_list.back(); | |
298 RenderPassDrawQuad* quad = | |
299 dest_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>(); | |
300 quad->SetNew(shared_quad_state, | |
301 surface_quad->rect, | |
302 surface_quad->visible_rect, | |
303 remapped_pass_id, | |
304 0, | |
305 gfx::Vector2dF(), | |
306 gfx::Size(), | |
307 FilterOperations(), | |
308 gfx::Vector2dF(), | |
309 FilterOperations()); | |
310 } | |
311 dest_pass->damage_rect = | |
312 gfx::UnionRects(dest_pass->damage_rect, | |
313 MathUtil::MapEnclosingClippedRect( | |
314 surface_quad->quadTransform(), surface_damage)); | |
315 | |
316 referenced_surfaces_.erase(it); | |
317 } | |
318 | |
319 void SurfaceAggregator::CopySharedQuadState( | |
320 const SharedQuadState* source_sqs, | |
321 const gfx::Transform& content_to_target_transform, | |
322 const ClipData& clip_rect, | |
323 RenderPass* dest_render_pass) { | |
324 SharedQuadState* copy_shared_quad_state = | |
325 dest_render_pass->CreateAndAppendSharedQuadState(); | |
326 copy_shared_quad_state->CopyFrom(source_sqs); | |
327 // content_to_target_transform contains any transformation that may exist | |
328 // between the context that these quads are being copied from (i.e. the | |
329 // surface's draw transform when aggregated from within a surface) to the | |
330 // target space of the pass. This will be identity except when copying the | |
331 // root draw pass from a surface into a pass when the surface draw quad's | |
332 // transform is not identity. | |
333 copy_shared_quad_state->content_to_target_transform.ConcatTransform( | |
334 content_to_target_transform); | |
335 | |
336 ClipData new_clip_rect = CalculateClipRect( | |
337 clip_rect, ClipData(source_sqs->is_clipped, source_sqs->clip_rect), | |
338 content_to_target_transform); | |
339 copy_shared_quad_state->is_clipped = new_clip_rect.is_clipped; | |
340 copy_shared_quad_state->clip_rect = new_clip_rect.rect; | |
341 } | |
342 | |
343 void SurfaceAggregator::CopyQuadsToPass( | |
344 const QuadList& source_quad_list, | |
345 const SharedQuadStateList& source_shared_quad_state_list, | |
346 const gfx::Transform& content_to_target_transform, | |
347 const ClipData& clip_rect, | |
348 RenderPass* dest_pass, | |
349 SurfaceId surface_id) { | |
350 const SharedQuadState* last_copied_source_shared_quad_state = NULL; | |
351 | |
352 SharedQuadStateList::ConstIterator sqs_iter = | |
353 source_shared_quad_state_list.begin(); | |
354 for (const auto& quad : source_quad_list) { | |
355 while (quad->shared_quad_state != *sqs_iter) { | |
356 ++sqs_iter; | |
357 DCHECK(sqs_iter != source_shared_quad_state_list.end()); | |
358 } | |
359 DCHECK_EQ(quad->shared_quad_state, *sqs_iter); | |
360 | |
361 if (quad->material == DrawQuad::SURFACE_CONTENT) { | |
362 const SurfaceDrawQuad* surface_quad = SurfaceDrawQuad::MaterialCast(quad); | |
363 HandleSurfaceQuad(surface_quad, content_to_target_transform, clip_rect, | |
364 dest_pass); | |
365 } else { | |
366 if (quad->shared_quad_state != last_copied_source_shared_quad_state) { | |
367 CopySharedQuadState(quad->shared_quad_state, | |
368 content_to_target_transform, clip_rect, dest_pass); | |
369 last_copied_source_shared_quad_state = quad->shared_quad_state; | |
370 } | |
371 if (quad->material == DrawQuad::RENDER_PASS) { | |
372 const RenderPassDrawQuad* pass_quad = | |
373 RenderPassDrawQuad::MaterialCast(quad); | |
374 RenderPassId original_pass_id = pass_quad->render_pass_id; | |
375 RenderPassId remapped_pass_id = | |
376 RemapPassId(original_pass_id, surface_id); | |
377 | |
378 gfx::Rect pass_damage; | |
379 for (const auto* pass : *dest_pass_list_) { | |
380 if (pass->id == remapped_pass_id) { | |
381 pass_damage = pass->damage_rect; | |
382 break; | |
383 } | |
384 } | |
385 | |
386 DrawQuad* rpdq = dest_pass->CopyFromAndAppendRenderPassDrawQuad( | |
387 pass_quad, dest_pass->shared_quad_state_list.back(), | |
388 remapped_pass_id); | |
389 dest_pass->damage_rect = gfx::UnionRects( | |
390 dest_pass->damage_rect, MathUtil::MapEnclosingClippedRect( | |
391 rpdq->quadTransform(), pass_damage)); | |
392 } else { | |
393 dest_pass->CopyFromAndAppendDrawQuad( | |
394 quad, dest_pass->shared_quad_state_list.back()); | |
395 } | |
396 } | |
397 } | |
398 } | |
399 | |
400 void SurfaceAggregator::CopyPasses(const DelegatedFrameData* frame_data, | |
401 Surface* surface) { | |
402 RenderPassList source_pass_list; | |
403 | |
404 // The root surface is allowed to have copy output requests, so grab them | |
405 // off its render passes. | |
406 std::multimap<RenderPassId, CopyOutputRequest*> copy_requests; | |
407 surface->TakeCopyOutputRequests(©_requests); | |
408 | |
409 bool invalid_frame = TakeResources(surface, frame_data, &source_pass_list); | |
410 DCHECK(!invalid_frame); | |
411 | |
412 for (size_t i = 0; i < source_pass_list.size(); ++i) { | |
413 const RenderPass& source = *source_pass_list[i]; | |
414 | |
415 size_t sqs_size = source.shared_quad_state_list.size(); | |
416 size_t dq_size = source.quad_list.size(); | |
417 scoped_ptr<RenderPass> copy_pass(RenderPass::Create(sqs_size, dq_size)); | |
418 | |
419 MoveMatchingRequests(source.id, ©_requests, ©_pass->copy_requests); | |
420 | |
421 RenderPassId remapped_pass_id = | |
422 RemapPassId(source.id, surface->surface_id()); | |
423 | |
424 gfx::Rect damage_rect = | |
425 (i < source_pass_list.size() - 1) | |
426 ? gfx::Rect() | |
427 : DamageRectForSurface(surface, source, source.output_rect); | |
428 copy_pass->SetAll(remapped_pass_id, source.output_rect, damage_rect, | |
429 source.transform_to_root_target, | |
430 source.has_transparent_background); | |
431 | |
432 CopyQuadsToPass(source.quad_list, source.shared_quad_state_list, | |
433 gfx::Transform(), ClipData(), copy_pass.get(), | |
434 surface->surface_id()); | |
435 | |
436 dest_pass_list_->push_back(copy_pass.Pass()); | |
437 } | |
438 } | |
439 | |
440 void SurfaceAggregator::RemoveUnreferencedChildren() { | |
441 for (const auto& surface : previous_contained_surfaces_) { | |
442 if (!contained_surfaces_.count(surface.first)) { | |
443 SurfaceToResourceChildIdMap::iterator it = | |
444 surface_id_to_resource_child_id_.find(surface.first); | |
445 if (it != surface_id_to_resource_child_id_.end()) { | |
446 provider_->DestroyChild(it->second); | |
447 surface_id_to_resource_child_id_.erase(it); | |
448 } | |
449 | |
450 Surface* surface_ptr = manager_->GetSurfaceForId(surface.first); | |
451 if (surface_ptr) | |
452 surface_ptr->RunDrawCallbacks(SurfaceDrawStatus::DRAW_SKIPPED); | |
453 } | |
454 } | |
455 } | |
456 | |
457 scoped_ptr<CompositorFrame> SurfaceAggregator::Aggregate(SurfaceId surface_id) { | |
458 Surface* surface = manager_->GetSurfaceForId(surface_id); | |
459 DCHECK(surface); | |
460 contained_surfaces_[surface_id] = surface->frame_index(); | |
461 const CompositorFrame* root_surface_frame = surface->GetEligibleFrame(); | |
462 if (!root_surface_frame) | |
463 return nullptr; | |
464 TRACE_EVENT0("cc", "SurfaceAggregator::Aggregate"); | |
465 | |
466 scoped_ptr<CompositorFrame> frame(new CompositorFrame); | |
467 frame->delegated_frame_data = make_scoped_ptr(new DelegatedFrameData); | |
468 | |
469 DCHECK(root_surface_frame->delegated_frame_data); | |
470 | |
471 SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first; | |
472 | |
473 dest_resource_list_ = &frame->delegated_frame_data->resource_list; | |
474 dest_pass_list_ = &frame->delegated_frame_data->render_pass_list; | |
475 | |
476 CopyPasses(root_surface_frame->delegated_frame_data.get(), surface); | |
477 | |
478 referenced_surfaces_.erase(it); | |
479 DCHECK(referenced_surfaces_.empty()); | |
480 | |
481 if (dest_pass_list_->empty()) | |
482 return nullptr; | |
483 | |
484 dest_pass_list_ = NULL; | |
485 RemoveUnreferencedChildren(); | |
486 contained_surfaces_.swap(previous_contained_surfaces_); | |
487 contained_surfaces_.clear(); | |
488 | |
489 for (SurfaceIndexMap::iterator it = previous_contained_surfaces_.begin(); | |
490 it != previous_contained_surfaces_.end(); | |
491 ++it) { | |
492 Surface* surface = manager_->GetSurfaceForId(it->first); | |
493 if (surface) | |
494 surface->TakeLatencyInfo(&frame->metadata.latency_info); | |
495 } | |
496 | |
497 // TODO(jamesr): Aggregate all resource references into the returned frame's | |
498 // resource list. | |
499 | |
500 return frame.Pass(); | |
501 } | |
502 | |
503 void SurfaceAggregator::ReleaseResources(SurfaceId surface_id) { | |
504 SurfaceToResourceChildIdMap::iterator it = | |
505 surface_id_to_resource_child_id_.find(surface_id); | |
506 if (it != surface_id_to_resource_child_id_.end()) { | |
507 provider_->DestroyChild(it->second); | |
508 surface_id_to_resource_child_id_.erase(it); | |
509 } | |
510 } | |
511 | |
512 } // namespace cc | |
OLD | NEW |