Commit 4942ccee authored by Sasha McIntosh's avatar Sasha McIntosh Committed by Commit Bot

viz: Extend Occlusion Culling in display

Extend Display::RemoveOverdrawQuads to track up to 10 occluding rects as a
region in target space.

cc::Region::GetRegionComplexity() exposes the number of rects
contributing to a region. In the past, using regions as occluders
without limiting the  number of rects contributing to a region resulted
in cases with significant CPU times.

RemoveOverdrawQuadPerfTest benchmark results show little difference
among kMaximumOccluderComplexity values when compared to pre-change
results.

See Occlusion Culling Improvements doc [1] for results details.

[1] https://docs.google.com/document/d/1LeBa-f80FG4eioHpX706Th65FEoXcEzWmNgooZQyNSw/edit?usp=sharing#heading=h.dgeynqo2z2ly

Test: viz_perftests
Bug: 1022544
Change-Id: I1c844490fe57146d9448bcf408ee23dd627657ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2040201
Commit-Queue: Sasha McIntosh <sashamcintosh@chromium.org>
Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Reviewed-by: default avatarDaniele Castagna <dcastagna@chromium.org>
Cr-Commit-Position: refs/heads/master@{#740476}
parent 441ccbcb
...@@ -46,6 +46,9 @@ class VIZ_COMMON_EXPORT RendererSettings { ...@@ -46,6 +46,9 @@ class VIZ_COMMON_EXPORT RendererSettings {
// The required minimum size for DrawQuad to apply Draw Occlusion on. // The required minimum size for DrawQuad to apply Draw Occlusion on.
gfx::Size kMinimumDrawOcclusionSize = gfx::Size(60, 60); gfx::Size kMinimumDrawOcclusionSize = gfx::Size(60, 60);
// The maximum number of occluding Rects to track during occlusion culling.
int kMaximumOccluderComplexity = 10;
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// The screen size at renderer creation time. // The screen size at renderer creation time.
gfx::Size initial_screen_size = gfx::Size(0, 0); gfx::Size initial_screen_size = gfx::Size(0, 0);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/timer/elapsed_timer.h" #include "base/timer/elapsed_timer.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "cc/base/region.h"
#include "cc/base/simple_enclosed_region.h" #include "cc/base/simple_enclosed_region.h"
#include "cc/benchmarks/benchmark_instrumentation.h" #include "cc/benchmarks/benchmark_instrumentation.h"
#include "components/viz/common/display/renderer_settings.h" #include "components/viz/common/display/renderer_settings.h"
...@@ -129,6 +130,21 @@ gfx::RectF GetOccludingRectForRRectF(const gfx::RRectF& bounds) { ...@@ -129,6 +130,21 @@ gfx::RectF GetOccludingRectForRRectF(const gfx::RRectF& bounds) {
return occluding_rect; return occluding_rect;
} }
// SkRegion uses INT_MAX as a sentinel. Reduce gfx::Rect values when they are
// equal to INT_MAX to prevent conversion to an empty region.
gfx::Rect SafeConvertRectForRegion(const gfx::Rect& r) {
gfx::Rect safe_rect(r);
if (safe_rect.x() == INT_MAX)
safe_rect.set_x(INT_MAX - 1);
if (safe_rect.y() == INT_MAX)
safe_rect.set_y(INT_MAX - 1);
if (safe_rect.width() == INT_MAX)
safe_rect.set_width(INT_MAX - 1);
if (safe_rect.height() == INT_MAX)
safe_rect.set_height(INT_MAX - 1);
return safe_rect;
}
} // namespace } // namespace
constexpr base::TimeDelta Display::kDrawToSwapMin; constexpr base::TimeDelta Display::kDrawToSwapMin;
...@@ -845,7 +861,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { ...@@ -845,7 +861,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
return; return;
const SharedQuadState* last_sqs = nullptr; const SharedQuadState* last_sqs = nullptr;
cc::SimpleEnclosedRegion occlusion_in_target_space; cc::Region occlusion_in_target_space;
bool current_sqs_intersects_occlusion = false; bool current_sqs_intersects_occlusion = false;
int minimum_draw_occlusion_height = int minimum_draw_occlusion_height =
settings_.kMinimumDrawOcclusionSize.height() * device_scale_factor_; settings_.kMinimumDrawOcclusionSize.height() * device_scale_factor_;
...@@ -857,7 +873,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { ...@@ -857,7 +873,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
// draw occlusion on render pass. // draw occlusion on render pass.
if (pass->filters.IsEmpty() && pass->backdrop_filters.IsEmpty()) { if (pass->filters.IsEmpty() && pass->backdrop_filters.IsEmpty()) {
auto quad_list_end = pass->quad_list.end(); auto quad_list_end = pass->quad_list.end();
gfx::Rect occlusion_in_quad_content_space; cc::Region occlusion_in_quad_content_space;
for (auto quad = pass->quad_list.begin(); quad != quad_list_end;) { for (auto quad = pass->quad_list.begin(); quad != quad_list_end;) {
// Skip quad if it is a RenderPassDrawQuad because RenderPassDrawQuad is a // Skip quad if it is a RenderPassDrawQuad because RenderPassDrawQuad is a
// special type of DrawQuad where the visible_rect of shared quad state is // special type of DrawQuad where the visible_rect of shared quad state is
...@@ -898,15 +914,30 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { ...@@ -898,15 +914,30 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
if (last_sqs->is_clipped) if (last_sqs->is_clipped)
sqs_rect_in_target.Intersect(last_sqs->clip_rect); sqs_rect_in_target.Intersect(last_sqs->clip_rect);
// If region complexity is above our threshold, remove the smallest
// rects from occlusion region.
occlusion_in_target_space.Union(sqs_rect_in_target); occlusion_in_target_space.Union(sqs_rect_in_target);
while (occlusion_in_target_space.GetRegionComplexity() >
settings_.kMaximumOccluderComplexity) {
gfx::Rect smallest_rect = *occlusion_in_target_space.begin();
for (const auto& occluding_rect : occlusion_in_target_space) {
if (occluding_rect.size().GetArea() <
smallest_rect.size().GetArea())
smallest_rect = occluding_rect;
}
occlusion_in_target_space.Subtract(smallest_rect);
}
} }
// If the visible_rect of the current shared quad state does not // If the visible_rect of the current shared quad state does not
// intersect with the occlusion rect, we can skip draw occlusion checks // intersect with the occlusion rect, we can skip draw occlusion checks
// for quads in the current SharedQuadState. // for quads in the current SharedQuadState.
last_sqs = quad->shared_quad_state; last_sqs = quad->shared_quad_state;
current_sqs_intersects_occlusion = occlusion_in_target_space.Intersects( occlusion_in_quad_content_space.Clear();
const auto current_sqs_in_target_space =
cc::MathUtil::MapEnclosingClippedRect( cc::MathUtil::MapEnclosingClippedRect(
transform, last_sqs->visible_quad_layer_rect)); transform, last_sqs->visible_quad_layer_rect);
current_sqs_intersects_occlusion =
occlusion_in_target_space.Intersects(current_sqs_in_target_space);
// Compute the occlusion region in the quad content space for scale and // Compute the occlusion region in the quad content space for scale and
// translation transforms. Note that 0 scale transform will fail the // translation transforms. Note that 0 scale transform will fail the
...@@ -920,20 +951,24 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { ...@@ -920,20 +951,24 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
// the reversed directional translation. Therefore, |transform| is // the reversed directional translation. Therefore, |transform| is
// always invertible. // always invertible.
DCHECK(is_invertible); DCHECK(is_invertible);
DCHECK_LE(occlusion_in_target_space.GetRegionComplexity(),
// TODO(yiyix): Make |occlusion_coordinate_space| to work with settings_.kMaximumOccluderComplexity);
// occlusion region consists multiple rect.
DCHECK_EQ(occlusion_in_target_space.GetRegionComplexity(), 1u);
// Since transform can only be a scale or a translation matrix, it is // Since transform can only be a scale or a translation matrix, it is
// safe to use function MapEnclosedRectWith2dAxisAlignedTransform to // safe to use function MapEnclosedRectWith2dAxisAlignedTransform to
// define occluded region in the quad content space with inverted // define occluded region in the quad content space with inverted
// transform. // transform.
occlusion_in_quad_content_space = for (const gfx::Rect& rect_in_target_space :
cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform( occlusion_in_target_space) {
reverse_transform, occlusion_in_target_space.bounds()); if (current_sqs_in_target_space.Intersects(rect_in_target_space)) {
} else { auto rect_in_content =
occlusion_in_quad_content_space = gfx::Rect(); cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
reverse_transform, rect_in_target_space);
occlusion_in_quad_content_space.Union(
SafeConvertRectForRegion(rect_in_content));
}
}
DCHECK(!occlusion_in_quad_content_space.IsEmpty());
} }
} }
...@@ -953,7 +988,9 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { ...@@ -953,7 +988,9 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
// Case 2: for simple transforms, if the quad is partially shown on // Case 2: for simple transforms, if the quad is partially shown on
// screen and the region formed by (occlusion region - visible_rect) is // screen and the region formed by (occlusion region - visible_rect) is
// a rect, then update visible_rect to the resulting rect. // a rect, then update visible_rect to the resulting rect.
quad->visible_rect.Subtract(occlusion_in_quad_content_space); cc::Region origin_rect = quad->visible_rect;
origin_rect.Subtract(occlusion_in_quad_content_space);
quad->visible_rect = origin_rect.bounds();
++quad; ++quad;
} else if (occlusion_in_quad_content_space.IsEmpty() && } else if (occlusion_in_quad_content_space.IsEmpty() &&
occlusion_in_target_space.Contains( occlusion_in_target_space.Contains(
......
...@@ -328,15 +328,15 @@ TEST_F(RemoveOverdrawQuadPerfTest, IterateOverlapShareQuadStates) { ...@@ -328,15 +328,15 @@ TEST_F(RemoveOverdrawQuadPerfTest, IterateOverlapShareQuadStates) {
TEST_F(RemoveOverdrawQuadPerfTest, IterateIsolatedSharedQuadStates) { TEST_F(RemoveOverdrawQuadPerfTest, IterateIsolatedSharedQuadStates) {
IterateIsolatedSharedQuadStates("2_sqs_with_4_quads", 2, 2); IterateIsolatedSharedQuadStates("2_sqs_with_4_quads", 2, 2);
IterateIsolatedSharedQuadStates("4_sqs_with_100_quads", 2, 10); IterateIsolatedSharedQuadStates("2_sqs_with_100_quads", 2, 10);
IterateIsolatedSharedQuadStates("10_sqs_with_4_quads", 10, 2); IterateIsolatedSharedQuadStates("10_sqs_with_4_quads", 10, 2);
IterateIsolatedSharedQuadStates("10_sqs_with_100_quads", 10, 10); IterateIsolatedSharedQuadStates("10_sqs_with_100_quads", 10, 10);
} }
TEST_F(RemoveOverdrawQuadPerfTest, IteratePartiallyOverlapSharedQuadStates) { TEST_F(RemoveOverdrawQuadPerfTest, IteratePartiallyOverlapSharedQuadStates) {
IteratePartiallyOverlapSharedQuadStates("2_sqs_with_4_quads", 2, 0.5, 2); IteratePartiallyOverlapSharedQuadStates("2_sqs_with_4_quads", 2, 0.5, 2);
IteratePartiallyOverlapSharedQuadStates("10_sqs_with_100 quads", 2, 0.5, 10); IteratePartiallyOverlapSharedQuadStates("2_sqs_with_100_quads", 2, 0.5, 10);
IteratePartiallyOverlapSharedQuadStates("2_sqs_with_4_quads", 10, 0.5, 2); IteratePartiallyOverlapSharedQuadStates("10_sqs_with_4_quads", 10, 0.5, 2);
IteratePartiallyOverlapSharedQuadStates("10_sqs_with_100_quads", 10, 0.5, 10); IteratePartiallyOverlapSharedQuadStates("10_sqs_with_100_quads", 10, 0.5, 10);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment