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 "base/bind.h" 8#include "base/containers/hash_tables.h" 9#include "base/debug/trace_event.h" 10#include "base/logging.h" 11#include "cc/base/math_util.h" 12#include "cc/output/compositor_frame.h" 13#include "cc/output/delegated_frame_data.h" 14#include "cc/quads/draw_quad.h" 15#include "cc/quads/render_pass_draw_quad.h" 16#include "cc/quads/shared_quad_state.h" 17#include "cc/quads/surface_draw_quad.h" 18#include "cc/surfaces/surface.h" 19#include "cc/surfaces/surface_factory.h" 20#include "cc/surfaces/surface_manager.h" 21#include "cc/trees/blocking_task_runner.h" 22 23namespace cc { 24 25SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager, 26 ResourceProvider* provider) 27 : manager_(manager), provider_(provider) { 28 DCHECK(manager_); 29} 30 31SurfaceAggregator::~SurfaceAggregator() {} 32 33class SurfaceAggregator::RenderPassIdAllocator { 34 public: 35 explicit RenderPassIdAllocator(SurfaceId surface_id) 36 : surface_id_(surface_id), next_index_(1) {} 37 ~RenderPassIdAllocator() {} 38 39 void AddKnownPass(RenderPassId id) { 40 if (id_to_index_map_.find(id) != id_to_index_map_.end()) 41 return; 42 id_to_index_map_[id] = next_index_++; 43 } 44 45 RenderPassId Remap(RenderPassId id) { 46 DCHECK(id_to_index_map_.find(id) != id_to_index_map_.end()); 47 return RenderPassId(surface_id_.id, id_to_index_map_[id]); 48 } 49 50 private: 51 base::hash_map<RenderPassId, int> id_to_index_map_; 52 SurfaceId surface_id_; 53 int next_index_; 54 55 DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator); 56}; 57 58static void UnrefHelper(base::WeakPtr<SurfaceFactory> surface_factory, 59 const ReturnedResourceArray& resources, 60 BlockingTaskRunner* main_thread_task_runner) { 61 if (surface_factory) 62 surface_factory->UnrefResources(resources); 63} 64 65RenderPassId SurfaceAggregator::RemapPassId(RenderPassId surface_local_pass_id, 66 SurfaceId surface_id) { 67 RenderPassIdAllocator* allocator = render_pass_allocator_map_.get(surface_id); 68 if (!allocator) { 69 allocator = new RenderPassIdAllocator(surface_id); 70 render_pass_allocator_map_.set(surface_id, make_scoped_ptr(allocator)); 71 } 72 allocator->AddKnownPass(surface_local_pass_id); 73 return allocator->Remap(surface_local_pass_id); 74} 75 76int SurfaceAggregator::ChildIdForSurface(Surface* surface) { 77 SurfaceToResourceChildIdMap::iterator it = 78 surface_id_to_resource_child_id_.find(surface->surface_id()); 79 if (it == surface_id_to_resource_child_id_.end()) { 80 int child_id = provider_->CreateChild( 81 base::Bind(&UnrefHelper, surface->factory()->AsWeakPtr())); 82 surface_id_to_resource_child_id_[surface->surface_id()] = child_id; 83 return child_id; 84 } else { 85 return it->second; 86 } 87} 88 89static ResourceProvider::ResourceId ResourceRemapHelper( 90 bool* invalid_frame, 91 const ResourceProvider::ResourceIdMap& child_to_parent_map, 92 ResourceProvider::ResourceIdArray* resources_in_frame, 93 ResourceProvider::ResourceId id) { 94 ResourceProvider::ResourceIdMap::const_iterator it = 95 child_to_parent_map.find(id); 96 if (it == child_to_parent_map.end()) { 97 *invalid_frame = true; 98 return 0; 99 } 100 101 DCHECK_EQ(it->first, id); 102 ResourceProvider::ResourceId remapped_id = it->second; 103 resources_in_frame->push_back(id); 104 return remapped_id; 105} 106 107bool SurfaceAggregator::TakeResources(Surface* surface, 108 const DelegatedFrameData* frame_data, 109 RenderPassList* render_pass_list) { 110 RenderPass::CopyAll(frame_data->render_pass_list, render_pass_list); 111 if (!provider_) // TODO(jamesr): hack for unit tests that don't set up rp 112 return false; 113 114 int child_id = ChildIdForSurface(surface); 115 provider_->ReceiveFromChild(child_id, frame_data->resource_list); 116 surface->factory()->RefResources(frame_data->resource_list); 117 118 typedef ResourceProvider::ResourceIdArray IdArray; 119 IdArray referenced_resources; 120 121 bool invalid_frame = false; 122 DrawQuad::ResourceIteratorCallback remap = 123 base::Bind(&ResourceRemapHelper, 124 &invalid_frame, 125 provider_->GetChildToParentMap(child_id), 126 &referenced_resources); 127 for (RenderPassList::iterator it = render_pass_list->begin(); 128 it != render_pass_list->end(); 129 ++it) { 130 QuadList& quad_list = (*it)->quad_list; 131 for (QuadList::Iterator quad_it = quad_list.begin(); 132 quad_it != quad_list.end(); 133 ++quad_it) { 134 quad_it->IterateResources(remap); 135 } 136 } 137 if (!invalid_frame) 138 provider_->DeclareUsedResourcesFromChild(child_id, referenced_resources); 139 140 return invalid_frame; 141} 142 143gfx::Rect SurfaceAggregator::DamageRectForSurface(const Surface* surface, 144 const RenderPass& source) { 145 int previous_index = previous_contained_surfaces_[surface->surface_id()]; 146 if (previous_index == surface->frame_index()) 147 return gfx::Rect(); 148 else if (previous_index == surface->frame_index() - 1) 149 return source.damage_rect; 150 return gfx::Rect(surface->size()); 151} 152 153void SurfaceAggregator::HandleSurfaceQuad(const SurfaceDrawQuad* surface_quad, 154 RenderPass* dest_pass) { 155 SurfaceId surface_id = surface_quad->surface_id; 156 // If this surface's id is already in our referenced set then it creates 157 // a cycle in the graph and should be dropped. 158 if (referenced_surfaces_.count(surface_id)) 159 return; 160 Surface* surface = manager_->GetSurfaceForId(surface_id); 161 if (!surface) { 162 contained_surfaces_[surface_id] = 0; 163 return; 164 } 165 contained_surfaces_[surface_id] = surface->frame_index(); 166 const CompositorFrame* frame = surface->GetEligibleFrame(); 167 if (!frame) 168 return; 169 const DelegatedFrameData* frame_data = frame->delegated_frame_data.get(); 170 if (!frame_data) 171 return; 172 173 RenderPassList render_pass_list; 174 bool invalid_frame = TakeResources(surface, frame_data, &render_pass_list); 175 if (invalid_frame) 176 return; 177 178 SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first; 179 180 ScopedPtrVector<CopyOutputRequest> copy_requests; 181 surface->TakeCopyOutputRequests(©_requests); 182 183 bool merge_pass = copy_requests.empty(); 184 185 const RenderPassList& referenced_passes = render_pass_list; 186 size_t passes_to_copy = 187 merge_pass ? referenced_passes.size() - 1 : referenced_passes.size(); 188 for (size_t j = 0; j < passes_to_copy; ++j) { 189 const RenderPass& source = *referenced_passes[j]; 190 191 scoped_ptr<RenderPass> copy_pass(RenderPass::Create()); 192 193 RenderPassId remapped_pass_id = RemapPassId(source.id, surface_id); 194 195 copy_pass->SetAll(remapped_pass_id, 196 source.output_rect, 197 source.damage_rect, 198 source.transform_to_root_target, 199 source.has_transparent_background); 200 201 // Contributing passes aggregated in to the pass list need to take the 202 // transform of the surface quad into account to update their transform to 203 // the root surface. 204 // TODO(jamesr): Make sure this is sufficient for surfaces nested several 205 // levels deep and add tests. 206 copy_pass->transform_to_root_target.ConcatTransform( 207 surface_quad->quadTransform()); 208 209 CopyQuadsToPass(source.quad_list, 210 source.shared_quad_state_list, 211 gfx::Transform(), 212 copy_pass.get(), 213 surface_id); 214 215 dest_pass_list_->push_back(copy_pass.Pass()); 216 } 217 218 const RenderPass& last_pass = *render_pass_list.back(); 219 if (merge_pass) { 220 // TODO(jamesr): Clean up last pass special casing. 221 const QuadList& quads = last_pass.quad_list; 222 223 // TODO(jamesr): Make sure clipping is enforced. 224 CopyQuadsToPass(quads, 225 last_pass.shared_quad_state_list, 226 surface_quad->quadTransform(), 227 dest_pass, 228 surface_id); 229 } else { 230 RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id); 231 232 dest_pass_list_->back()->copy_requests.swap(copy_requests); 233 234 SharedQuadState* shared_quad_state = 235 dest_pass->CreateAndAppendSharedQuadState(); 236 shared_quad_state->CopyFrom(surface_quad->shared_quad_state); 237 RenderPassDrawQuad* quad = 238 dest_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>(); 239 quad->SetNew(shared_quad_state, 240 surface_quad->rect, 241 surface_quad->visible_rect, 242 remapped_pass_id, 243 0, 244 gfx::RectF(), 245 FilterOperations(), 246 gfx::Vector2dF(), 247 FilterOperations()); 248 } 249 dest_pass->damage_rect = 250 gfx::UnionRects(dest_pass->damage_rect, 251 MathUtil::MapEnclosingClippedRect( 252 surface_quad->quadTransform(), 253 DamageRectForSurface(surface, last_pass))); 254 255 referenced_surfaces_.erase(it); 256} 257 258void SurfaceAggregator::CopySharedQuadState( 259 const SharedQuadState* source_sqs, 260 const gfx::Transform& content_to_target_transform, 261 RenderPass* dest_render_pass) { 262 SharedQuadState* copy_shared_quad_state = 263 dest_render_pass->CreateAndAppendSharedQuadState(); 264 copy_shared_quad_state->CopyFrom(source_sqs); 265 // content_to_target_transform contains any transformation that may exist 266 // between the context that these quads are being copied from (i.e. the 267 // surface's draw transform when aggregated from within a surface) to the 268 // target space of the pass. This will be identity except when copying the 269 // root draw pass from a surface into a pass when the surface draw quad's 270 // transform is not identity. 271 copy_shared_quad_state->content_to_target_transform.ConcatTransform( 272 content_to_target_transform); 273 if (copy_shared_quad_state->is_clipped) { 274 copy_shared_quad_state->clip_rect = MathUtil::MapEnclosingClippedRect( 275 content_to_target_transform, copy_shared_quad_state->clip_rect); 276 } 277} 278 279void SurfaceAggregator::CopyQuadsToPass( 280 const QuadList& source_quad_list, 281 const SharedQuadStateList& source_shared_quad_state_list, 282 const gfx::Transform& content_to_target_transform, 283 RenderPass* dest_pass, 284 SurfaceId surface_id) { 285 const SharedQuadState* last_copied_source_shared_quad_state = NULL; 286 287 size_t sqs_i = 0; 288 for (QuadList::ConstIterator iter = source_quad_list.begin(); 289 iter != source_quad_list.end(); 290 ++iter) { 291 const DrawQuad* quad = &*iter; 292 while (quad->shared_quad_state != source_shared_quad_state_list[sqs_i]) { 293 ++sqs_i; 294 DCHECK_LT(sqs_i, source_shared_quad_state_list.size()); 295 } 296 DCHECK_EQ(quad->shared_quad_state, source_shared_quad_state_list[sqs_i]); 297 298 if (quad->material == DrawQuad::SURFACE_CONTENT) { 299 const SurfaceDrawQuad* surface_quad = SurfaceDrawQuad::MaterialCast(quad); 300 HandleSurfaceQuad(surface_quad, dest_pass); 301 } else { 302 if (quad->shared_quad_state != last_copied_source_shared_quad_state) { 303 CopySharedQuadState( 304 quad->shared_quad_state, content_to_target_transform, dest_pass); 305 last_copied_source_shared_quad_state = quad->shared_quad_state; 306 } 307 if (quad->material == DrawQuad::RENDER_PASS) { 308 const RenderPassDrawQuad* pass_quad = 309 RenderPassDrawQuad::MaterialCast(quad); 310 RenderPassId original_pass_id = pass_quad->render_pass_id; 311 RenderPassId remapped_pass_id = 312 RemapPassId(original_pass_id, surface_id); 313 314 dest_pass->CopyFromAndAppendRenderPassDrawQuad( 315 pass_quad, 316 dest_pass->shared_quad_state_list.back(), 317 remapped_pass_id); 318 } else { 319 dest_pass->CopyFromAndAppendDrawQuad( 320 quad, dest_pass->shared_quad_state_list.back()); 321 } 322 } 323 } 324} 325 326void SurfaceAggregator::CopyPasses(const RenderPassList& source_pass_list, 327 const Surface* surface) { 328 for (size_t i = 0; i < source_pass_list.size(); ++i) { 329 const RenderPass& source = *source_pass_list[i]; 330 331 scoped_ptr<RenderPass> copy_pass(RenderPass::Create()); 332 333 RenderPassId remapped_pass_id = 334 RemapPassId(source.id, surface->surface_id()); 335 336 copy_pass->SetAll(remapped_pass_id, 337 source.output_rect, 338 DamageRectForSurface(surface, source), 339 source.transform_to_root_target, 340 source.has_transparent_background); 341 342 CopyQuadsToPass(source.quad_list, 343 source.shared_quad_state_list, 344 gfx::Transform(), 345 copy_pass.get(), 346 surface->surface_id()); 347 348 dest_pass_list_->push_back(copy_pass.Pass()); 349 } 350} 351 352scoped_ptr<CompositorFrame> SurfaceAggregator::Aggregate(SurfaceId surface_id) { 353 Surface* surface = manager_->GetSurfaceForId(surface_id); 354 DCHECK(surface); 355 contained_surfaces_[surface_id] = surface->frame_index(); 356 const CompositorFrame* root_surface_frame = surface->GetEligibleFrame(); 357 if (!root_surface_frame) 358 return scoped_ptr<CompositorFrame>(); 359 TRACE_EVENT0("cc", "SurfaceAggregator::Aggregate"); 360 361 scoped_ptr<CompositorFrame> frame(new CompositorFrame); 362 frame->delegated_frame_data = make_scoped_ptr(new DelegatedFrameData); 363 364 DCHECK(root_surface_frame->delegated_frame_data); 365 366 RenderPassList source_pass_list; 367 368 SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first; 369 370 dest_resource_list_ = &frame->delegated_frame_data->resource_list; 371 dest_pass_list_ = &frame->delegated_frame_data->render_pass_list; 372 373 bool invalid_frame = 374 TakeResources(surface, 375 root_surface_frame->delegated_frame_data.get(), 376 &source_pass_list); 377 DCHECK(!invalid_frame); 378 379 CopyPasses(source_pass_list, surface); 380 381 referenced_surfaces_.erase(it); 382 DCHECK(referenced_surfaces_.empty()); 383 384 dest_pass_list_ = NULL; 385 contained_surfaces_.swap(previous_contained_surfaces_); 386 contained_surfaces_.clear(); 387 388 for (SurfaceIndexMap::iterator it = previous_contained_surfaces_.begin(); 389 it != previous_contained_surfaces_.end(); 390 ++it) { 391 Surface* surface = manager_->GetSurfaceForId(it->first); 392 if (surface) 393 surface->TakeLatencyInfo(&frame->metadata.latency_info); 394 } 395 396 // TODO(jamesr): Aggregate all resource references into the returned frame's 397 // resource list. 398 399 return frame.Pass(); 400} 401 402} // namespace cc 403