Skip to content

Commit f30b04b

Browse files
mfish33Keavon
andauthored
Implement viewport culling (#667)
* culling is working * fixed tests * Ready for review * cleanup * code review * Fix import Co-authored-by: Keavon Chambers <[email protected]>
1 parent e039b46 commit f30b04b

12 files changed

+47
-27
lines changed

editor/src/consts.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use graphene::color::Color;
2-
use graphene::layers::text_layer::Font;
32

43
// Viewport
54
pub const VIEWPORT_ZOOM_WHEEL_RATE: f64 = 1. / 600.;

editor/src/document/artboard_message_handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl MessageHandler<ArtboardMessage, &FontCache> for ArtboardMessageHandler {
8787
} else {
8888
responses.push_back(
8989
FrontendMessage::UpdateDocumentArtboards {
90-
svg: self.artboards_graphene_document.render_root(ViewMode::Normal, font_cache),
90+
svg: self.artboards_graphene_document.render_root(ViewMode::Normal, font_cache, None),
9191
}
9292
.into(),
9393
);

editor/src/document/document_message_handler.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -873,8 +873,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
873873
}
874874
#[remain::unsorted]
875875
Overlays(message) => {
876-
self.overlays_message_handler.process_action(message, (self.overlays_visible, font_cache), responses);
877-
// responses.push_back(OverlaysMessage::RenderOverlays.into());
876+
self.overlays_message_handler.process_action(message, (self.overlays_visible, font_cache, ipp), responses);
878877
}
879878
#[remain::unsorted]
880879
TransformLayers(message) => {
@@ -1060,7 +1059,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
10601059
false => file_name + file_suffix,
10611060
};
10621061

1063-
let rendered = self.graphene_document.render_root(self.view_mode, font_cache);
1062+
let rendered = self.graphene_document.render_root(self.view_mode, font_cache, None);
10641063
let document = format!(
10651064
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="{} {} {} {}" width="{}px" height="{}">{}{}</svg>"#,
10661065
bbox[0].x, bbox[0].y, size.x, size.y, size.x, size.y, "\n", rendered
@@ -1100,7 +1099,6 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
11001099
}
11011100
}
11021101
FolderChanged { affected_folder_path } => {
1103-
let _ = self.graphene_document.render_root(self.view_mode, font_cache);
11041102
let affected_layer_path = affected_folder_path;
11051103
responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]);
11061104
}
@@ -1220,7 +1218,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
12201218
RenderDocument => {
12211219
responses.push_back(
12221220
FrontendMessage::UpdateDocumentArtwork {
1223-
svg: self.graphene_document.render_root(self.view_mode, font_cache),
1221+
svg: self.graphene_document.render_root(self.view_mode, font_cache, Some(ipp.document_bounds())),
12241222
}
12251223
.into(),
12261224
);

editor/src/document/layer_panel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub fn layer_panel_entry(layer_metadata: &LayerMetadata, transform: DAffine2, la
2727

2828
let mut thumbnail = String::new();
2929
let mut svg_defs = String::new();
30-
layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], ViewMode::Normal, font_cache);
30+
layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], ViewMode::Normal, font_cache, None);
3131
let transform = transform.to_cols_array().iter().map(ToString::to_string).collect::<Vec<_>>().join(",");
3232
let thumbnail = if let [(x_min, y_min), (x_max, y_max)] = arr.as_slice() {
3333
format!(

editor/src/document/overlays_message_handler.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::input::InputPreprocessorMessageHandler;
12
use crate::message_prelude::*;
23

34
use graphene::document::Document as GrapheneDocument;
@@ -9,9 +10,9 @@ pub struct OverlaysMessageHandler {
910
pub overlays_graphene_document: GrapheneDocument,
1011
}
1112

12-
impl MessageHandler<OverlaysMessage, (bool, &FontCache)> for OverlaysMessageHandler {
13+
impl MessageHandler<OverlaysMessage, (bool, &FontCache, &InputPreprocessorMessageHandler)> for OverlaysMessageHandler {
1314
#[remain::check]
14-
fn process_action(&mut self, message: OverlaysMessage, (overlays_visible, font_cache): (bool, &FontCache), responses: &mut VecDeque<Message>) {
15+
fn process_action(&mut self, message: OverlaysMessage, (overlays_visible, font_cache, ipp): (bool, &FontCache, &InputPreprocessorMessageHandler), responses: &mut VecDeque<Message>) {
1516
use OverlaysMessage::*;
1617

1718
#[remain::sorted]
@@ -31,7 +32,7 @@ impl MessageHandler<OverlaysMessage, (bool, &FontCache)> for OverlaysMessageHand
3132
responses.push_back(
3233
FrontendMessage::UpdateDocumentOverlays {
3334
svg: if overlays_visible {
34-
self.overlays_graphene_document.render_root(ViewMode::Normal, font_cache)
35+
self.overlays_graphene_document.render_root(ViewMode::Normal, font_cache, Some(ipp.document_bounds()))
3536
} else {
3637
String::from("")
3738
},

editor/src/input/input_preprocessor_message_handler.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::message_prelude::*;
66
#[doc(inline)]
77
pub use graphene::DocumentResponse;
88

9+
use glam::DVec2;
10+
911
#[derive(Debug, Default)]
1012
pub struct InputPreprocessorMessageHandler {
1113
pub keyboard: KeyStates,
@@ -152,4 +154,9 @@ impl InputPreprocessorMessageHandler {
152154
responses.push_back(InputMapperMessage::KeyDown(key).into());
153155
}
154156
}
157+
158+
pub fn document_bounds(&self) -> [DVec2; 2] {
159+
// ipp bounds are relative to the entire screen
160+
[(0., 0.).into(), self.viewport_bounds.bottom_right - self.viewport_bounds.top_left]
161+
}
155162
}

graphene/src/document.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ impl Default for Document {
4242

4343
impl Document {
4444
/// Wrapper around render, that returns the whole document as a Response.
45-
pub fn render_root(&mut self, mode: ViewMode, font_cache: &FontCache) -> String {
45+
pub fn render_root(&mut self, mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) -> String {
4646
let mut svg_defs = String::from("<defs>");
4747

48-
self.root.render(&mut vec![], mode, &mut svg_defs, font_cache);
48+
self.root.render(&mut vec![], mode, &mut svg_defs, font_cache, culling_bounds);
4949

5050
svg_defs.push_str("</defs>");
5151

graphene/src/layers/folder_layer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ pub struct FolderLayer {
2222
}
2323

2424
impl LayerData for FolderLayer {
25-
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache) {
25+
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) {
2626
for layer in &mut self.layers {
27-
let _ = writeln!(svg, "{}", layer.render(transforms, view_mode, svg_defs, font_cache));
27+
let _ = writeln!(svg, "{}", layer.render(transforms, view_mode, svg_defs, font_cache, culling_bounds));
2828
}
2929
}
3030

graphene/src/layers/image_layer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub struct ImageLayer {
2424
}
2525

2626
impl LayerData for ImageLayer {
27-
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, _font_cache: &FontCache) {
27+
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, _font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) {
2828
let transform = self.transform(transforms, view_mode);
2929
let inverse = transform.inverse();
3030

graphene/src/layers/layer_info.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub trait LayerData {
6161
/// let mut svg = String::new();
6262
///
6363
/// // Render the shape without any transforms, in normal view mode
64-
/// shape.render(&mut svg, &mut String::new(), &mut vec![], ViewMode::Normal, &Default::default());
64+
/// shape.render(&mut svg, &mut String::new(), &mut vec![], ViewMode::Normal, &Default::default(), None);
6565
///
6666
/// assert_eq!(
6767
/// svg,
@@ -70,7 +70,7 @@ pub trait LayerData {
7070
/// </g>"
7171
/// );
7272
/// ```
73-
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache);
73+
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>);
7474

7575
/// Determine the layers within this layer that intersect a given quad.
7676
/// # Example
@@ -117,8 +117,8 @@ pub trait LayerData {
117117
}
118118

119119
impl LayerData for LayerDataType {
120-
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache) {
121-
self.inner_mut().render(svg, svg_defs, transforms, view_mode, font_cache)
120+
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache, viewport_bounds: Option<[DVec2; 2]>) {
121+
self.inner_mut().render(svg, svg_defs, transforms, view_mode, font_cache, viewport_bounds)
122122
}
123123

124124
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
@@ -223,16 +223,29 @@ impl Layer {
223223
LayerIter { stack: vec![self] }
224224
}
225225

226-
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, svg_defs: &mut String, font_cache: &FontCache) -> &str {
226+
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, svg_defs: &mut String, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) -> &str {
227227
if !self.visible {
228228
return "";
229229
}
230230

231+
transforms.push(self.transform);
232+
if let Some(viewport_bounds) = culling_bounds {
233+
if let Some(bounding_box) = self.data.bounding_box(transforms.iter().cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY), font_cache) {
234+
let is_overlapping =
235+
viewport_bounds[0].x < bounding_box[1].x && bounding_box[0].x < viewport_bounds[1].x && viewport_bounds[0].y < bounding_box[1].y && bounding_box[0].y < viewport_bounds[1].y;
236+
if !is_overlapping {
237+
transforms.pop();
238+
self.cache.clear();
239+
self.cache_dirty = true;
240+
return "";
241+
}
242+
}
243+
}
244+
231245
if self.cache_dirty {
232-
transforms.push(self.transform);
233246
self.thumbnail_cache.clear();
234247
self.svg_defs_cache.clear();
235-
self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, view_mode, font_cache);
248+
self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, view_mode, font_cache, culling_bounds);
236249

237250
self.cache.clear();
238251
let _ = writeln!(self.cache, r#"<g transform="matrix("#);
@@ -246,9 +259,10 @@ impl Layer {
246259
self.opacity,
247260
self.thumbnail_cache.as_str()
248261
);
249-
transforms.pop();
262+
250263
self.cache_dirty = false;
251264
}
265+
transforms.pop();
252266
svg_defs.push_str(&self.svg_defs_cache);
253267

254268
self.cache.as_str()

graphene/src/layers/shape_layer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ pub struct ShapeLayer {
2525
pub path: BezPath,
2626
/// The visual style of the shape.
2727
pub style: style::PathStyle,
28+
// TODO: We might be able to remove this in a future refactor
2829
pub render_index: i32,
2930
/// Whether or not the [path](ShapeLayer::path) connects to itself.
3031
pub closed: bool,
3132
}
3233

3334
impl LayerData for ShapeLayer {
34-
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, _font_cache: &FontCache) {
35+
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, _font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) {
3536
let mut path = self.path.clone();
3637

3738
let kurbo::Rect { x0, y0, x1, y1 } = path.bounding_box();
@@ -89,7 +90,7 @@ impl ShapeLayer {
8990
(_, -1) => 0,
9091
(_, x) => (transforms.len() as i32 - x).max(0) as usize,
9192
};
92-
transforms.iter().skip(start).cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY)
93+
transforms.iter().skip(start).fold(DAffine2::IDENTITY, |a, b| a * *b)
9394
}
9495

9596
pub fn from_bez_path(bez_path: BezPath, style: PathStyle, closed: bool) -> Self {

graphene/src/layers/text_layer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub struct TextLayer {
3737
}
3838

3939
impl LayerData for TextLayer {
40-
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, font_cache: &FontCache) {
40+
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) {
4141
let transform = self.transform(transforms, view_mode);
4242
let inverse = transform.inverse();
4343

0 commit comments

Comments
 (0)