Allow occlusion culling for TextureDrawQuads

Occlusion culling is currently disabled for all TextureDrawQuads because
they might be overlay candidates. However, not all TextureDrawQuads are
actually potential overlays. This change uses the overlay priority hint
and underlying resource to determine overlay candidates and only exclude
them from occlusion culling.

Bug: b:390503465
Change-Id: I11a6654bccc997e649014523762d4b4c5fc6e2b6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6180705
Reviewed-by: Peter McNeeley <petermcneeley@google.com>
Commit-Queue: Zoraiz Naeem <zoraiznaeem@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1412490}
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 807096b16..96475b4 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -267,6 +267,11 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
+// Enables occlusion culling for TextureDrawQuads when possible.
+BASE_FEATURE(kOcclusionCullingForTextureQuads,
+             "OcclusionCullingForTextureQuads",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Allow SurfaceAggregator to merge render passes when they contain quads that
 // require overlay (e.g. protected video). See usage in |EmitSurfaceContent|.
 BASE_FEATURE(kAllowForceMergeRenderPassWithRequireOverlayQuads,
@@ -565,6 +570,12 @@
   return base::FeatureList::IsEnabled(features::kOnBeginFrameAcks);
 }
 
+bool IsOcclusionCullingForTextureQuadsEnabled() {
+  static bool enabled =
+      base::FeatureList::IsEnabled(features::kOcclusionCullingForTextureQuads);
+  return enabled;
+}
+
 bool ShouldDrawImmediatelyWhenInteractive() {
   return base::FeatureList::IsEnabled(
       features::kDrawImmediatelyWhenInteractive);
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index 7b87cea..9132dca1 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -96,6 +96,7 @@
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAllowUndamagedNonrootRenderPassToSkip);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(
     kAllowForceMergeRenderPassWithRequireOverlayQuads);
+VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kOcclusionCullingForTextureQuads);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kOnBeginFrameAcks);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kOnBeginFrameThrottleVideo);
 VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAdpf);
@@ -163,6 +164,7 @@
 VIZ_COMMON_EXPORT int MaxOverlaysConsidered();
 VIZ_COMMON_EXPORT bool ShouldOnBeginFrameThrottleVideo();
 VIZ_COMMON_EXPORT bool IsOnBeginFrameAcksEnabled();
+VIZ_COMMON_EXPORT bool IsOcclusionCullingForTextureQuadsEnabled();
 VIZ_COMMON_EXPORT bool ShouldDrawImmediatelyWhenInteractive();
 VIZ_COMMON_EXPORT std::optional<double> SnapshotEvictedRootSurfaceScale();
 VIZ_COMMON_EXPORT bool IsVSyncAlignedPresentEnabled();
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 5bea765..823ac404 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -249,9 +249,6 @@
   }
 #endif
 
-  occlusion_culler_ = std::make_unique<OcclusionCuller>(
-      overlay_processor_.get(), settings_.occlusion_culler_settings);
-
   if (scheduler_)
     scheduler_->SetClient(this);
 }
@@ -321,6 +318,10 @@
 
   damage_tracker_ = std::make_unique<DisplayDamageTracker>(surface_manager_,
                                                            aggregator_.get());
+  occlusion_culler_ = std::make_unique<OcclusionCuller>(
+      overlay_processor_.get(), resource_provider_.get(),
+      settings_.occlusion_culler_settings);
+
   if (scheduler_)
     scheduler_->SetDamageTracker(damage_tracker_.get());
 
diff --git a/components/viz/service/display/display_perftest.cc b/components/viz/service/display/display_perftest.cc
index 30b00ed..5c1bdf9 100644
--- a/components/viz/service/display/display_perftest.cc
+++ b/components/viz/service/display/display_perftest.cc
@@ -18,6 +18,7 @@
 #include "components/viz/common/quads/shared_quad_state.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/service/display/aggregated_frame.h"
+#include "components/viz/service/display/display_resource_provider_skia.h"
 #include "components/viz/service/display/occlusion_culler.h"
 #include "components/viz/service/display/overlay_processor_interface.h"
 #include "components/viz/service/display/overlay_processor_stub.h"
@@ -70,8 +71,11 @@
     DCHECK(!occlusion_culler_);
 
     overlay_processor_ = std::make_unique<OverlayProcessorStub>();
+    display_resource_provider_ =
+        std::make_unique<DisplayResourceProviderSkia>();
     occlusion_culler_ = std::make_unique<OcclusionCuller>(
-        overlay_processor_.get(), RendererSettings::OcclusionCullerSettings());
+        overlay_processor_.get(), display_resource_provider_.get(),
+        RendererSettings::OcclusionCullerSettings());
     occlusion_culler_->UpdateDeviceScaleFactor(kDeviceScaleFactor);
   }
 
@@ -305,6 +309,7 @@
  private:
   AggregatedFrame frame_;
   base::LapTimer timer_;
+  std::unique_ptr<DisplayResourceProvider> display_resource_provider_;
   std::unique_ptr<OverlayProcessorInterface> overlay_processor_;
   std::unique_ptr<OcclusionCuller> occlusion_culler_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/components/viz/service/display/occlusion_culler.cc b/components/viz/service/display/occlusion_culler.cc
index 200e199..606a892 100644
--- a/components/viz/service/display/occlusion_culler.cc
+++ b/components/viz/service/display/occlusion_culler.cc
@@ -13,9 +13,12 @@
 #include "cc/base/math_util.h"
 #include "cc/base/region.h"
 #include "components/viz/common/display/renderer_settings.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
 #include "components/viz/common/quads/draw_quad.h"
 #include "components/viz/common/quads/shared_quad_state.h"
+#include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/service/display/display_resource_provider.h"
 #include "components/viz/service/display/overlay_processor_interface.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -90,53 +93,6 @@
   return safe_rect;
 }
 
-// Decides whether or not a DrawQuad should be split into a more complex visible
-// region in order to avoid overdraw.
-bool CanSplitQuad(const DrawQuad::Material quad_material,
-                  const std::vector<gfx::Rect>& visible_region_rects,
-                  const gfx::Size& visible_region_bounding_size,
-                  int minimum_fragments_reduced,
-                  float device_scale_factor) {
-  static constexpr DrawQuad::Material kNonSplittableMaterials[] = {
-      // Exclude debug quads from quad splitting.
-      DrawQuad::Material::kDebugBorder,
-      // Exclude possible overlay candidates from quad splitting
-      // See `OverlayCandidate::FromDrawQuad()`.
-      DrawQuad::Material::kTextureContent,
-      DrawQuad::Material::kVideoHole,
-  };
-
-  if (base::Contains(kNonSplittableMaterials, quad_material)) {
-    return false;
-  }
-
-  base::CheckedNumeric<int> area = 0;
-  for (const auto& r : visible_region_rects) {
-    area += r.size().GetCheckedArea();
-    // In calculations below, assume false if this addition overflows.
-    if (!area.IsValid()) {
-      return false;
-    }
-  }
-
-  base::CheckedNumeric<int> visible_region_bounding_area =
-      visible_region_bounding_size.GetCheckedArea();
-  if (!visible_region_bounding_area.IsValid()) {
-    // In calculations below, assume true if this overflows.
-    return true;
-  }
-
-  area = visible_region_bounding_area - area;
-  if (!area.IsValid()) {
-    // In calculations below, assume false if this subtraction underflows.
-    return false;
-  }
-
-  const int int_area = area.ValueOrDie();
-  return int_area * device_scale_factor * device_scale_factor >
-         minimum_fragments_reduced;
-}
-
 // Returns the bounds for the largest rect that can be inscribed in a rounded
 // rect.
 gfx::RectF GetOccludingRectForRRectF(const gfx::RRectF& bounds) {
@@ -234,8 +190,11 @@
 
 OcclusionCuller::OcclusionCuller(
     OverlayProcessorInterface* overlay_processor,
+    DisplayResourceProvider* resource_provider,
     const RendererSettings::OcclusionCullerSettings& settings)
-    : overlay_processor_(overlay_processor), settings_(settings) {}
+    : overlay_processor_(overlay_processor),
+      resource_provider_(resource_provider),
+      settings_(settings) {}
 
 OcclusionCuller::~OcclusionCuller() = default;
 
@@ -433,10 +392,8 @@
             !visible_region.Intersects(render_pass_quads_in_content_space) &&
             ReduceComplexity(visible_region, settings_.quad_split_limit,
                              reduced_visible_region) &&
-            CanSplitQuad(quad->material, reduced_visible_region,
-                         visible_region.bounds().size(),
-                         settings_.minimum_fragments_reduced,
-                         device_scale_factor_);
+            CanSplitDrawQuad(*quad, visible_region.bounds().size(),
+                             reduced_visible_region);
         if (should_split_quads) {
           auto new_quad = pass->quad_list.InsertCopyBeforeDrawQuad(
               quad, reduced_visible_region.size() - 1);
@@ -463,4 +420,52 @@
   }
 }
 
+bool OcclusionCuller::CanSplitDrawQuad(
+    const DrawQuad* quad,
+    const gfx::Size& visible_region_bounding_size,
+    const std::vector<gfx::Rect>& visible_region_rects) {
+  if (quad->material == DrawQuad::Material::kDebugBorder ||
+      quad->material == DrawQuad::Material::kVideoHole) {
+    return false;
+  }
+
+  if (quad->material == DrawQuad::Material::kTextureContent) {
+    if (!features::IsOcclusionCullingForTextureQuadsEnabled()) {
+      return false;
+    }
+
+    // Exclude possible overlay candidates from quad splitting. See
+    // `OverlayCandidateFactory::FromDrawQuad()`.
+    if (resource_provider_->IsOverlayCandidate(quad->resource_id)) {
+      return false;
+    }
+  }
+
+  base::CheckedNumeric<int> area = 0;
+  for (const auto& r : visible_region_rects) {
+    area += r.size().GetCheckedArea();
+    // In calculations below, assume false if this addition overflows.
+    if (!area.IsValid()) {
+      return false;
+    }
+  }
+
+  base::CheckedNumeric<int> visible_region_bounding_area =
+      visible_region_bounding_size.GetCheckedArea();
+  if (!visible_region_bounding_area.IsValid()) {
+    // In calculations below, assume true if this overflows.
+    return true;
+  }
+
+  area = visible_region_bounding_area - area;
+  if (!area.IsValid()) {
+    // In calculations below, assume false if this subtraction underflows.
+    return false;
+  }
+
+  const int int_area = area.ValueOrDie();
+  return int_area * device_scale_factor_ * device_scale_factor_ >
+         settings_.minimum_fragments_reduced;
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display/occlusion_culler.h b/components/viz/service/display/occlusion_culler.h
index 02eb176..dc9d174b 100644
--- a/components/viz/service/display/occlusion_culler.h
+++ b/components/viz/service/display/occlusion_culler.h
@@ -5,17 +5,23 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_OCCLUSION_CULLER_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_OCCLUSION_CULLER_H_
 
+#include <vector>
+
 #include "base/memory/raw_ptr.h"
 #include "components/viz/common/display/renderer_settings.h"
 #include "components/viz/service/viz_service_export.h"
+#include "ui/gfx/geometry/rect.h"
 
 namespace viz {
+class DisplayResourceProvider;
+class DrawQuad;
 class AggregatedFrame;
 class OverlayProcessorInterface;
 
 class VIZ_SERVICE_EXPORT OcclusionCuller {
  public:
   OcclusionCuller(OverlayProcessorInterface* overlay_processor,
+                  DisplayResourceProvider* resource_provider,
                   const RendererSettings::OcclusionCullerSettings& settings);
 
   OcclusionCuller(const OcclusionCuller&) = delete;
@@ -27,9 +33,16 @@
   void RemoveOverdrawQuads(AggregatedFrame* frame);
 
  private:
+  // Decides whether or not a DrawQuad should be split into a more complex
+  // visible region in order to avoid overdraw.
+  bool CanSplitDrawQuad(const DrawQuad* quad,
+                        const gfx::Size& visible_region_bounding_size,
+                        const std::vector<gfx::Rect>& visible_region_rects);
+
   float device_scale_factor_ = 1.0f;
 
   const raw_ptr<OverlayProcessorInterface> overlay_processor_;
+  const raw_ptr<DisplayResourceProvider> resource_provider_;
   const RendererSettings::OcclusionCullerSettings settings_;
 };
 
diff --git a/components/viz/service/display/occlusion_culler_unittest.cc b/components/viz/service/display/occlusion_culler_unittest.cc
index 87987bf..5d9500e 100644
--- a/components/viz/service/display/occlusion_culler_unittest.cc
+++ b/components/viz/service/display/occlusion_culler_unittest.cc
@@ -10,16 +10,22 @@
 #include <utility>
 #include <vector>
 
+#include "base/check.h"
 #include "cc/base/math_util.h"
 #include "components/viz/common/display/renderer_settings.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/common/surfaces/surface_id.h"
 #include "components/viz/service/display/overlay_processor_interface.h"
 #include "components/viz/service/display/overlay_processor_stub.h"
+#include "components/viz/service/display/test_resource_factory.h"
 #include "components/viz/test/compositor_frame_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/mask_filter_info.h"
+#include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -52,8 +58,11 @@
 
   void InitOcclusionCuller(RendererSettings::OcclusionCullerSettings settings) {
     CHECK(!occlusion_culler_);
-    occlusion_culler_ =
-        std::make_unique<OcclusionCuller>(overlay_processor_.get(), settings);
+    CHECK(resource_factory_);
+
+    occlusion_culler_ = std::make_unique<OcclusionCuller>(
+        overlay_processor_.get(), resource_factory_->resource_provider(),
+        settings);
     occlusion_culler_->UpdateDeviceScaleFactor(kDefaultDeviceScaleFactor);
   }
 
@@ -62,9 +71,41 @@
   // testing::Test:
   void SetUp() override {
     overlay_processor_ = std::make_unique<OverlayProcessorStub>();
+    resource_factory_ = std::make_unique<TestResourceFactory>();
+  }
+  void TearDown() override {
+    occlusion_culler_.reset();
+    overlay_processor_.reset();
+    resource_factory_.reset();
+  }
+
+  TextureDrawQuad* CreateTextureQuadAt(const SharedQuadState* shared_quad_state,
+                                       AggregatedRenderPass* render_pass,
+                                       const gfx::Rect& rect,
+                                       bool is_overlay_candidate) {
+    bool nearest_neighbor = false;
+    bool needs_blending = false;
+    bool premultiplied_alpha = false;
+    gfx::Size resource_size_in_pixels = rect.size();
+
+    const ResourceId resource_id = resource_factory_->CreateResource(
+        resource_size_in_pixels, is_overlay_candidate,
+        SinglePlaneFormat::kRGBA_8888, SurfaceId());
+
+    auto* overlay_quad =
+        render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
+    overlay_quad->SetNew(
+        shared_quad_state, rect, rect, needs_blending, resource_id,
+        premultiplied_alpha, /*top_left=*/gfx::PointF(),
+        /*bottom_right=*/gfx::PointF(), SkColors::kBlack, nearest_neighbor,
+        /*secure_output=*/false, gfx::ProtectedVideoType::kClear);
+    overlay_quad->set_resource_size_in_pixels(resource_size_in_pixels);
+
+    return overlay_quad;
   }
 
   std::unique_ptr<OverlayProcessorInterface> overlay_processor_;
+  std::unique_ptr<TestResourceFactory> resource_factory_;
   std::unique_ptr<OcclusionCuller> occlusion_culler_;
 };
 
@@ -3828,5 +3869,144 @@
   EXPECT_EQ(small_rect, quad_list.ElementAt(1)->visible_rect);
 }
 
+TEST_F(OcclusionCullerTest, RemoveOverlayCandidateIfFullyOccluded) {
+  RendererSettings::OcclusionCullerSettings settings;
+  settings.minimum_fragments_reduced = 0;
+
+  InitOcclusionCuller(settings);
+
+  AggregatedFrame frame = MakeDefaultAggregatedFrame();
+  auto root_render_pass = frame.render_pass_list.back().get();
+  gfx::Rect rect(128, 128);
+  gfx::Rect overlay_rect(64, 64);
+
+  bool are_contents_opaque = true;
+  float opacity = 1.0f;
+
+  SharedQuadState* shared_quad_state2 =
+      frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
+  shared_quad_state2->SetAll(
+      gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), std::nullopt,
+      are_contents_opaque, opacity, SkBlendMode::kSrcOver,
+      /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false);
+
+  auto* quad2 = frame.render_pass_list.front()
+                    ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
+  quad2->SetNew(shared_quad_state2, rect, rect, SkColors::kBlack, false);
+
+  SharedQuadState* shared_quad_state =
+      frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
+  shared_quad_state->SetAll(
+      gfx::Transform(), overlay_rect, overlay_rect, gfx::MaskFilterInfo(),
+      std::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver,
+      /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false);
+
+  CreateTextureQuadAt(shared_quad_state, root_render_pass, overlay_rect,
+                      /*is_overlay_candidate=*/true);
+
+  EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
+  occlusion_culler()->RemoveOverdrawQuads(&frame);
+
+  EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
+
+  const QuadList& quad_list = frame.render_pass_list.front()->quad_list;
+  EXPECT_EQ(rect, quad_list.ElementAt(0)->visible_rect);
+}
+
+TEST_F(OcclusionCullerTest, DontSplitOverlayTextureQuad) {
+  RendererSettings::OcclusionCullerSettings settings;
+  settings.minimum_fragments_reduced = 0;
+
+  InitOcclusionCuller(settings);
+
+  AggregatedFrame frame = MakeDefaultAggregatedFrame();
+  auto root_render_pass = frame.render_pass_list.back().get();
+  gfx::Rect rect(64, 64);
+  gfx::Rect overlay_rect(128, 128);
+
+  bool are_contents_opaque = true;
+  float opacity = 1.0f;
+
+  SharedQuadState* shared_quad_state2 =
+      frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
+  shared_quad_state2->SetAll(
+      gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), std::nullopt,
+      are_contents_opaque, opacity, SkBlendMode::kSrcOver,
+      /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false);
+
+  auto* quad2 = frame.render_pass_list.front()
+                    ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
+  quad2->SetNew(shared_quad_state2, rect, rect, SkColors::kBlack, false);
+
+  SharedQuadState* shared_quad_state =
+      frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
+  shared_quad_state->SetAll(
+      gfx::Transform(), overlay_rect, overlay_rect, gfx::MaskFilterInfo(),
+      std::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver,
+      /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false);
+
+  CreateTextureQuadAt(shared_quad_state, root_render_pass, overlay_rect,
+                      /*is_overlay_candidate=*/true);
+
+  EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
+  occlusion_culler()->RemoveOverdrawQuads(&frame);
+
+  EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
+
+  const QuadList& quad_list = frame.render_pass_list.front()->quad_list;
+  EXPECT_EQ(rect, quad_list.ElementAt(0)->visible_rect);
+  EXPECT_EQ(overlay_rect, quad_list.ElementAt(1)->visible_rect);
+}
+
+TEST_F(OcclusionCullerTest, SplitNonOverlayTextureQuad) {
+  if (!features::IsOcclusionCullingForTextureQuadsEnabled()) {
+    GTEST_SKIP();
+  }
+
+  RendererSettings::OcclusionCullerSettings settings;
+  settings.minimum_fragments_reduced = 0;
+
+  InitOcclusionCuller(settings);
+
+  AggregatedFrame frame = MakeDefaultAggregatedFrame();
+  auto root_render_pass = frame.render_pass_list.back().get();
+  gfx::Rect rect(64, 64);
+  gfx::Rect overlay_rect(128, 128);
+
+  bool are_contents_opaque = true;
+  float opacity = 1.0f;
+
+  SharedQuadState* shared_quad_state2 =
+      frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
+  shared_quad_state2->SetAll(
+      gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), std::nullopt,
+      are_contents_opaque, opacity, SkBlendMode::kSrcOver,
+      /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false);
+
+  auto* quad2 = frame.render_pass_list.front()
+                    ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
+  quad2->SetNew(shared_quad_state2, rect, rect, SkColors::kBlack, false);
+
+  SharedQuadState* shared_quad_state =
+      frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
+  shared_quad_state->SetAll(
+      gfx::Transform(), overlay_rect, overlay_rect, gfx::MaskFilterInfo(),
+      std::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver,
+      /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false);
+
+  CreateTextureQuadAt(shared_quad_state, root_render_pass, overlay_rect,
+                      /*is_overlay_candidate=*/false);
+
+  EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
+  occlusion_culler()->RemoveOverdrawQuads(&frame);
+
+  EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
+
+  const QuadList& quad_list = frame.render_pass_list.front()->quad_list;
+  EXPECT_EQ(rect, quad_list.ElementAt(0)->visible_rect);
+  EXPECT_EQ(gfx::Rect(64, 0, 64, 64), quad_list.ElementAt(1)->visible_rect);
+  EXPECT_EQ(gfx::Rect(0, 64, 128, 64), quad_list.ElementAt(2)->visible_rect);
+}
+
 }  // namespace
 }  // namespace viz