Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc
new file mode 100644
index 0000000..35107e8
--- /dev/null
+++ b/cc/surfaces/surface_aggregator.cc
@@ -0,0 +1,402 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/surfaces/surface_aggregator.h"
+
+#include "base/bind.h"
+#include "base/containers/hash_tables.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "cc/base/math_util.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/draw_quad.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/trees/blocking_task_runner.h"
+
+namespace cc {
+
+SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager,
+ ResourceProvider* provider)
+ : manager_(manager), provider_(provider) {
+ DCHECK(manager_);
+}
+
+SurfaceAggregator::~SurfaceAggregator() {}
+
+class SurfaceAggregator::RenderPassIdAllocator {
+ public:
+ explicit RenderPassIdAllocator(SurfaceId surface_id)
+ : surface_id_(surface_id), next_index_(1) {}
+ ~RenderPassIdAllocator() {}
+
+ void AddKnownPass(RenderPassId id) {
+ if (id_to_index_map_.find(id) != id_to_index_map_.end())
+ return;
+ id_to_index_map_[id] = next_index_++;
+ }
+
+ RenderPassId Remap(RenderPassId id) {
+ DCHECK(id_to_index_map_.find(id) != id_to_index_map_.end());
+ return RenderPassId(surface_id_.id, id_to_index_map_[id]);
+ }
+
+ private:
+ base::hash_map<RenderPassId, int> id_to_index_map_;
+ SurfaceId surface_id_;
+ int next_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator);
+};
+
+static void UnrefHelper(base::WeakPtr<SurfaceFactory> surface_factory,
+ const ReturnedResourceArray& resources,
+ BlockingTaskRunner* main_thread_task_runner) {
+ if (surface_factory)
+ surface_factory->UnrefResources(resources);
+}
+
+RenderPassId SurfaceAggregator::RemapPassId(RenderPassId surface_local_pass_id,
+ SurfaceId surface_id) {
+ RenderPassIdAllocator* allocator = render_pass_allocator_map_.get(surface_id);
+ if (!allocator) {
+ allocator = new RenderPassIdAllocator(surface_id);
+ render_pass_allocator_map_.set(surface_id, make_scoped_ptr(allocator));
+ }
+ allocator->AddKnownPass(surface_local_pass_id);
+ return allocator->Remap(surface_local_pass_id);
+}
+
+int SurfaceAggregator::ChildIdForSurface(Surface* surface) {
+ SurfaceToResourceChildIdMap::iterator it =
+ surface_id_to_resource_child_id_.find(surface->surface_id());
+ if (it == surface_id_to_resource_child_id_.end()) {
+ int child_id = provider_->CreateChild(
+ base::Bind(&UnrefHelper, surface->factory()->AsWeakPtr()));
+ surface_id_to_resource_child_id_[surface->surface_id()] = child_id;
+ return child_id;
+ } else {
+ return it->second;
+ }
+}
+
+static ResourceProvider::ResourceId ResourceRemapHelper(
+ bool* invalid_frame,
+ const ResourceProvider::ResourceIdMap& child_to_parent_map,
+ ResourceProvider::ResourceIdArray* resources_in_frame,
+ ResourceProvider::ResourceId id) {
+ ResourceProvider::ResourceIdMap::const_iterator it =
+ child_to_parent_map.find(id);
+ if (it == child_to_parent_map.end()) {
+ *invalid_frame = true;
+ return 0;
+ }
+
+ DCHECK_EQ(it->first, id);
+ ResourceProvider::ResourceId remapped_id = it->second;
+ resources_in_frame->push_back(id);
+ return remapped_id;
+}
+
+bool SurfaceAggregator::TakeResources(Surface* surface,
+ const DelegatedFrameData* frame_data,
+ RenderPassList* render_pass_list) {
+ RenderPass::CopyAll(frame_data->render_pass_list, render_pass_list);
+ if (!provider_) // TODO(jamesr): hack for unit tests that don't set up rp
+ return false;
+
+ int child_id = ChildIdForSurface(surface);
+ provider_->ReceiveFromChild(child_id, frame_data->resource_list);
+ surface->factory()->RefResources(frame_data->resource_list);
+
+ typedef ResourceProvider::ResourceIdArray IdArray;
+ IdArray referenced_resources;
+
+ bool invalid_frame = false;
+ DrawQuad::ResourceIteratorCallback remap =
+ base::Bind(&ResourceRemapHelper,
+ &invalid_frame,
+ provider_->GetChildToParentMap(child_id),
+ &referenced_resources);
+ for (RenderPassList::iterator it = render_pass_list->begin();
+ it != render_pass_list->end();
+ ++it) {
+ QuadList& quad_list = (*it)->quad_list;
+ for (QuadList::Iterator quad_it = quad_list.begin();
+ quad_it != quad_list.end();
+ ++quad_it) {
+ quad_it->IterateResources(remap);
+ }
+ }
+ if (!invalid_frame)
+ provider_->DeclareUsedResourcesFromChild(child_id, referenced_resources);
+
+ return invalid_frame;
+}
+
+gfx::Rect SurfaceAggregator::DamageRectForSurface(const Surface* surface,
+ const RenderPass& source) {
+ int previous_index = previous_contained_surfaces_[surface->surface_id()];
+ if (previous_index == surface->frame_index())
+ return gfx::Rect();
+ else if (previous_index == surface->frame_index() - 1)
+ return source.damage_rect;
+ return gfx::Rect(surface->size());
+}
+
+void SurfaceAggregator::HandleSurfaceQuad(const SurfaceDrawQuad* surface_quad,
+ RenderPass* dest_pass) {
+ SurfaceId surface_id = surface_quad->surface_id;
+ // If this surface's id is already in our referenced set then it creates
+ // a cycle in the graph and should be dropped.
+ if (referenced_surfaces_.count(surface_id))
+ return;
+ Surface* surface = manager_->GetSurfaceForId(surface_id);
+ if (!surface) {
+ contained_surfaces_[surface_id] = 0;
+ return;
+ }
+ contained_surfaces_[surface_id] = surface->frame_index();
+ const CompositorFrame* frame = surface->GetEligibleFrame();
+ if (!frame)
+ return;
+ const DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
+ if (!frame_data)
+ return;
+
+ RenderPassList render_pass_list;
+ bool invalid_frame = TakeResources(surface, frame_data, &render_pass_list);
+ if (invalid_frame)
+ return;
+
+ SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first;
+
+ ScopedPtrVector<CopyOutputRequest> copy_requests;
+ surface->TakeCopyOutputRequests(©_requests);
+
+ bool merge_pass = copy_requests.empty();
+
+ const RenderPassList& referenced_passes = render_pass_list;
+ size_t passes_to_copy =
+ merge_pass ? referenced_passes.size() - 1 : referenced_passes.size();
+ for (size_t j = 0; j < passes_to_copy; ++j) {
+ const RenderPass& source = *referenced_passes[j];
+
+ scoped_ptr<RenderPass> copy_pass(RenderPass::Create());
+
+ RenderPassId remapped_pass_id = RemapPassId(source.id, surface_id);
+
+ copy_pass->SetAll(remapped_pass_id,
+ source.output_rect,
+ source.damage_rect,
+ source.transform_to_root_target,
+ source.has_transparent_background);
+
+ // Contributing passes aggregated in to the pass list need to take the
+ // transform of the surface quad into account to update their transform to
+ // the root surface.
+ // TODO(jamesr): Make sure this is sufficient for surfaces nested several
+ // levels deep and add tests.
+ copy_pass->transform_to_root_target.ConcatTransform(
+ surface_quad->quadTransform());
+
+ CopyQuadsToPass(source.quad_list,
+ source.shared_quad_state_list,
+ gfx::Transform(),
+ copy_pass.get(),
+ surface_id);
+
+ dest_pass_list_->push_back(copy_pass.Pass());
+ }
+
+ const RenderPass& last_pass = *render_pass_list.back();
+ if (merge_pass) {
+ // TODO(jamesr): Clean up last pass special casing.
+ const QuadList& quads = last_pass.quad_list;
+
+ // TODO(jamesr): Make sure clipping is enforced.
+ CopyQuadsToPass(quads,
+ last_pass.shared_quad_state_list,
+ surface_quad->quadTransform(),
+ dest_pass,
+ surface_id);
+ } else {
+ RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id);
+
+ dest_pass_list_->back()->copy_requests.swap(copy_requests);
+
+ SharedQuadState* shared_quad_state =
+ dest_pass->CreateAndAppendSharedQuadState();
+ shared_quad_state->CopyFrom(surface_quad->shared_quad_state);
+ RenderPassDrawQuad* quad =
+ dest_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
+ quad->SetNew(shared_quad_state,
+ surface_quad->rect,
+ surface_quad->visible_rect,
+ remapped_pass_id,
+ 0,
+ gfx::RectF(),
+ FilterOperations(),
+ gfx::Vector2dF(),
+ FilterOperations());
+ }
+ dest_pass->damage_rect =
+ gfx::UnionRects(dest_pass->damage_rect,
+ MathUtil::MapEnclosingClippedRect(
+ surface_quad->quadTransform(),
+ DamageRectForSurface(surface, last_pass)));
+
+ referenced_surfaces_.erase(it);
+}
+
+void SurfaceAggregator::CopySharedQuadState(
+ const SharedQuadState* source_sqs,
+ const gfx::Transform& content_to_target_transform,
+ RenderPass* dest_render_pass) {
+ SharedQuadState* copy_shared_quad_state =
+ dest_render_pass->CreateAndAppendSharedQuadState();
+ copy_shared_quad_state->CopyFrom(source_sqs);
+ // content_to_target_transform contains any transformation that may exist
+ // between the context that these quads are being copied from (i.e. the
+ // surface's draw transform when aggregated from within a surface) to the
+ // target space of the pass. This will be identity except when copying the
+ // root draw pass from a surface into a pass when the surface draw quad's
+ // transform is not identity.
+ copy_shared_quad_state->content_to_target_transform.ConcatTransform(
+ content_to_target_transform);
+ if (copy_shared_quad_state->is_clipped) {
+ copy_shared_quad_state->clip_rect = MathUtil::MapEnclosingClippedRect(
+ content_to_target_transform, copy_shared_quad_state->clip_rect);
+ }
+}
+
+void SurfaceAggregator::CopyQuadsToPass(
+ const QuadList& source_quad_list,
+ const SharedQuadStateList& source_shared_quad_state_list,
+ const gfx::Transform& content_to_target_transform,
+ RenderPass* dest_pass,
+ SurfaceId surface_id) {
+ const SharedQuadState* last_copied_source_shared_quad_state = NULL;
+
+ size_t sqs_i = 0;
+ for (QuadList::ConstIterator iter = source_quad_list.begin();
+ iter != source_quad_list.end();
+ ++iter) {
+ const DrawQuad* quad = &*iter;
+ while (quad->shared_quad_state != source_shared_quad_state_list[sqs_i]) {
+ ++sqs_i;
+ DCHECK_LT(sqs_i, source_shared_quad_state_list.size());
+ }
+ DCHECK_EQ(quad->shared_quad_state, source_shared_quad_state_list[sqs_i]);
+
+ if (quad->material == DrawQuad::SURFACE_CONTENT) {
+ const SurfaceDrawQuad* surface_quad = SurfaceDrawQuad::MaterialCast(quad);
+ HandleSurfaceQuad(surface_quad, dest_pass);
+ } else {
+ if (quad->shared_quad_state != last_copied_source_shared_quad_state) {
+ CopySharedQuadState(
+ quad->shared_quad_state, content_to_target_transform, dest_pass);
+ last_copied_source_shared_quad_state = quad->shared_quad_state;
+ }
+ if (quad->material == DrawQuad::RENDER_PASS) {
+ const RenderPassDrawQuad* pass_quad =
+ RenderPassDrawQuad::MaterialCast(quad);
+ RenderPassId original_pass_id = pass_quad->render_pass_id;
+ RenderPassId remapped_pass_id =
+ RemapPassId(original_pass_id, surface_id);
+
+ dest_pass->CopyFromAndAppendRenderPassDrawQuad(
+ pass_quad,
+ dest_pass->shared_quad_state_list.back(),
+ remapped_pass_id);
+ } else {
+ dest_pass->CopyFromAndAppendDrawQuad(
+ quad, dest_pass->shared_quad_state_list.back());
+ }
+ }
+ }
+}
+
+void SurfaceAggregator::CopyPasses(const RenderPassList& source_pass_list,
+ const Surface* surface) {
+ for (size_t i = 0; i < source_pass_list.size(); ++i) {
+ const RenderPass& source = *source_pass_list[i];
+
+ scoped_ptr<RenderPass> copy_pass(RenderPass::Create());
+
+ RenderPassId remapped_pass_id =
+ RemapPassId(source.id, surface->surface_id());
+
+ copy_pass->SetAll(remapped_pass_id,
+ source.output_rect,
+ DamageRectForSurface(surface, source),
+ source.transform_to_root_target,
+ source.has_transparent_background);
+
+ CopyQuadsToPass(source.quad_list,
+ source.shared_quad_state_list,
+ gfx::Transform(),
+ copy_pass.get(),
+ surface->surface_id());
+
+ dest_pass_list_->push_back(copy_pass.Pass());
+ }
+}
+
+scoped_ptr<CompositorFrame> SurfaceAggregator::Aggregate(SurfaceId surface_id) {
+ Surface* surface = manager_->GetSurfaceForId(surface_id);
+ DCHECK(surface);
+ contained_surfaces_[surface_id] = surface->frame_index();
+ const CompositorFrame* root_surface_frame = surface->GetEligibleFrame();
+ if (!root_surface_frame)
+ return nullptr;
+ TRACE_EVENT0("cc", "SurfaceAggregator::Aggregate");
+
+ scoped_ptr<CompositorFrame> frame(new CompositorFrame);
+ frame->delegated_frame_data = make_scoped_ptr(new DelegatedFrameData);
+
+ DCHECK(root_surface_frame->delegated_frame_data);
+
+ RenderPassList source_pass_list;
+
+ SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first;
+
+ dest_resource_list_ = &frame->delegated_frame_data->resource_list;
+ dest_pass_list_ = &frame->delegated_frame_data->render_pass_list;
+
+ bool invalid_frame =
+ TakeResources(surface,
+ root_surface_frame->delegated_frame_data.get(),
+ &source_pass_list);
+ DCHECK(!invalid_frame);
+
+ CopyPasses(source_pass_list, surface);
+
+ referenced_surfaces_.erase(it);
+ DCHECK(referenced_surfaces_.empty());
+
+ dest_pass_list_ = NULL;
+ contained_surfaces_.swap(previous_contained_surfaces_);
+ contained_surfaces_.clear();
+
+ for (SurfaceIndexMap::iterator it = previous_contained_surfaces_.begin();
+ it != previous_contained_surfaces_.end();
+ ++it) {
+ Surface* surface = manager_->GetSurfaceForId(it->first);
+ if (surface)
+ surface->TakeLatencyInfo(&frame->metadata.latency_info);
+ }
+
+ // TODO(jamesr): Aggregate all resource references into the returned frame's
+ // resource list.
+
+ return frame.Pass();
+}
+
+} // namespace cc