Remove use of deprecated GrContext functions.
[chromium-blink-merge.git] / cc / surfaces / surface_aggregator.cc
blob7c6f4f74582ba6e758c22b25e9d4b52cae6d6bac
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"
7 #include <map>
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 #include "cc/trees/blocking_task_runner.h"
25 namespace cc {
26 namespace {
28 void 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;
38 copy_requests->erase(request_range.first, request_range.second);
41 } // namespace
43 SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager,
44 ResourceProvider* provider)
45 : manager_(manager), provider_(provider), next_render_pass_id_(1) {
46 DCHECK(manager_);
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) {
57 ClipData out_clip;
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);
68 else
69 out_clip.rect = final_clip;
70 out_clip.is_clipped = true;
73 return out_clip;
76 class SurfaceAggregator::RenderPassIdAllocator {
77 public:
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())
83 return;
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]);
92 private:
93 base::hash_map<RenderPassId, int> id_to_index_map_;
94 int* next_index_;
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) {
102 if (surface_factory)
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);
109 if (!allocator) {
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()) {
121 int child_id =
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;
128 return child_id;
129 } else {
130 return it->second;
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;
143 return remapped_id;
146 static ResourceProvider::ResourceId ValidateResourceHelper(
147 bool* invalid_frame,
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;
155 return id;
157 resources_in_frame->insert(id);
158 return id;
161 bool SurfaceAggregator::ValidateResources(
162 Surface* surface,
163 const DelegatedFrameData* frame_data) {
164 if (!provider_) // TODO(jamesr): hack for unit tests that don't set up rp
165 return false;
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
179 // on stlport).
180 // TODO(jbauman): Replace with reserve when C++11 is supported everywhere.
181 if (reserve_size)
182 referenced_resources.resize(reserve_size);
183 #endif
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);
195 if (!invalid_frame)
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())
206 return gfx::Rect();
207 else if (previous_index == surface->frame_index() - 1)
208 return source.damage_rect;
209 return full_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))
221 return;
222 Surface* surface = manager_->GetSurfaceForId(surface_id);
223 if (!surface) {
224 contained_surfaces_[surface_id] = 0;
225 return;
227 contained_surfaces_[surface_id] = surface->frame_index();
228 const CompositorFrame* frame = surface->GetEligibleFrame();
229 if (!frame)
230 return;
231 const DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
232 if (!frame_data)
233 return;
235 std::multimap<RenderPassId, CopyOutputRequest*> copy_requests;
236 surface->TakeCopyOutputRequests(&copy_requests);
238 const RenderPassList& render_pass_list = frame_data->render_pass_list;
239 bool invalid_frame = ValidateResources(surface, frame_data);
240 if (invalid_frame) {
241 for (auto& request : copy_requests) {
242 request.second->SendEmptyResult();
243 delete request.second;
245 return;
248 SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first;
249 DrawQuad::ResourceIteratorCallback remap;
250 if (provider_) {
251 int child_id = ChildIdForSurface(surface);
252 remap =
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, &copy_requests, &copy_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
281 // the root surface.
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();
299 if (merge_pass) {
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);
319 } else {
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,
330 surface_quad->rect,
331 surface_quad->visible_rect,
332 remapped_pass_id,
334 gfx::Vector2dF(),
335 gfx::Size(),
336 FilterOperations(),
337 gfx::Vector2dF(),
338 FilterOperations());
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) {
386 ++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,
394 dest_pass);
395 } else {
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;
401 DrawQuad* dest_quad;
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;
413 break;
417 dest_quad = dest_pass->CopyFromAndAppendRenderPassDrawQuad(
418 pass_quad, dest_pass->shared_quad_state_list.back(),
419 remapped_pass_id);
420 dest_pass->damage_rect =
421 gfx::UnionRects(dest_pass->damage_rect,
422 MathUtil::MapEnclosingClippedRect(
423 dest_quad->quadTransform(), pass_damage));
424 } else {
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,
435 Surface* surface) {
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(&copy_requests);
441 const RenderPassList& source_pass_list = frame_data->render_pass_list;
442 bool invalid_frame = ValidateResources(surface, frame_data);
443 DCHECK(!invalid_frame);
444 if (invalid_frame)
445 return;
447 DrawQuad::ResourceIteratorCallback remap;
448 if (provider_) {
449 int child_id = ChildIdForSurface(surface);
450 remap =
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, &copy_requests, &copy_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)
469 ? gfx::Rect()
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);
494 if (surface_ptr)
495 surface_ptr->RunDrawCallbacks(SurfaceDrawStatus::DRAW_SKIPPED);
500 scoped_ptr<CompositorFrame> SurfaceAggregator::Aggregate(SurfaceId surface_id) {
501 Surface* surface = manager_->GetSurfaceForId(surface_id);
502 DCHECK(surface);
503 contained_surfaces_[surface_id] = surface->frame_index();
504 const CompositorFrame* root_surface_frame = surface->GetEligibleFrame();
505 if (!root_surface_frame)
506 return nullptr;
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())
525 return nullptr;
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();
534 ++it) {
535 Surface* surface = manager_->GetSurfaceForId(it->first);
536 if (surface)
537 surface->TakeLatencyInfo(&frame->metadata.latency_info);
540 // TODO(jamesr): Aggregate all resource references into the returned frame's
541 // resource list.
543 return frame.Pass();
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())
558 return;
559 // Set the last drawn index as 0 to ensure full damage next time it's drawn.
560 it->second = 0;
563 } // namespace cc