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) { ...@@ -926,12 +926,22 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
const SharedQuadState* last_sqs = nullptr; const SharedQuadState* last_sqs = nullptr;
cc::Region occlusion_in_target_space; cc::Region occlusion_in_target_space;
cc::Region backdrop_filters_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_;
int minimum_draw_occlusion_width = int minimum_draw_occlusion_width =
settings_.kMinimumDrawOcclusionSize.width() * device_scale_factor_; 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(); const auto& pass = frame->render_pass_list.back();
// TODO(yiyix): Add filter effects to draw occlusion calculation and perform // TODO(yiyix): Add filter effects to draw occlusion calculation and perform
// draw occlusion on render pass. // draw occlusion on render pass.
...@@ -940,14 +950,26 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { ...@@ -940,14 +950,26 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
auto quad_list_end = pass->quad_list.end(); auto quad_list_end = pass->quad_list.end();
cc::Region occlusion_in_quad_content_space; 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;) { 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
// not entirely covered by draw quads in it; or the DrawQuad size is // not entirely covered by draw quads in it.
// smaller than the kMinimumDrawOcclusionSize; or the DrawQuad is inside if (quad->material == ContentDrawQuadBase::Material::kRenderPass) {
// a 3d objects. // A RenderPass with backdrop filters may apply to a quad underlying
if (quad->material == ContentDrawQuadBase::Material::kRenderPass || // RenderPassQuad. These regions should be tracked so that correctly
(quad->visible_rect.width() <= minimum_draw_occlusion_width && // 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->visible_rect.height() <= minimum_draw_occlusion_height) ||
quad->shared_quad_state->sorting_context_id != 0) { quad->shared_quad_state->sorting_context_id != 0) {
++quad; ++quad;
...@@ -999,6 +1021,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { ...@@ -999,6 +1021,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
// for quads in the current SharedQuadState. // for quads in the current SharedQuadState.
last_sqs = quad->shared_quad_state; last_sqs = quad->shared_quad_state;
occlusion_in_quad_content_space.Clear(); occlusion_in_quad_content_space.Clear();
render_pass_quads_in_content_space = gfx::Rect();
const auto current_sqs_in_target_space = 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);
...@@ -1034,6 +1057,20 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) { ...@@ -1034,6 +1057,20 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
SafeConvertRectForRegion(rect_in_content)); 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) { ...@@ -1060,6 +1097,7 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
// more than X fragments. // more than X fragments.
const bool should_split_quads = const bool should_split_quads =
enable_quad_splitting_ && enable_quad_splitting_ &&
!visible_region.Intersects(render_pass_quads_in_content_space) &&
ReduceComplexity(visible_region, settings_.quad_split_limit, ReduceComplexity(visible_region, settings_.quad_split_limit,
&cached_visible_region_) && &cached_visible_region_) &&
CanSplitQuad(quad->material, ComputeArea(cached_visible_region_), CanSplitQuad(quad->material, ComputeArea(cached_visible_region_),
......
...@@ -904,6 +904,89 @@ TEST_F(DisplayTest, CompositorFrameDamagesCorrectDisplay) { ...@@ -904,6 +904,89 @@ TEST_F(DisplayTest, CompositorFrameDamagesCorrectDisplay) {
TearDownDisplay(); 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 // Check if draw occlusion does not remove any DrawQuads when no quad is being
// covered completely. // covered completely.
TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) { 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