Bug 1621390 - Optimize backdrop primitives by collapsing to clear color. r=lsalzman,Bert

Differential Revision: https://phabricator.services.mozilla.com/D66362

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Glenn Watson 2020-03-16 03:47:53 +00:00
Родитель 9cec9676e6
Коммит d69ec052be
5 изменённых файлов: 159 добавлений и 63 удалений

Просмотреть файл

@ -19,7 +19,8 @@ use crate::internal_types::{FastHashMap, SavedTargetIndex, Swizzle, TextureSourc
use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive};
use crate::prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex, PrimitiveVisibilityMask};
use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, VECS_PER_SEGMENT, SpaceMapper};
use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveVisibilityFlags};
use crate::prim_store::{VECS_PER_SEGMENT, SpaceMapper};
use crate::prim_store::image::ImageSource;
use crate::render_target::RenderTargetContext;
use crate::render_task_graph::{RenderTaskId, RenderTaskGraph};
@ -193,6 +194,16 @@ impl AlphaBatchList {
}
}
/// Clear all current batches in this list. This is typically used
/// when a primitive is encountered that occludes all previous
/// content in this batch list.
fn clear(&mut self) {
self.current_batch_index = usize::MAX;
self.current_z_id = ZBufferId::invalid();
self.batches.clear();
self.item_rects.clear();
}
pub fn set_params_and_get_batch(
&mut self,
key: BatchKey,
@ -290,6 +301,14 @@ impl OpaqueBatchList {
}
}
/// Clear all current batches in this list. This is typically used
/// when a primitive is encountered that occludes all previous
/// content in this batch list.
fn clear(&mut self) {
self.current_batch_index = usize::MAX;
self.batches.clear();
}
pub fn set_params_and_get_batch(
&mut self,
key: BatchKey,
@ -502,6 +521,14 @@ impl AlphaBatchBuilder {
}
}
/// Clear all current batches in this builder. This is typically used
/// when a primitive is encountered that occludes all previous
/// content in this batch list.
fn clear(&mut self) {
self.alpha_batch_list.clear();
self.opaque_batch_list.clear();
}
pub fn build(
mut self,
batch_containers: &mut Vec<AlphaBatchContainer>,
@ -653,6 +680,14 @@ impl BatchBuilder {
}
}
/// Clear all current batchers. This is typically used when a primitive
/// is encountered that occludes all previous content in this batch list.
fn clear_batches(&mut self) {
for batcher in &mut self.batchers {
batcher.clear();
}
}
/// Add a picture to a given batch builder.
pub fn add_pic_to_batch(
&mut self,
@ -731,6 +766,16 @@ impl BatchBuilder {
let prim_info = &ctx.scratch.prim_info[prim_instance.visibility_info.0 as usize];
let bounding_rect = &prim_info.clip_chain.pic_clip_rect;
// If this primitive is a backdrop, that means that it is known to cover
// the entire picture cache background. In that case, the renderer will
// use the backdrop color as a clear color, and so we can drop this
// primitive and any prior primitives from the batch lists for this
// picture cache slice.
if prim_info.flags.contains(PrimitiveVisibilityFlags::IS_BACKDROP) {
self.clear_batches();
return;
}
let z_id = z_generator.next();
let prim_common_data = &ctx.data_stores.as_common_data(&prim_instance);

Просмотреть файл

@ -15,7 +15,7 @@ use crate::gpu_types::TransformData;
use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex};
use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion};
use crate::picture::{RetainedTiles, TileCacheInstance, DirtyRegion, SurfaceRenderTasks, SubpixelMode};
use crate::picture::{TileCacheLogger};
use crate::picture::{BackdropKind, TileCacheLogger};
use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer};
use crate::prim_store::{DeferredResolve, PrimitiveVisibilityMask};
use crate::profiler::{FrameProfileCounters, TextureCacheProfileCounters, ResourceProfileCounters};
@ -883,14 +883,19 @@ pub fn build_render_pass(
Some(color) => color.a >= 1.0,
None => false,
};
// TODO(gw): Once we have multiple slices enabled, take advantage of
// option to skip clears if the slice is opaque.
let clear_color = if forced_opaque {
let mut clear_color = if forced_opaque {
Some(ColorF::WHITE)
} else {
Some(ColorF::TRANSPARENT)
};
// If this picture cache has a valid color backdrop, we will use
// that as the clear color, skipping the draw of the backdrop
// primitive (and anything prior to it) during batching.
if let Some(BackdropKind::Color { color }) = tile_cache.backdrop.kind {
clear_color = Some(color);
}
// Create an alpha batcher for each of the tasks of this picture.
let mut batchers = Vec::new();
for task_id in &task_ids {

Просмотреть файл

@ -120,7 +120,7 @@ use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, Primitiv
use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind};
use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey};
use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex};
use crate::prim_store::{ColorBindingStorage, ColorBindingIndex};
use crate::prim_store::{ColorBindingStorage, ColorBindingIndex, PrimitiveVisibilityFlags};
use crate::print_tree::{PrintTree, PrintTreePrinter};
use crate::render_backend::DataStores;
use crate::render_task_graph::RenderTaskId;
@ -1252,7 +1252,7 @@ impl Tile {
let clipped_rect = self.current_descriptor.local_valid_rect
.intersection(&ctx.local_clip_rect)
.unwrap_or_else(PictureRect::zero);
let mut is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect);
let mut is_opaque = ctx.backdrop.opaque_rect.contains_rect(&clipped_rect);
if self.has_compositor_surface {
// If we found primitive(s) that are ordered _after_ the first compositor
@ -1338,7 +1338,7 @@ impl Tile {
// color tiles. We can definitely support this in DC, so this
// should be added as a follow up.
let is_simple_prim =
ctx.backdrop.kind.can_be_promoted_to_compositor_surface() &&
ctx.backdrop.kind.is_some() &&
self.current_descriptor.prims.len() == 1 &&
self.is_opaque &&
supports_simple_prims;
@ -1349,15 +1349,15 @@ impl Tile {
// surface unconditionally (this will drop any previously used
// texture cache backing surface).
match ctx.backdrop.kind {
BackdropKind::Color { color } => {
Some(BackdropKind::Color { color }) => {
TileSurface::Color {
color,
}
}
BackdropKind::Clear => {
Some(BackdropKind::Clear) => {
TileSurface::Clear
}
BackdropKind::Image => {
None => {
// This should be prevented by the is_simple_prim check above.
unreachable!();
}
@ -1807,42 +1807,29 @@ impl ::std::fmt::Debug for RecordedDirtyRegion {
}
#[derive(Debug, Copy, Clone)]
enum BackdropKind {
pub enum BackdropKind {
Color {
color: ColorF,
},
Clear,
Image,
}
impl BackdropKind {
/// Returns true if the compositor can directly draw this backdrop.
fn can_be_promoted_to_compositor_surface(&self) -> bool {
match self {
BackdropKind::Color { .. } | BackdropKind::Clear => true,
BackdropKind::Image => false,
}
}
}
/// Stores information about the calculated opaque backdrop of this slice.
#[derive(Debug, Copy, Clone)]
struct BackdropInfo {
pub struct BackdropInfo {
/// The picture space rectangle that is known to be opaque. This is used
/// to determine where subpixel AA can be used, and where alpha blending
/// can be disabled.
rect: PictureRect,
pub opaque_rect: PictureRect,
/// Kind of the backdrop
kind: BackdropKind,
pub kind: Option<BackdropKind>,
}
impl BackdropInfo {
fn empty() -> Self {
BackdropInfo {
rect: PictureRect::zero(),
kind: BackdropKind::Color {
color: ColorF::BLACK,
},
opaque_rect: PictureRect::zero(),
kind: None,
}
}
}
@ -2215,7 +2202,7 @@ pub struct TileCacheInstance {
/// fine to clear the tiles to this and allow subpixel text on the first slice.
pub background_color: Option<ColorF>,
/// Information about the calculated backdrop content of this cache.
backdrop: BackdropInfo,
pub backdrop: BackdropInfo,
/// The allowed subpixel mode for this surface, which depends on the detected
/// opacity of the background.
pub subpixel_mode: SubpixelMode,
@ -2830,7 +2817,7 @@ impl TileCacheInstance {
image_instances: &ImageInstanceStorage,
surface_stack: &[SurfaceIndex],
composite_state: &mut CompositeState,
) -> bool {
) -> Option<PrimitiveVisibilityFlags> {
// This primitive exists on the last element on the current surface stack.
let prim_surface_index = *surface_stack.last().unwrap();
@ -2838,7 +2825,7 @@ impl TileCacheInstance {
// is no need to add it to any primitive dependencies.
let prim_clip_chain = match prim_clip_chain {
Some(prim_clip_chain) => prim_clip_chain,
None => return false,
None => return None,
};
self.map_local_to_surface.set_target_spatial_node(
@ -2849,12 +2836,12 @@ impl TileCacheInstance {
// Map the primitive local rect into picture space.
let prim_rect = match self.map_local_to_surface.map(&local_prim_rect) {
Some(rect) => rect,
None => return false,
None => return None,
};
// If the rect is invalid, no need to create dependencies.
if prim_rect.size.is_empty_or_negative() {
return false;
return None;
}
// If the primitive is directly drawn onto this picture cache surface, then
@ -2894,7 +2881,7 @@ impl TileCacheInstance {
rect.inflate(surface.inflation_factor, surface.inflation_factor)
}
None => {
return false;
return None;
}
};
@ -2910,7 +2897,7 @@ impl TileCacheInstance {
// If the primitive is outside the tiling rects, it's known to not
// be visible.
if p0.x == p1.x || p0.y == p1.y {
return false;
return None;
}
// Build the list of resources that this primitive has dependencies on.
@ -2975,7 +2962,10 @@ impl TileCacheInstance {
_ => unreachable!(),
};
if color.a >= 1.0 {
backdrop_candidate = Some(BackdropKind::Color { color });
backdrop_candidate = Some(BackdropInfo {
opaque_rect: pic_clip_rect,
kind: Some(BackdropKind::Color { color }),
});
}
} else {
let opacity_binding = &opacity_binding_store[opacity_binding_index];
@ -2997,9 +2987,17 @@ impl TileCacheInstance {
if opacity_binding_index == OpacityBindingIndex::INVALID {
if let Some(image_properties) = resource_cache.get_image_properties(image_data.key) {
// If this image is opaque, it can be considered as a possible opaque backdrop
if image_properties.descriptor.is_opaque() {
backdrop_candidate = Some(BackdropKind::Image);
// For an image to be a possible opaque backdrop, it must:
// - Have a valid, opaque image descriptor
// - Not use tiling (since they can fail to draw)
// - Not having any spacing / padding
if image_properties.descriptor.is_opaque() &&
image_properties.tiling.is_none() &&
image_data.tile_spacing == LayoutSize::zero() {
backdrop_candidate = Some(BackdropInfo {
opaque_rect: pic_clip_rect,
kind: None,
});
}
}
} else {
@ -3209,7 +3207,7 @@ impl TileCacheInstance {
PrimitiveInstanceKind::PushClipChain |
PrimitiveInstanceKind::PopClipChain => {
// Early exit to ensure this doesn't get added as a dependency on the tile.
return false;
return None;
}
PrimitiveInstanceKind::TextRun { data_handle, .. } => {
// Only do these checks if we haven't already disabled subpx
@ -3229,13 +3227,16 @@ impl TileCacheInstance {
// correctly determined as we recurse through pictures in take_context.
if on_picture_surface
&& subpx_requested
&& !self.backdrop.rect.contains_rect(&pic_clip_rect) {
&& !self.backdrop.opaque_rect.contains_rect(&pic_clip_rect) {
self.subpixel_mode = SubpixelMode::Deny;
}
}
}
PrimitiveInstanceKind::Clear { .. } => {
backdrop_candidate = Some(BackdropKind::Clear);
backdrop_candidate = Some(BackdropInfo {
opaque_rect: pic_clip_rect,
kind: Some(BackdropKind::Clear),
});
}
PrimitiveInstanceKind::LineDecoration { .. } |
PrimitiveInstanceKind::NormalBorder { .. } |
@ -3249,16 +3250,18 @@ impl TileCacheInstance {
// If this primitive considers itself a backdrop candidate, apply further
// checks to see if it matches all conditions to be a backdrop.
let mut vis_flags = PrimitiveVisibilityFlags::empty();
if let Some(backdrop_candidate) = backdrop_candidate {
let is_suitable_backdrop = match backdrop_candidate {
BackdropKind::Clear => {
let is_suitable_backdrop = match backdrop_candidate.kind {
Some(BackdropKind::Clear) => {
// Clear prims are special - they always end up in their own slice,
// and always set the backdrop. In future, we hope to completely
// remove clear prims, since they don't integrate with the compositing
// system cleanly.
true
}
BackdropKind::Image | BackdropKind::Color { .. } => {
Some(BackdropKind::Color { .. }) | None => {
// Check a number of conditions to see if we can consider this
// primitive as an opaque backdrop rect. Several of these are conservative
// checks and could be relaxed in future. However, these checks
@ -3281,12 +3284,25 @@ impl TileCacheInstance {
};
if is_suitable_backdrop
&& !prim_clip_chain.needs_mask
&& pic_clip_rect.contains_rect(&self.backdrop.rect) {
self.backdrop = BackdropInfo {
rect: pic_clip_rect,
kind: backdrop_candidate,
};
&& self.external_surfaces.is_empty()
&& !prim_clip_chain.needs_mask {
if backdrop_candidate.opaque_rect.contains_rect(&self.backdrop.opaque_rect) {
self.backdrop.opaque_rect = backdrop_candidate.opaque_rect;
}
if let Some(kind) = backdrop_candidate.kind {
if backdrop_candidate.opaque_rect.contains_rect(&self.local_rect) {
// If we have a color backdrop, mark the visibility flags
// of the primitive so it is skipped during batching (and
// also clears any previous primitives).
if let BackdropKind::Color { .. } = kind {
vis_flags |= PrimitiveVisibilityFlags::IS_BACKDROP;
}
self.backdrop.kind = Some(kind);
}
}
}
}
@ -3312,7 +3328,7 @@ impl TileCacheInstance {
}
}
true
Some(vis_flags)
}
/// Print debug information about this picture cache to a tree printer.
@ -3380,8 +3396,8 @@ impl TileCacheInstance {
// Register the opaque region of this tile cache as an occluder, which
// is used later in the frame to occlude other tiles.
if self.backdrop.rect.is_well_formed_and_nonempty() {
let backdrop_rect = self.backdrop.rect
if self.backdrop.opaque_rect.is_well_formed_and_nonempty() {
let backdrop_rect = self.backdrop.opaque_rect
.intersection(&self.local_rect)
.and_then(|r| {
r.intersection(&self.local_clip_rect)

Просмотреть файл

@ -1519,6 +1519,20 @@ impl PrimitiveVisibilityMask {
pub const MAX_DIRTY_REGIONS: usize = 8 * mem::size_of::<PrimitiveVisibilityMask>();
}
bitflags! {
/// A set of bitflags that can be set in the visibility information
/// for a primitive instance. This can be used to control how primitives
/// are treated during batching.
// TODO(gw): We should also move `is_compositor_surface` to be part of
// this flags struct.
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct PrimitiveVisibilityFlags: u16 {
/// Implies that this primitive covers the entire picture cache slice,
/// and can thus be dropped during batching and drawn with clear color.
const IS_BACKDROP = 1;
}
}
/// Information stored for a visible primitive about the visible
/// rect and associated clip information.
#[cfg_attr(feature = "capture", derive(Serialize))]
@ -1539,6 +1553,10 @@ pub struct PrimitiveVisibility {
/// a list of clip task ids (one per segment).
pub clip_task_index: ClipTaskIndex,
/// A set of flags that define how this primitive should be handled
/// during batching of visibile primitives.
pub flags: PrimitiveVisibilityFlags,
/// A mask defining which of the dirty regions this primitive is visible in.
pub visibility_mask: PrimitiveVisibilityMask,
@ -2095,6 +2113,7 @@ impl PrimitiveStore {
clip_task_index: ClipTaskIndex::INVALID,
combined_local_clip_rect: LayoutRect::zero(),
visibility_mask: PrimitiveVisibilityMask::empty(),
flags: PrimitiveVisibilityFlags::empty(),
}
);
@ -2156,11 +2175,16 @@ impl PrimitiveStore {
prim_instance.is_chased(),
);
// Primitive visibility flags default to empty, but may be supplied
// by the `update_prim_dependencies` method below when picture caching
// is active.
let mut vis_flags = PrimitiveVisibilityFlags::empty();
if let Some(ref mut tile_cache) = frame_state.tile_cache {
// TODO(gw): Refactor how tile_cache is stored in frame_state
// so that we can pass frame_state directly to
// update_prim_dependencies, rather than splitting borrows.
if !tile_cache.update_prim_dependencies(
match tile_cache.update_prim_dependencies(
prim_instance,
cluster.spatial_node_index,
clip_chain.as_ref(),
@ -2176,11 +2200,16 @@ impl PrimitiveStore {
&frame_state.surface_stack,
&mut frame_state.composite_state,
) {
prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
// Ensure the primitive clip is popped - perhaps we can use
// some kind of scope to do this automatically in future.
frame_state.clip_chain_stack.pop_clip();
continue;
Some(flags) => {
vis_flags = flags;
}
None => {
prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
// Ensure the primitive clip is popped - perhaps we can use
// some kind of scope to do this automatically in future.
frame_state.clip_chain_stack.pop_clip();
continue;
}
}
}
@ -2308,6 +2337,7 @@ impl PrimitiveStore {
clip_task_index: ClipTaskIndex::INVALID,
combined_local_clip_rect,
visibility_mask: PrimitiveVisibilityMask::empty(),
flags: vis_flags,
}
);

Просмотреть файл

@ -94,5 +94,5 @@ fuzzy-if(winWidget,0-150,0-120) == component-alpha-1.html component-alpha-1-ref.
== intermediate-1.html intermediate-1-ref.html
== preserves3d-nested-filter-1.html preserves3d-nested-filter-1-ref.html
!= preserve3d-scale.html about:blank
fuzzy-if(webrender,0-1,0-3) == perspective-overflow-1.html perspective-overflow-1-ref.html
fuzzy-if(webrender,0-1,0-5) == perspective-overflow-1.html perspective-overflow-1-ref.html
== 1544995-1.html 1544995-1-ref.html