Fix viewport culling with nested layers (#939)

* Fix viewport culling with nested layers

* Clean up naming
This commit is contained in:
0HyperCube
2023-01-02 15:27:11 +00:00
committed by Keavon Chambers
parent df44e8d978
commit 7cc1a192cb
6 changed files with 41 additions and 19 deletions
+6 -3
View File
@@ -6,7 +6,6 @@ use crate::{DocumentError, LayerId};
use glam::DVec2; use glam::DVec2;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Write;
/// A layer that encapsulates other layers, including potentially more folders. /// A layer that encapsulates other layers, including potentially more folders.
/// The contained layers are rendered in the same order they are /// The contained layers are rendered in the same order they are
@@ -22,10 +21,14 @@ pub struct FolderLayer {
} }
impl LayerData for FolderLayer { impl LayerData for FolderLayer {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) { fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool {
let mut any_child_requires_refresh = false;
for layer in &mut self.layers { for layer in &mut self.layers {
let _ = writeln!(svg, "{}", layer.render(transforms, svg_defs, render_data)); let (svg_value, requires_refresh) = layer.render(transforms, svg_defs, render_data);
*svg += svg_value;
any_child_requires_refresh = any_child_requires_refresh || requires_refresh;
} }
!any_child_requires_refresh
} }
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) { fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
+4 -2
View File
@@ -23,13 +23,13 @@ pub struct ImageLayer {
} }
impl LayerData for ImageLayer { impl LayerData for ImageLayer {
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) { fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
let transform = self.transform(transforms, render_data.view_mode); let transform = self.transform(transforms, render_data.view_mode);
let inverse = transform.inverse(); let inverse = transform.inverse();
if !inverse.is_finite() { if !inverse.is_finite() {
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->"); let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
return; return false;
} }
let _ = writeln!(svg, r#"<g transform="matrix("#); let _ = writeln!(svg, r#"<g transform="matrix("#);
@@ -53,6 +53,8 @@ impl LayerData for ImageLayer {
self.blob_url.as_ref().unwrap_or(&String::new()) self.blob_url.as_ref().unwrap_or(&String::new())
); );
let _ = svg.write_str("</g>"); let _ = svg.write_str("</g>");
false
} }
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> { fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
+19 -8
View File
@@ -119,7 +119,7 @@ impl<'a> TryFrom<&'a Layer> for &'a Subpath {
/// Defines shared behavior for every layer type. /// Defines shared behavior for every layer type.
pub trait LayerData { pub trait LayerData {
/// Render the layer as an SVG tag to a given string. /// Render the layer as an SVG tag to a given string, returning a boolean to indicate if a redraw is required next frame.
/// ///
/// # Example /// # Example
/// ``` /// ```
@@ -143,7 +143,7 @@ pub trait LayerData {
/// </g>" /// </g>"
/// ); /// );
/// ``` /// ```
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData); fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool;
/// Determine the layers within this layer that intersect a given quad. /// Determine the layers within this layer that intersect a given quad.
/// # Example /// # Example
@@ -190,7 +190,7 @@ pub trait LayerData {
} }
impl LayerData for LayerDataType { impl LayerData for LayerDataType {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) { fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool {
self.inner_mut().render(svg, svg_defs, transforms, render_data) self.inner_mut().render(svg, svg_defs, transforms, render_data)
} }
@@ -304,12 +304,15 @@ impl Layer {
LayerIter { stack: vec![self] } LayerIter { stack: vec![self] }
} }
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, svg_defs: &mut String, render_data: RenderData) -> &str { /// Renders the layer, returning the result and if a redraw is required
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, svg_defs: &mut String, render_data: RenderData) -> (&str, bool) {
if !self.visible { if !self.visible {
return ""; return ("", false);
} }
transforms.push(self.transform); transforms.push(self.transform);
// Skip rendering if outside the viewport bounds
if let Some(viewport_bounds) = render_data.culling_bounds { if let Some(viewport_bounds) = render_data.culling_bounds {
if let Some(bounding_box) = self if let Some(bounding_box) = self
.data .data
@@ -321,15 +324,17 @@ impl Layer {
transforms.pop(); transforms.pop();
self.cache.clear(); self.cache.clear();
self.cache_dirty = true; self.cache_dirty = true;
return ""; return ("", true);
} }
} }
} }
let mut requires_redraw = false;
if self.cache_dirty { if self.cache_dirty {
self.thumbnail_cache.clear(); self.thumbnail_cache.clear();
self.svg_defs_cache.clear(); self.svg_defs_cache.clear();
self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, render_data); requires_redraw = self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, render_data);
self.cache.clear(); self.cache.clear();
let _ = writeln!(self.cache, r#"<g transform="matrix("#); let _ = writeln!(self.cache, r#"<g transform="matrix("#);
@@ -346,10 +351,16 @@ impl Layer {
self.cache_dirty = false; self.cache_dirty = false;
} }
transforms.pop(); transforms.pop();
svg_defs.push_str(&self.svg_defs_cache); svg_defs.push_str(&self.svg_defs_cache);
self.cache.as_str() // If a redraw is required then set the cache to dirty.
if requires_redraw {
self.cache_dirty = true;
}
(self.cache.as_str(), requires_redraw)
} }
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) { pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
@@ -33,7 +33,7 @@ pub struct ImageData {
} }
impl LayerData for NodeGraphFrameLayer { impl LayerData for NodeGraphFrameLayer {
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) { fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
let transform = self.transform(transforms, render_data.view_mode); let transform = self.transform(transforms, render_data.view_mode);
let inverse = transform.inverse(); let inverse = transform.inverse();
@@ -41,7 +41,7 @@ impl LayerData for NodeGraphFrameLayer {
if !inverse.is_finite() { if !inverse.is_finite() {
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->"); let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
return; return false;
} }
let _ = writeln!(svg, r#"<g transform="matrix("#); let _ = writeln!(svg, r#"<g transform="matrix("#);
@@ -75,6 +75,8 @@ impl LayerData for NodeGraphFrameLayer {
); );
let _ = svg.write_str(r#"</g>"#); let _ = svg.write_str(r#"</g>"#);
false
} }
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> { fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
+4 -2
View File
@@ -27,7 +27,7 @@ pub struct ShapeLayer {
} }
impl LayerData for ShapeLayer { impl LayerData for ShapeLayer {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) { fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
let mut subpath = self.shape.clone(); let mut subpath = self.shape.clone();
let layer_bounds = subpath.bounding_box().unwrap_or_default(); let layer_bounds = subpath.bounding_box().unwrap_or_default();
@@ -36,7 +36,7 @@ impl LayerData for ShapeLayer {
let inverse = transform.inverse(); let inverse = transform.inverse();
if !inverse.is_finite() { if !inverse.is_finite() {
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->"); let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
return; return false;
} }
subpath.apply_affine(transform); subpath.apply_affine(transform);
@@ -54,6 +54,8 @@ impl LayerData for ShapeLayer {
self.style.render(render_data.view_mode, svg_defs, transform, layer_bounds, transformed_bounds) self.style.render(render_data.view_mode, svg_defs, transform, layer_bounds, transformed_bounds)
); );
let _ = svg.write_str("</g>"); let _ = svg.write_str("</g>");
false
} }
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> { fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
+4 -2
View File
@@ -34,13 +34,13 @@ pub struct TextLayer {
} }
impl LayerData for TextLayer { impl LayerData for TextLayer {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) { fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
let transform = self.transform(transforms, render_data.view_mode); let transform = self.transform(transforms, render_data.view_mode);
let inverse = transform.inverse(); let inverse = transform.inverse();
if !inverse.is_finite() { if !inverse.is_finite() {
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->"); let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
return; return false;
} }
let _ = writeln!(svg, r#"<g transform="matrix("#); let _ = writeln!(svg, r#"<g transform="matrix("#);
@@ -85,6 +85,8 @@ impl LayerData for TextLayer {
); );
} }
let _ = svg.write_str("</g>"); let _ = svg.write_str("</g>");
false
} }
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> { fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {