summaryrefslogtreecommitdiffstats
path: root/gfx/wr/webrender/src/picture_graph.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/webrender/src/picture_graph.rs')
-rw-r--r--gfx/wr/webrender/src/picture_graph.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/gfx/wr/webrender/src/picture_graph.rs b/gfx/wr/webrender/src/picture_graph.rs
new file mode 100644
index 0000000000..1446784ca6
--- /dev/null
+++ b/gfx/wr/webrender/src/picture_graph.rs
@@ -0,0 +1,212 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 crate::frame_builder::FrameBuildingContext;
+use crate::internal_types::FastHashMap;
+use crate::prim_store::PictureIndex;
+use crate::picture::{PicturePrimitive, SurfaceIndex, SurfaceInfo};
+use crate::picture::{TileCacheInstance, SliceId};
+use smallvec::SmallVec;
+
+#[derive(Debug)]
+pub struct PictureInfo {
+ pub update_pass: Option<usize>,
+ pub surface_index: Option<SurfaceIndex>,
+ pub parent: Option<PictureIndex>,
+}
+
+/// A graph of picture dependencies, allowing updates to be processed without recursion
+/// by building a list of passes.
+pub struct PictureGraph {
+ roots: Vec<PictureIndex>,
+ pic_info: Vec<PictureInfo>,
+ update_passes: Vec<Vec<PictureIndex>>,
+}
+
+impl PictureGraph {
+ pub fn new() -> Self {
+ PictureGraph {
+ roots: Vec::new(),
+ pic_info: Vec::new(),
+ update_passes: Vec::new(),
+ }
+ }
+
+ /// Add a root picture to the graph
+ pub fn add_root(
+ &mut self,
+ pic_index: PictureIndex,
+ ) {
+ self.roots.push(pic_index);
+ }
+
+ /// Build a list of update passes based on the dependencies between pictures
+ pub fn build_update_passes(
+ &mut self,
+ pictures: &mut [PicturePrimitive],
+ frame_context: &FrameBuildingContext
+ ) {
+ self.pic_info.clear();
+ self.pic_info.reserve(pictures.len());
+
+ for _ in 0 .. pictures.len() {
+ self.pic_info.push(PictureInfo {
+ update_pass: None,
+ parent: None,
+ surface_index: None,
+ })
+ };
+
+ let mut max_pass_index = 0;
+
+ for pic_index in &self.roots {
+ assign_update_pass(
+ *pic_index,
+ None,
+ 0,
+ pictures,
+ &mut self.pic_info,
+ &mut max_pass_index,
+ frame_context,
+ );
+ }
+
+ let pass_count = max_pass_index + 1;
+
+ self.update_passes.clear();
+ self.update_passes.resize_with(pass_count, Vec::new);
+
+ for (pic_index, info) in self.pic_info.iter().enumerate() {
+ if let Some(update_pass) = info.update_pass {
+ let pass = &mut self.update_passes[update_pass];
+ pass.push(PictureIndex(pic_index));
+ }
+ }
+ }
+
+ /// Assign surfaces and scale factors to each picture (root -> leaf ordered pass)
+ pub fn assign_surfaces(
+ &mut self,
+ pictures: &mut [PicturePrimitive],
+ surfaces: &mut Vec<SurfaceInfo>,
+ tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
+ frame_context: &FrameBuildingContext,
+ ) {
+ for pass in &self.update_passes {
+ for pic_index in pass {
+ let parent = self.pic_info[pic_index.0].parent;
+
+ let parent_surface_index = parent.map(|parent| {
+ // Can unwrap here as by the time we have a parent that parent's
+ // surface must have been assigned.
+ self.pic_info[parent.0].surface_index.unwrap()
+ });
+
+ let info = &mut self.pic_info[pic_index.0];
+
+ match pictures[pic_index.0].assign_surface(
+ frame_context,
+ parent_surface_index,
+ tile_caches,
+ surfaces,
+ ) {
+ Some(surface_index) => {
+ info.surface_index = Some(surface_index);
+ }
+ None => {
+ info.surface_index = Some(parent_surface_index.unwrap());
+ }
+ }
+ }
+ }
+ }
+
+ /// Propegate bounding rects from pictures to parents (leaf -> root ordered pass)
+ pub fn propagate_bounding_rects(
+ &mut self,
+ pictures: &mut [PicturePrimitive],
+ surfaces: &mut [SurfaceInfo],
+ frame_context: &FrameBuildingContext,
+ ) {
+ for pass in self.update_passes.iter().rev() {
+ for pic_index in pass {
+ let parent = self.pic_info[pic_index.0].parent;
+
+ let surface_index = self.pic_info[pic_index.0]
+ .surface_index
+ .expect("bug: no surface assigned during propagate_bounding_rects");
+
+ let parent_surface_index = parent.map(|parent| {
+ // Can unwrap here as by the time we have a parent that parent's
+ // surface must have been assigned.
+ self.pic_info[parent.0].surface_index.unwrap()
+ });
+
+ pictures[pic_index.0].propagate_bounding_rect(
+ surface_index,
+ parent_surface_index,
+ surfaces,
+ frame_context,
+ );
+ }
+ }
+ }
+}
+
+/// Recursive function that assigns pictures to the earliest pass possible that they
+/// can be processed in, while maintaining dependency ordering.
+fn assign_update_pass(
+ pic_index: PictureIndex,
+ parent_pic_index: Option<PictureIndex>,
+ pass: usize,
+ pictures: &mut [PicturePrimitive],
+ pic_info: &mut [PictureInfo],
+ max_pass_index: &mut usize,
+ frame_context: &FrameBuildingContext
+) {
+ let pic = &mut pictures[pic_index.0];
+ let info = &mut pic_info[pic_index.0];
+
+ info.parent = parent_pic_index;
+
+ // Run pre-update to resolve animation properties etc
+ pic.pre_update(frame_context);
+
+ let can_be_drawn = match info.update_pass {
+ Some(update_pass) => {
+ // No point in recursing into paths in the graph if this picture already
+ // has been set to update after this pass.
+ if update_pass > pass {
+ return;
+ }
+
+ true
+ }
+ None => {
+ // Check if this picture can be dropped from the graph we're building this frame
+ pic.is_visible(frame_context.spatial_tree)
+ }
+ };
+
+ if can_be_drawn {
+ info.update_pass = Some(pass);
+
+ *max_pass_index = pass.max(*max_pass_index);
+
+ let mut child_pictures: SmallVec<[PictureIndex; 8]> = SmallVec::new();
+ child_pictures.extend_from_slice(&pic.prim_list.child_pictures);
+
+ for child_pic_index in child_pictures {
+ assign_update_pass(
+ child_pic_index,
+ Some(pic_index),
+ pass + 1,
+ pictures,
+ pic_info,
+ max_pass_index,
+ frame_context,
+ );
+ }
+ }
+}