Skip to content

Commit 4084ee1

Browse files
committed
Start integrating vello into render pipeline
Cache vello render creation Implement viewport navigation Close vello path Add transform parameter to vello render pass
1 parent 76a5ece commit 4084ee1

File tree

21 files changed

+494
-532
lines changed

21 files changed

+494
-532
lines changed

Cargo.lock

Lines changed: 84 additions & 254 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ web-sys = "=0.3.69"
6666
winit = "0.29"
6767
url = "2.5"
6868
tokio = { version = "1.29", features = ["fs", "io-std"] }
69-
vello = "0.1"
70-
resvg = "0.39"
71-
usvg = "0.39"
69+
vello = "0.2"
70+
resvg = "0.41"
71+
usvg = "0.41"
7272
rand = { version = "0.8", default-features = false }
7373
rand_chacha = "0.3"
7474
glam = { version = "0.25", default-features = false, features = ["serde"] }
@@ -89,7 +89,7 @@ syn = { version = "2.0", default-features = false, features = [
8989
"full",
9090
"derive",
9191
] }
92-
kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
92+
kurbo = { version = "0.11.0", features = [
9393
"serde",
9494
] }
9595

editor/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ gpu = [
2020
"wgpu-executor",
2121
"gpu-executor",
2222
]
23+
resvg = ["graphene-std/resvg"]
24+
vello = ["graphene-std/vello", "resvg", "graphene-core/vello"]
2325
quantization = [
2426
"graphene-std/quantization",
2527
"interpreted-executor/quantization",

editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,8 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
569569
parent,
570570
insert_index,
571571
} => {
572-
let tree = match usvg::Tree::from_str(&svg, &usvg::Options::default()) {
572+
let database = usvg::fontdb::Database::new();
573+
let tree = match usvg::Tree::from_str(&svg, &usvg::Options::default(), &database) {
573574
Ok(t) => t,
574575
Err(e) => {
575576
responses.add(DocumentMessage::DocumentHistoryBackward);
@@ -582,7 +583,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
582583
};
583584
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
584585

585-
import_usvg_node(&mut modify_inputs, &usvg::Node::Group(Box::new(tree.root)), transform, id, parent, insert_index);
586+
import_usvg_node(&mut modify_inputs, &usvg::Node::Group(Box::new(tree.root().clone())), transform, id, parent, insert_index);
586587
load_network_structure(document_network, document_metadata, collapsed);
587588
}
588589
GraphOperationMessage::SetNodePosition { node_id, position } => {
@@ -715,7 +716,7 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
715716
modify_inputs.layer_node = Some(layer);
716717
match node {
717718
usvg::Node::Group(group) => {
718-
for child in &group.children {
719+
for child in group.children() {
719720
import_usvg_node(modify_inputs, child, transform, NodeId(generate_uuid()), LayerNodeIdentifier::new_unchecked(layer), -1);
720721
}
721722
modify_inputs.layer_node = Some(layer);
@@ -736,71 +737,74 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
736737
let bounds_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
737738
let transformed_bound_transform = DAffine2::from_scale_angle_translation(transformed_bounds[1] - transformed_bounds[0], 0., transformed_bounds[0]);
738739
apply_usvg_fill(
739-
&path.fill,
740+
path.fill(),
740741
modify_inputs,
741742
transform * usvg_transform(node.abs_transform()),
742743
bounds_transform,
743744
transformed_bound_transform,
744745
);
745-
apply_usvg_stroke(&path.stroke, modify_inputs);
746+
apply_usvg_stroke(path.stroke(), modify_inputs);
746747
}
747748
usvg::Node::Image(_image) => {
748749
warn!("Skip image")
749750
}
750751
usvg::Node::Text(text) => {
751752
let font = Font::new(graphene_core::consts::DEFAULT_FONT_FAMILY.to_string(), graphene_core::consts::DEFAULT_FONT_STYLE.to_string());
752-
modify_inputs.insert_text(text.chunks.iter().map(|chunk| chunk.text.clone()).collect(), font, 24., layer);
753+
modify_inputs.insert_text(text.chunks().iter().map(|chunk| chunk.text().clone()).collect(), font, 24., layer);
753754
modify_inputs.fill_set(Fill::Solid(Color::BLACK));
754755
}
755756
}
756757
}
757758

758-
fn apply_usvg_stroke(stroke: &Option<usvg::Stroke>, modify_inputs: &mut ModifyInputsContext) {
759+
fn apply_usvg_stroke(stroke: Option<&usvg::Stroke>, modify_inputs: &mut ModifyInputsContext) {
759760
if let Some(stroke) = stroke {
760-
if let usvg::Paint::Color(color) = &stroke.paint {
761+
if let usvg::Paint::Color(color) = &stroke.paint() {
761762
modify_inputs.stroke_set(Stroke {
762-
color: Some(usvg_color(*color, stroke.opacity.get())),
763-
weight: stroke.width.get() as f64,
764-
dash_lengths: stroke.dasharray.as_ref().map(|lengths| lengths.iter().map(|&length| length as f64).collect()).unwrap_or_default(),
765-
dash_offset: stroke.dashoffset as f64,
766-
line_cap: match stroke.linecap {
763+
color: Some(usvg_color(*color, stroke.opacity().get())),
764+
weight: stroke.width().get() as f64,
765+
dash_lengths: stroke.dasharray().as_ref().map(|lengths| lengths.iter().map(|&length| length as f64).collect()).unwrap_or_default(),
766+
dash_offset: stroke.dashoffset() as f64,
767+
line_cap: match stroke.linecap() {
767768
usvg::LineCap::Butt => LineCap::Butt,
768769
usvg::LineCap::Round => LineCap::Round,
769770
usvg::LineCap::Square => LineCap::Square,
770771
},
771-
line_join: match stroke.linejoin {
772+
line_join: match stroke.linejoin() {
772773
usvg::LineJoin::Miter => LineJoin::Miter,
773774
usvg::LineJoin::MiterClip => LineJoin::Miter,
774775
usvg::LineJoin::Round => LineJoin::Round,
775776
usvg::LineJoin::Bevel => LineJoin::Bevel,
776777
},
777-
line_join_miter_limit: stroke.miterlimit.get() as f64,
778+
line_join_miter_limit: stroke.miterlimit().get() as f64,
778779
})
779780
} else {
780781
warn!("Skip non-solid stroke")
781782
}
782783
}
783784
}
784785

785-
fn apply_usvg_fill(fill: &Option<usvg::Fill>, modify_inputs: &mut ModifyInputsContext, transform: DAffine2, bounds_transform: DAffine2, transformed_bound_transform: DAffine2) {
786+
fn apply_usvg_fill(fill: Option<&usvg::Fill>, modify_inputs: &mut ModifyInputsContext, transform: DAffine2, bounds_transform: DAffine2, transformed_bound_transform: DAffine2) {
786787
if let Some(fill) = &fill {
787-
modify_inputs.fill_set(match &fill.paint {
788-
usvg::Paint::Color(color) => Fill::solid(usvg_color(*color, fill.opacity.get())),
788+
modify_inputs.fill_set(match &fill.paint() {
789+
usvg::Paint::Color(color) => Fill::solid(usvg_color(*color, fill.opacity().get())),
789790
usvg::Paint::LinearGradient(linear) => {
790-
let local = [DVec2::new(linear.x1 as f64, linear.y1 as f64), DVec2::new(linear.x2 as f64, linear.y2 as f64)];
791+
let local = [DVec2::new(linear.x1() as f64, linear.y1() as f64), DVec2::new(linear.x2() as f64, linear.y2() as f64)];
791792

792-
let to_doc_transform = if linear.base.units == usvg::Units::UserSpaceOnUse {
793+
// TODO: fix this
794+
/*
795+
let to_doc_transform = if linear.base.units() == usvg::Units::UserSpaceOnUse {
793796
transform
794797
} else {
795798
transformed_bound_transform
796-
};
797-
let to_doc = to_doc_transform * usvg_transform(linear.transform);
799+
};*/
800+
let to_doc_transform = transform;
801+
let to_doc = to_doc_transform * usvg_transform(linear.transform());
798802

799803
let document = [to_doc.transform_point2(local[0]), to_doc.transform_point2(local[1])];
800804
let layer = [transform.inverse().transform_point2(document[0]), transform.inverse().transform_point2(document[1])];
801805

802806
let [start, end] = [bounds_transform.inverse().transform_point2(layer[0]), bounds_transform.inverse().transform_point2(layer[1])];
803-
let stops = linear.stops.iter().map(|stop| (stop.offset.get() as f64, usvg_color(stop.color, stop.opacity.get()))).collect();
807+
let stops = linear.stops().iter().map(|stop| (stop.offset().get() as f64, usvg_color(stop.color(), stop.opacity().get()))).collect();
804808
let stops = GradientStops(stops);
805809

806810
Fill::Gradient(Gradient {
@@ -812,20 +816,23 @@ fn apply_usvg_fill(fill: &Option<usvg::Fill>, modify_inputs: &mut ModifyInputsCo
812816
})
813817
}
814818
usvg::Paint::RadialGradient(radial) => {
815-
let local = [DVec2::new(radial.cx as f64, radial.cy as f64), DVec2::new(radial.fx as f64, radial.fy as f64)];
819+
let local = [DVec2::new(radial.cx() as f64, radial.cy() as f64), DVec2::new(radial.fx() as f64, radial.fy() as f64)];
816820

821+
// TODO: fix this
822+
/*
817823
let to_doc_transform = if radial.base.units == usvg::Units::UserSpaceOnUse {
818824
transform
819825
} else {
820826
transformed_bound_transform
821-
};
822-
let to_doc = to_doc_transform * usvg_transform(radial.transform);
827+
};*/
828+
let to_doc_transform = transform;
829+
let to_doc = to_doc_transform * usvg_transform(radial.transform());
823830

824831
let document = [to_doc.transform_point2(local[0]), to_doc.transform_point2(local[1])];
825832
let layer = [transform.inverse().transform_point2(document[0]), transform.inverse().transform_point2(document[1])];
826833

827834
let [start, end] = [bounds_transform.inverse().transform_point2(layer[0]), bounds_transform.inverse().transform_point2(layer[1])];
828-
let stops = radial.stops.iter().map(|stop| (stop.offset.get() as f64, usvg_color(stop.color, stop.opacity.get()))).collect();
835+
let stops = radial.stops().iter().map(|stop| (stop.offset().get() as f64, usvg_color(stop.color(), stop.opacity().get()))).collect();
829836
let stops = GradientStops(stops);
830837

831838
Fill::Gradient(Gradient {

editor/src/messages/portfolio/document/node_graph/document_node_types.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,15 +1311,16 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
13111311
nodes: [
13121312
DocumentNode {
13131313
name: "Create Gpu Surface".to_string(),
1314+
manual_composition: Some(concrete!(Footprint)),
13141315
inputs: vec![NodeInput::scope("editor-api")],
1315-
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("wgpu_executor::CreateGpuSurfaceNode")),
1316+
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("wgpu_executor::CreateGpuSurfaceNode<_>")),
13161317
..Default::default()
13171318
},
13181319
DocumentNode {
13191320
name: "Cache".to_string(),
1320-
manual_composition: Some(concrete!(())),
1321+
manual_composition: Some(concrete!(Footprint)),
13211322
inputs: vec![NodeInput::node(NodeId(0), 0)],
1322-
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
1323+
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode<_, _, _>")),
13231324
..Default::default()
13241325
},
13251326
]
@@ -2789,16 +2790,17 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
27892790
nodes: [
27902791
DocumentNode {
27912792
name: "Create Canvas".to_string(),
2792-
inputs: vec![NodeInput::network(concrete!(&WasmEditorApi), 1)],
2793-
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
2793+
inputs: vec![NodeInput::scope("editor-api")],
2794+
manual_composition: Some(concrete!(Footprint)),
2795+
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("wgpu_executor::CreateGpuSurfaceNode<_>")),
27942796
skip_deduplication: true,
27952797
..Default::default()
27962798
},
27972799
DocumentNode {
27982800
name: "Cache".to_string(),
2799-
manual_composition: Some(concrete!(())),
2801+
manual_composition: Some(concrete!(Footprint)),
28002802
inputs: vec![NodeInput::node(NodeId(0), 0)],
2801-
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
2803+
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode<_, _, _>")),
28022804
..Default::default()
28032805
},
28042806
// TODO: Add conversion step
@@ -2809,7 +2811,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
28092811
NodeInput::network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T))), 0),
28102812
NodeInput::node(NodeId(1), 0),
28112813
],
2812-
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _, _>")),
2814+
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _, _, _>")),
28132815
..Default::default()
28142816
},
28152817
]

editor/src/node_graph_executor.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ impl NodeGraphExecutor {
634634

635635
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, transform: DAffine2, responses: &mut VecDeque<Message>) -> Result<(), String> {
636636
match node_graph_output {
637-
TaggedValue::SurfaceFrame(SurfaceFrame { surface_id: _, transform: _ }) => {
637+
TaggedValue::SurfaceFrame(SurfaceFrame { surface_id: _, transform: _, .. }) => {
638638
// TODO: Reimplement this now that document-legacy is gone
639639
}
640640
TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::Svg(svg)) => {
@@ -643,6 +643,7 @@ impl NodeGraphExecutor {
643643
responses.add(DocumentMessage::RenderScrollbars);
644644
}
645645
TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::CanvasFrame(frame)) => {
646+
let resolution = frame.resolution;
646647
// Send to frontend
647648
responses.add(DocumentMessage::RenderScrollbars);
648649
let matrix = frame
@@ -655,7 +656,7 @@ impl NodeGraphExecutor {
655656
r#"
656657
<svg><foreignObject width="{}" height="{}" transform="matrix({})"><div data-canvas-placeholder="canvas{}"></div></foreignObject></svg>
657658
"#,
658-
1920, 1080, matrix, frame.surface_id.0
659+
resolution.x, resolution.y, matrix, frame.surface_id.0
659660
);
660661
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
661662
}

frontend/wasm/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ crate-type = ["cdylib", "rlib"]
2121
[dependencies]
2222
# Local dependencies
2323
editor = { path = "../../editor", package = "graphite-editor", features = [
24-
"gpu",
24+
"gpu",
25+
"resvg",
26+
"vello"
2527
] }
2628

2729
# Workspace dependencies

libraries/bezier-rs/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ homepage = "https://github.com/GraphiteEditor/Graphite/tree/master/libraries/bez
1313
repository = "https://github.com/GraphiteEditor/Graphite/tree/master/libraries/bezier-rs"
1414
documentation = "https://graphite.rs/libraries/bezier-rs/"
1515

16+
1617
[dependencies]
1718
# Required dependencies
1819
glam = { version = "0.25", features = ["serde"] }
@@ -21,5 +22,6 @@ glam = { version = "0.25", features = ["serde"] }
2122
dyn-any = { version = "0.3.0", path = "../dyn-any", optional = true }
2223

2324
# Optional workspace dependencies
25+
kurbo = { workspace = true, optional = true }
2426
serde = { workspace = true, optional = true }
2527
log = { workspace = true, optional = true }

libraries/bezier-rs/src/subpath/core.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,27 @@ impl<PointId: crate::Identifier> Subpath<PointId> {
343343

344344
subpath
345345
}
346+
347+
#[cfg(feature = "kurbo")]
348+
pub fn to_vello_path(&self, transform: glam::DAffine2) -> kurbo::BezPath {
349+
use crate::BezierHandles;
350+
351+
let mut path = kurbo::BezPath::new();
352+
let to_point = |p: DVec2| kurbo::Point::new(p.x, p.y);
353+
for segment in self.iter() {
354+
let segment = segment.apply_transformation(|p| transform.transform_point2(p));
355+
path.move_to(to_point(segment.start));
356+
match segment.handles {
357+
BezierHandles::Linear => path.line_to(to_point(segment.end)),
358+
BezierHandles::Quadratic { handle } => path.quad_to(to_point(handle), to_point(segment.end)),
359+
BezierHandles::Cubic { handle_start, handle_end } => path.curve_to(to_point(handle_start), to_point(handle_end), to_point(segment.end)),
360+
}
361+
}
362+
if self.closed {
363+
path.close_path();
364+
}
365+
path
366+
}
346367
}
347368

348369
pub fn solve_spline_first_handle(points: &[DVec2]) -> Vec<DVec2> {

node-graph/gcore/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ nightly = []
1414
alloc = ["dyn-any", "bezier-rs"]
1515
type_id_logging = []
1616
wasm = ["web-sys"]
17+
vello = ["dep:vello", "bezier-rs/kurbo"]
1718
std = [
1819
"dyn-any",
1920
"dyn-any/std",
@@ -57,6 +58,7 @@ rand_chacha = { workspace = true, optional = true }
5758
bezier-rs = { workspace = true, optional = true }
5859
kurbo = { workspace = true, optional = true }
5960
base64 = { workspace = true, optional = true }
61+
vello = { workspace = true, optional = true }
6062
specta = { workspace = true, optional = true }
6163
rustybuzz = { workspace = true, optional = true }
6264
wasm-bindgen = { workspace = true, optional = true }

node-graph/gcore/src/application_io.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use core::future::Future;
1010
use core::hash::{Hash, Hasher};
1111
use core::pin::Pin;
1212
use core::ptr::addr_of;
13-
use glam::DAffine2;
13+
use glam::{DAffine2, UVec2};
1414

1515
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1616
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -26,6 +26,7 @@ impl core::fmt::Display for SurfaceId {
2626
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2727
pub struct SurfaceFrame {
2828
pub surface_id: SurfaceId,
29+
pub resolution: UVec2,
2930
pub transform: DAffine2,
3031
}
3132

@@ -51,11 +52,23 @@ unsafe impl StaticType for SurfaceFrame {
5152
type Static = SurfaceFrame;
5253
}
5354

54-
impl<S> From<SurfaceHandleFrame<S>> for SurfaceFrame {
55+
pub trait Size {
56+
fn size(&self) -> UVec2;
57+
}
58+
59+
#[cfg(target_arch = "wasm32")]
60+
impl Size for web_sys::HtmlCanvasElement {
61+
fn size(&self) -> UVec2 {
62+
UVec2::new(self.width(), self.height())
63+
}
64+
}
65+
66+
impl<S: Size> From<SurfaceHandleFrame<S>> for SurfaceFrame {
5567
fn from(x: SurfaceHandleFrame<S>) -> Self {
5668
Self {
5769
surface_id: x.surface_handle.surface_id,
5870
transform: x.transform,
71+
resolution: x.surface_handle.surface.size(),
5972
}
6073
}
6174
}
@@ -70,6 +83,12 @@ pub struct SurfaceHandle<Surface> {
7083
// #[cfg(target_arch = "wasm32")]
7184
// unsafe impl<T: dyn_any::WasmNotSync> Sync for SurfaceHandle<T> {}
7285

86+
impl<S: Size> Size for SurfaceHandle<S> {
87+
fn size(&self) -> UVec2 {
88+
self.surface.size()
89+
}
90+
}
91+
7392
unsafe impl<T: 'static> StaticType for SurfaceHandle<T> {
7493
type Static = SurfaceHandle<T>;
7594
}

0 commit comments

Comments
 (0)