Implement Infrastructure to reuse previous frames for brush drawing

Implement Infrastructuro to reuse the previous evaluation of the
node graph to blend the new stroke with instead of drawing the
entire trace from scratch.
This does not transition to a blending based approach because that still
caused regressions but allows the brush node to work with input data
natively.

Test Plan:
- Use the brush tool in the editor and check for regressions
- Evaluate the performance

Reviewers: Keavon

Pull Request: https://github.com/GraphiteEditor/Graphite/pull/1190
This commit is contained in:
Dennis Kobert
2023-05-03 13:14:41 +02:00
committed by Keavon Chambers
parent ebf67eaa82
commit 3adcc3031a
19 changed files with 282 additions and 172 deletions

View File

@@ -17,6 +17,8 @@ pub struct DynamicExecutor {
output: NodeId,
tree: BorrowTree,
typing_context: TypingContext,
// This allows us to keep the nodes around for one more frame which is used for introspection
orphaned_nodes: Vec<NodeId>,
}
impl Default for DynamicExecutor {
@@ -25,6 +27,7 @@ impl Default for DynamicExecutor {
output: Default::default(),
tree: Default::default(),
typing_context: TypingContext::new(&node_registry::NODE_REGISTRY),
orphaned_nodes: Vec::new(),
}
}
}
@@ -36,21 +39,27 @@ impl DynamicExecutor {
let output = proto_network.output;
let tree = BorrowTree::new(proto_network, &typing_context)?;
Ok(Self { tree, output, typing_context })
Ok(Self {
tree,
output,
typing_context,
orphaned_nodes: Vec::new(),
})
}
pub fn update(&mut self, proto_network: ProtoNetwork) -> Result<(), String> {
self.output = proto_network.output;
self.typing_context.update(&proto_network)?;
trace!("setting output to {}", self.output);
let orphans = self.tree.update(proto_network, &self.typing_context)?;
let mut orphans = self.tree.update(proto_network, &self.typing_context)?;
core::mem::swap(&mut self.orphaned_nodes, &mut orphans);
for node_id in orphans {
self.tree.free_node(node_id)
}
Ok(())
}
pub fn introspect(&self, node_path: &[NodeId]) -> Option<Option<String>> {
pub fn introspect(&self, node_path: &[NodeId]) -> Option<Option<Arc<dyn std::any::Any>>> {
self.tree.introspect(node_path)
}
@@ -128,7 +137,7 @@ impl BorrowTree {
node.reset();
}
old_nodes.remove(&id);
self.source_map.retain(|_, nid| *nid != id);
self.source_map.retain(|_, nid| !old_nodes.contains(nid));
}
Ok(old_nodes.into_iter().collect())
}
@@ -145,7 +154,7 @@ impl BorrowTree {
node
}
pub fn introspect(&self, node_path: &[NodeId]) -> Option<Option<String>> {
pub fn introspect(&self, node_path: &[NodeId]) -> Option<Option<Arc<dyn std::any::Any>>> {
let id = self.source_map.get(node_path)?;
let node = self.nodes.get(id)?;
let reader = node.read().unwrap();

View File

@@ -154,7 +154,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Luma>]),
register_node!(graphene_std::raster::EmptyImageNode<_, _>, input: DAffine2, params: [Color]),
register_node!(graphene_std::memo::MonitorNode<_, _>, input: (), params: [ImageFrame<Color>]),
register_node!(graphene_std::memo::MonitorNode<_>, input: ImageFrame<Color>, params: []),
#[cfg(feature = "gpu")]
register_node!(graphene_std::executor::MapGpuSingleImageNode<_>, input: Image<Color>, params: [String]),
vec![(
@@ -175,14 +175,16 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
vec![(
NodeIdentifier::new("graphene_std::brush::BrushNode"),
|args| {
use graphene_core::structural::*;
use graphene_core::value::*;
use graphene_std::brush::*;
let trace: DowncastBothNode<(), Vec<DVec2>> = DowncastBothNode::new(args[0]);
let diameter: DowncastBothNode<(), f64> = DowncastBothNode::new(args[1]);
let hardness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]);
let flow: DowncastBothNode<(), f64> = DowncastBothNode::new(args[3]);
let color: DowncastBothNode<(), Color> = DowncastBothNode::new(args[4]);
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let trace: DowncastBothNode<(), Vec<DVec2>> = DowncastBothNode::new(args[1]);
let diameter: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]);
let hardness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[3]);
let flow: DowncastBothNode<(), f64> = DowncastBothNode::new(args[4]);
let color: DowncastBothNode<(), Color> = DowncastBothNode::new(args[5]);
let stamp = BrushStampGeneratorNode::new(color, CopiedNode::new(hardness.eval(())), CopiedNode::new(flow.eval(())));
let stamp = stamp.eval(diameter.eval(()));
@@ -191,16 +193,19 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
let frames = MapNode::new(ValueNode::new(frames));
let frames = frames.eval(trace.eval(()).into_iter()).collect::<Vec<_>>();
let background_bounds = ReduceNode::new(DebugClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new()));
let background_bounds = ReduceNode::new(ClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new()));
let background_bounds = background_bounds.eval(frames.clone().into_iter());
let background_bounds = DebugClonedNode::new(background_bounds.unwrap().to_transform());
let background_bounds = MergeBoundingBoxNode::new().eval((background_bounds, image.eval(())));
let background_bounds = ClonedNode::new(background_bounds.unwrap().to_transform());
let background_image = background_bounds.then(EmptyImageNode::new(CopiedNode::new(Color::TRANSPARENT)));
let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(BlendMode::Normal), CopiedNode::new(100.));
let background = ExtendImageNode::new(background_image);
let background_image = image.then(background);
let final_image = ReduceNode::new(background_image, ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node))));
let final_image = DebugClonedNode::new(frames.into_iter()).then(final_image);
let final_image = ClonedNode::new(frames.into_iter()).then(final_image);
let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(final_image));
Box::pin(any)
@@ -208,7 +213,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
NodeIOTypes::new(
concrete!(()),
concrete!(ImageFrame<Color>),
vec![value_fn!(Vec<DVec2>), value_fn!(f64), value_fn!(f64), value_fn!(f64), value_fn!(Color)],
vec![value_fn!(ImageFrame<Color>), value_fn!(Vec<DVec2>), value_fn!(f64), value_fn!(f64), value_fn!(f64), value_fn!(Color)],
),
)],
vec![(