Bug 1821233 - Simplify and optimize how prims are pushed to command buffers r=gfx-reviewers,lsalzman

Previously, we would do a fine-grained visibility check for
prims against the dirty rect stack (after coarse grained
tile visibility), then prepare the primitive, then determine
which command buffer(s) the prim should be added to, based
on which tile(s) the prim affects.

The patch changes this so that the fine-grained visibility
check returns a list of command buffer(s) that the prim
should be added to. This is passed to the prim prepare
step, and then used to directly add prims to the buffers
rather than checking which tiles are affected by the prim.

The motivation for doing this will become apparent in
follow up patches. We want to be able to encode
multiple command buffer commands per-prim, whereas it
was previously only possible to encode primitive
commands. By allowing prim-prepare to write directly
to the command buffers, rather than return a list of
primitive commands, we can write whatever commands
are needed. Future patches will use this to write
segment rect streams, and other information.

A side effect of this is that the `tile_rect` field
in the `PrimitiveVisibility` struct is no longer
required. This reduces the size of `PrimitiveInstance`
from 104 bytes to 88 bytes, which is likely to be
a reasonable performance win on pages that have
high primitive counts.

Differential Revision: https://phabricator.services.mozilla.com/D172081
This commit is contained in:
Glenn Watson 2023-03-19 22:57:54 +00:00
Родитель e427011917
Коммит a7e386d79f
10 изменённых файлов: 173 добавлений и 172 удалений

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

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::units::PictureRect;
use crate::{spatial_tree::SpatialNodeIndex, render_task_graph::RenderTaskId, surface::SurfaceTileDescriptor, picture::TileKey, renderer::GpuBufferAddress, FastHashMap, prim_store::PrimitiveInstanceIndex, gpu_cache::GpuCacheAddress};
use crate::gpu_types::TransformPaletteId;
use crate::segment::EdgeAaSegmentMask;
@ -20,9 +21,9 @@ impl Command {
/// Draw a complex (3d-split) primitive, that has multiple GPU cache addresses.
const CMD_DRAW_COMPLEX_PRIM: u32 = 0x20000000;
/// Draw a primitive, that has a single GPU buffer addresses.
const CMD_DRAW_INSTANCE: u32 = 0x40000000;
const CMD_DRAW_INSTANCE: u32 = 0x30000000;
/// Draw a generic quad primitive
const CMD_DRAW_QUAD: u32 = 0x80000000;
const CMD_DRAW_QUAD: u32 = 0x40000000;
/// Bitmask for command bits of the command.
const CMD_MASK: u32 = 0xf0000000;
@ -63,7 +64,7 @@ bitflags! {
#[repr(transparent)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct QuadFlags : u32 {
pub struct QuadFlags : u8 {
const IS_OPAQUE = 1 << 0;
}
}
@ -195,7 +196,7 @@ impl CommandBuffer {
self.commands.push(Command::draw_quad(prim_instance_index));
self.commands.push(Command::data((gpu_buffer_address.u as u32) << 16 | gpu_buffer_address.v as u32));
self.commands.push(Command::data(transform_id.0));
self.commands.push(Command::data(quad_flags.bits << 16 | edge_flags.bits() as u32));
self.commands.push(Command::data((quad_flags.bits as u32) << 16 | edge_flags.bits() as u32));
}
}
}
@ -239,7 +240,7 @@ impl CommandBuffer {
let data = cmd_iter.next().unwrap();
let transform_id = TransformPaletteId(cmd_iter.next().unwrap().0);
let bits = cmd_iter.next().unwrap().0;
let quad_flags = QuadFlags::from_bits(bits >> 16).unwrap();
let quad_flags = QuadFlags::from_bits((bits >> 16) as u8).unwrap();
let edge_flags = EdgeAaSegmentMask::from_bits((bits & 0xff) as u8).unwrap();
let gpu_buffer_address = GpuBufferAddress {
u: (data.0 >> 16) as u16,
@ -290,6 +291,7 @@ pub enum CommandBufferBuilderKind {
Simple {
render_task_id: RenderTaskId,
root_task_id: Option<RenderTaskId>,
dirty_rect: PictureRect,
},
Invalid,
}
@ -342,11 +344,13 @@ impl CommandBufferBuilder {
render_task_id: RenderTaskId,
establishes_sub_graph: bool,
root_task_id: Option<RenderTaskId>,
dirty_rect: PictureRect,
) -> Self {
CommandBufferBuilder {
kind: CommandBufferBuilderKind::Simple {
render_task_id,
root_task_id,
dirty_rect,
},
establishes_sub_graph,
resolve_source: None,

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

@ -7,7 +7,7 @@ use api::units::*;
use plane_split::BspSplitter;
use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
use crate::clip::{ClipStore, ClipTree};
use crate::command_buffer::CommandBufferList;
use crate::command_buffer::{PrimitiveCommand, CommandBufferList, CommandBufferIndex};
use crate::spatial_tree::{SpatialTree, SpatialNodeIndex};
use crate::composite::{CompositorKind, CompositeState, CompositeStatePreallocator};
use crate::debug_item::DebugItem;
@ -188,6 +188,19 @@ impl<'a> FrameBuildingState<'a> {
pub fn pop_dirty_region(&mut self) {
self.dirty_region_stack.pop().unwrap();
}
/// Push a primitive command to a set of command buffers
pub fn push_prim(
&mut self,
cmd: &PrimitiveCommand,
spatial_node_index: SpatialNodeIndex,
targets: &[CommandBufferIndex],
) {
for cmd_buffer_index in targets {
let cmd_buffer = self.cmd_buffers.get_mut(*cmd_buffer_index);
cmd_buffer.add_prim(cmd, spatial_node_index);
}
}
}
/// Immutable context of a picture when processing children.

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

@ -257,7 +257,7 @@ pub type TileRect = Box2D<i32, TileCoordinate>;
/// The maximum number of compositor surfaces that are allowed per picture cache. This
/// is an arbitrary number that should be enough for common cases, but low enough to
/// prevent performance and memory usage drastically degrading in pathological cases.
const MAX_COMPOSITOR_SURFACES: usize = 4;
pub const MAX_COMPOSITOR_SURFACES: usize = 4;
/// The size in device pixels of a normal cached tile.
pub const TILE_SIZE_DEFAULT: DeviceIntSize = DeviceIntSize {
@ -1643,6 +1643,11 @@ impl SubSliceIndex {
pub fn is_primary(&self) -> bool {
self.0 == 0
}
/// Get an array index for this sub-slice
pub fn as_usize(&self) -> usize {
self.0 as usize
}
}
/// Wrapper struct around an external surface descriptor with a little more information
@ -3429,7 +3434,6 @@ impl TileCacheInstance {
prim_instance.vis.state = VisibilityState::Visible {
vis_flags,
tile_rect: TileRect::new(p0, p1),
sub_slice_index: SubSliceIndex::new(sub_slice_index),
};
}
@ -4563,7 +4567,6 @@ impl PicturePrimitive {
let tile_cache = tile_caches.get_mut(&slice_id).unwrap();
let mut debug_info = SliceDebugInfo::new();
let mut surface_render_tasks = FastHashMap::default();
let mut surface_dirty_rects = Vec::new();
let mut surface_local_dirty_rect = PictureRect::zero();
let device_pixel_scale = frame_state
.surfaces[surface_index.0]
@ -4955,6 +4958,7 @@ impl PicturePrimitive {
SurfaceTileDescriptor {
current_task_id: render_task_id,
composite_task_id: Some(composite_task_id),
dirty_rect: tile.local_dirty_rect,
},
);
} else {
@ -4988,11 +4992,10 @@ impl PicturePrimitive {
SurfaceTileDescriptor {
current_task_id: render_task_id,
composite_task_id: None,
dirty_rect: tile.local_dirty_rect,
},
);
}
surface_dirty_rects.push(tile.local_dirty_rect);
}
if frame_context.fb_config.testing {
@ -5147,10 +5150,7 @@ impl PicturePrimitive {
);
}
let descriptor = SurfaceDescriptor::new_tiled(
surface_render_tasks,
surface_dirty_rects,
);
let descriptor = SurfaceDescriptor::new_tiled(surface_render_tasks);
frame_state.surface_builder.push_surface(
surface_index,
@ -5763,20 +5763,25 @@ impl PicturePrimitive {
);
// Add the child prims to the relevant command buffers
let mut cmd_buffer_targets = Vec::new();
for child in list {
let child_prim_instance = &prim_instances[child.anchor.instance_index.0 as usize];
let prim_cmd = PrimitiveCommand::complex(
child.anchor.instance_index,
child.gpu_address
);
frame_state.surface_builder.push_prim(
&prim_cmd,
child.anchor.spatial_node_index,
if frame_state.surface_builder.get_cmd_buffer_targets_for_prim(
&child_prim_instance.vis,
frame_state.cmd_buffers,
);
&mut cmd_buffer_targets,
) {
let prim_cmd = PrimitiveCommand::complex(
child.anchor.instance_index,
child.gpu_address
);
frame_state.push_prim(
&prim_cmd,
child.anchor.spatial_node_index,
&cmd_buffer_targets,
);
}
}
}

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

@ -11,7 +11,7 @@ use api::{BoxShadowClipMode, BorderStyle, ClipMode};
use api::units::*;
use euclid::Scale;
use smallvec::SmallVec;
use crate::command_buffer::{PrimitiveCommand, QuadFlags};
use crate::command_buffer::{PrimitiveCommand, QuadFlags, CommandBufferIndex};
use crate::image_tiling::{self, Repetition};
use crate::border::{get_max_scale_for_border, build_border_instances};
use crate::clip::{ClipStore};
@ -54,6 +54,8 @@ pub fn prepare_primitives(
prim_instances: &mut Vec<PrimitiveInstance>,
) {
profile_scope!("prepare_primitives");
let mut cmd_buffer_targets = Vec::new();
for cluster in &mut prim_list.clusters {
if !cluster.flags.contains(ClusterFlags::IS_VISIBLE) {
continue;
@ -65,7 +67,10 @@ pub fn prepare_primitives(
);
for prim_instance_index in cluster.prim_range() {
if frame_state.surface_builder.is_prim_visible_and_in_dirty_region(&prim_instances[prim_instance_index].vis) {
if frame_state.surface_builder.get_cmd_buffer_targets_for_prim(
&prim_instances[prim_instance_index].vis,
&mut cmd_buffer_targets,
) {
let plane_split_anchor = PlaneSplitAnchor::new(
cluster.spatial_node_index,
PrimitiveInstanceIndex(prim_instance_index as u32),
@ -84,21 +89,11 @@ pub fn prepare_primitives(
scratch,
tile_caches,
prim_instances,
&cmd_buffer_targets,
);
if !scratch.prim_cmds.is_empty() {
for prim_cmd in scratch.prim_cmds.drain(..) {
frame_state.surface_builder.push_prim(
&prim_cmd,
cluster.spatial_node_index,
&prim_instances[prim_instance_index].vis,
frame_state.cmd_buffers,
);
}
frame_state.num_visible_primitives += 1;
continue;
}
frame_state.num_visible_primitives += 1;
continue;
}
// TODO(gw): Technically no need to clear visibility here, since from this point it
@ -122,9 +117,9 @@ fn prepare_prim_for_render(
scratch: &mut PrimitiveScratchBuffer,
tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
prim_instances: &mut Vec<PrimitiveInstance>,
targets: &[CommandBufferIndex],
) {
profile_scope!("prepare_prim_for_render");
debug_assert!(scratch.prim_cmds.is_empty());
// If we have dependencies, we need to prepare them first, in order
// to know the actual rect of this primitive.
@ -237,6 +232,7 @@ fn prepare_prim_for_render(
frame_state,
data_stores,
scratch,
targets,
)
}
@ -254,6 +250,7 @@ fn prepare_interned_prim_for_render(
frame_state: &mut FrameBuildingState,
data_stores: &mut DataStores,
scratch: &mut PrimitiveScratchBuffer,
targets: &[CommandBufferIndex],
) {
let prim_spatial_node_index = cluster.spatial_node_index;
let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale;
@ -620,15 +617,19 @@ fn prepare_interned_prim_for_render(
frame_context.spatial_tree,
);
scratch.prim_cmds.push(
PrimitiveCommand::quad(
frame_state.push_prim(
&PrimitiveCommand::quad(
prim_instance_index,
prim_address,
transform_id,
quad_flags,
aa_flags,
)
),
prim_spatial_node_index,
targets,
);
return;
}
}
PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, .. } => {
@ -740,7 +741,12 @@ fn prepare_interned_prim_for_render(
// TODO(gw): Consider whether it's worth doing segment building
// for gradient primitives.
scratch.prim_cmds.push(PrimitiveCommand::instance(prim_instance_index, stops_address));
frame_state.push_prim(
&PrimitiveCommand::instance(prim_instance_index, stops_address),
prim_spatial_node_index,
targets,
);
return;
}
PrimitiveInstanceKind::CachedLinearGradient { data_handle, ref mut visible_tiles_range, .. } => {
profile_scope!("CachedLinearGradient");
@ -914,8 +920,18 @@ fn prepare_interned_prim_for_render(
}
}
if scratch.prim_cmds.is_empty() {
scratch.prim_cmds.push(PrimitiveCommand::simple(prim_instance_index));
match prim_instance.vis.state {
VisibilityState::Unset => {
panic!("bug: invalid vis state");
}
VisibilityState::Visible { .. } => {
frame_state.push_prim(
&PrimitiveCommand::simple(prim_instance_index),
prim_spatial_node_index,
targets,
);
}
VisibilityState::PassThrough | VisibilityState::Culled => {}
}
}

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

@ -9,7 +9,6 @@ use api::{PrimitiveKeyKind, FillRule, POLYGON_CLIP_VERTEX_MAX};
use api::units::*;
use euclid::{SideOffsets2D, Size2D};
use malloc_size_of::MallocSizeOf;
use crate::command_buffer::PrimitiveCommand;
use crate::clip::ClipLeafId;
use crate::segment::EdgeAaSegmentMask;
use crate::border::BorderSegmentCacheKey;
@ -1213,9 +1212,6 @@ pub struct PrimitiveScratchBuffer {
/// Set of sub-graphs that are required, determined during visibility pass
pub required_sub_graphs: FastHashSet<PictureIndex>,
/// Buffer for building primitive command lists during prepare pass
pub prim_cmds: Vec<PrimitiveCommand>,
}
impl Default for PrimitiveScratchBuffer {
@ -1230,7 +1226,6 @@ impl Default for PrimitiveScratchBuffer {
debug_items: Vec::new(),
messages: Vec::new(),
required_sub_graphs: FastHashSet::default(),
prim_cmds: Vec::new(),
}
}
}
@ -1244,12 +1239,9 @@ impl PrimitiveScratchBuffer {
self.segment_instances.recycle(recycler);
self.gradient_tiles.recycle(recycler);
recycler.recycle_vec(&mut self.debug_items);
recycler.recycle_vec(&mut self.prim_cmds);
}
pub fn begin_frame(&mut self) {
assert!(self.prim_cmds.is_empty());
// Clear the clip mask tasks for the beginning of the frame. Append
// a single kind representing no clip mask, at the ClipTaskIndex::INVALID
// location.
@ -1448,7 +1440,7 @@ fn test_struct_sizes() {
// test expectations and move on.
// (b) You made a structure larger. This is not necessarily a problem, but should only
// be done with care, and after checking if talos performance regresses badly.
assert_eq!(mem::size_of::<PrimitiveInstance>(), 104, "PrimitiveInstance size changed");
assert_eq!(mem::size_of::<PrimitiveInstance>(), 88, "PrimitiveInstance size changed");
assert_eq!(mem::size_of::<PrimitiveInstanceKind>(), 24, "PrimitiveInstanceKind size changed");
assert_eq!(mem::size_of::<PrimitiveTemplate>(), 56, "PrimitiveTemplate size changed");
assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 28, "PrimitiveTemplateKind size changed");

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

@ -3,12 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::units::*;
use crate::command_buffer::{CommandBufferBuilderKind, CommandBufferList, CommandBufferBuilder, CommandBufferIndex, PrimitiveCommand};
use crate::command_buffer::{CommandBufferBuilderKind, CommandBufferList, CommandBufferBuilder, CommandBufferIndex};
use crate::internal_types::FastHashMap;
use crate::picture::{SurfaceInfo, SurfaceIndex, TileKey, SubSliceIndex};
use crate::picture::{SurfaceInfo, SurfaceIndex, TileKey, SubSliceIndex, MAX_COMPOSITOR_SURFACES};
use crate::prim_store::{PictureIndex};
use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder};
use crate::spatial_tree::SpatialNodeIndex;
use crate::render_target::ResolveOp;
use crate::render_task::{RenderTask, RenderTaskKind, RenderTaskLocation};
use crate::visibility::{VisibilityState, PrimitiveVisibility};
@ -31,6 +30,8 @@ pub struct SurfaceTileDescriptor {
/// The compositing task for this tile, if required. This is only needed
/// when a tile contains one or more sub-graphs.
pub composite_task_id: Option<RenderTaskId>,
/// Dirty rect for this tile
pub dirty_rect: PictureRect,
}
// Details of how a surface is rendered
@ -42,31 +43,30 @@ pub enum SurfaceDescriptorKind {
// A single surface (e.g. for an opacity filter)
Simple {
render_task_id: RenderTaskId,
dirty_rect: PictureRect,
},
// A surface with 1+ intermediate tasks (e.g. blur)
Chained {
render_task_id: RenderTaskId,
root_task_id: RenderTaskId,
dirty_rect: PictureRect,
},
}
// Describes how a surface is rendered
pub struct SurfaceDescriptor {
kind: SurfaceDescriptorKind,
dirty_rects: Vec<PictureRect>,
}
impl SurfaceDescriptor {
// Create a picture cache tiled surface
pub fn new_tiled(
tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
dirty_rects: Vec<PictureRect>,
) -> Self {
SurfaceDescriptor {
kind: SurfaceDescriptorKind::Tiled {
tiles,
},
dirty_rects,
}
}
@ -80,8 +80,8 @@ impl SurfaceDescriptor {
kind: SurfaceDescriptorKind::Chained {
render_task_id,
root_task_id,
dirty_rect,
},
dirty_rects: vec![dirty_rect],
}
}
@ -93,8 +93,8 @@ impl SurfaceDescriptor {
SurfaceDescriptor {
kind: SurfaceDescriptorKind::Simple {
render_task_id,
dirty_rect,
},
dirty_rects: vec![dirty_rect],
}
}
}
@ -102,93 +102,69 @@ impl SurfaceDescriptor {
// Describes a list of command buffers that we are adding primitives to
// for a given surface. These are created from a command buffer builder
// as an optimization - skipping the indirection pic_task -> cmd_buffer_index
enum CommandBufferTargets {
// Picture cache targets target multiple command buffers
Tiled {
tiles: FastHashMap<TileKey, CommandBufferIndex>,
},
// Child surfaces target a single command buffer
Simple {
cmd_buffer_index: CommandBufferIndex,
},
struct CommandBufferTargets {
available_cmd_buffers: Vec<Vec<(PictureRect, CommandBufferIndex)>>,
}
impl CommandBufferTargets {
// Initialize command buffer targets from a command buffer builder
fn new() -> Self {
CommandBufferTargets {
available_cmd_buffers: vec![Vec::new(); MAX_COMPOSITOR_SURFACES+1],
}
}
fn init(
&mut self,
cb: &CommandBufferBuilder,
rg_builder: &RenderTaskGraphBuilder,
) {
let new_target = match cb.kind {
CommandBufferBuilderKind::Tiled { ref tiles, .. } => {
let mut cb_tiles = FastHashMap::default();
for available_cmd_buffers in &mut self.available_cmd_buffers {
available_cmd_buffers.clear();
}
match cb.kind {
CommandBufferBuilderKind::Tiled { ref tiles, .. } => {
for (key, desc) in tiles {
let task = rg_builder.get_task(desc.current_task_id);
match task.kind {
RenderTaskKind::Picture(ref info) => {
cb_tiles.insert(*key, info.cmd_buffer_index);
let available_cmd_buffers = &mut self.available_cmd_buffers[key.sub_slice_index.as_usize()];
available_cmd_buffers.push((desc.dirty_rect, info.cmd_buffer_index));
}
_ => unreachable!("bug: not a picture"),
}
}
CommandBufferTargets::Tiled { tiles: cb_tiles }
}
CommandBufferBuilderKind::Simple { render_task_id, .. } => {
CommandBufferBuilderKind::Simple { render_task_id, dirty_rect, .. } => {
let task = rg_builder.get_task(render_task_id);
match task.kind {
RenderTaskKind::Picture(ref info) => {
CommandBufferTargets::Simple { cmd_buffer_index: info.cmd_buffer_index }
for sub_slice_buffer in &mut self.available_cmd_buffers {
sub_slice_buffer.push((dirty_rect, info.cmd_buffer_index));
}
}
_ => unreachable!("bug: not a picture"),
}
}
CommandBufferBuilderKind::Invalid => {
CommandBufferTargets::Tiled { tiles: FastHashMap::default() }
}
CommandBufferBuilderKind::Invalid => {}
};
*self = new_target;
}
/// Push a new primitive in to the command buffer builder
fn push_prim(
/// For a given rect and sub-slice, get a list of command buffers to write commands to
fn get_cmd_buffer_targets_for_rect(
&mut self,
prim_cmd: &PrimitiveCommand,
spatial_node_index: SpatialNodeIndex,
tile_rect: crate::picture::TileRect,
rect: &PictureRect,
sub_slice_index: SubSliceIndex,
cmd_buffers: &mut CommandBufferList,
) {
match self {
CommandBufferTargets::Tiled { ref mut tiles } => {
// For tiled builders, add the prim to the command buffer of each
// tile that this primitive affects.
for y in tile_rect.min.y .. tile_rect.max.y {
for x in tile_rect.min.x .. tile_rect.max.x {
let key = TileKey {
tile_offset: crate::picture::TileOffset::new(x, y),
sub_slice_index,
};
if let Some(cmd_buffer_index) = tiles.get(&key) {
cmd_buffers.get_mut(*cmd_buffer_index).add_prim(
prim_cmd,
spatial_node_index,
);
}
}
}
}
CommandBufferTargets::Simple { cmd_buffer_index, .. } => {
// For simple builders, just add the prim
cmd_buffers.get_mut(*cmd_buffer_index).add_prim(
prim_cmd,
spatial_node_index,
);
targets: &mut Vec<CommandBufferIndex>,
) -> bool {
for (dirty_rect, cmd_buffer_index) in &self.available_cmd_buffers[sub_slice_index.as_usize()] {
if dirty_rect.intersects(rect) {
targets.push(*cmd_buffer_index);
}
}
!targets.is_empty()
}
}
@ -199,8 +175,6 @@ pub struct SurfaceBuilder {
current_cmd_buffers: CommandBufferTargets,
// Stack of surfaces that are parents to the current targets
builder_stack: Vec<CommandBufferBuilder>,
// Dirty rect stack used to reject adding primitives
dirty_rect_stack: Vec<Vec<PictureRect>>,
// A map of the output render tasks from any sub-graphs that haven't
// been consumed by BackdropRender prims yet
pub sub_graph_output_map: FastHashMap<PictureIndex, RenderTaskId>,
@ -209,9 +183,8 @@ pub struct SurfaceBuilder {
impl SurfaceBuilder {
pub fn new() -> Self {
SurfaceBuilder {
current_cmd_buffers: CommandBufferTargets::Tiled { tiles: FastHashMap::default() },
current_cmd_buffers: CommandBufferTargets::new(),
builder_stack: Vec::new(),
dirty_rect_stack: Vec::new(),
sub_graph_output_map: FastHashMap::default(),
}
}
@ -251,26 +224,26 @@ impl SurfaceBuilder {
// Init the surface
surfaces[surface_index.0].clipping_rect = clipping_rect;
self.dirty_rect_stack.push(descriptor.dirty_rects);
let builder = match descriptor.kind {
SurfaceDescriptorKind::Tiled { tiles } => {
CommandBufferBuilder::new_tiled(
tiles,
)
}
SurfaceDescriptorKind::Simple { render_task_id } => {
SurfaceDescriptorKind::Simple { render_task_id, dirty_rect, .. } => {
CommandBufferBuilder::new_simple(
render_task_id,
is_sub_graph,
None,
dirty_rect,
)
}
SurfaceDescriptorKind::Chained { render_task_id, root_task_id } => {
SurfaceDescriptorKind::Chained { render_task_id, root_task_id, dirty_rect, .. } => {
CommandBufferBuilder::new_simple(
render_task_id,
is_sub_graph,
Some(root_task_id),
dirty_rect,
)
}
};
@ -321,12 +294,15 @@ impl SurfaceBuilder {
.push(child_task_id);
}
// Returns true if the given primitive is visible and also intersects the dirty
// region of the current surface
pub fn is_prim_visible_and_in_dirty_region(
&self,
// Get a list of command buffer indices that primitives should be pushed
// to for a given current visbility / dirty state
pub fn get_cmd_buffer_targets_for_prim(
&mut self,
vis: &PrimitiveVisibility,
targets: &mut Vec<CommandBufferIndex>,
) -> bool {
targets.clear();
match vis.state {
VisibilityState::Unset => {
panic!("bug: invalid vis state");
@ -334,14 +310,12 @@ impl SurfaceBuilder {
VisibilityState::Culled => {
false
}
VisibilityState::Visible { .. } => {
self.dirty_rect_stack
.last()
.unwrap()
.iter()
.any(|dirty_rect| {
dirty_rect.intersects(&vis.clip_chain.pic_coverage_rect)
})
VisibilityState::Visible { sub_slice_index, .. } => {
self.current_cmd_buffers.get_cmd_buffer_targets_for_rect(
&vis.clip_chain.pic_coverage_rect,
sub_slice_index,
targets,
)
}
VisibilityState::PassThrough => {
true
@ -349,31 +323,6 @@ impl SurfaceBuilder {
}
}
// Push a primitive to the current cmd buffer target(s)
pub fn push_prim(
&mut self,
prim_cmd: &PrimitiveCommand,
spatial_node_index: SpatialNodeIndex,
vis: &PrimitiveVisibility,
cmd_buffers: &mut CommandBufferList,
) {
match vis.state {
VisibilityState::Unset => {
panic!("bug: invalid vis state");
}
VisibilityState::Visible { tile_rect, sub_slice_index, .. } => {
self.current_cmd_buffers.push_prim(
prim_cmd,
spatial_node_index,
tile_rect,
sub_slice_index,
cmd_buffers,
)
}
VisibilityState::PassThrough | VisibilityState::Culled => {}
}
}
// Finish adding primitives and child tasks to a surface and pop it off the stack
pub fn pop_surface(
&mut self,
@ -381,8 +330,6 @@ impl SurfaceBuilder {
rg_builder: &mut RenderTaskGraphBuilder,
cmd_buffers: &mut CommandBufferList,
) {
self.dirty_rect_stack.pop().unwrap();
let builder = self.builder_stack.pop().unwrap();
if builder.establishes_sub_graph {
@ -391,7 +338,7 @@ impl SurfaceBuilder {
CommandBufferBuilderKind::Tiled { .. } | CommandBufferBuilderKind::Invalid => {
unreachable!("bug: sub-graphs can only be simple surfaces");
}
CommandBufferBuilderKind::Simple { render_task_id: child_render_task_id, root_task_id: child_root_task_id } => {
CommandBufferBuilderKind::Simple { render_task_id: child_render_task_id, root_task_id: child_root_task_id, .. } => {
// Get info about the resolve operation to copy from parent surface or tiles to the picture cache task
if let Some(resolve_task_id) = builder.resolve_source {
let mut src_task_ids = Vec::new();
@ -601,7 +548,7 @@ impl SurfaceBuilder {
}
}
}
CommandBufferBuilderKind::Simple { render_task_id: child_task_id, root_task_id: child_root_task_id } => {
CommandBufferBuilderKind::Simple { render_task_id: child_task_id, root_task_id: child_root_task_id, .. } => {
match self.builder_stack.last().unwrap().kind {
CommandBufferBuilderKind::Tiled { ref tiles } => {
// For a tiled render task, add as a dependency to every tile.

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

@ -17,7 +17,7 @@ use crate::clip::{ClipChainInstance, ClipTree};
use crate::frame_builder::FrameBuilderConfig;
use crate::gpu_cache::GpuCache;
use crate::picture::{PictureCompositeMode, ClusterFlags, SurfaceInfo, TileCacheInstance};
use crate::picture::{SurfaceIndex, RasterConfig, TileRect, SubSliceIndex};
use crate::picture::{SurfaceIndex, RasterConfig, SubSliceIndex};
use crate::prim_store::{ClipTaskIndex, PictureIndex, PrimitiveInstanceKind};
use crate::prim_store::{PrimitiveStore, PrimitiveInstance};
use crate::render_backend::{DataStores, ScratchBuffer};
@ -94,9 +94,6 @@ pub enum VisibilityState {
/// during batching of visible primitives.
vis_flags: PrimitiveVisibilityFlags,
/// Tiles that this primitive intersects with
tile_rect: TileRect,
/// Sub-slice within the picture cache that this prim exists on
sub_slice_index: SubSliceIndex,
},

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

@ -0,0 +1,11 @@
---
root:
items:
- image: checkerboard(2,16,16)
bounds: [50, 50, 262, 262]
- type: stacking-context
filters: [opacity(0.5)]
items:
- type: rect
bounds: [100, 100, 162, 162]
color: green

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

@ -0,0 +1,15 @@
# Verify that an off-screen surface that is placed over top
# of a compositor surface selects the correct sub-slice when
# adding to command buffers.
---
root:
items:
- image: checkerboard(2,16,16)
bounds: [50, 50, 262, 262]
prefer-compositor-surface: true
- type: stacking-context
filters: [opacity(0.5)]
items:
- type: rect
bounds: [100, 100, 162, 162]
color: green

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

@ -3,3 +3,4 @@ skip_on(android) fuzzy(2,500) == basic.yaml basic-ref.yaml
!= picture-passthrough.yaml blank.yaml
fuzzy(2,1000) == mix-blend.yaml mix-blend-ref.yaml
!= coord-systems.yaml blank.yaml
fuzzy(2,2500) == filter-overlay.yaml filter-overlay-ref.yaml