Commit 42f22d64 authored by Sunny Sachanandani's avatar Sunny Sachanandani Committed by Commit Bot

viz: Do not promote quads affected by backdrop filters to overlays

Make DCLayerOverlayProcessor keep track of output rects of each child
render pass that has filter operations and compare that with the quad
target rect when deciding whether to promote to overlay.

Bug: 997517
Change-Id: Iee6cc6acfea1ae00ce1aa676138440949cfabd21
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1817269Reviewed-by: default avatarMaggie Chen <magchen@chromium.org>
Reviewed-by: default avatarZhenyao Mo <zmo@chromium.org>
Reviewed-by: default avatarMason Freed <masonfreed@chromium.org>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Cr-Commit-Position: refs/heads/master@{#700061}
parent c8236710
......@@ -43,7 +43,8 @@ enum DCLayerResult {
DC_LAYER_FAILED_TOO_MANY_OVERLAYS,
DC_LAYER_FAILED_NO_HW_OVERLAY_SUPPORT, // deprecated
DC_LAYER_FAILED_ROUNDED_CORNERS,
kMaxValue = DC_LAYER_FAILED_ROUNDED_CORNERS,
DC_LAYER_FAILED_BACKDROP_FILTERS,
kMaxValue = DC_LAYER_FAILED_BACKDROP_FILTERS,
};
enum : size_t {
......@@ -52,8 +53,19 @@ enum : size_t {
kUVPlaneResourceIndex = 1,
};
// This returns the smallest rectangle in target space that contains the quad.
gfx::RectF ClippedQuadRectangle(const DrawQuad* quad) {
gfx::RectF quad_rect = cc::MathUtil::MapClippedRect(
quad->shared_quad_state->quad_to_target_transform,
gfx::RectF(quad->rect));
if (quad->shared_quad_state->is_clipped)
quad_rect.Intersect(gfx::RectF(quad->shared_quad_state->clip_rect));
return quad_rect;
}
DCLayerResult FromYUVQuad(const YUVVideoDrawQuad* quad,
const gfx::Transform& transform_to_root_target,
const std::vector<gfx::Rect>& backdrop_filter_rects,
bool has_hw_overlay_support,
int current_frame_processed_overlay_count,
DisplayResourceProvider* resource_provider,
......@@ -94,6 +106,12 @@ DCLayerResult FromYUVQuad(const YUVVideoDrawQuad* quad,
// Rounded corner on overlays are not supported.
if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty())
return DC_LAYER_FAILED_ROUNDED_CORNERS;
auto quad_target_rect = gfx::ToEnclosingRect(ClippedQuadRectangle(quad));
for (const auto& filter_target_rect : backdrop_filter_rects) {
if (filter_target_rect.Intersects(quad_target_rect))
return DC_LAYER_FAILED_BACKDROP_FILTERS;
}
}
// Direct composition path only supports single NV12 buffer, or two buffers
......@@ -130,8 +148,10 @@ DCLayerResult FromYUVQuad(const YUVVideoDrawQuad* quad,
return DC_LAYER_SUCCESS;
}
DCLayerResult FromTextureQuad(const TextureDrawQuad* quad,
DCLayerResult FromTextureQuad(
const TextureDrawQuad* quad,
const gfx::Transform& transform_to_root_target,
const std::vector<gfx::Rect>& backdrop_filter_rects,
DisplayResourceProvider* resource_provider,
DCLayerOverlay* dc_layer) {
// Check that resources are overlay compatible first so that subsequent
......@@ -145,9 +165,7 @@ DCLayerResult FromTextureQuad(const TextureDrawQuad* quad,
return DC_LAYER_FAILED_QUAD_BLEND_MODE;
if (!quad->shared_quad_state->quad_to_target_transform
.Preserves2dAxisAlignment() &&
!base::FeatureList::IsEnabled(
features::kDirectCompositionComplexOverlays)) {
.Preserves2dAxisAlignment()) {
return DC_LAYER_FAILED_COMPLEX_TRANSFORM;
}
......@@ -155,6 +173,12 @@ DCLayerResult FromTextureQuad(const TextureDrawQuad* quad,
if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty())
return DC_LAYER_FAILED_ROUNDED_CORNERS;
auto quad_target_rect = gfx::ToEnclosingRect(ClippedQuadRectangle(quad));
for (const auto& filter_target_rect : backdrop_filter_rects) {
if (filter_target_rect.Intersects(quad_target_rect))
return DC_LAYER_FAILED_BACKDROP_FILTERS;
}
dc_layer->resources[kTextureResourceIndex] = quad->resource_id();
dc_layer->z_order = 1;
dc_layer->content_rect = gfx::Rect(quad->resource_size_in_pixels());
......@@ -205,16 +229,6 @@ DCLayerResult IsUnderlayAllowed(const QuadList::Iterator& it,
return DC_LAYER_SUCCESS;
}
// This returns the smallest rectangle in target space that contains the quad.
gfx::RectF ClippedQuadRectangle(const DrawQuad* quad) {
gfx::RectF quad_rect = cc::MathUtil::MapClippedRect(
quad->shared_quad_state->quad_to_target_transform,
gfx::RectF(quad->rect));
if (quad->shared_quad_state->is_clipped)
quad_rect.Intersect(gfx::RectF(quad->shared_quad_state->clip_rect));
return quad_rect;
}
// Any occluding quads in the quad list on top of the overlay/underlay
bool HasOccludingQuads(const gfx::RectF& target_quad,
QuadList::ConstIterator quad_list_begin,
......@@ -298,6 +312,11 @@ DCLayerOverlay& DCLayerOverlay::operator=(const DCLayerOverlay& other) =
default;
DCLayerOverlay::~DCLayerOverlay() = default;
DCLayerOverlayProcessor::RenderPassData::RenderPassData() = default;
DCLayerOverlayProcessor::RenderPassData::RenderPassData(
const RenderPassData& other) = default;
DCLayerOverlayProcessor::RenderPassData::~RenderPassData() = default;
DCLayerOverlayProcessor::DCLayerOverlayProcessor(
const OutputSurface::Capabilities& capabilities,
const RendererSettings& settings)
......@@ -315,7 +334,7 @@ void DCLayerOverlayProcessor::Process(
RenderPassList* render_passes,
gfx::Rect* damage_rect,
DCLayerOverlayList* dc_layer_overlays) {
pass_punch_through_rects_.clear();
render_pass_data_.clear();
for (auto& pass : *render_passes) {
bool is_root = (pass == render_passes->back());
ProcessRenderPass(resource_provider, display_rect, pass.get(), is_root,
......@@ -338,12 +357,21 @@ QuadList::Iterator DCLayerOverlayProcessor::ProcessRenderPassDrawQuad(
const RenderPassDrawQuad* rpdq = RenderPassDrawQuad::MaterialCast(*it);
++it;
// Check if this quad is broken to avoid corrupting pass_info.
// Check if this quad is broken to avoid corrupting |render_pass_data_|.
if (rpdq->render_pass_id == render_pass->id)
return it;
// |pass_punch_through_rects_| will be empty unless non-root overlays are
// enabled.
if (!pass_punch_through_rects_.count(rpdq->render_pass_id))
// This will be filled in for all render passes even non-root overlays are
// disabled.
const auto& render_pass_data = render_pass_data_[rpdq->render_pass_id];
if (render_pass_data.has_backdrop_filters) {
render_pass_data_[render_pass->id].backdrop_filter_rects.push_back(
gfx::ToEnclosingRect(ClippedQuadRectangle(rpdq)));
}
// |punch_through_rects| will be empty unless non-root overlays are enabled.
const auto& punch_through_rects = render_pass_data.punch_through_rects;
if (punch_through_rects.empty())
return it;
// Punch holes through for all child video quads that will be displayed in
......@@ -386,8 +414,6 @@ QuadList::Iterator DCLayerOverlayProcessor::ProcessRenderPassDrawQuad(
// color mask in solid color draw quad which we don't have today. Another
// difficulty is undoing the SrcOver blending in child render passes if any
// render pass above has a non-supported blend mode.
const auto& punch_through_rects =
pass_punch_through_rects_[rpdq->render_pass_id];
const SharedQuadState* original_shared_quad_state = rpdq->shared_quad_state;
// Copy shared state from RPDQ to get the same clip rect.
......@@ -417,7 +443,8 @@ QuadList::Iterator DCLayerOverlayProcessor::ProcessRenderPassDrawQuad(
// Add transformed info to list in case this renderpass is included in
// another pass.
pass_punch_through_rects_[render_pass->id].push_back(clipped_quad_rect);
render_pass_data_[render_pass->id].punch_through_rects.push_back(
clipped_quad_rect);
}
return it;
}
......@@ -442,7 +469,7 @@ void DCLayerOverlayProcessor::InsertDebugBorderDrawQuads(
}
const auto& punch_through_rects =
pass_punch_through_rects_[root_render_pass->id];
render_pass_data_[root_render_pass->id].punch_through_rects;
auto it = quad_list.InsertBeforeAndInvalidateAllPointers<DebugBorderDrawQuad>(
quad_list.begin(), punch_through_rects.size());
......@@ -472,6 +499,15 @@ void DCLayerOverlayProcessor::ProcessRenderPass(
gfx::Rect this_frame_overlay_rect;
gfx::Rect this_frame_underlay_rect;
// Always fill in |has_backdrop_filters| even if non-root overlays are
// disabled because it's needed to reject overlays that are read by backdrop
// filters. Note that the backdrop filter rejection doesn't work properly for
// overlays that are in non-root render passes since we can't determine if
// there's an indirect ancestor render pass which has child RPDQs that could
// read this quad's output.
render_pass_data_[render_pass->id].has_backdrop_filters =
!render_pass->backdrop_filters.IsEmpty();
QuadList* quad_list = &render_pass->quad_list;
auto next_it = quad_list->begin();
for (auto it = quad_list->begin(); it != quad_list->end(); it = next_it) {
......@@ -489,15 +525,18 @@ void DCLayerOverlayProcessor::ProcessRenderPass(
DCLayerResult result;
switch (it->material) {
case DrawQuad::Material::kYuvVideoContent:
result = FromYUVQuad(YUVVideoDrawQuad::MaterialCast(*it),
result = FromYUVQuad(
YUVVideoDrawQuad::MaterialCast(*it),
render_pass->transform_to_root_target,
has_hw_overlay_support_,
current_frame_processed_overlay_count_,
render_pass_data_[render_pass->id].backdrop_filter_rects,
has_hw_overlay_support_, current_frame_processed_overlay_count_,
resource_provider, &dc_layer);
break;
case DrawQuad::Material::kTextureContent:
result = FromTextureQuad(TextureDrawQuad::MaterialCast(*it),
result = FromTextureQuad(
TextureDrawQuad::MaterialCast(*it),
render_pass->transform_to_root_target,
render_pass_data_[render_pass->id].backdrop_filter_rects,
resource_provider, &dc_layer);
break;
default:
......@@ -722,7 +761,7 @@ void DCLayerOverlayProcessor::ProcessForUnderlay(
// Propagate the punched holes up the chain of render passes. Punch through
// rects are in quad target (child render pass) space, and are transformed to
// RPDQ target (parent render pass) in ProcessRenderPassDrawQuad().
pass_punch_through_rects_[render_pass->id].push_back(
render_pass_data_[render_pass->id].punch_through_rects.push_back(
gfx::ToEnclosingRect(ClippedQuadRectangle(*it)));
}
......
......@@ -139,11 +139,25 @@ class VIZ_SERVICE_EXPORT DCLayerOverlayProcessor {
int previous_frame_processed_overlay_count_ = 0;
int current_frame_processed_overlay_count_ = 0;
struct RenderPassData {
RenderPassData();
RenderPassData(const RenderPassData& other);
~RenderPassData();
// Store information about clipped punch-through rects in target space for
// non-root render passes. These rects are used to clear the corresponding
// areas in parent render passes.
base::flat_map<RenderPassId, std::vector<gfx::Rect>>
pass_punch_through_rects_;
std::vector<gfx::Rect> punch_through_rects;
// Output rects of child render passes that have backdrop filters in target
// space. These rects are used to determine if the overlay rect could be
// read by backdrop filters.
std::vector<gfx::Rect> backdrop_filter_rects;
// Whether this render pass has backdrop filters.
bool has_backdrop_filters = false;
};
base::flat_map<RenderPassId, RenderPassData> render_pass_data_;
DISALLOW_COPY_AND_ASSIGN(DCLayerOverlayProcessor);
};
......
<!DOCTYPE HTML>
<!-- READ BEFORE UPDATING:
If this test is updated make sure to increment the "revision" value of the
associated test in content/test/gpu/page_sets/pixel_tests.py. This will ensure
that the baseline images are regenerated on the next run.
-->
<html>
<head>
<meta name="viewport" content="initial-scale=1">
<title>MP4 Video with Backdrop Filter Test</title>
<style type="text/css">
.nomargin {
margin: 0px auto;
}
.absolute {
position: absolute;
top: 0px;
left: 0px;
}
.backdrop {
width: 200px;
height: 100px;
backdrop-filter: grayscale(1);
z-index: 1;
}
</style>
<script src="pixel_video_test.js"></script>
</head>
<body onload="main()">
<div id="container" class="absolute">
<video class="nomargin" id="video" width="240" height="135">
<source src="/media/test/data/four-colors.mp4" type="video/mp4">
</video>
<div class="nomargin absolute backdrop"></div>
</div>
</body>
</html>
......@@ -62,7 +62,9 @@ class PixelTestPage(object):
# action here is "CrashGpuProcess" then it would be defined in a
# "_CrashGpuProcess" method in PixelIntegrationTest.
self.optional_action = optional_action
# Whatever other settings a test need to specify.
# These are used to pass additional arguments to the test harness.
# VideoPathTraceTest and OverlayModeTest support the following boolean
# arguments: expect_yuy2, zero_copy, video_is_rotated, and no_overlay.
self.other_args = other_args
def CopyWithNewBrowserArgsAndSuffix(self, browser_args, suffix):
......@@ -467,6 +469,13 @@ class PixelTestPages(object):
tolerance=tolerance_vp9,
expected_colors=_FOUR_COLOR_VIDEO_240x135_EXPECTED_COLORS),
PixelTestPage(
'pixel_video_backdrop_filter.html',
base_name + '_Video_BackdropFilter',
test_rect=[0, 0, 240, 135],
revision=1,
tolerance=tolerance),
PixelTestPage(
'pixel_webgl_premultiplied_alpha_false.html',
base_name + '_WebGL_PremultipliedAlpha_False',
......@@ -1238,8 +1247,6 @@ class PixelTestPages(object):
# All bots are connected with a power source, however, we want to to test
# with the code path that's enabled with battery power.
'--disable_vp_scaling=1']
browser_args_Underlay = browser_args + [
'--enable-features=DirectCompositionUnderlays']
browser_args_Nonroot = browser_args +[
'--enable-features=DirectCompositionNonrootOverlays,' +
'DirectCompositionUnderlays']
......@@ -1251,9 +1258,6 @@ class PixelTestPages(object):
'--disable-features=DirectCompositionPreferNV12Overlays']
browser_args_DXVA = browser_args + [
'--disable-features=D3D11VideoDecoder']
browser_args_Underlay_DXVA = browser_args + [
'--enable-features=DirectCompositionUnderlays',
'--disable-features=D3D11VideoDecoder']
tolerance_dc = 5
tolerance_dc_vp9 = 15
......@@ -1562,7 +1566,7 @@ class PixelTestPages(object):
base_name + '_DirectComposition_Underlay',
test_rect=[0, 0, 240, 136],
revision=0, # Golden image revision is not used
browser_args=browser_args_Underlay,
browser_args=browser_args,
tolerance=tolerance_dc,
expected_colors=[
{
......@@ -1602,7 +1606,7 @@ class PixelTestPages(object):
base_name + '_DirectComposition_Underlay_DXVA',
test_rect=[0, 0, 240, 136],
revision=0, # Golden image revision is not used
browser_args=browser_args_Underlay_DXVA,
browser_args=browser_args_DXVA,
tolerance=tolerance_dc,
expected_colors=[
{
......@@ -1642,7 +1646,7 @@ class PixelTestPages(object):
base_name + '_DirectComposition_Underlay_Fullsize',
test_rect=[0, 0, 960, 540],
revision=0, # Golden image revision is not used
browser_args=browser_args_Underlay,
browser_args=browser_args,
other_args={'zero_copy': True},
tolerance=tolerance_dc,
expected_colors=[
......@@ -1765,4 +1769,13 @@ class PixelTestPages(object):
revision=0,
browser_args=browser_args,
tolerance=tolerance_dc),
PixelTestPage(
'pixel_video_backdrop_filter.html',
base_name + '_DirectComposition_Video_BackdropFilter',
test_rect=[0, 0, 240, 135],
revision=1,
browser_args=browser_args,
other_args={'no_overlay': True},
tolerance=tolerance_dc),
]
......@@ -222,6 +222,7 @@ crbug.com/991291 [ android skia-renderer use-vulkan ] Pixel_Video_MP4_FourColors
crbug.com/991291 [ android skia-renderer use-vulkan ] Pixel_Video_MP4_FourColors_Rot_270 [ Skip ]
crbug.com/991291 [ android skia-renderer use-vulkan ] Pixel_Video_MP4_FourColors_Rot_90 [ Skip ]
crbug.com/991291 [ android skia-renderer use-vulkan ] Pixel_Video_VP9 [ Skip ]
crbug.com/991291 [ android skia-renderer use-vulkan ] Pixel_Video_BackdropFilter [ Skip ]
# Fails when the browser features SkiaRenderer & GL are enabled on Android.
# The more specific matching criteria is to prevent collisions with other expectations.
......
......@@ -261,8 +261,7 @@ class TraceIntegrationTest(gpu_integration_test.GpuIntegrationTest):
assert os_name and os_name.lower() == 'win'
# Calculate expectations.
if other_args is None:
other_args = {}
other_args = other_args if other_args is not None else {}
expect_yuy2 = other_args.get('expect_yuy2', False)
zero_copy = other_args.get('zero_copy', False)
......@@ -285,12 +284,16 @@ class TraceIntegrationTest(gpu_integration_test.GpuIntegrationTest):
if not supports_nv12_overlays:
zero_copy = False
expect_no_overlay = other_args.get('no_overlay', False)
# Verify expectations through captured trace events.
for event in event_iterator:
if event.category != category:
continue
if event.name != _SWAP_CHAIN_PRESENT_EVENT_NAME:
continue
if expect_no_overlay:
self.fail('Expected no overlay got %s' % _SWAP_CHAIN_PRESENT_EVENT_NAME)
detected_pixel_format = event.args.get('PixelFormat', None)
if detected_pixel_format is None:
self.fail('PixelFormat is missing from event %s' %
......@@ -307,6 +310,8 @@ class TraceIntegrationTest(gpu_integration_test.GpuIntegrationTest):
(zero_copy, detected_zero_copy))
break
else:
if expect_no_overlay:
return
self.fail('Events with name %s were not found' %
_SWAP_CHAIN_PRESENT_EVENT_NAME)
......@@ -325,17 +330,26 @@ class TraceIntegrationTest(gpu_integration_test.GpuIntegrationTest):
if overlay_bot_config.get('supports_overlays', False):
expected_presentation_mode = _SWAP_CHAIN_PRESENTATION_MODE_OVERLAY
other_args = other_args if other_args is not None else {}
expect_no_overlay = other_args.get('no_overlay', False)
presentation_mode_history = []
for event in event_iterator:
if event.category != category:
continue
if event.name != _GET_STATISTICS_EVENT_NAME:
continue
if expect_no_overlay:
self.fail('Expected no overlay got %s' % _GET_STATISTICS_EVENT_NAME)
detected_presentation_mode = event.args.get('CompositionMode', None)
if detected_presentation_mode is None:
self.fail('PresentationMode is missing from event %s' %
_GET_STATISTICS_EVENT_NAME)
presentation_mode_history.append(detected_presentation_mode)
if expect_no_overlay:
return
valid_entry_found = False
for index in range(len(presentation_mode_history)):
mode = presentation_mode_history[index]
......
......@@ -13104,6 +13104,7 @@ Called by update_net_error_codes.py.-->
<int value="8" label="Failed too many overlays"/>
<int value="9" label="Failed no hw overlay support (deprecated)"/>
<int value="10" label="Failed quad has rounded corners"/>
<int value="11" label="Failed read by backdrop filters"/>
</enum>
<enum name="DeclarativeAPIFunctionType">
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