mirror of https://github.com/penpot/penpot.git
WIP
This commit is contained in:
parent
ee3bf72034
commit
4274db11e3
|
|
@ -98,6 +98,9 @@
|
|||
|
||||
;; This should never be called from the outside.
|
||||
(defn- render
|
||||
([]
|
||||
(render (.now js/performance)))
|
||||
|
||||
([timestamp]
|
||||
(render timestamp false))
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,100 @@ const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 1;
|
|||
const MAX_BLOCKING_TIME_MS: i32 = 32;
|
||||
const NODE_BATCH_THRESHOLD: i32 = 10;
|
||||
|
||||
pub struct PendingNodes {
|
||||
pub list: Vec<NodeRenderState>,
|
||||
}
|
||||
|
||||
impl PendingNodes {
|
||||
pub fn new_empty() -> Self {
|
||||
Self { list: vec![] }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.list.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.list.len()
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, valid_ids: Vec<Uuid>) {
|
||||
self.list
|
||||
.extend(valid_ids.into_iter().map(|id| NodeRenderState {
|
||||
id,
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<NodeRenderState> {
|
||||
self.list.pop()
|
||||
}
|
||||
|
||||
pub fn prepare(&mut self, tree: &mut HashMap<Uuid, &mut Shape>) {
|
||||
self.list.clear();
|
||||
if self.list.capacity() < tree.len() {
|
||||
self.list.reserve(tree.len() - self.list.capacity());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_root(&mut self) {
|
||||
self.list.push(NodeRenderState {
|
||||
id: Uuid::nil(),
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_child(
|
||||
&mut self,
|
||||
child_id: &Uuid,
|
||||
children_clip_bounds: Option<(Rect, Option<Corners>, Matrix)>,
|
||||
) {
|
||||
self.list.push(NodeRenderState {
|
||||
id: *child_id,
|
||||
visited_children: false,
|
||||
clip_bounds: children_clip_bounds,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_children_safeguard(&mut self, element: &Shape, mask: bool) {
|
||||
// Set the node as visited_children before processing children
|
||||
self.list.push(NodeRenderState {
|
||||
id: element.id,
|
||||
visited_children: true,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_mask(&mut self, element: &Shape) {
|
||||
self.list.push(NodeRenderState {
|
||||
id: element.id,
|
||||
visited_children: true,
|
||||
clip_bounds: None,
|
||||
visited_mask: true,
|
||||
mask: false,
|
||||
});
|
||||
if let Some(&mask_id) = element.mask_id() {
|
||||
self.list.push(NodeRenderState {
|
||||
id: mask_id,
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NodeRenderState {
|
||||
pub id: Uuid,
|
||||
// We use this bool to keep that we've traversed all the children inside this node.
|
||||
|
|
@ -165,7 +259,7 @@ pub(crate) struct RenderState {
|
|||
pub render_in_progress: bool,
|
||||
pub render_is_full: bool,
|
||||
// Stack of nodes pending to be rendered.
|
||||
pending_nodes: Vec<NodeRenderState>,
|
||||
pending_nodes: PendingNodes,
|
||||
pub current_tile: Option<tiles::Tile>,
|
||||
pub sampling_options: skia::SamplingOptions,
|
||||
pub render_area: Rect,
|
||||
|
|
@ -236,7 +330,7 @@ impl RenderState {
|
|||
render_request_id: None,
|
||||
render_in_progress: false,
|
||||
render_is_full: false,
|
||||
pending_nodes: vec![],
|
||||
pending_nodes: PendingNodes::new_empty(),
|
||||
current_tile: None,
|
||||
sampling_options,
|
||||
render_area: Rect::new_empty(),
|
||||
|
|
@ -565,9 +659,13 @@ impl RenderState {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn update_render_context(&mut self, tile: tiles::Tile) {
|
||||
self.current_tile = Some(tile);
|
||||
self.render_area = tiles::get_tile_rect(tile, self.get_scale());
|
||||
pub fn get_tiles_of(&mut self, id: Uuid) -> Option<&HashSet<tiles::Tile>> {
|
||||
self.tiles.get_tiles_of(id)
|
||||
}
|
||||
|
||||
pub fn update_render_context(&mut self, tile: &tiles::Tile) {
|
||||
self.current_tile = Some(*tile);
|
||||
self.render_area = tiles::get_tile_rect(*tile, self.get_scale());
|
||||
self.surfaces
|
||||
.update_render_context(self.render_area, self.get_scale());
|
||||
}
|
||||
|
|
@ -663,23 +761,13 @@ impl RenderState {
|
|||
self.pending_tiles.update(&self.tile_viewbox);
|
||||
performance::end_measure!("tile_cache");
|
||||
|
||||
self.pending_nodes.clear();
|
||||
if self.pending_nodes.capacity() < tree.len() {
|
||||
self.pending_nodes
|
||||
.reserve(tree.len() - self.pending_nodes.capacity());
|
||||
}
|
||||
self.pending_nodes.prepare(tree);
|
||||
// reorder by distance to the center.
|
||||
self.current_tile = None;
|
||||
self.render_in_progress = true;
|
||||
self.render_is_full = full;
|
||||
if self.render_is_full {
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: Uuid::nil(),
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
});
|
||||
self.pending_nodes.add_root();
|
||||
}
|
||||
self.apply_drawing_to_render_canvas(None);
|
||||
self.process_animation_frame(tree, modifiers, structure, scale_content, timestamp)?;
|
||||
|
|
@ -799,22 +887,7 @@ impl RenderState {
|
|||
// the blend mode 'destination-in') the content
|
||||
// of the group and the mask.
|
||||
if group.masked {
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: element.id,
|
||||
visited_children: true,
|
||||
clip_bounds: None,
|
||||
visited_mask: true,
|
||||
mask: false,
|
||||
});
|
||||
if let Some(&mask_id) = element.mask_id() {
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: mask_id,
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: true,
|
||||
});
|
||||
}
|
||||
self.pending_nodes.add_mask(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -923,6 +996,66 @@ impl RenderState {
|
|||
Ok((is_empty, should_stop_rendering))
|
||||
}
|
||||
|
||||
pub fn render_shape_tree_full_uncached_shape_tile(
|
||||
&mut self,
|
||||
element: &mut Shape,
|
||||
node_render_state: &NodeRenderState,
|
||||
tile: &tiles::Tile,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
_structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
scale_content: &HashMap<Uuid, f32>,
|
||||
) {
|
||||
let NodeRenderState {
|
||||
id: _node_id,
|
||||
visited_children: _visited_children,
|
||||
clip_bounds,
|
||||
visited_mask: _visited_mask,
|
||||
mask,
|
||||
} = *node_render_state;
|
||||
|
||||
self.update_render_context(tile);
|
||||
|
||||
self.render_shape_enter(element, mask);
|
||||
if !node_render_state.id.is_nil() {
|
||||
self.render_shape(
|
||||
element,
|
||||
modifiers.get(&element.id),
|
||||
scale_content.get(&element.id),
|
||||
clip_bounds,
|
||||
);
|
||||
} else {
|
||||
self.apply_drawing_to_render_canvas(Some(element));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_shape_tree_full_uncached_shape_tiles(
|
||||
&mut self,
|
||||
element: &mut Shape,
|
||||
node_render_state: &NodeRenderState,
|
||||
tiles: &HashSet<tiles::Tile>,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
scale_content: &HashMap<Uuid, f32>,
|
||||
) {
|
||||
// We need to iterate over every tile of the element.
|
||||
for tile in tiles.iter() {
|
||||
println!(
|
||||
"render_shape_tree_full_uncached::for::start {} {}",
|
||||
tile.0, tile.1
|
||||
);
|
||||
self.render_shape_tree_full_uncached_shape_tile(
|
||||
element,
|
||||
node_render_state,
|
||||
tile,
|
||||
modifiers,
|
||||
structure,
|
||||
scale_content,
|
||||
);
|
||||
|
||||
println!("render_shape_tree_full_uncached::for::end");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_shape_tree_full_uncached(
|
||||
&mut self,
|
||||
tree: &mut HashMap<Uuid, &mut Shape>,
|
||||
|
|
@ -934,7 +1067,7 @@ impl RenderState {
|
|||
let mut iteration = 0;
|
||||
let mut is_empty = true;
|
||||
println!("render_shape_tree_full_uncached::while::start");
|
||||
while let Some(node_render_state) = self.pending_nodes.pop() {
|
||||
while let Some(node_render_state) = self.pending_nodes.next() {
|
||||
println!(
|
||||
"render_shape_tree_full_uncached::while::pop {}",
|
||||
self.pending_nodes.len()
|
||||
|
|
@ -942,7 +1075,7 @@ impl RenderState {
|
|||
let NodeRenderState {
|
||||
id: node_id,
|
||||
visited_children,
|
||||
clip_bounds,
|
||||
clip_bounds: _clip_bounds,
|
||||
visited_mask,
|
||||
mask,
|
||||
} = node_render_state;
|
||||
|
|
@ -953,94 +1086,60 @@ impl RenderState {
|
|||
.to_string(),
|
||||
)?;
|
||||
|
||||
if !node_render_state.id.is_nil() {
|
||||
let mut transformed_element: Cow<Shape> = Cow::Borrowed(element);
|
||||
|
||||
if let Some(modifier) = modifiers.get(&node_id) {
|
||||
transformed_element.to_mut().apply_transform(modifier);
|
||||
}
|
||||
|
||||
let is_visible = transformed_element.extrect().intersects(self.render_area)
|
||||
&& !transformed_element.hidden
|
||||
&& !transformed_element.visually_insignificant(self.get_scale_mut());
|
||||
|
||||
if self.options.is_debug_visible() {
|
||||
debug::render_debug_shape(self, &transformed_element, is_visible);
|
||||
}
|
||||
|
||||
if !is_visible {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the shape is not in the tile set, then we update
|
||||
// it.
|
||||
if self.tiles.get_tiles_of(node_id).is_none() {
|
||||
if self.get_tiles_of(node_id).is_none() {
|
||||
self.update_tile_for(element);
|
||||
}
|
||||
|
||||
let tiles = self.tiles.get_tiles_of(node_id).as_mut().unwrap();
|
||||
// We need to iterate over every tile of the element.
|
||||
for tile in tiles.iter() {
|
||||
println!(
|
||||
"render_shape_tree_full_uncached::for::start {} {}",
|
||||
tile.0, tile.1
|
||||
);
|
||||
self.update_render_context(*tile);
|
||||
if visited_children {
|
||||
if !visited_mask {
|
||||
if let Type::Group(group) = element.shape_type {
|
||||
// When we're dealing with masked groups we need to
|
||||
// do a separate extra step to draw the mask (the last
|
||||
// element of a masked group) and blend (using
|
||||
// the blend mode 'destination-in') the content
|
||||
// of the group and the mask.
|
||||
if group.masked {
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: node_id,
|
||||
visited_children: true,
|
||||
clip_bounds: None,
|
||||
visited_mask: true,
|
||||
mask: false,
|
||||
});
|
||||
if let Some(&mask_id) = element.mask_id() {
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: mask_id,
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
let mut tiles_opt = self.get_tiles_of(element.id);
|
||||
let tiles = tiles_opt.as_mut().unwrap().clone();
|
||||
self.render_shape_tree_full_uncached_shape_tiles(
|
||||
element,
|
||||
&node_render_state,
|
||||
&tiles,
|
||||
modifiers,
|
||||
structure,
|
||||
scale_content,
|
||||
);
|
||||
if visited_children {
|
||||
if !visited_mask {
|
||||
if let Type::Group(group) = element.shape_type {
|
||||
// When we're dealing with masked groups we need to
|
||||
// do a separate extra step to draw the mask (the last
|
||||
// element of a masked group) and blend (using
|
||||
// the blend mode 'destination-in') the content
|
||||
// of the group and the mask.
|
||||
if group.masked {
|
||||
self.pending_nodes.add_mask(element);
|
||||
}
|
||||
}
|
||||
self.render_shape_exit(element, visited_mask);
|
||||
continue;
|
||||
}
|
||||
|
||||
if !node_render_state.id.is_nil() {
|
||||
let mut transformed_element: Cow<Shape> = Cow::Borrowed(element);
|
||||
|
||||
if let Some(modifier) = modifiers.get(&node_id) {
|
||||
transformed_element.to_mut().apply_transform(modifier);
|
||||
}
|
||||
|
||||
let is_visible = transformed_element.extrect().intersects(self.render_area)
|
||||
&& !transformed_element.hidden
|
||||
&& !transformed_element.visually_insignificant(self.get_scale_mut());
|
||||
|
||||
if self.options.is_debug_visible() {
|
||||
debug::render_debug_shape(self, &transformed_element, is_visible);
|
||||
}
|
||||
|
||||
if !is_visible {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
self.render_shape_enter(element, mask);
|
||||
if !node_render_state.id.is_nil() {
|
||||
self.render_shape(
|
||||
element,
|
||||
modifiers.get(&element.id),
|
||||
scale_content.get(&element.id),
|
||||
clip_bounds,
|
||||
);
|
||||
} else {
|
||||
self.apply_drawing_to_render_canvas(Some(element));
|
||||
}
|
||||
println!("render_shape_tree_full_uncached::for::end");
|
||||
self.render_shape_exit(element, visited_mask);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the node as visited_children before processing children
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: node_id,
|
||||
visited_children: true,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask,
|
||||
});
|
||||
self.pending_nodes.add_children_safeguard(element, mask);
|
||||
|
||||
if element.is_recursive() {
|
||||
let children_clip_bounds =
|
||||
|
|
@ -1058,13 +1157,7 @@ impl RenderState {
|
|||
}
|
||||
|
||||
for child_id in children_ids.iter() {
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: *child_id,
|
||||
visited_children: false,
|
||||
clip_bounds: children_clip_bounds,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
});
|
||||
self.pending_nodes.add_child(child_id, children_clip_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1089,7 +1182,7 @@ impl RenderState {
|
|||
let mut iteration = 0;
|
||||
let mut is_empty = true;
|
||||
let scale = self.get_scale();
|
||||
while let Some(node_render_state) = self.pending_nodes.pop() {
|
||||
while let Some(node_render_state) = self.pending_nodes.next() {
|
||||
let NodeRenderState {
|
||||
id: node_id,
|
||||
visited_children,
|
||||
|
|
@ -1152,13 +1245,7 @@ impl RenderState {
|
|||
}
|
||||
|
||||
// Set the node as visited_children before processing children
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: node_id,
|
||||
visited_children: true,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask,
|
||||
});
|
||||
self.pending_nodes.add_children_safeguard(element, mask);
|
||||
|
||||
if element.is_recursive() {
|
||||
let children_clip_bounds =
|
||||
|
|
@ -1177,13 +1264,7 @@ impl RenderState {
|
|||
}
|
||||
|
||||
for child_id in children_ids.iter() {
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: *child_id,
|
||||
visited_children: false,
|
||||
clip_bounds: children_clip_bounds,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
});
|
||||
self.pending_nodes.add_child(child_id, children_clip_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1264,7 +1345,7 @@ impl RenderState {
|
|||
// If we finish processing every node rendering is complete
|
||||
// let's check if there are more pending nodes
|
||||
if let Some(next_tile) = self.pending_tiles.pop() {
|
||||
self.update_render_context(next_tile);
|
||||
self.update_render_context(&next_tile);
|
||||
|
||||
if !self.surfaces.has_cached_tile_surface(next_tile) {
|
||||
if let Some(ids) = self.tiles.get_shapes_at(next_tile) {
|
||||
|
|
@ -1273,19 +1354,9 @@ impl RenderState {
|
|||
.iter()
|
||||
.filter_map(|id| root_ids.get(id).map(|_| *id))
|
||||
.collect();
|
||||
|
||||
// These shapes for the tile should be ordered as they are in the parent node
|
||||
valid_ids.sort_by_key(|id| root_ids.get_index_of(id));
|
||||
|
||||
self.pending_nodes.extend(valid_ids.into_iter().map(|id| {
|
||||
NodeRenderState {
|
||||
id,
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
}
|
||||
}));
|
||||
self.pending_nodes.extend(valid_ids);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue