From 30e9b3c5fdc8ecc0a1f735d17b694fed3395497c Mon Sep 17 00:00:00 2001 From: hypercube <0hypercube@gmail.com> Date: Fri, 4 Jul 2025 22:33:02 +0100 Subject: [PATCH] Skip complex layer thumbnails --- editor/src/node_graph_executor/runtime.rs | 13 +++++ node-graph/gcore/src/context.rs | 2 +- node-graph/gcore/src/lib.rs | 1 + node-graph/gcore/src/render_complexity.rs | 58 ++++++++++++++++++++ node-graph/graph-craft/src/document/value.rs | 2 +- node-graph/gsvg-renderer/src/renderer.rs | 5 +- 6 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 node-graph/gcore/src/render_complexity.rs diff --git a/editor/src/node_graph_executor/runtime.rs b/editor/src/node_graph_executor/runtime.rs index a93f73546..850afa516 100644 --- a/editor/src/node_graph_executor/runtime.rs +++ b/editor/src/node_graph_executor/runtime.rs @@ -328,6 +328,19 @@ impl NodeRuntime { return; } + // Skip thumbnails if the layer is too complex (for performance) + if graphic_element.render_complexity() > 1000 { + let old = thumbnail_renders.insert(parent_network_node_id, Vec::new()); + + if old.is_none_or(|v| !v.is_empty()) { + responses.push_back(FrontendMessage::UpdateNodeThumbnail { + id: parent_network_node_id, + value: "Layer too complex for thumbnail\u{2102}".to_string(), + }); + } + return; + } + let bounds = graphic_element.bounding_box(DAffine2::IDENTITY, true); // Render the thumbnail from a `GraphicElement` into an SVG string diff --git a/node-graph/gcore/src/context.rs b/node-graph/gcore/src/context.rs index 3adb839b0..4e8854c90 100644 --- a/node-graph/gcore/src/context.rs +++ b/node-graph/gcore/src/context.rs @@ -356,7 +356,7 @@ pub struct ContextImpl<'a> { } impl<'a> ContextImpl<'a> { - pub fn with_footprint<'f>(&self, new_footprint: &'f Footprint, varargs: Option<&'f impl (Borrow<[DynRef<'f>]>)>) -> ContextImpl<'f> + pub fn with_footprint<'f>(&self, new_footprint: &'f Footprint, varargs: Option<&'f impl Borrow<[DynRef<'f>]>>) -> ContextImpl<'f> where 'a: 'f, { diff --git a/node-graph/gcore/src/lib.rs b/node-graph/gcore/src/lib.rs index 973b2f4d2..b63f83c95 100644 --- a/node-graph/gcore/src/lib.rs +++ b/node-graph/gcore/src/lib.rs @@ -22,6 +22,7 @@ pub mod ops; pub mod raster; pub mod raster_types; pub mod registry; +pub mod render_complexity; pub mod structural; pub mod text; pub mod transform; diff --git a/node-graph/gcore/src/render_complexity.rs b/node-graph/gcore/src/render_complexity.rs new file mode 100644 index 000000000..8472320b0 --- /dev/null +++ b/node-graph/gcore/src/render_complexity.rs @@ -0,0 +1,58 @@ +use crate::raster_types::{CPU, GPU, Raster}; +use crate::{Artboard, Color, GraphicElement, instances::Instances, vector::VectorData}; +use glam::DVec2; + +pub trait RenderComplexity { + fn render_complexity(&self) -> usize { + 0 + } +} + +impl RenderComplexity for Instances { + fn render_complexity(&self) -> usize { + self.instance_ref_iter().map(|instance| instance.instance.render_complexity()).sum() + } +} + +impl RenderComplexity for Artboard { + fn render_complexity(&self) -> usize { + self.graphic_group.render_complexity() + } +} + +impl RenderComplexity for GraphicElement { + fn render_complexity(&self) -> usize { + match self { + Self::GraphicGroup(instances) => instances.render_complexity(), + Self::VectorData(instances) => instances.render_complexity(), + Self::RasterDataCPU(instances) => instances.render_complexity(), + Self::RasterDataGPU(instances) => instances.render_complexity(), + } + } +} + +impl RenderComplexity for VectorData { + fn render_complexity(&self) -> usize { + self.segment_domain.ids().len() + } +} + +impl RenderComplexity for Raster { + fn render_complexity(&self) -> usize { + (self.width * self.height) as usize + } +} + +impl RenderComplexity for Raster { + fn render_complexity(&self) -> usize { + (self.width() * self.height()) as usize + } +} + +impl RenderComplexity for String {} +impl RenderComplexity for bool {} +impl RenderComplexity for f32 {} +impl RenderComplexity for f64 {} +impl RenderComplexity for DVec2 {} +impl RenderComplexity for Option {} +impl RenderComplexity for Vec {} diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index d3b82eae1..00402776f 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -97,7 +97,7 @@ macro_rules! tagged_value { } } /// Attempts to downcast the dynamic type to a tagged value - pub fn try_from_std_any_ref(input: &(dyn std::any::Any)) -> Result { + pub fn try_from_std_any_ref(input: &dyn std::any::Any) -> Result { use std::any::TypeId; match input.type_id() { diff --git a/node-graph/gsvg-renderer/src/renderer.rs b/node-graph/gsvg-renderer/src/renderer.rs index 95e9890a8..f8faa2bc8 100644 --- a/node-graph/gsvg-renderer/src/renderer.rs +++ b/node-graph/gsvg-renderer/src/renderer.rs @@ -10,6 +10,7 @@ use graphene_core::instances::Instance; use graphene_core::math::quad::Quad; use graphene_core::raster::Image; use graphene_core::raster_types::{CPU, GPU, RasterDataTable}; +use graphene_core::render_complexity::RenderComplexity; use graphene_core::transform::{Footprint, Transform}; use graphene_core::uuid::{NodeId, generate_uuid}; use graphene_core::vector::VectorDataTable; @@ -203,7 +204,7 @@ pub struct RenderMetadata { } // TODO: Rename to "Graphical" -pub trait GraphicElementRendered: BoundingBox { +pub trait GraphicElementRendered: BoundingBox + RenderComplexity { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams); #[cfg(feature = "vello")] @@ -1149,7 +1150,7 @@ impl GraphicElementRendered for GraphicElement { } /// Used to stop rust complaining about upstream traits adding display implementations to `Option`. This would not be an issue as we control that crate. -trait Primitive: std::fmt::Display + BoundingBox {} +trait Primitive: std::fmt::Display + BoundingBox + RenderComplexity {} impl Primitive for String {} impl Primitive for bool {} impl Primitive for f32 {}