/* 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, pub surface_index: Option, pub parent: Option, } /// A graph of picture dependencies, allowing updates to be processed without recursion /// by building a list of passes. pub struct PictureGraph { roots: Vec, pic_info: Vec, update_passes: Vec>, } 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, tile_caches: &mut FastHashMap>, 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, 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, ); } } }