Commit 17521fd2 authored by Sasha McIntosh's avatar Sasha McIntosh Committed by Commit Bot

viz: Split one quad into multiple quads.

In cases where drawing additional smaller quads instead of one large
quad is significantly less costly, split one quad into multiple quads.

In order to minimize the cost of drawing additional quads and resizing the quad list
we limit the when quads may be split. We limit the number of quads one
quad may be split into and enforce a minimum number of fragments saved
by splitting.

Test: viz_perftests,viz_unittests
Bug: 1022544
Change-Id: Id47c0a5b33b0de456fad10635c86b880882fb549
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2057289Reviewed-by: default avatarMaggie Chen <magchen@chromium.org>
Reviewed-by: default avatarccameron <ccameron@chromium.org>
Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Reviewed-by: default avatarDaniele Castagna <dcastagna@chromium.org>
Commit-Queue: Sasha McIntosh <sashamcintosh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#744003}
parent 2d4448a9
......@@ -49,6 +49,14 @@ class VIZ_COMMON_EXPORT RendererSettings {
// The maximum number of occluding Rects to track during occlusion culling.
int kMaximumOccluderComplexity = 10;
// The maximum number (exclusive) of quads one draw quad may be split into
// during occlusion culling. e.g. an L-shaped visible region split into two
// quads
int quad_split_limit = 5;
// The minimum number of fragments that would not be drawn if a quads was
// split into multiple quads during occlusion culling.
int minimum_fragments_reduced = 128 * 128;
#if defined(OS_ANDROID)
// The screen size at renderer creation time.
gfx::Size initial_screen_size = gfx::Size(0, 0);
......
......@@ -60,6 +60,63 @@ void QuadList::ReplaceExistingQuadWithOpaqueTransparentSolidColor(Iterator at) {
needs_blending, SK_ColorTRANSPARENT, true);
}
QuadList::Iterator QuadList::InsertCopyBeforeDrawQuad(Iterator at,
size_t count) {
DCHECK(at->shared_quad_state);
switch (at->material) {
case DrawQuad::Material::kDebugBorder: {
const auto copy = *DebugBorderDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<DebugBorderDrawQuad>(
at, count, copy);
}
case DrawQuad::Material::kPictureContent: {
const auto copy = *PictureDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<PictureDrawQuad>(at, count,
copy);
}
case DrawQuad::Material::kTextureContent: {
const auto copy = *TextureDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<TextureDrawQuad>(at, count,
copy);
}
case DrawQuad::Material::kSolidColor: {
const auto copy = *SolidColorDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<SolidColorDrawQuad>(at, count,
copy);
}
case DrawQuad::Material::kTiledContent: {
const auto copy = *TileDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<TileDrawQuad>(at, count,
copy);
}
case DrawQuad::Material::kStreamVideoContent: {
const auto copy = *StreamVideoDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<StreamVideoDrawQuad>(
at, count, copy);
}
case DrawQuad::Material::kSurfaceContent: {
const auto copy = *SurfaceDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<SurfaceDrawQuad>(at, count,
copy);
}
case DrawQuad::Material::kVideoHole: {
const auto copy = *VideoHoleDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<VideoHoleDrawQuad>(at, count,
copy);
}
case DrawQuad::Material::kYuvVideoContent: {
const auto copy = *YUVVideoDrawQuad::MaterialCast(*at);
return InsertBeforeAndInvalidateAllPointers<YUVVideoDrawQuad>(at, count,
copy);
}
// RenderPass quads should not be copied.
case DrawQuad::Material::kRenderPass:
case DrawQuad::Material::kInvalid:
NOTREACHED(); // Invalid DrawQuad material.
return at;
}
}
std::unique_ptr<RenderPass> RenderPass::Create() {
return base::WrapUnique(new RenderPass());
}
......
......@@ -54,6 +54,7 @@ class VIZ_COMMON_EXPORT QuadList : public cc::ListContainer<DrawQuad> {
// This function is used by overlay algorithm to fill the backbuffer with
// transparent black.
void ReplaceExistingQuadWithOpaqueTransparentSolidColor(Iterator at);
Iterator InsertCopyBeforeDrawQuad(Iterator at, size_t count);
};
using SharedQuadStateList = cc::ListContainer<SharedQuadState>;
......
......@@ -10,6 +10,7 @@
#include "base/debug/dump_without_crashing.h"
#include "base/metrics/histogram_macros.h"
#include "base/optional.h"
#include "base/stl_util.h"
#include "base/timer/elapsed_timer.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
......@@ -47,6 +48,18 @@ namespace viz {
namespace {
const DrawQuad::Material kNonSplittableMaterials[] = {
// Exclude debug quads from quad splitting
DrawQuad::Material::kDebugBorder,
// Exclude possible overlay candidates from quad splitting
// See OverlayCandidate::FromDrawQuad
DrawQuad::Material::kStreamVideoContent,
DrawQuad::Material::kTextureContent,
DrawQuad::Material::kVideoHole,
// See DCLayerOverlayProcessor::ProcessRenderPass
DrawQuad::Material::kYuvVideoContent,
};
constexpr base::TimeDelta kAllowedDeltaFromFuture =
base::TimeDelta::FromMilliseconds(16);
......@@ -145,6 +158,28 @@ gfx::Rect SafeConvertRectForRegion(const gfx::Rect& r) {
return safe_rect;
}
// TODO(sashamcintosh): consider moving to cc::Region
int ComputeRegionArea(const cc::Region& region) {
int area = 0;
for (const auto& r : region)
area += r.size().GetArea();
return area;
}
bool CanSplitQuad(const DrawQuad::Material m,
const cc::Region& visible_region,
const int quad_split_limit,
const int minimum_fragments_reduced,
const int device_scale_factor,
const bool enable_quad_splitting) {
return enable_quad_splitting && !base::Contains(kNonSplittableMaterials, m) &&
visible_region.GetRegionComplexity() < quad_split_limit &&
(visible_region.bounds().size().GetArea() -
ComputeRegionArea(visible_region)) *
device_scale_factor * device_scale_factor >
minimum_fragments_reduced;
}
} // namespace
constexpr base::TimeDelta Display::kDrawToSwapMin;
......@@ -987,10 +1022,27 @@ void Display::RemoveOverdrawQuads(CompositorFrame* frame) {
// Case 2: for simple transforms, if the quad is partially shown on
// screen and the region formed by (occlusion region - visible_rect) is
// a rect, then update visible_rect to the resulting rect.
cc::Region origin_rect = quad->visible_rect;
origin_rect.Subtract(occlusion_in_quad_content_space);
quad->visible_rect = origin_rect.bounds();
++quad;
cc::Region visible_region = quad->visible_rect;
visible_region.Subtract(occlusion_in_quad_content_space);
quad->visible_rect = visible_region.bounds();
// Split quad into multiple draw quads when area can be reduce by
// more than X fragments.
const bool should_split_quads = CanSplitQuad(
quad->material, visible_region, settings_.quad_split_limit,
settings_.minimum_fragments_reduced, device_scale_factor_,
!overlay_processor_->DisableSplittingQuads());
if (should_split_quads) {
auto new_quad = pass->quad_list.InsertCopyBeforeDrawQuad(
quad, visible_region.GetRegionComplexity() - 1);
for (const auto& visible_rect : visible_region) {
new_quad->visible_rect = visible_rect;
++new_quad;
}
quad = new_quad;
} else {
++quad;
}
} else if (occlusion_in_quad_content_space.IsEmpty() &&
occlusion_in_target_space.Contains(
cc::MathUtil::MapEnclosingClippedRect(
......
......@@ -12,6 +12,7 @@
#include "base/test/metrics/histogram_tester.h"
#include "base/test/null_task_runner.h"
#include "cc/base/math_util.h"
#include "cc/base/region.h"
#include "cc/test/scheduler_test_common.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
......@@ -4116,6 +4117,99 @@ TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerDoesOcclude) {
TearDownDisplay();
}
TEST_F(DisplayTest, DrawOcclusionSplit) {
SetUpGpuDisplay(RendererSettings());
StubDisplayClient client;
display_->Initialize(&client, manager_.surface_manager());
// The two partially occluded quads will be split into two additional quads,
// preserving only the visible regions.
CompositorFrame frame = MakeDefaultCompositorFrame();
// +--------------------------------+
// |***+----------------------+ <- Large occluding Rect
// +---|- - - - + - - -|-----+
// |***| . |*****|
// |***+----------------------+*****|
// |****************|***************|
// +----------------+---------------+
//
// * -> Visible rect for the quads.
const gfx::Rect occluding_rect(10, 10, 1000, 490);
const gfx::Rect quad_rects[3] = {
gfx::Rect(0, 0, 1200, 20),
gfx::Rect(0, 20, 600, 490),
gfx::Rect(600, 20, 600, 490),
};
gfx::Rect occluded_sqs_rect(0, 0, 1200, 510);
const bool is_clipped = false;
const bool are_contents_opaque = true;
const float opacity = 1.f;
SharedQuadState* shared_quad_state_occluder =
frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
SharedQuadState* shared_quad_state_occluded =
frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
SolidColorDrawQuad* quads[4];
for (auto*& quad : quads) {
quad = frame.render_pass_list.front()
->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
}
{
shared_quad_state_occluder->SetAll(
gfx::Transform(), occluding_rect, occluding_rect, gfx::RRectF(),
occluding_rect, is_clipped, are_contents_opaque, opacity,
SkBlendMode::kSrcOver, 0);
quads[0]->SetNew(shared_quad_state_occluder, occluding_rect, occluding_rect,
SK_ColorRED, false);
shared_quad_state_occluded->SetAll(
gfx::Transform(), occluded_sqs_rect, occluded_sqs_rect, gfx::RRectF(),
occluded_sqs_rect, is_clipped, are_contents_opaque, opacity,
SkBlendMode::kSrcOver, 0);
for (int i = 1; i < 4; i++) {
quads[i]->SetNew(shared_quad_state_occluded, quad_rects[i - 1],
quad_rects[i - 1], SK_ColorRED, false);
}
EXPECT_EQ(4u, frame.render_pass_list.front()->quad_list.size());
display_->RemoveOverdrawQuads(&frame);
ASSERT_EQ(6u, frame.render_pass_list.front()->quad_list.size());
EXPECT_EQ(occluding_rect.ToString(), frame.render_pass_list.front()
->quad_list.ElementAt(0)
->visible_rect.ToString());
// Computed the expected quads
// +--------------------------------+
// | 1 |
// +---+----------------------+-----+
// | 2 | | 3 |
// +---+------------+---------+-----+
// | 4 | 5 |
// +----------------+---------------+
const gfx::Rect expected_visible_rects[5]{
// The occluded region of rest one is small, so we do not split the
// quad.
quad_rects[0],
gfx::Rect(0, 20, 10, 480),
gfx::Rect(0, 500, 600, 10),
gfx::Rect(1010, 20, 190, 480),
gfx::Rect(600, 500, 600, 10),
};
const QuadList& quad_list = frame.render_pass_list.front()->quad_list;
for (int i = 0; i < 5; i++) {
EXPECT_EQ(expected_visible_rects[i],
quad_list.ElementAt(i + 1)->visible_rect);
}
}
TearDownDisplay();
}
TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerPartialOcclude) {
SetUpGpuDisplay(RendererSettings());
......@@ -4139,7 +4233,7 @@ TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerPartialOcclude) {
// | |
// +----------------------+
//
// * -> Visiblg rect for the quads.
// * -> Visible rect for the quads.
gfx::Rect quad_rect(10, 10, 1000, 1000);
gfx::RRectF rounded_corner_bounds(gfx::RectF(quad_rect), 10.f);
gfx::Rect occluded_quad_rect_1(0, 20, 600, 490);
......
......@@ -136,6 +136,10 @@ OverlayProcessorInterface::CreateOverlayProcessor(
#endif
}
bool OverlayProcessorInterface::DisableSplittingQuads() const {
return false;
}
OverlayProcessorInterface::OutputSurfaceOverlayPlane
OverlayProcessorInterface::ProcessOutputSurfaceAsOverlay(
const gfx::Size& viewport_size,
......
......@@ -52,6 +52,8 @@ class VIZ_SERVICE_EXPORT OverlayProcessorInterface {
using FilterOperationsMap =
base::flat_map<RenderPassId, cc::FilterOperations*>;
virtual bool DisableSplittingQuads() const;
// Used by Window's DCLayerOverlay system and OverlayProcessorUsingStrategy.
static void RecordOverlayDamageRectHistograms(
bool is_overlay,
......
......@@ -29,6 +29,10 @@ OverlayProcessorMac::OverlayProcessorMac(
OverlayProcessorMac::~OverlayProcessorMac() = default;
bool OverlayProcessorMac::DisableSplittingQuads() const {
return true;
}
bool OverlayProcessorMac::IsOverlaySupported() const {
return could_overlay_;
}
......
......@@ -32,6 +32,8 @@ class VIZ_SERVICE_EXPORT OverlayProcessorMac
std::unique_ptr<CALayerOverlayProcessor> ca_layer_overlay_processor);
~OverlayProcessorMac() override;
bool DisableSplittingQuads() const override;
bool IsOverlaySupported() const override;
gfx::Rect GetAndResetOverlayDamage() override;
......
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