From 7cc1a192cb6d31a1b3ff646129be20a2f0219661 Mon Sep 17 00:00:00 2001 From: 0HyperCube <78500760+0HyperCube@users.noreply.github.com> Date: Mon, 2 Jan 2023 15:27:11 +0000 Subject: [PATCH] Fix viewport culling with nested layers (#939) * Fix viewport culling with nested layers * Clean up naming --- document-legacy/src/layers/folder_layer.rs | 9 ++++--- document-legacy/src/layers/image_layer.rs | 6 +++-- document-legacy/src/layers/layer_info.rs | 27 +++++++++++++------ document-legacy/src/layers/nodegraph_layer.rs | 6 +++-- document-legacy/src/layers/shape_layer.rs | 6 +++-- document-legacy/src/layers/text_layer.rs | 6 +++-- 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/document-legacy/src/layers/folder_layer.rs b/document-legacy/src/layers/folder_layer.rs index d3a933fe..f81d7b51 100644 --- a/document-legacy/src/layers/folder_layer.rs +++ b/document-legacy/src/layers/folder_layer.rs @@ -6,7 +6,6 @@ use crate::{DocumentError, LayerId}; use glam::DVec2; use serde::{Deserialize, Serialize}; -use std::fmt::Write; /// A layer that encapsulates other layers, including potentially more folders. /// The contained layers are rendered in the same order they are @@ -22,10 +21,14 @@ pub struct FolderLayer { } impl LayerData for FolderLayer { - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) { + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) -> bool { + let mut any_child_requires_refresh = false; 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, intersections: &mut Vec>, font_cache: &FontCache) { diff --git a/document-legacy/src/layers/image_layer.rs b/document-legacy/src/layers/image_layer.rs index 354ef527..c3c2ccb7 100644 --- a/document-legacy/src/layers/image_layer.rs +++ b/document-legacy/src/layers/image_layer.rs @@ -23,13 +23,13 @@ pub struct ImageLayer { } impl LayerData for ImageLayer { - fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) { + fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) -> bool { let transform = self.transform(transforms, render_data.view_mode); let inverse = transform.inverse(); if !inverse.is_finite() { let _ = write!(svg, ""); - return; + return false; } let _ = writeln!(svg, r#""); + + false } fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> { diff --git a/document-legacy/src/layers/layer_info.rs b/document-legacy/src/layers/layer_info.rs index 51a81a47..e0805f41 100644 --- a/document-legacy/src/layers/layer_info.rs +++ b/document-legacy/src/layers/layer_info.rs @@ -119,7 +119,7 @@ impl<'a> TryFrom<&'a Layer> for &'a Subpath { /// Defines shared behavior for every layer type. 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 /// ``` @@ -143,7 +143,7 @@ pub trait LayerData { /// " /// ); /// ``` - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData); + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) -> bool; /// Determine the layers within this layer that intersect a given quad. /// # Example @@ -190,7 +190,7 @@ pub trait LayerData { } impl LayerData for LayerDataType { - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) { + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) -> bool { self.inner_mut().render(svg, svg_defs, transforms, render_data) } @@ -304,12 +304,15 @@ impl Layer { LayerIter { stack: vec![self] } } - pub fn render(&mut self, transforms: &mut Vec, 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, svg_defs: &mut String, render_data: RenderData) -> (&str, bool) { if !self.visible { - return ""; + return ("", false); } transforms.push(self.transform); + + // Skip rendering if outside the viewport bounds if let Some(viewport_bounds) = render_data.culling_bounds { if let Some(bounding_box) = self .data @@ -321,15 +324,17 @@ impl Layer { transforms.pop(); self.cache.clear(); self.cache_dirty = true; - return ""; + return ("", true); } } } + let mut requires_redraw = false; + if self.cache_dirty { self.thumbnail_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(); let _ = writeln!(self.cache, r#", intersections: &mut Vec>, font_cache: &FontCache) { diff --git a/document-legacy/src/layers/nodegraph_layer.rs b/document-legacy/src/layers/nodegraph_layer.rs index 8a9e947f..bf27d17c 100644 --- a/document-legacy/src/layers/nodegraph_layer.rs +++ b/document-legacy/src/layers/nodegraph_layer.rs @@ -33,7 +33,7 @@ pub struct ImageData { } impl LayerData for NodeGraphFrameLayer { - fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) { + fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) -> bool { let transform = self.transform(transforms, render_data.view_mode); let inverse = transform.inverse(); @@ -41,7 +41,7 @@ impl LayerData for NodeGraphFrameLayer { if !inverse.is_finite() { let _ = write!(svg, ""); - return; + return false; } let _ = writeln!(svg, r#""#); + + false } fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> { diff --git a/document-legacy/src/layers/shape_layer.rs b/document-legacy/src/layers/shape_layer.rs index 8235f208..fa522e7a 100644 --- a/document-legacy/src/layers/shape_layer.rs +++ b/document-legacy/src/layers/shape_layer.rs @@ -27,7 +27,7 @@ pub struct ShapeLayer { } impl LayerData for ShapeLayer { - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) { + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) -> bool { let mut subpath = self.shape.clone(); let layer_bounds = subpath.bounding_box().unwrap_or_default(); @@ -36,7 +36,7 @@ impl LayerData for ShapeLayer { let inverse = transform.inverse(); if !inverse.is_finite() { let _ = write!(svg, ""); - return; + return false; } 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) ); let _ = svg.write_str(""); + + false } fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> { diff --git a/document-legacy/src/layers/text_layer.rs b/document-legacy/src/layers/text_layer.rs index 602b38b4..d83822cd 100644 --- a/document-legacy/src/layers/text_layer.rs +++ b/document-legacy/src/layers/text_layer.rs @@ -34,13 +34,13 @@ pub struct TextLayer { } impl LayerData for TextLayer { - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) { + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) -> bool { let transform = self.transform(transforms, render_data.view_mode); let inverse = transform.inverse(); if !inverse.is_finite() { let _ = write!(svg, ""); - return; + return false; } let _ = writeln!(svg, r#""); + + false } fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {