Commit 486da1f4 authored by Sasha McIntosh's avatar Sasha McIntosh Committed by Commit Bot

viz: Do not split quads that intersect a renderpass.

In the linked bug, we cut a hole in the background that is occluded
by the foreground app folder. This hole changes the background blur
and we see that around the foreground occluder there is less blur.

Bug: 1059734
Test: DisplayTest.DrawOcclusionWithIntersectingBackdropFilter
Change-Id: I665b483ed4bede53b9e4640962a3ccf8d16de3fe
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2097512
Commit-Queue: Sasha McIntosh <sashamcintosh@chromium.org>
Reviewed-by: default avatarDaniele Castagna <dcastagna@chromium.org>
Cr-Commit-Position: refs/heads/master@{#753599}
parent 9c15931d
......@@ -926,12 +926,22 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
const SharedQuadState* last_sqs = nullptr;
cc::Region occlusion_in_target_space;
cc::Region backdrop_filters_in_target_space;
bool current_sqs_intersects_occlusion = false;
int minimum_draw_occlusion_height =
settings_.kMinimumDrawOcclusionSize.height() * device_scale_factor_;
int minimum_draw_occlusion_width =
settings_.kMinimumDrawOcclusionSize.width() * device_scale_factor_;
base::flat_map<RenderPassId, gfx::Rect> backdrop_filter_rects;
for (const auto& pass : frame->render_pass_list) {
if (!pass->backdrop_filters.IsEmpty() &&
pass->backdrop_filters.HasFilterThatMovesPixels()) {
backdrop_filter_rects[pass->id] = cc::MathUtil::MapEnclosingClippedRect(
pass->transform_to_root_target, pass->output_rect);
}
}
const auto& pass = frame->render_pass_list.back();
// TODO(yiyix): Add filter effects to draw occlusion calculation and perform
// draw occlusion on render pass.
......@@ -940,14 +950,26 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
auto quad_list_end = pass->quad_list.end();
cc::Region occlusion_in_quad_content_space;
gfx::Rect render_pass_quads_in_content_space;
for (auto quad = pass->quad_list.begin(); quad != quad_list_end;) {
// Skip quad if it is a RenderPassDrawQuad because RenderPassDrawQuad is a
// special type of DrawQuad where the visible_rect of shared quad state is
// not entirely covered by draw quads in it; or the DrawQuad size is
// smaller than the kMinimumDrawOcclusionSize; or the DrawQuad is inside
// a 3d objects.
if (quad->material == ContentDrawQuadBase::Material::kRenderPass ||
(quad->visible_rect.width() <= minimum_draw_occlusion_width &&
// not entirely covered by draw quads in it.
if (quad->material == ContentDrawQuadBase::Material::kRenderPass) {
// A RenderPass with backdrop filters may apply to a quad underlying
// RenderPassQuad. These regions should be tracked so that correctly
// handle splitting and occlusion of the underlying quad.
auto it = backdrop_filter_rects.find(
RenderPassDrawQuad::MaterialCast(*quad)->render_pass_id);
if (it != backdrop_filter_rects.end()) {
backdrop_filters_in_target_space.Union(it->second);
}
++quad;
continue;
}
// Also skip quad if the DrawQuad size is smaller than the
// kMinimumDrawOcclusionSize; or the DrawQuad is inside a 3d object.
if ((quad->visible_rect.width() <= minimum_draw_occlusion_width &&
quad->visible_rect.height() <= minimum_draw_occlusion_height) ||
quad->shared_quad_state->sorting_context_id != 0) {
++quad;
......@@ -999,6 +1021,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
// for quads in the current SharedQuadState.
last_sqs = quad->shared_quad_state;
occlusion_in_quad_content_space.Clear();
render_pass_quads_in_content_space = gfx::Rect();
const auto current_sqs_in_target_space =
cc::MathUtil::MapEnclosingClippedRect(
transform, last_sqs->visible_quad_layer_rect);
......@@ -1034,6 +1057,20 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
SafeConvertRectForRegion(rect_in_content));
}
}
// A render pass quad may apply some filter or transform to an
// underlying quad. Do not split quads when they intersect with a render
// pass quad.
if (current_sqs_in_target_space.Intersects(
backdrop_filters_in_target_space.bounds())) {
for (const auto& rect_in_target_space :
backdrop_filters_in_target_space) {
auto rect_in_content =
cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
reverse_transform, rect_in_target_space);
render_pass_quads_in_content_space.Union(rect_in_content);
}
}
}
}
......@@ -1060,6 +1097,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
// more than X fragments.
const bool should_split_quads =
enable_quad_splitting_ &&
!visible_region.Intersects(render_pass_quads_in_content_space) &&
ReduceComplexity(visible_region, settings_.quad_split_limit,
&cached_visible_region_) &&
CanSplitQuad(quad->material, ComputeArea(cached_visible_region_),
......
......@@ -904,6 +904,89 @@ TEST_F(DisplayTest, CompositorFrameDamagesCorrectDisplay) {
TearDownDisplay();
}
// Quads that intersect backdrop filter render pass quads should not be
// split because splitting may affect how the filter applies to an
// underlying quad.
TEST_F(DisplayTest, DrawOcclusionWithIntersectingBackdropFilter) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kSplitPartiallyOccludedQuads);
RendererSettings settings;
settings.minimum_fragments_reduced = 0;
settings.kMinimumDrawOcclusionSize.set_width(0);
SetUpGpuDisplay(settings);
StubDisplayClient client;
display_->Initialize(&client, manager_.surface_manager());
CompositorFrame frame = CompositorFrameBuilder()
.AddDefaultRenderPass()
.AddDefaultRenderPass()
.Build();
bool is_clipped = false;
bool are_contents_opaque = true;
float opacity = 1.f;
// Rects, shared quad states and quads map 1:1:1
gfx::Rect rects[3] = {
gfx::Rect(75, 0, 50, 100),
gfx::Rect(0, 0, 50, 50),
gfx::Rect(0, 0, 100, 100),
};
SharedQuadState* shared_quad_states[3];
DrawQuad* quads[3];
// Set up the backdrop filter render pass
auto& bd_render_pass = frame.render_pass_list.at(0);
auto& root_render_pass = frame.render_pass_list.at(1);
auto bd_filter_rect = rects[0];
cc::FilterOperations backdrop_filters;
backdrop_filters.Append(cc::FilterOperation::CreateBlurFilter(5.0));
bd_render_pass->SetAll(
2, bd_filter_rect, gfx::Rect(), gfx::Transform(), cc::FilterOperations(),
backdrop_filters, gfx::RRectF(gfx::RectF(bd_filter_rect), 0),
gfx::ContentColorUsage::kSRGB, false, false, false, false);
// Add quads to root render pass
for (int i = 0; i < 3; i++) {
shared_quad_states[i] = root_render_pass->CreateAndAppendSharedQuadState();
shared_quad_states[i]->SetAll(
gfx::Transform(), rects[i], rects[i], gfx::RRectF(), rects[i],
is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
if (i == 0) { // Backdrop filter quad
auto* new_quad = root_render_pass->quad_list
.AllocateAndConstruct<RenderPassDrawQuad>();
new_quad->SetNew(shared_quad_states[i], rects[i], rects[i],
bd_render_pass->id, 2, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
1.f);
quads[i] = new_quad;
} else {
auto* new_quad = root_render_pass->quad_list
.AllocateAndConstruct<SolidColorDrawQuad>();
new_quad->SetNew(shared_quad_states[i], rects[i], rects[i], SK_ColorBLACK,
false);
quads[i] = new_quad;
}
}
// +---+-+-+-+
// | 1 | | . |
// +---+ | 0 |
// | 2 | . |
// +-----+---+
EXPECT_EQ(base::size(rects), root_render_pass->quad_list.size());
display_->RemoveOverdrawQuads(&frame);
ASSERT_EQ(base::size(rects), root_render_pass->quad_list.size());
for (int i = 0; i < 3; i++) {
EXPECT_EQ(rects[i], root_render_pass->quad_list.ElementAt(i)->visible_rect);
}
TearDownDisplay();
}
// Check if draw occlusion does not remove any DrawQuads when no quad is being
// covered completely.
TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) {
......
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