diff options
author | jamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-29 02:34:21 +0000 |
---|---|---|
committer | jamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-29 02:34:21 +0000 |
commit | 6a8b3390b17b96d8702b09ed66b569209902c85f (patch) | |
tree | d7636a92d1d4c528a37a250e421c16a206788d90 | |
parent | b23624b1286b39b81df10d82d4d2654ac4af3c39 (diff) | |
download | chromium_src-6a8b3390b17b96d8702b09ed66b569209902c85f.zip chromium_src-6a8b3390b17b96d8702b09ed66b569209902c85f.tar.gz chromium_src-6a8b3390b17b96d8702b09ed66b569209902c85f.tar.bz2 |
Initial surface aggregator implementation
BUG=334876
Review URL: https://codereview.chromium.org/139763003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@247598 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | cc/cc.gyp | 3 | ||||
-rw-r--r-- | cc/cc_tests.gyp | 3 | ||||
-rw-r--r-- | cc/quads/shared_quad_state.h | 5 | ||||
-rw-r--r-- | cc/surfaces/surface.cc | 7 | ||||
-rw-r--r-- | cc/surfaces/surface.h | 8 | ||||
-rw-r--r-- | cc/surfaces/surface_aggregator.cc | 221 | ||||
-rw-r--r-- | cc/surfaces/surface_aggregator.h | 65 | ||||
-rw-r--r-- | cc/surfaces/surface_aggregator_test_helpers.cc | 162 | ||||
-rw-r--r-- | cc/surfaces/surface_aggregator_test_helpers.h | 93 | ||||
-rw-r--r-- | cc/surfaces/surface_aggregator_unittest.cc | 410 | ||||
-rw-r--r-- | cc/surfaces/surface_unittest.cc | 1 |
11 files changed, 977 insertions, 1 deletions
@@ -458,6 +458,7 @@ 'cc', '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '<(DEPTH)/skia/skia.gyp:skia', '<(DEPTH)/ui/gfx/gfx.gyp:gfx', '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry', ], @@ -467,6 +468,8 @@ 'sources': [ 'surfaces/surface.cc', 'surfaces/surface.h', + 'surfaces/surface_aggregator.cc', + 'surfaces/surface_aggregator.h', 'surfaces/surface_manager.cc', 'surfaces/surface_manager.h', 'surfaces/surfaces_export.h', diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 6f0e383..75d9178 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -106,6 +106,9 @@ ], 'cc_surfaces_unit_tests_source_files': [ 'surfaces/surface_unittest.cc', + 'surfaces/surface_aggregator_unittest.cc', + 'surfaces/surface_aggregator_test_helpers.cc', + 'surfaces/surface_aggregator_test_helpers.h', ], 'cc_tests_support_files': [ 'test/animation_test_common.cc', diff --git a/cc/quads/shared_quad_state.h b/cc/quads/shared_quad_state.h index 9098bfb..9c10f6c 100644 --- a/cc/quads/shared_quad_state.h +++ b/cc/quads/shared_quad_state.h @@ -17,6 +17,11 @@ class Value; namespace cc { +// SharedQuadState holds a set of properties that are common across multiple +// DrawQuads. It's purely an optimization - the properties behave in exactly the +// same way as if they were replicated on each DrawQuad. A given SharedQuadState +// can only be shared by DrawQuads that are adjacent in their RenderPass' +// QuadList. class CC_EXPORT SharedQuadState { public: static scoped_ptr<SharedQuadState> Create(); diff --git a/cc/surfaces/surface.cc b/cc/surfaces/surface.cc index 12f360a..82a1322 100644 --- a/cc/surfaces/surface.cc +++ b/cc/surfaces/surface.cc @@ -4,6 +4,7 @@ #include "cc/surfaces/surface.h" +#include "cc/output/compositor_frame.h" #include "cc/surfaces/surface_manager.h" namespace cc { @@ -21,4 +22,10 @@ Surface::~Surface() { manager_->DeregisterSurface(surface_id_); } +void Surface::QueueFrame(scoped_ptr<CompositorFrame> frame) { + current_frame_ = frame.Pass(); +} + +CompositorFrame* Surface::GetEligibleFrame() { return current_frame_.get(); } + } // namespace cc diff --git a/cc/surfaces/surface.h b/cc/surfaces/surface.h index e18d121..8ed4399 100644 --- a/cc/surfaces/surface.h +++ b/cc/surfaces/surface.h @@ -6,10 +6,12 @@ #define CC_SURFACES_SURFACE_H_ #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "cc/surfaces/surfaces_export.h" #include "ui/gfx/size.h" namespace cc { +class CompositorFrame; class SurfaceManager; class SurfaceClient; @@ -23,11 +25,17 @@ class CC_SURFACES_EXPORT Surface { const gfx::Size& size() const { return size_; } int surface_id() const { return surface_id_; } + void QueueFrame(scoped_ptr<CompositorFrame> frame); + // Returns the most recent frame that is eligible to be rendered. + CompositorFrame* GetEligibleFrame(); + private: SurfaceManager* manager_; SurfaceClient* client_; gfx::Size size_; int surface_id_; + // TODO(jamesr): Support multiple frames in flight. + scoped_ptr<CompositorFrame> current_frame_; DISALLOW_COPY_AND_ASSIGN(Surface); }; diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc new file mode 100644 index 0000000..a85a2bc --- /dev/null +++ b/cc/surfaces/surface_aggregator.cc @@ -0,0 +1,221 @@ +// 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/containers/hash_tables.h" +#include "base/logging.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_manager.h" + +namespace cc { + +SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager) + : manager_(manager) { + DCHECK(manager_); +} + +SurfaceAggregator::~SurfaceAggregator() {} + +DelegatedFrameData* SurfaceAggregator::GetReferencedDataForSurfaceID( + int surface_id) { + Surface* referenced_surface = manager_->GetSurfaceForID(surface_id); + if (!referenced_surface) + return NULL; // Invalid surface id, skip this quad. + CompositorFrame* referenced_frame = referenced_surface->GetEligibleFrame(); + if (!referenced_frame) + return NULL; + return referenced_frame->delegated_frame_data.get(); +} + +class SurfaceAggregator::RenderPassIdAllocator { + public: + explicit RenderPassIdAllocator(int surface_id) + : surface_id_(surface_id), next_index_(1) {} + ~RenderPassIdAllocator() {} + + void AddKnownPass(RenderPass::Id id) { + if (id_to_index_map_.find(id) != id_to_index_map_.end()) + return; + id_to_index_map_[id] = next_index_++; + } + + RenderPass::Id Remap(RenderPass::Id id) { + DCHECK(id_to_index_map_.find(id) != id_to_index_map_.end()); + return RenderPass::Id(surface_id_, id_to_index_map_[id]); + } + + private: + base::hash_map<RenderPass::Id, int> id_to_index_map_; + int surface_id_; + int next_index_; + + DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator); +}; + +RenderPass::Id SurfaceAggregator::RemapPassId( + RenderPass::Id surface_local_pass_id, + int 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); +} + +void SurfaceAggregator::HandleSurfaceQuad(const SurfaceDrawQuad* surface_quad, + RenderPass* dest_pass) { + int 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; + DelegatedFrameData* referenced_data = + GetReferencedDataForSurfaceID(surface_id); + if (!referenced_data) + return; + std::set<int>::iterator it = referenced_surfaces_.insert(surface_id).first; + + const RenderPassList& referenced_passes = referenced_data->render_pass_list; + for (size_t j = 0; j + 1 < referenced_passes.size(); ++j) { + const RenderPass& source = *referenced_passes[j]; + + scoped_ptr<RenderPass> copy_pass(RenderPass::Create()); + + RenderPass::Id 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); + + CopyQuadsToPass(source.quad_list, + source.shared_quad_state_list, + copy_pass.get(), + surface_id); + + dest_pass_list_->push_back(copy_pass.Pass()); + } + + // TODO(jamesr): Clean up last pass special casing. + const RenderPass& last_pass = *referenced_data->render_pass_list.back(); + const QuadList& quads = last_pass.quad_list; + + for (size_t j = 0; j < last_pass.shared_quad_state_list.size(); ++j) { + dest_pass->shared_quad_state_list.push_back( + last_pass.shared_quad_state_list[j]->Copy()); + } + // TODO(jamesr): Map transform correctly for quads in the referenced + // surface into this pass's space. + // TODO(jamesr): Make sure clipping is enforced. + CopyQuadsToPass( + quads, last_pass.shared_quad_state_list, dest_pass, surface_id); + + referenced_surfaces_.erase(it); +} + +void SurfaceAggregator::CopyQuadsToPass( + const QuadList& source_quad_list, + const SharedQuadStateList& source_shared_quad_state_list, + RenderPass* dest_pass, + int surface_id) { + for (size_t j = 0; j < source_shared_quad_state_list.size(); ++j) { + dest_pass->shared_quad_state_list.push_back( + source_shared_quad_state_list[j]->Copy()); + } + + for (size_t i = 0, sqs_i = 0; i < source_quad_list.size(); ++i) { + DrawQuad* quad = source_quad_list[i]; + + 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_LT(sqs_i, dest_pass->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->material == DrawQuad::RENDER_PASS) { + const RenderPassDrawQuad* pass_quad = + RenderPassDrawQuad::MaterialCast(quad); + RenderPass::Id original_pass_id = pass_quad->render_pass_id; + RenderPass::Id remapped_pass_id = + RemapPassId(original_pass_id, surface_id); + + dest_pass->quad_list.push_back( + pass_quad->Copy(dest_pass->shared_quad_state_list[sqs_i], + remapped_pass_id).PassAs<DrawQuad>()); + } else { + dest_pass->quad_list.push_back( + quad->Copy(dest_pass->shared_quad_state_list[sqs_i])); + } + } +} + +void SurfaceAggregator::CopyPasses(const RenderPassList& source_pass_list, + int surface_id) { + 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()); + + RenderPass::Id 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); + + CopyQuadsToPass(source.quad_list, + source.shared_quad_state_list, + copy_pass.get(), + surface_id); + + dest_pass_list_->push_back(copy_pass.Pass()); + } +} + +scoped_ptr<CompositorFrame> SurfaceAggregator::Aggregate(int surface_id) { + Surface* surface = manager_->GetSurfaceForID(surface_id); + if (!surface) + return scoped_ptr<CompositorFrame>(); + CompositorFrame* root_surface_frame = surface->GetEligibleFrame(); + if (!root_surface_frame) + return scoped_ptr<CompositorFrame>(); + + scoped_ptr<CompositorFrame> frame(new CompositorFrame); + frame->delegated_frame_data = make_scoped_ptr(new DelegatedFrameData); + + DCHECK(root_surface_frame->delegated_frame_data); + + const RenderPassList& source_pass_list = + root_surface_frame->delegated_frame_data->render_pass_list; + + referenced_surfaces_.insert(surface_id); + + dest_pass_list_ = &frame->delegated_frame_data->render_pass_list; + CopyPasses(source_pass_list, surface_id); + + referenced_surfaces_.clear(); + dest_pass_list_ = NULL; + + // TODO(jamesr): Aggregate all resource references into the returned frame's + // resource list. + + return frame.Pass(); +} + +} // namespace cc diff --git a/cc/surfaces/surface_aggregator.h b/cc/surfaces/surface_aggregator.h new file mode 100644 index 0000000..aa0fd85 --- /dev/null +++ b/cc/surfaces/surface_aggregator.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef CC_SURFACES_SURFACE_AGGREGATOR_H_ +#define CC_SURFACES_SURFACE_AGGREGATOR_H_ + +#include <set> + +#include "base/containers/scoped_ptr_hash_map.h" +#include "base/memory/scoped_ptr.h" +#include "cc/quads/render_pass.h" +#include "cc/surfaces/surfaces_export.h" + +namespace cc { + +class CompositorFrame; +class DelegatedFrameData; +class SurfaceDrawQuad; +class SurfaceManager; + +class CC_SURFACES_EXPORT SurfaceAggregator { + public: + explicit SurfaceAggregator(SurfaceManager* manager); + ~SurfaceAggregator(); + + scoped_ptr<CompositorFrame> Aggregate(int surface_id); + + private: + DelegatedFrameData* GetReferencedDataForSurfaceID(int surface_id); + RenderPass::Id RemapPassId(RenderPass::Id surface_local_pass_id, + int surface_id); + + void HandleSurfaceQuad(const SurfaceDrawQuad* surface_quad, + RenderPass* dest_pass); + void CopyQuadsToPass(const QuadList& source_quad_list, + const SharedQuadStateList& source_shared_quad_state_list, + RenderPass* dest_pass, + int surface_id); + void CopyPasses(const RenderPassList& source_pass_list, int surface_id); + + SurfaceManager* manager_; + + class RenderPassIdAllocator; + typedef base::ScopedPtrHashMap<int, RenderPassIdAllocator> + RenderPassIdAllocatorMap; + RenderPassIdAllocatorMap render_pass_allocator_map_; + + // The following state is only valid for the duration of one Aggregate call + // and is only stored on the class to avoid having to pass through every + // function call. + + // This is the set of surfaces referenced in the aggregation so far, used to + // detect cycles. + std::set<int> referenced_surfaces_; + + // This is the pass list for the aggregated frame. + RenderPassList* dest_pass_list_; + + DISALLOW_COPY_AND_ASSIGN(SurfaceAggregator); +}; + +} // namespace cc + +#endif // CC_SURFACES_SURFACE_AGGREGATOR_H_ diff --git a/cc/surfaces/surface_aggregator_test_helpers.cc b/cc/surfaces/surface_aggregator_test_helpers.cc new file mode 100644 index 0000000..e5f0fb4 --- /dev/null +++ b/cc/surfaces/surface_aggregator_test_helpers.cc @@ -0,0 +1,162 @@ +// 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_test_helpers.h" + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "cc/layers/append_quads_data.h" +#include "cc/output/compositor_frame.h" +#include "cc/output/delegated_frame_data.h" +#include "cc/quads/render_pass_draw_quad.h" +#include "cc/quads/shared_quad_state.h" +#include "cc/quads/solid_color_draw_quad.h" +#include "cc/quads/surface_draw_quad.h" +#include "cc/surfaces/surface.h" +#include "cc/test/mock_quad_culler.h" +#include "cc/test/render_pass_test_common.h" +#include "cc/test/render_pass_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkXfermode.h" + +namespace cc { +namespace test { + +void AddTestSurfaceQuad(TestRenderPass* pass, + const gfx::Size& surface_size, + int surface_id) { + gfx::Transform content_to_target_transform; + gfx::Size content_bounds = surface_size; + gfx::Rect visible_content_rect = gfx::Rect(surface_size); + gfx::Rect clip_rect = gfx::Rect(surface_size); + bool is_clipped = false; + float opacity = 1.0; + SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode; + + scoped_ptr<SharedQuadState> shared_quad_state = SharedQuadState::Create(); + shared_quad_state->SetAll(content_to_target_transform, + content_bounds, + visible_content_rect, + clip_rect, + is_clipped, + opacity, + blend_mode); + pass->shared_quad_state_list.push_back(shared_quad_state.Pass()); + + scoped_ptr<SurfaceDrawQuad> surface_quad = SurfaceDrawQuad::Create(); + gfx::Rect quad_rect = gfx::Rect(surface_size); + surface_quad->SetNew( + pass->shared_quad_state_list.back(), gfx::Rect(surface_size), surface_id); + pass->quad_list.push_back(surface_quad.PassAs<DrawQuad>()); +} +void AddTestRenderPassQuad(TestRenderPass* pass, + RenderPass::Id render_pass_id) { + MockQuadCuller quad_sink(&pass->quad_list, &pass->shared_quad_state_list); + AppendQuadsData data(pass->id); + gfx::Rect output_rect = gfx::Rect(0, 0, 5, 5); + SharedQuadState* shared_state = + quad_sink.UseSharedQuadState(SharedQuadState::Create()); + shared_state->SetAll(gfx::Transform(), + output_rect.size(), + output_rect, + output_rect, + false, + 1, + SkXfermode::kSrcOver_Mode); + scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create(); + quad->SetNew(shared_state, + output_rect, + render_pass_id, + false, + 0, + output_rect, + gfx::RectF(), + FilterOperations(), + FilterOperations()); + quad_sink.Append(quad.PassAs<DrawQuad>(), &data); +} + +void AddQuadInPass(TestRenderPass* pass, Quad desc) { + switch (desc.material) { + case DrawQuad::SOLID_COLOR: + AddQuad(pass, gfx::Rect(0, 0, 5, 5), desc.color); + break; + case DrawQuad::SURFACE_CONTENT: + AddTestSurfaceQuad(pass, gfx::Size(5, 5), desc.surface_id); + break; + case DrawQuad::RENDER_PASS: + AddTestRenderPassQuad(pass, desc.render_pass_id); + break; + default: + NOTREACHED(); + } +} + +void AddPasses(RenderPassList* pass_list, + const gfx::Rect& output_rect, + Pass* passes, + size_t pass_count) { + gfx::Transform root_transform; + for (size_t i = 0; i < pass_count; ++i) { + Pass pass = passes[i]; + TestRenderPass* test_pass = + AddRenderPass(pass_list, pass.id, output_rect, root_transform); + for (size_t j = 0; j < pass.quad_count; ++j) { + AddQuadInPass(test_pass, pass.quads[j]); + } + } +} + +void TestQuadMatchesExpectations(Quad expected_quad, DrawQuad* quad) { + switch (expected_quad.material) { + case DrawQuad::SOLID_COLOR: { + ASSERT_EQ(DrawQuad::SOLID_COLOR, quad->material); + + const SolidColorDrawQuad* solid_color_quad = + SolidColorDrawQuad::MaterialCast(quad); + + EXPECT_EQ(expected_quad.color, solid_color_quad->color); + break; + } + default: + NOTREACHED(); + break; + } +} + +void TestPassMatchesExpectations(Pass expected_pass, RenderPass* pass) { + ASSERT_EQ(expected_pass.quad_count, pass->quad_list.size()); + for (size_t i = 0u; i < pass->quad_list.size(); ++i) { + SCOPED_TRACE(base::StringPrintf("Quad number %" PRIuS, i)); + TestQuadMatchesExpectations(expected_pass.quads[i], pass->quad_list.at(i)); + } +} + +void TestPassesMatchExpectations(Pass* expected_passes, + size_t expected_pass_count, + RenderPassList* passes) { + ASSERT_EQ(expected_pass_count, passes->size()); + + for (size_t i = 0; i < passes->size(); ++i) { + SCOPED_TRACE(base::StringPrintf("Pass number %" PRIuS, i)); + RenderPass* pass = passes->at(i); + TestPassMatchesExpectations(expected_passes[i], pass); + } +} + +void SubmitFrame(Pass* passes, size_t pass_count, Surface* surface) { + RenderPassList pass_list; + AddPasses(&pass_list, gfx::Rect(surface->size()), passes, pass_count); + + scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData); + pass_list.swap(frame_data->render_pass_list); + + scoped_ptr<CompositorFrame> frame(new CompositorFrame); + frame->delegated_frame_data = frame_data.Pass(); + + surface->QueueFrame(frame.Pass()); +} + +} // namespace test +} // namespace cc diff --git a/cc/surfaces/surface_aggregator_test_helpers.h b/cc/surfaces/surface_aggregator_test_helpers.h new file mode 100644 index 0000000..48acf27 --- /dev/null +++ b/cc/surfaces/surface_aggregator_test_helpers.h @@ -0,0 +1,93 @@ +// 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. + +#ifndef CC_SURFACES_SURFACE_AGGREGATOR_TEST_HELPERS_H_ +#define CC_SURFACES_SURFACE_AGGREGATOR_TEST_HELPERS_H_ + +#include "cc/quads/draw_quad.h" +#include "cc/quads/render_pass.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/size.h" + +namespace cc { + +class Surface; +class TestRenderPass; + +namespace test { + +struct Quad { + static Quad SolidColorQuad(SkColor color) { + Quad quad; + quad.material = DrawQuad::SOLID_COLOR; + quad.color = color; + return quad; + } + + static Quad SurfaceQuad(int surface_id) { + Quad quad; + quad.material = DrawQuad::SURFACE_CONTENT; + quad.surface_id = surface_id; + return quad; + } + + static Quad RenderPassQuad(RenderPass::Id id) { + Quad quad; + quad.material = DrawQuad::RENDER_PASS; + quad.render_pass_id = id; + return quad; + } + + DrawQuad::Material material; + // Set when material==DrawQuad::SURFACE_CONTENT. + int surface_id; + // Set when material==DrawQuad::SOLID_COLOR. + SkColor color; + // Set when material==DrawQuad::RENDER_PASS. + RenderPass::Id render_pass_id; + + private: + Quad() + : material(DrawQuad::INVALID), + surface_id(-1), + color(SK_ColorWHITE), + render_pass_id(-1, -1) {} +}; + +struct Pass { + Pass(Quad* quads, size_t quad_count, RenderPass::Id id) + : quads(quads), quad_count(quad_count), id(id) {} + Pass(Quad* quads, size_t quad_count) + : quads(quads), quad_count(quad_count), id(1, 1) {} + + Quad* quads; + size_t quad_count; + RenderPass::Id id; +}; + +void AddSurfaceQuad(TestRenderPass* pass, + const gfx::Size& surface_size, + int surface_id); + +void AddQuadInPass(TestRenderPass* pass, Quad desc); + +void AddPasses(RenderPassList* pass_list, + const gfx::Rect& output_rect, + Pass* passes, + size_t pass_count); + +void TestQuadMatchesExpectations(Quad expected_quad, DrawQuad* quad); + +void TestPassMatchesExpectations(Pass expected_pass, RenderPass* pass); + +void TestPassesMatchExpectations(Pass* expected_passes, + size_t expected_pass_count, + RenderPassList* passes); + +void SubmitFrame(Pass* passes, size_t pass_count, Surface* surface); + +} // namespace test +} // namespace cc + +#endif // CC_SURFACES_SURFACE_AGGREGATOR_TEST_HELPERS_H_ diff --git a/cc/surfaces/surface_aggregator_unittest.cc b/cc/surfaces/surface_aggregator_unittest.cc new file mode 100644 index 0000000..22ce646 --- /dev/null +++ b/cc/surfaces/surface_aggregator_unittest.cc @@ -0,0 +1,410 @@ +// 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/output/compositor_frame.h" +#include "cc/output/delegated_frame_data.h" +#include "cc/quads/render_pass.h" +#include "cc/quads/render_pass_draw_quad.h" +#include "cc/quads/solid_color_draw_quad.h" +#include "cc/quads/surface_draw_quad.h" +#include "cc/surfaces/surface.h" +#include "cc/surfaces/surface_aggregator.h" +#include "cc/surfaces/surface_aggregator_test_helpers.h" +#include "cc/surfaces/surface_manager.h" +#include "cc/test/render_pass_test_common.h" +#include "cc/test/render_pass_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkColor.h" + +namespace cc { +namespace { +const int kInvalidSurfaceId = -1; + +class SurfaceAggregatorTest : public testing::Test { + public: + SurfaceAggregatorTest() : aggregator_(&manager_) {} + + protected: + SurfaceManager manager_; + SurfaceAggregator aggregator_; +}; + +TEST_F(SurfaceAggregatorTest, InvalidSurfaceId) { + scoped_ptr<CompositorFrame> frame = aggregator_.Aggregate(kInvalidSurfaceId); + EXPECT_FALSE(frame); +} + +TEST_F(SurfaceAggregatorTest, ValidSurfaceNoFrame) { + Surface one(&manager_, NULL, gfx::Size(5, 5)); + scoped_ptr<CompositorFrame> frame = aggregator_.Aggregate(one.surface_id()); + EXPECT_FALSE(frame); +} + +class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest { + public: + SurfaceAggregatorValidSurfaceTest() + : root_surface_(&manager_, NULL, gfx::Size(5, 5)) {} + + void AggregateAndVerify(test::Pass* expected_passes, + size_t expected_pass_count) { + scoped_ptr<CompositorFrame> aggregated_frame = + aggregator_.Aggregate(root_surface_.surface_id()); + + ASSERT_TRUE(aggregated_frame); + ASSERT_TRUE(aggregated_frame->delegated_frame_data); + + DelegatedFrameData* frame_data = + aggregated_frame->delegated_frame_data.get(); + + TestPassesMatchExpectations( + expected_passes, expected_pass_count, &frame_data->render_pass_list); + } + + protected: + Surface root_surface_; +}; + +// Tests that a very simple frame containing only two solid color quads makes it +// through the aggregator correctly. +TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) { + test::Quad quads[] = {test::Quad::SolidColorQuad(SK_ColorRED), + test::Quad::SolidColorQuad(SK_ColorBLUE)}; + test::Pass passes[] = {test::Pass(quads, arraysize(quads))}; + + SubmitFrame(passes, arraysize(passes), &root_surface_); + + AggregateAndVerify(passes, arraysize(passes)); +} + +TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSimpleFrame) { + test::Quad quads[][2] = {{test::Quad::SolidColorQuad(SK_ColorWHITE), + test::Quad::SolidColorQuad(SK_ColorLTGRAY)}, + {test::Quad::SolidColorQuad(SK_ColorGRAY), + test::Quad::SolidColorQuad(SK_ColorDKGRAY)}}; + test::Pass passes[] = {test::Pass(quads[0], arraysize(quads[0])), + test::Pass(quads[1], arraysize(quads[1]))}; + + SubmitFrame(passes, arraysize(passes), &root_surface_); + + AggregateAndVerify(passes, arraysize(passes)); +} + +// This tests very simple embedding. root_surface has a frame containing a few +// solid color quads and a surface quad referencing embedded_surface. +// embedded_surface has a frame containing only a solid color quad. The solid +// color quad should be aggregated into the final frame. +TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleSurfaceReference) { + gfx::Size surface_size(5, 5); + + Surface embedded_surface(&manager_, NULL, surface_size); + + test::Quad embedded_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN)}; + test::Pass embedded_passes[] = { + test::Pass(embedded_quads, arraysize(embedded_quads))}; + + SubmitFrame(embedded_passes, arraysize(embedded_passes), &embedded_surface); + + test::Quad root_quads[] = { + test::Quad::SolidColorQuad(SK_ColorWHITE), + test::Quad::SurfaceQuad(embedded_surface.surface_id()), + test::Quad::SolidColorQuad(SK_ColorBLACK)}; + test::Pass root_passes[] = {test::Pass(root_quads, arraysize(root_quads))}; + + SubmitFrame(root_passes, arraysize(root_passes), &root_surface_); + + test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorWHITE), + test::Quad::SolidColorQuad(SK_ColorGREEN), + test::Quad::SolidColorQuad(SK_ColorBLACK)}; + test::Pass expected_passes[] = { + test::Pass(expected_quads, arraysize(expected_quads))}; + AggregateAndVerify(expected_passes, arraysize(expected_passes)); +} + +// This tests referencing a surface that has multiple render passes. +TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSurfaceReference) { + gfx::Size surface_size(5, 5); + + Surface embedded_surface(&manager_, NULL, surface_size); + + RenderPass::Id pass_ids[] = {RenderPass::Id(1, 1), RenderPass::Id(1, 2), + RenderPass::Id(1, 3)}; + + test::Quad embedded_quads[][2] = { + {test::Quad::SolidColorQuad(1), test::Quad::SolidColorQuad(2)}, + {test::Quad::SolidColorQuad(3), test::Quad::RenderPassQuad(pass_ids[0])}, + {test::Quad::SolidColorQuad(4), test::Quad::RenderPassQuad(pass_ids[1])}}; + test::Pass embedded_passes[] = { + test::Pass(embedded_quads[0], arraysize(embedded_quads[0]), pass_ids[0]), + test::Pass(embedded_quads[1], arraysize(embedded_quads[1]), pass_ids[1]), + test::Pass(embedded_quads[2], arraysize(embedded_quads[2]), pass_ids[2])}; + + SubmitFrame(embedded_passes, arraysize(embedded_passes), &embedded_surface); + + test::Quad root_quads[][2] = { + {test::Quad::SolidColorQuad(5), test::Quad::SolidColorQuad(6)}, + {test::Quad::SurfaceQuad(embedded_surface.surface_id()), + test::Quad::RenderPassQuad(pass_ids[0])}, + {test::Quad::SolidColorQuad(7), test::Quad::RenderPassQuad(pass_ids[1])}}; + test::Pass root_passes[] = { + test::Pass(root_quads[0], arraysize(root_quads[0]), pass_ids[0]), + test::Pass(root_quads[1], arraysize(root_quads[1]), pass_ids[1]), + test::Pass(root_quads[2], arraysize(root_quads[2]), pass_ids[2])}; + + SubmitFrame(root_passes, arraysize(root_passes), &root_surface_); + + scoped_ptr<CompositorFrame> aggregated_frame = + aggregator_.Aggregate(root_surface_.surface_id()); + + ASSERT_TRUE(aggregated_frame); + ASSERT_TRUE(aggregated_frame->delegated_frame_data); + + DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get(); + + const RenderPassList& aggregated_pass_list = frame_data->render_pass_list; + + ASSERT_EQ(5u, aggregated_pass_list.size()); + RenderPass::Id actual_pass_ids[] = { + aggregated_pass_list[0]->id, aggregated_pass_list[1]->id, + aggregated_pass_list[2]->id, aggregated_pass_list[3]->id, + aggregated_pass_list[4]->id}; + for (size_t i = 0; i < 5; ++i) { + for (size_t j = 0; j < i; ++j) { + EXPECT_NE(actual_pass_ids[i], actual_pass_ids[j]); + } + } + + { + SCOPED_TRACE("First pass"); + // The first pass will just be the first pass from the root surfaces quad + // with no render pass quads to remap. + TestPassMatchesExpectations(root_passes[0], aggregated_pass_list[0]); + } + + { + SCOPED_TRACE("Second pass"); + // The next two passes will be from the embedded surface since we have to + // draw those passes before they are referenced from the render pass draw + // quad embedded into the root surface's second pass. + // First, there's the first embedded pass which doesn't reference anything + // else. + TestPassMatchesExpectations(embedded_passes[0], aggregated_pass_list[1]); + } + + { + SCOPED_TRACE("Third pass"); + const QuadList& third_pass_quad_list = aggregated_pass_list[2]->quad_list; + ASSERT_EQ(2u, third_pass_quad_list.size()); + TestQuadMatchesExpectations(embedded_quads[1][0], + third_pass_quad_list.at(0u)); + + // This render pass pass quad will reference the first pass from the + // embedded surface, which is the second pass in the aggregated frame. + ASSERT_EQ(DrawQuad::RENDER_PASS, third_pass_quad_list.at(1u)->material); + const RenderPassDrawQuad* third_pass_render_pass_draw_quad = + RenderPassDrawQuad::MaterialCast(third_pass_quad_list.at(1u)); + EXPECT_EQ(actual_pass_ids[1], + third_pass_render_pass_draw_quad->render_pass_id); + } + + { + SCOPED_TRACE("Fourth pass"); + // The fourth pass will have aggregated quads from the root surface's second + // pass and the embedded surface's first pass. + const QuadList& fourth_pass_quad_list = aggregated_pass_list[3]->quad_list; + ASSERT_EQ(3u, fourth_pass_quad_list.size()); + + // The first quad will be the yellow quad from the embedded surface's last + // pass. + TestQuadMatchesExpectations(embedded_quads[2][0], + fourth_pass_quad_list.at(0u)); + + // The next quad will be a render pass quad referencing the second pass from + // the embedded surface, which is the third pass in the aggregated frame. + ASSERT_EQ(DrawQuad::RENDER_PASS, fourth_pass_quad_list.at(1u)->material); + const RenderPassDrawQuad* fourth_pass_first_render_pass_draw_quad = + RenderPassDrawQuad::MaterialCast(fourth_pass_quad_list.at(1u)); + EXPECT_EQ(actual_pass_ids[2], + fourth_pass_first_render_pass_draw_quad->render_pass_id); + + // The last quad will be a render pass quad referencing the first pass from + // the root surface, which is the first pass overall. + ASSERT_EQ(DrawQuad::RENDER_PASS, fourth_pass_quad_list.at(2u)->material); + const RenderPassDrawQuad* fourth_pass_second_render_pass_draw_quad = + RenderPassDrawQuad::MaterialCast(fourth_pass_quad_list.at(2u)); + EXPECT_EQ(actual_pass_ids[0], + fourth_pass_second_render_pass_draw_quad->render_pass_id); + } + + { + SCOPED_TRACE("Fifth pass"); + const QuadList& fifth_pass_quad_list = aggregated_pass_list[4]->quad_list; + ASSERT_EQ(2u, fifth_pass_quad_list.size()); + + TestQuadMatchesExpectations(root_quads[2][0], fifth_pass_quad_list.at(0)); + + // The last quad in the last pass will reference the second pass from the + // root surface, which after aggregating is the fourth pass in the overall + // list. + ASSERT_EQ(DrawQuad::RENDER_PASS, fifth_pass_quad_list.at(1u)->material); + const RenderPassDrawQuad* fifth_pass_render_pass_draw_quad = + RenderPassDrawQuad::MaterialCast(fifth_pass_quad_list.at(1u)); + EXPECT_EQ(actual_pass_ids[3], + fifth_pass_render_pass_draw_quad->render_pass_id); + } +} + +// Tests an invalid surface reference in a frame. The surface quad should just +// be dropped. +TEST_F(SurfaceAggregatorValidSurfaceTest, InvalidSurfaceReference) { + test::Quad quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN), + test::Quad::SurfaceQuad(kInvalidSurfaceId), + test::Quad::SolidColorQuad(SK_ColorBLUE)}; + test::Pass passes[] = {test::Pass(quads, arraysize(quads))}; + + SubmitFrame(passes, arraysize(passes), &root_surface_); + + test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN), + test::Quad::SolidColorQuad(SK_ColorBLUE)}; + test::Pass expected_passes[] = { + test::Pass(expected_quads, arraysize(expected_quads))}; + AggregateAndVerify(expected_passes, arraysize(expected_passes)); +} + +// Tests a reference to a valid surface with no submitted frame. This quad +// should also just be dropped. +TEST_F(SurfaceAggregatorValidSurfaceTest, ValidSurfaceReferenceWithNoFrame) { + Surface surface_with_no_frame(&manager_, NULL, gfx::Size(5, 5)); + test::Quad quads[] = { + test::Quad::SolidColorQuad(SK_ColorGREEN), + test::Quad::SurfaceQuad(surface_with_no_frame.surface_id()), + test::Quad::SolidColorQuad(SK_ColorBLUE)}; + test::Pass passes[] = {test::Pass(quads, arraysize(quads))}; + + SubmitFrame(passes, arraysize(passes), &root_surface_); + + test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN), + test::Quad::SolidColorQuad(SK_ColorBLUE)}; + test::Pass expected_passes[] = { + test::Pass(expected_quads, arraysize(expected_quads))}; + AggregateAndVerify(expected_passes, arraysize(expected_passes)); +} + +// Tests a surface quad referencing itself, generating a trivial cycle. +// The quad creating the cycle should be dropped from the final frame. +TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleCyclicalReference) { + test::Quad quads[] = {test::Quad::SurfaceQuad(root_surface_.surface_id()), + test::Quad::SolidColorQuad(SK_ColorYELLOW)}; + test::Pass passes[] = {test::Pass(quads, arraysize(quads))}; + + SubmitFrame(passes, arraysize(passes), &root_surface_); + + test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorYELLOW)}; + test::Pass expected_passes[] = { + test::Pass(expected_quads, arraysize(expected_quads))}; + AggregateAndVerify(expected_passes, arraysize(expected_passes)); +} + +// Tests a more complex cycle with one intermediate surface. +TEST_F(SurfaceAggregatorValidSurfaceTest, TwoSurfaceCyclicalReference) { + gfx::Size surface_size(5, 5); + + Surface child_surface(&manager_, NULL, surface_size); + + test::Quad parent_quads[] = { + test::Quad::SolidColorQuad(SK_ColorBLUE), + test::Quad::SurfaceQuad(child_surface.surface_id()), + test::Quad::SolidColorQuad(SK_ColorCYAN)}; + test::Pass parent_passes[] = { + test::Pass(parent_quads, arraysize(parent_quads))}; + + SubmitFrame(parent_passes, arraysize(parent_passes), &root_surface_); + + test::Quad child_quads[] = { + test::Quad::SolidColorQuad(SK_ColorGREEN), + test::Quad::SurfaceQuad(root_surface_.surface_id()), + test::Quad::SolidColorQuad(SK_ColorMAGENTA)}; + test::Pass child_passes[] = {test::Pass(child_quads, arraysize(child_quads))}; + + SubmitFrame(child_passes, arraysize(child_passes), &child_surface); + + // The child surface's reference to the root_surface_ will be dropped, so + // we'll end up with: + // SK_ColorBLUE from the parent + // SK_ColorGREEN from the child + // SK_ColorMAGENTA from the child + // SK_ColorCYAN from the parent + test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorBLUE), + test::Quad::SolidColorQuad(SK_ColorGREEN), + test::Quad::SolidColorQuad(SK_ColorMAGENTA), + test::Quad::SolidColorQuad(SK_ColorCYAN)}; + test::Pass expected_passes[] = { + test::Pass(expected_quads, arraysize(expected_quads))}; + AggregateAndVerify(expected_passes, arraysize(expected_passes)); +} + +// Tests that we map render pass IDs from different surfaces into a unified +// namespace and update RenderPassDrawQuad's id references to match. +TEST_F(SurfaceAggregatorValidSurfaceTest, RenderPassIdMapping) { + gfx::Size surface_size(5, 5); + + Surface child_surface(&manager_, NULL, surface_size); + + RenderPass::Id child_pass_id[] = {RenderPass::Id(1, 1), RenderPass::Id(1, 2)}; + test::Quad child_quad[][1] = {{test::Quad::SolidColorQuad(SK_ColorGREEN)}, + {test::Quad::RenderPassQuad(child_pass_id[0])}}; + test::Pass surface_passes[] = { + test::Pass(child_quad[0], arraysize(child_quad[0]), child_pass_id[0]), + test::Pass(child_quad[1], arraysize(child_quad[1]), child_pass_id[1])}; + + SubmitFrame(surface_passes, arraysize(surface_passes), &child_surface); + + // Pass IDs from the parent surface may collide with ones from the child. + RenderPass::Id parent_pass_id[] = {RenderPass::Id(2, 1), + RenderPass::Id(1, 2)}; + test::Quad parent_quad[][1] = { + {test::Quad::SurfaceQuad(child_surface.surface_id())}, + {test::Quad::RenderPassQuad(parent_pass_id[0])}}; + test::Pass parent_passes[] = { + test::Pass(parent_quad[0], arraysize(parent_quad[0]), parent_pass_id[0]), + test::Pass(parent_quad[1], arraysize(parent_quad[1]), parent_pass_id[1])}; + + SubmitFrame(parent_passes, arraysize(parent_passes), &root_surface_); + scoped_ptr<CompositorFrame> aggregated_frame = + aggregator_.Aggregate(root_surface_.surface_id()); + + ASSERT_TRUE(aggregated_frame); + ASSERT_TRUE(aggregated_frame->delegated_frame_data); + + DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get(); + + const RenderPassList& aggregated_pass_list = frame_data->render_pass_list; + + ASSERT_EQ(3u, aggregated_pass_list.size()); + RenderPass::Id actual_pass_ids[] = {aggregated_pass_list[0]->id, + aggregated_pass_list[1]->id, + aggregated_pass_list[2]->id}; + // Make sure the aggregated frame's pass IDs are all unique. + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < i; ++j) { + EXPECT_NE(actual_pass_ids[j], actual_pass_ids[i]) << "pass ids " << i + << " and " << j; + } + } + + // Make sure the render pass quads reference the remapped pass IDs. + DrawQuad* render_pass_quads[] = {aggregated_pass_list[1]->quad_list[0], + aggregated_pass_list[2]->quad_list[0]}; + ASSERT_EQ(render_pass_quads[0]->material, DrawQuad::RENDER_PASS); + EXPECT_EQ( + actual_pass_ids[0], + RenderPassDrawQuad::MaterialCast(render_pass_quads[0])->render_pass_id); + + ASSERT_EQ(render_pass_quads[1]->material, DrawQuad::RENDER_PASS); + EXPECT_EQ( + actual_pass_ids[1], + RenderPassDrawQuad::MaterialCast(render_pass_quads[1])->render_pass_id); +} + +} // namespace +} // namespace cc diff --git a/cc/surfaces/surface_unittest.cc b/cc/surfaces/surface_unittest.cc index 669ec38..7b18512 100644 --- a/cc/surfaces/surface_unittest.cc +++ b/cc/surfaces/surface_unittest.cc @@ -26,4 +26,3 @@ TEST(SurfaceTest, SurfaceLifetime) { } // namespace } // namespace cc - |