/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! # Visibility pass //! //! TODO: document what this pass does! //!
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))] #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] pubstruct PrimitiveVisibilityFlags: u8 { /// 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;
}
}
/// Contains the current state of the primitive's visibility. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] pubenum DrawState { /// Uninitialized - this should never be encountered after prim reset
Unset, /// Culled for being off-screen, or not possible to render (e.g. missing image resource)
Culled, /// A picture that doesn't have a surface - primitives are composed into the /// parent picture with a surface.
PassThrough, /// A primitive that has been found to be visible
Visible { /// A set of flags that define how this primitive should be handled /// during batching of visible primitives.
vis_flags: PrimitiveVisibilityFlags,
/// Sub-slice within the picture cache that this prim exists on
sub_slice_index: SubSliceIndex,
},
}
/// Per-draw, per-kind scratch handle. Reaches the appropriate /// per-frame scratch entry for the drawn primitive's kind. The variant /// matches the prim's PrimitiveKind. None for kinds without per-frame /// scratch. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] pubenum KindScratchHandle {
None,
NormalBorder(storage::Index<NormalBorderScratch>),
ImageBorder(storage::Index<ImageBorderScratch>),
Image(storage::Index<ImageScratch>),
TextRun(storage::Index<TextRunScratch>),
Picture(storage::Index<PictureScratch>),
BackdropRender(storage::Index<BackdropRenderScratch>),
}
impl KindScratchHandle { /// Extract the specific scratch index. Panics if the variant /// doesn't match — readers in the specific arm of the /// PrimitiveKind match know the variant by construction. pubfn unwrap_normal_border(&self) -> storage::Index<NormalBorderScratch> { match *self {
KindScratchHandle::NormalBorder(h) => h,
_ => panic!("kind_scratch mismatch: expected NormalBorder, got {:?}", self),
}
} pubfn unwrap_image_border(&self) -> storage::Index<ImageBorderScratch> { match *self {
KindScratchHandle::ImageBorder(h) => h,
_ => panic!("kind_scratch mismatch: expected ImageBorder, got {:?}", self),
}
} pubfn unwrap_image(&self) -> storage::Index<ImageScratch> { match *self {
KindScratchHandle::Image(h) => h,
_ => panic!("kind_scratch mismatch: expected Image, got {:?}", self),
}
} pubfn unwrap_text_run(&self) -> storage::Index<TextRunScratch> { match *self {
KindScratchHandle::TextRun(h) => h,
_ => panic!("kind_scratch mismatch: expected TextRun, got {:?}", self),
}
} pubfn unwrap_picture(&self) -> storage::Index<PictureScratch> { match *self {
KindScratchHandle::Picture(h) => h,
_ => panic!("kind_scratch mismatch: expected Picture, got {:?}", self),
}
} pubfn unwrap_backdrop_render(&self) -> storage::Index<BackdropRenderScratch> { match *self {
KindScratchHandle::BackdropRender(h) => h,
_ => panic!("kind_scratch mismatch: expected BackdropRender, got {:?}", self),
}
}
}
/// Information stored for a visible primitive about the visible /// rect and associated clip information. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] pubstruct PrimitiveDrawHeader { /// Back-reference to the prim instance this draw belongs to. /// Currently redundant with the identity-indexed lookup from /// `scratch.frame.draws[PrimitiveInstanceIndex.0]`, but reserved /// for a follow-up that switches the storage to push-per-draw — /// readers iterating draws directly will need this to reach the /// instance. pub prim_instance_index: PrimitiveInstanceIndex,
/// The clip chain instance that was built for this primitive. pub clip_chain: ClipChainInstance,
/// Current visibility state of the primitive. // TODO(gw): Move more of the fields from this struct into // the state enum. pub state: DrawState,
/// An index into the clip task instances array in the primitive /// store. If this is ClipTaskIndex::INVALID, then the primitive /// has no clip mask. Otherwise, it may store the offset of the /// global clip mask task for this primitive, or the first of /// a list of clip task ids (one per segment). pub clip_task_index: ClipTaskIndex,
/// Per-kind scratch handle for this draw. Variant matches the /// drawn prim's `PrimitiveKind`; `None` for kinds without per- /// frame scratch (e.g. ImageBorder, gradients, BackdropCapture, /// BoxShadow, Rectangle/YuvImage). pub kind_scratch: KindScratchHandle,
/// Index into PrimitiveFrameScratch.segment_instances for prims /// that opt into segmented brush rendering (Rectangle, YuvImage, /// non-tiled Image). UNUSED for prims that don't segment, or for /// the trivial single-segment case. Built fresh each frame in /// build_segments_if_needed. pub segment_instance_index: SegmentInstanceIndex,
/// Per-frame compositing decision for Image / YuvImage primitives. /// Set during the visibility pass by tile-cache promotion logic; /// `Blit` for kinds that aren't candidates for compositor surfaces /// or for draws that didn't get promoted this frame. pub compositor_surface_kind: CompositorSurfaceKind,
/// Local-space rect of the primitive after device-pixel snapping has /// been applied. Populated for every prim each frame by the visibility /// pass (snapping `PrimitiveInstance.unsnapped_prim_rect` against the /// surface raster node) before any visibility / prepare consumer reads it. pub snapped_local_rect: LayoutRect,
}
impl PrimitiveDrawHeader { /// Allocate a fresh draw header. `snapped_local_rect` is left at zero /// here; the per-frame snap pass overwrites it before any consumer runs. pubfn new() -> Self {
PrimitiveDrawHeader {
prim_instance_index: PrimitiveInstanceIndex::INVALID,
state: DrawState::Unset,
clip_chain: ClipChainInstance::empty(),
clip_task_index: ClipTaskIndex::INVALID,
kind_scratch: KindScratchHandle::None,
segment_instance_index: SegmentInstanceIndex::UNUSED,
compositor_surface_kind: CompositorSurfaceKind::Blit,
snapped_local_rect: LayoutRect::zero(),
}
}
let surface_local_rect = frame_state.surfaces[raster_config.surface_index.0]
.unclipped_local_rect
.cast_unit();
// Let the picture cache know that we are pushing an off-screen // surface, so it can treat dependencies of surface atomically. iflet Some(tile_cache) = tile_cache {
tile_cache.push_surface(
surface_local_rect,
pic.spatial_node_index,
frame_context.spatial_tree,
);
}
(raster_config.surface_index, true)
}
None => {
(parent_surface_index.expect("bug: pass-through with no parent"), false)
}
};
let surface = &frame_state.surfaces[surface_index.0as usize]; let surface_culling_rect = surface.culling_rect;
let map_surface_to_vis = SpaceMapper::new_with_target( // TODO: switch from root to raster space.
frame_context.root_spatial_node_index,
surface.surface_spatial_node_index,
surface.culling_rect,
frame_context.spatial_tree,
); let visibility_spatial_node_index = surface.visibility_spatial_node_index;
// Snappers into this surface's raster space (the space its content is // rasterized in), reused across all clusters/prims in this surface (and a // no-op for surfaces that don't snap). `snapper` is re-targeted once per // cluster and snaps prim/clip-leaf rects (all prims in a cluster share its // spatial node, so it stays a cache hit); `clip_snapper` snaps the per-prim // clip chain. letmut snapper = SpaceSnapper::new(surface, frame_context.spatial_tree); letmut clip_snapper = snapper.clone();
for cluster in &pic.prim_list.clusters {
profile_scope!("cluster");
// Each prim instance must have reset called each frame, to clear // indices into various scratch buffers. If this doesn't occur, // the primitive may incorrectly be considered visible, which can // cause unexpected conditions to occur later during the frame. // Primitive instances are normally reset in the main loop below, // but we must also reset them in the rare case that the cluster // visibility has changed (due to an invalid transform and/or // backface visibility changing for this cluster). // TODO(gw): This is difficult to test for in CI - as a follow up, // we should add a debug flag that validates the prim // instance is always reset every frame to catch similar // issues in future. for idx in cluster.prim_range() {
frame_state.scratch.primitive.frame.draws[idx].reset();
frame_state.scratch.primitive.frame.draws[idx].prim_instance_index =
PrimitiveInstanceIndex(idx as u32);
}
// Get the cluster and see if is visible if !cluster.flags.contains(ClusterFlags::IS_VISIBLE) { continue;
}
// Snap each prim's rect and clip-leaf rect from this cluster's // spatial-node space into the surface's raster space, before any // visibility / prepare / batch consumer reads them.
snapper.set_target_spatial_node(cluster.spatial_node_index, frame_context.spatial_tree);
for prim_instance_index in cluster.prim_range() { let snapped_local_rect = snapper.snap_rect(
&frame_state.prim_instances[prim_instance_index].unsnapped_prim_rect,
);
frame_state.scratch.primitive.frame.draws[prim_instance_index].snapped_local_rect =
snapped_local_rect;
// Picture / tile-cache leaves carry `max_rect`; snapping `max_rect` // would overflow through the snap transform, so pass those through. let leaf_id = frame_state.prim_instances[prim_instance_index].clip_leaf_id; let leaf = frame_state.clip_tree.get_leaf_mut(leaf_id); if leaf.unsnapped_local_clip_rect == LayoutRect::max_rect() {
leaf.snapped_local_clip_rect = leaf.unsnapped_local_clip_rect;
} else { let unsnapped = leaf.unsnapped_local_clip_rect;
leaf.snapped_local_clip_rect = snapper.snap_rect(&unsnapped);
}
let is_passthrough = match store.pictures[pic_index.0].raster_config {
Some(..) => false,
None => true,
};
if !is_passthrough { let clip_root = store
.pictures[pic_index.0]
.clip_root
.unwrap_or_else(|| { // If we couldn't find a common ancestor then just use the // clip node of the picture primitive itself let leaf_id = frame_state.prim_instances[prim_instance_index].clip_leaf_id;
frame_state.clip_tree.get_leaf(leaf_id).node_id
}
);
if is_passthrough { // Pass through pictures are always considered visible in all dirty tiles.
frame_state.scratch.primitive.frame.draws[prim_instance_index].state = DrawState::PassThrough;
{ let prim_surface_index = frame_state.surface_stack.last().unwrap().1; let prim_clip_chain = &frame_state.scratch.primitive.frame.draws[prim_instance_index].clip_chain;
// Accumulate the exact (clipped) local rect into the parent surface. let surface = &mut frame_state.surfaces[prim_surface_index.0];
surface.clipped_local_rect = surface.clipped_local_rect.union(&prim_clip_chain.pic_coverage_rect);
}
let new_state = match tile_cache {
Some(tile_cache) => {
tile_cache.update_prim_dependencies(
PrimitiveInstanceIndex(prim_instance_index as u32),
prim_instance,
cluster.spatial_node_index, // It's OK to pass the local_coverage_rect here as it's only // used by primitives (for compositor surfaces) that don't // have inflation anyway.
local_coverage_rect,
frame_context,
frame_state.data_stores,
frame_state.clip_store,
&store.pictures,
frame_state.resource_cache,
&frame_state.surface_stack,
&mut frame_state.composite_state,
&mut frame_state.frame_gpu_data.f32,
&mut frame_state.scratch.primitive,
is_root_tile_cache,
frame_state.surfaces,
frame_state.profile,
)
}
None => {
DrawState::Visible {
vis_flags: PrimitiveVisibilityFlags::empty(),
sub_slice_index: SubSliceIndex::DEFAULT,
}
}
};
frame_state.scratch.primitive.frame.draws[prim_instance_index].state = new_state;
}
}
iflet Some(snapshot) = &pic.snapshot { if snapshot.detached { // If the snapshot is detached, then the contents of the stacking // context will only be shown via the snapshot, so there is no point // to rendering anything outside of the snapshot area. let prim_surface_index = frame_state.surface_stack.last().unwrap().1; let surface = &mut frame_state.surfaces[prim_surface_index.0]; let clip = snapshot.area.round_out().cast_unit();
surface.clipped_local_rect = surface.clipped_local_rect.intersection_unchecked(&clip);
}
}
if pop_surface {
frame_state.pop_surface();
}
iflet Some(ref rc) = pic.raster_config { iflet Some(tile_cache) = tile_cache { match rc.composite_mode {
PictureCompositeMode::TileCache { .. } => {}
_ => { // Pop the off-screen surface from the picture cache stack
tile_cache.pop_surface();
}
}
}
}
}
pubfn compute_conservative_visible_rect(
clip_chain: &ClipChainInstance,
culling_rect: VisRect,
visibility_node_index: SpatialNodeIndex,
prim_spatial_node_index: SpatialNodeIndex,
spatial_tree: &SpatialTree,
) -> LayoutRect { // Mapping from picture space -> world space let map_pic_to_vis: SpaceMapper<PicturePixel, VisPixel> = SpaceMapper::new_with_target(
visibility_node_index,
clip_chain.pic_spatial_node_index,
culling_rect,
spatial_tree,
);
// Mapping from local space -> picture space let map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target(
clip_chain.pic_spatial_node_index,
prim_spatial_node_index,
PictureRect::max_rect(),
spatial_tree,
);
// Unmap the world culling rect from world -> picture space. If this mapping fails due // to matrix weirdness, best we can do is use the clip chain's local clip rect. let pic_culling_rect = match map_pic_to_vis.unmap(&culling_rect) {
Some(rect) => rect,
None => return clip_chain.local_clip_rect,
};
// Intersect the unmapped world culling rect with the primitive's clip chain rect that // is in picture space (the clip-chain already takes into account the bounds of the // primitive local_rect and local_clip_rect). If there is no intersection here, the // primitive is not visible at all. let pic_culling_rect = match pic_culling_rect.intersection(&clip_chain.pic_coverage_rect) {
Some(rect) => rect,
None => return LayoutRect::zero(),
};
// Unmap the picture culling rect from picture -> local space. If this mapping fails due // to matrix weirdness, best we can do is use the clip chain's local clip rect. match map_local_to_pic.unmap(&pic_culling_rect) {
Some(rect) => rect,
None => clip_chain.local_clip_rect,
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.