summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-29 02:34:21 +0000
committerjamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-29 02:34:21 +0000
commit6a8b3390b17b96d8702b09ed66b569209902c85f (patch)
treed7636a92d1d4c528a37a250e421c16a206788d90
parentb23624b1286b39b81df10d82d4d2654ac4af3c39 (diff)
downloadchromium_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.gyp3
-rw-r--r--cc/cc_tests.gyp3
-rw-r--r--cc/quads/shared_quad_state.h5
-rw-r--r--cc/surfaces/surface.cc7
-rw-r--r--cc/surfaces/surface.h8
-rw-r--r--cc/surfaces/surface_aggregator.cc221
-rw-r--r--cc/surfaces/surface_aggregator.h65
-rw-r--r--cc/surfaces/surface_aggregator_test_helpers.cc162
-rw-r--r--cc/surfaces/surface_aggregator_test_helpers.h93
-rw-r--r--cc/surfaces/surface_aggregator_unittest.cc410
-rw-r--r--cc/surfaces/surface_unittest.cc1
11 files changed, 977 insertions, 1 deletions
diff --git a/cc/cc.gyp b/cc/cc.gyp
index 759453d..1b3b482 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -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
-