Add Viz unit tests for parent_clip_rect
With kAllowUndamagedNonrootRenderPassToSkip enabled, The render
pass with pixel-moving filters should have different damage_rect,
and the render pass should redraw if |parent_clip_rect| expands.
Bug: 1346502
Change-Id: I115f3127537fd41fef73e7c5488890e6384efce7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4576663
Commit-Queue: Maggie Chen <magchen@chromium.org>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1152047}
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index f6aa0d5..1466374 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -297,7 +297,7 @@
bool RenderPassNeedsFullDamage(ResolvedPassData& resolved_pass) {
auto& aggregation = resolved_pass.aggregation();
- static const bool can_skip_render_pass = base::FeatureList::IsEnabled(
+ const bool can_skip_render_pass = base::FeatureList::IsEnabled(
features::kAllowUndamagedNonrootRenderPassToSkip);
if (can_skip_render_pass) {
// Needs full damage when
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 8a2125f..3c60b0847 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -5691,6 +5691,483 @@
}
}
+// Tests that quads outside the damage rect are ignored.
+TEST_F(SurfaceAggregatorPartialSwapTest, AllowSkipAndIgnoreOutside) {
+ // The damage is different for the pixel-moving filter mode with
+ // kAllowUndamagedNonrootRenderPassToSkip enabled.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ features::kAllowUndamagedNonrootRenderPassToSkip);
+
+ TestSurfaceIdAllocator child_surface_id(child_sink_->frame_sink_id());
+ constexpr float device_scale_factor = 1.0f;
+
+ // The child surface has three quads, one with a visible rect of 13,13 4x4 and
+ // the other other with a visible rect of 10,10 2x2 (relative to root target
+ // space), and one with a non-invertible transform.
+ {
+ CompositorRenderPassId child_pass_ids[] = {CompositorRenderPassId{1},
+ CompositorRenderPassId{2},
+ CompositorRenderPassId{3}};
+ std::vector<Quad> child_quads1 = {
+ Quad::SolidColorQuad(SkColors::kGreen, gfx::Rect(5, 5))};
+ std::vector<Quad> child_quads2 = {
+ Quad::SolidColorQuad(SkColors::kGreen, gfx::Rect(5, 5))};
+ std::vector<Quad> child_quads3 = {
+ Quad::SolidColorQuad(SkColors::kGreen, gfx::Rect(5, 5))};
+ std::vector<Pass> child_passes = {
+ Pass(child_quads1, child_pass_ids[0], kSurfaceSize),
+ Pass(child_quads2, child_pass_ids[1], kSurfaceSize),
+ Pass(child_quads3, child_pass_ids[2], kSurfaceSize)};
+
+ CompositorRenderPassList child_pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&child_pass_list, child_passes, &referenced_surfaces);
+
+ child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(1, 1, 2, 2);
+ auto* child_sqs = child_pass_list[0]->shared_quad_state_list.ElementAt(0u);
+ child_sqs->quad_to_target_transform.Translate(1, 1);
+ child_sqs->quad_to_target_transform.Scale(2, 2);
+
+ child_pass_list[1]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(0, 0, 2, 2);
+
+ auto* child_noninvertible_sqs =
+ child_pass_list[2]->shared_quad_state_list.ElementAt(0u);
+ child_noninvertible_sqs->quad_to_target_transform.set_rc(0, 0, 0.0);
+ EXPECT_FALSE(
+ child_noninvertible_sqs->quad_to_target_transform.IsInvertible());
+ child_pass_list[2]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(0, 0, 2, 2);
+
+ // Check whether a nonroot render pass can be skipped or not.
+ child_pass_list[0]->has_damage_from_contributing_content = true;
+ child_pass_list[1]->has_damage_from_contributing_content = true;
+ child_pass_list[2]->has_damage_from_contributing_content = true;
+
+ SubmitPassListAsFrame(child_sink_.get(),
+ child_surface_id.local_surface_id(), &child_pass_list,
+ std::move(referenced_surfaces), device_scale_factor);
+ }
+
+ {
+ std::vector<Quad> root_quads = {Quad::SurfaceQuad(
+ SurfaceRange(absl::nullopt, child_surface_id), SkColors::kWhite,
+ gfx::Rect(5, 5), /*stretch_content_to_fill_bounds=*/false)};
+
+ std::vector<Pass> root_passes = {Pass(root_quads, kSurfaceSize)};
+
+ CompositorRenderPassList root_pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, root_passes, &referenced_surfaces);
+
+ auto* root_pass = root_pass_list[0].get();
+ root_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ root_pass->damage_rect = gfx::Rect(0, 0, 1, 1);
+ root_pass->has_damage_from_contributing_content = true;
+
+ SubmitPassListAsFrame(root_sink_.get(), root_surface_id_.local_surface_id(),
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
+ }
+
+ {
+ auto aggregated_frame = AggregateFrame(root_surface_id_);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ // Damage rect for first aggregation should contain entire root surface.
+ EXPECT_EQ(gfx::Rect(kSurfaceSize), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+
+ EXPECT_TRUE(aggregated_pass_list[0]->has_damage_from_contributing_content);
+ EXPECT_TRUE(aggregated_pass_list[1]->has_damage_from_contributing_content);
+ }
+
+ // Create a root surface with a smaller damage rect.
+ {
+ std::vector<Quad> root_quads = {Quad::SurfaceQuad(
+ SurfaceRange(absl::nullopt, child_surface_id), SkColors::kWhite,
+ gfx::Rect(5, 5), /*stretch_content_to_fill_bounds=*/false)};
+
+ std::vector<Pass> root_passes = {Pass(root_quads, kSurfaceSize)};
+
+ CompositorRenderPassList root_pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, root_passes, &referenced_surfaces);
+
+ auto* root_pass = root_pass_list[0].get();
+ root_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
+ SubmitPassListAsFrame(root_sink_.get(), root_surface_id_.local_surface_id(),
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
+ }
+
+ {
+ auto aggregated_frame = AggregateFrame(root_surface_id_);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ // Only first quad from surface is inside damage rect and should be
+ // included.
+ EXPECT_EQ(gfx::Rect(10, 10, 2, 2), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(0u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 2, 2),
+ aggregated_pass_list[1]->quad_list.back()->visible_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+
+ EXPECT_FALSE(aggregated_pass_list[0]->has_damage_from_contributing_content);
+ EXPECT_FALSE(aggregated_pass_list[1]->has_damage_from_contributing_content);
+ }
+
+ // New child frame has same content and no damage, but has a
+ // CopyOutputRequest.
+ {
+ CompositorRenderPassId child_pass_ids[] = {CompositorRenderPassId{1},
+ CompositorRenderPassId{2}};
+ std::vector<Quad> child_quads1 = {
+ Quad::SolidColorQuad({1.0, 0.0, 1.0, 1.0f / 255.0f}, gfx::Rect(5, 5))};
+ std::vector<Quad> child_quads2 = {
+ Quad::RenderPassQuad(child_pass_ids[0], gfx::Transform(), true)};
+ std::vector<Pass> child_passes = {
+ Pass(child_quads1, child_pass_ids[0], kSurfaceSize),
+ Pass(child_quads2, child_pass_ids[1], kSurfaceSize)};
+
+ CompositorRenderPassList child_pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&child_pass_list, child_passes, &referenced_surfaces);
+
+ child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(1, 1, 2, 2);
+ auto* child_sqs = child_pass_list[0]->shared_quad_state_list.ElementAt(0u);
+ child_sqs->quad_to_target_transform.Translate(1, 1);
+ child_sqs->quad_to_target_transform.Scale(2, 2);
+
+ child_pass_list[1]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(0, 0, 2, 2);
+
+ auto* child_root_pass = child_pass_list[1].get();
+
+ child_root_pass->copy_requests.push_back(
+ CopyOutputRequest::CreateStubForTesting());
+ child_root_pass->damage_rect = gfx::Rect();
+
+ child_pass_list[0]->has_damage_from_contributing_content = false;
+ child_pass_list[1]->has_damage_from_contributing_content = false;
+
+ SubmitPassListAsFrame(child_sink_.get(),
+ child_surface_id.local_surface_id(), &child_pass_list,
+ std::move(referenced_surfaces), device_scale_factor);
+ }
+
+ {
+ auto aggregated_frame = AggregateFrame(root_surface_id_);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ // Output frame should have no damage, but all quads included.
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ EXPECT_EQ(gfx::Rect(kSurfaceSize), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(kSurfaceSize), aggregated_pass_list[1]->damage_rect);
+ EXPECT_TRUE(aggregated_pass_list[2]->damage_rect.IsEmpty());
+ ASSERT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ ASSERT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(1, 1, 2, 2),
+ aggregated_pass_list[0]->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(gfx::Rect(0, 0, 2, 2),
+ aggregated_pass_list[1]->quad_list.ElementAt(0)->visible_rect);
+ ASSERT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+
+ EXPECT_TRUE(aggregated_pass_list[1]->has_damage_from_contributing_content);
+ EXPECT_FALSE(aggregated_pass_list[2]->has_damage_from_contributing_content);
+ }
+
+ {
+ auto aggregated_frame = AggregateFrame(root_surface_id_);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+ // There were no changes since last aggregation, so output should be empty
+ // and have no damage.
+ ASSERT_EQ(1u, aggregated_pass_list.size());
+ EXPECT_TRUE(aggregated_pass_list[0]->damage_rect.IsEmpty());
+ ASSERT_EQ(0u, aggregated_pass_list[0]->quad_list.size());
+ }
+
+ // Reset and make full damage for all render passes.
+ {
+ CompositorRenderPassId root_pass_ids[] = {CompositorRenderPassId{1},
+ CompositorRenderPassId{2},
+ CompositorRenderPassId{3}};
+ std::vector<Quad> root_quads1 = {Quad::SurfaceQuad(
+ SurfaceRange(absl::nullopt, child_surface_id), SkColors::kWhite,
+ gfx::Rect(5, 5), /*stretch_content_to_fill_bounds=*/false)};
+ std::vector<Quad> root_quads2 = {
+ Quad::RenderPassQuad(root_pass_ids[0], gfx::Transform(), true)};
+ std::vector<Quad> root_quads3 = {
+ Quad::RenderPassQuad(root_pass_ids[1], gfx::Transform(), true)};
+ std::vector<Pass> root_passes = {
+ Pass(root_quads1, root_pass_ids[0], kSurfaceSize),
+ Pass(root_quads2, root_pass_ids[1], kSurfaceSize),
+ Pass(root_quads3, root_pass_ids[2], kSurfaceSize)};
+
+ CompositorRenderPassList root_pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, root_passes, &referenced_surfaces);
+
+ root_pass_list[0]->has_damage_from_contributing_content = true;
+ root_pass_list[1]->has_damage_from_contributing_content = true;
+ root_pass_list[2]->has_damage_from_contributing_content = true;
+ root_pass_list[0]->damage_rect = gfx::Rect(kSurfaceSize);
+ root_pass_list[1]->damage_rect = gfx::Rect(kSurfaceSize);
+ root_pass_list[2]->damage_rect = gfx::Rect(kSurfaceSize);
+
+ SubmitPassListAsFrame(root_sink_.get(), root_surface_id_.local_surface_id(),
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
+
+ auto aggregated_frame = AggregateFrame(root_surface_id_);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(4u, aggregated_pass_list.size());
+
+ EXPECT_EQ(gfx::Rect(kSurfaceSize), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(kSurfaceSize), aggregated_pass_list[1]->damage_rect);
+ EXPECT_EQ(gfx::Rect(kSurfaceSize), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(gfx::Rect(kSurfaceSize), aggregated_pass_list[3]->damage_rect);
+
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[3]->quad_list.size());
+
+ // parent_clip_rect expands from
+ EXPECT_TRUE(aggregated_pass_list[0]->has_damage_from_contributing_content);
+ EXPECT_TRUE(aggregated_pass_list[1]->has_damage_from_contributing_content);
+ EXPECT_TRUE(aggregated_pass_list[2]->has_damage_from_contributing_content);
+ }
+
+ // Render passes with pixel-moving foreground filters will increase the damage
+ // only if the damage of the contents will overlap the expanded render pass
+ // draw quad. Since the root surface damage does not overlap, the render pass
+ // and its descendant passes should not be aggregated.
+ {
+ CompositorRenderPassId root_pass_ids[] = {CompositorRenderPassId{1},
+ CompositorRenderPassId{2},
+ CompositorRenderPassId{3}};
+ std::vector<Quad> root_quads1 = {Quad::SurfaceQuad(
+ SurfaceRange(absl::nullopt, child_surface_id), SkColors::kWhite,
+ gfx::Rect(5, 5), /*stretch_content_to_fill_bounds=*/false)};
+ std::vector<Quad> root_quads2 = {
+ Quad::RenderPassQuad(root_pass_ids[0], gfx::Transform(), true)};
+ std::vector<Quad> root_quads3 = {
+ Quad::RenderPassQuad(root_pass_ids[1], gfx::Transform(), true)};
+ std::vector<Pass> root_passes = {
+ Pass(root_quads1, root_pass_ids[0], kSurfaceSize),
+ Pass(root_quads2, root_pass_ids[1], kSurfaceSize),
+ Pass(root_quads3, root_pass_ids[2], kSurfaceSize)};
+
+ CompositorRenderPassList root_pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, root_passes, &referenced_surfaces);
+
+ auto* filter_pass = root_pass_list[1].get();
+ filter_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ // Create 3 pixel-moving filters with the same max pixel movement.
+ filter_pass->filters.Append(cc::FilterOperation::CreateBlurFilter(2));
+ filter_pass->filters.Append(cc::FilterOperation::CreateDropShadowFilter(
+ gfx::Point(0, 0), 2, SkColors::kTransparent));
+ filter_pass->filters.Append(cc::FilterOperation::CreateZoomFilter(2, 4));
+ auto* root_pass = root_pass_list[2].get();
+ // Set the root damage rect which doesn't intersect with the expanded
+ // filter_pass quad (-4, -4, 13, 13) (filter quad (0, 0, 5, 5) + blur filter
+ // pixel movement (2 * 3 = 6)), so we don't have to add more damage from the
+ // filter_pass and the first render pass draw quad will not be drawn.
+ root_pass->damage_rect = gfx::Rect(20, 20, 2, 2);
+ root_pass->has_damage_from_contributing_content = true;
+ root_pass_list[0]->has_damage_from_contributing_content = false;
+ root_pass_list[1]->has_damage_from_contributing_content = false;
+ root_pass_list[0]->damage_rect = gfx::Rect();
+ root_pass_list[1]->damage_rect = gfx::Rect();
+
+ SubmitPassListAsFrame(root_sink_.get(), root_surface_id_.local_surface_id(),
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
+
+ auto aggregated_frame = AggregateFrame(root_surface_id_);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(4u, aggregated_pass_list.size());
+
+ EXPECT_EQ(gfx::Rect(20, 20, 2, 2), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(20, 20, 2, 2), aggregated_pass_list[1]->damage_rect);
+ EXPECT_EQ(gfx::Rect(20, 20, 2, 2), aggregated_pass_list[2]->damage_rect);
+ // The filter pass does not intersects with the other damages. The root
+ // damage should not increase.
+ EXPECT_EQ(gfx::Rect(20, 20, 2, 2), aggregated_pass_list[3]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ // First render pass draw quad with filter is outside damage rect, so
+ // shouldn't be drawn.
+ EXPECT_EQ(0u, aggregated_pass_list[3]->quad_list.size());
+
+ EXPECT_FALSE(aggregated_pass_list[0]->has_damage_from_contributing_content);
+ EXPECT_FALSE(aggregated_pass_list[1]->has_damage_from_contributing_content);
+ EXPECT_FALSE(aggregated_pass_list[2]->has_damage_from_contributing_content);
+ }
+
+ // Render passes with pixel-moving foreground filters will increase the damage
+ // if the damage of the contents will overlap the expanded render pass draw
+ // quad (quad rect + maximum pixel movement). Since the root surface damage
+ // overlaps, the render pass and its descendant passes should be aggregated.
+ {
+ CompositorRenderPassId root_pass_ids[] = {CompositorRenderPassId{1},
+ CompositorRenderPassId{2},
+ CompositorRenderPassId{3}};
+ std::vector<Quad> root_quads1 = {Quad::SurfaceQuad(
+ SurfaceRange(absl::nullopt, child_surface_id), SkColors::kWhite,
+ gfx::Rect(5, 5), /*stretch_content_to_fill_bounds=*/false)};
+ std::vector<Quad> root_quads2 = {
+ Quad::RenderPassQuad(root_pass_ids[0], gfx::Transform(), true)};
+ std::vector<Quad> root_quads3 = {
+ Quad::RenderPassQuad(root_pass_ids[1], gfx::Transform(), true)};
+ std::vector<Pass> root_passes = {
+ Pass(root_quads1, root_pass_ids[0], kSurfaceSize),
+ Pass(root_quads2, root_pass_ids[1], kSurfaceSize),
+ Pass(root_quads3, root_pass_ids[2], kSurfaceSize)};
+
+ CompositorRenderPassList root_pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, root_passes, &referenced_surfaces);
+
+ auto* filter_pass = root_pass_list[1].get();
+ filter_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ // Create 3 pixel-moving filters with the same max pixel movement.
+ filter_pass->filters.Append(cc::FilterOperation::CreateBlurFilter(10));
+ filter_pass->filters.Append(cc::FilterOperation::CreateDropShadowFilter(
+ gfx::Point(0, 0), 10, SkColors::kTransparent));
+ filter_pass->filters.Append(cc::FilterOperation::CreateZoomFilter(2, 20));
+ auto* root_pass = root_pass_list[2].get();
+ // Make the root damage rect intersect with the expanded filter_pass quad
+ // (filter quad (0, 0, 5, 5) + blur filter pixel movement (10 * 3) = (-30,
+ // -30, 65, 65)), but not with filter_pass quad itself (0, 0, 5, 5). The
+ // first render pass will be drawn.
+ root_pass->damage_rect = gfx::Rect(20, 20, 2, 2);
+ root_pass->has_damage_from_contributing_content = true;
+ root_pass_list[0]->has_damage_from_contributing_content = false;
+ root_pass_list[1]->has_damage_from_contributing_content = false;
+
+ SubmitPassListAsFrame(root_sink_.get(), root_surface_id_.local_surface_id(),
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
+
+ auto aggregated_frame = AggregateFrame(root_surface_id_);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(4u, aggregated_pass_list.size());
+
+ EXPECT_EQ(gfx::Rect(0, 0, 35, 35), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(0, 0, 35, 35), aggregated_pass_list[1]->damage_rect);
+ EXPECT_EQ(gfx::Rect(0, 0, 35, 35), aggregated_pass_list[2]->damage_rect);
+ // The filter pass intersects with the root surface damage, the root damage
+ // should increase.
+ // damage_rect = original root damage (0, 0, 5, 5) + blur filter pixel
+ // movement (10 * 3) = (-30, -30, 65, 65). Then intersects with the root
+ // output_rect (0, 0, 100, 100) = (0, 0, 35, 35).
+ EXPECT_EQ(gfx::Rect(0, 0, 35, 35), aggregated_pass_list[3]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ // First render pass draw quad is damaged. It should be drawn.
+ EXPECT_EQ(1u, aggregated_pass_list[3]->quad_list.size());
+
+ EXPECT_FALSE(aggregated_pass_list[0]->has_damage_from_contributing_content);
+ EXPECT_FALSE(aggregated_pass_list[1]->has_damage_from_contributing_content);
+ EXPECT_FALSE(aggregated_pass_list[2]->has_damage_from_contributing_content);
+ }
+
+ // If child surface quad or child render pass output rect expands from the
+ // previous frame, the child render pass should redraw.
+ {
+ CompositorRenderPassId root_pass_ids[] = {CompositorRenderPassId{1},
+ CompositorRenderPassId{2},
+ CompositorRenderPassId{3}};
+ // Increase surface quad size from 5x5 to 30x30.
+ std::vector<Quad> root_quads1 = {Quad::SurfaceQuad(
+ SurfaceRange(absl::nullopt, child_surface_id), SkColors::kWhite,
+ gfx::Rect(30, 30), /*stretch_content_to_fill_bounds=*/false)};
+ std::vector<Quad> root_quads2 = {
+ Quad::RenderPassQuad(root_pass_ids[0], gfx::Transform(), true)};
+ std::vector<Quad> root_quads3 = {
+ Quad::RenderPassQuad(root_pass_ids[1], gfx::Transform(), true)};
+
+ // Expand the render pass [1] output rect size from 100x100 to 150x150.
+ std::vector<Pass> root_passes = {
+ Pass(root_quads1, root_pass_ids[0], kSurfaceSize),
+ Pass(root_quads2, root_pass_ids[1], gfx::Size(150, 150)),
+ Pass(root_quads3, root_pass_ids[2], kSurfaceSize)};
+
+ CompositorRenderPassList root_pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, root_passes, &referenced_surfaces);
+
+ // Make the root damage rect intersect with the child render passes.
+ auto* root_pass = root_pass_list[2].get();
+ root_pass->damage_rect = gfx::Rect(0, 0, 40, 40);
+ root_pass->has_damage_from_contributing_content = true;
+ root_pass_list[0]->has_damage_from_contributing_content = false;
+ root_pass_list[1]->has_damage_from_contributing_content = false;
+
+ SubmitPassListAsFrame(root_sink_.get(), root_surface_id_.local_surface_id(),
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
+
+ auto aggregated_frame = AggregateFrame(root_surface_id_);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(4u, aggregated_pass_list.size());
+
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40), aggregated_pass_list[1]->damage_rect);
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40), aggregated_pass_list[2]->damage_rect);
+
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40), aggregated_pass_list[3]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ // First render pass draw quad is damaged. It should be drawn.
+ EXPECT_EQ(1u, aggregated_pass_list[3]->quad_list.size());
+
+ // SurfaceQuad |parent_clip_rect| expands from (0,0 5x5) to (0,0 30x30). It
+ // should redraw. |has_damage_from_contributing_content| should be true.
+ EXPECT_TRUE(aggregated_pass_list[0]->has_damage_from_contributing_content);
+ EXPECT_TRUE(aggregated_pass_list[1]->has_damage_from_contributing_content);
+
+ // RenderPassDrawQuad |parent_clip_rect| expands from (0,0 100x100) to (0,0
+ // 150x150). It should redraw. |has_damage_from_contributing_content| should
+ // be true.
+ EXPECT_TRUE(aggregated_pass_list[2]->has_damage_from_contributing_content);
+ }
+}
+
TEST_F(SurfaceAggregatorPartialSwapTest, ExpandByTargetDamage) {
TestSurfaceIdAllocator child_surface_id(child_sink_->frame_sink_id());
constexpr float device_scale_factor = 1.0f;