mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 17:18:11 +08:00
Deprecate LetNodes in favor of new scope API (#1814)
* WIP * Start deprecating let nodes * Replace WasmEditorApi network imports with new Scope input * Add missing unwrap * Add #[serde(default)] to scope_injections * Restructure WasmEditorApi definition to be available as a TaggedValue * Fix text node * Use stable toolchain in nix shell again * Code review * FIx text node and remove all remaining warnings * Require executor input to be 'static --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
a17ed68008
commit
3657b37574
Generated
+7
-2
@@ -2245,7 +2245,6 @@ dependencies = [
|
|||||||
"dyn-any",
|
"dyn-any",
|
||||||
"futures",
|
"futures",
|
||||||
"glam",
|
"glam",
|
||||||
"graph-craft",
|
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
"log",
|
"log",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
@@ -2264,11 +2263,18 @@ dependencies = [
|
|||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"reqwest 0.12.5",
|
||||||
"rustc-hash 2.0.0",
|
"rustc-hash 2.0.0",
|
||||||
"serde",
|
"serde",
|
||||||
"specta",
|
"specta",
|
||||||
|
"url",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
"wgpu-executor",
|
||||||
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7401,7 +7407,6 @@ dependencies = [
|
|||||||
"futures-intrusive",
|
"futures-intrusive",
|
||||||
"glam",
|
"glam",
|
||||||
"gpu-executor",
|
"gpu-executor",
|
||||||
"graph-craft",
|
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
"log",
|
"log",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ resolver = "2"
|
|||||||
dyn-any = { path = "libraries/dyn-any", features = ["derive", "glam"] }
|
dyn-any = { path = "libraries/dyn-any", features = ["derive", "glam"] }
|
||||||
graphene-core = { path = "node-graph/gcore" }
|
graphene-core = { path = "node-graph/gcore" }
|
||||||
graph-craft = { path = "node-graph/graph-craft", features = ["serde"] }
|
graph-craft = { path = "node-graph/graph-craft", features = ["serde"] }
|
||||||
|
wgpu-executor = { path = "node-graph/wgpu-executor" }
|
||||||
bezier-rs = { path = "libraries/bezier-rs", features = ["dyn-any"] }
|
bezier-rs = { path = "libraries/bezier-rs", features = ["dyn-any"] }
|
||||||
node-macro = { path = "node-graph/node-macro" }
|
node-macro = { path = "node-graph/node-macro" }
|
||||||
|
|
||||||
|
|||||||
+59
-46
@@ -410,65 +410,78 @@ mod test {
|
|||||||
assert_eq!(layers_after_copy[5], shape_id);
|
assert_eq!(layers_after_copy[5], shape_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
/// This test will fail when you make changes to the underlying serialization format for a document.
|
/// This test will fail when you make changes to the underlying serialization format for a document.
|
||||||
async fn check_if_demo_art_opens() {
|
fn check_if_demo_art_opens() {
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
futures::executor::block_on(async {
|
||||||
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
|
|
||||||
let print_problem_to_terminal_on_failure = |value: &String| {
|
let print_problem_to_terminal_on_failure = |value: &String| {
|
||||||
println!();
|
println!();
|
||||||
println!("-------------------------------------------------");
|
println!("-------------------------------------------------");
|
||||||
println!("Failed test due to receiving a DisplayDialogError while loading a Graphite demo file.");
|
println!("Failed test due to receiving a DisplayDialogError while loading a Graphite demo file.");
|
||||||
println!();
|
println!();
|
||||||
println!("DisplayDialogError details:");
|
println!("DisplayDialogError details:");
|
||||||
println!();
|
println!();
|
||||||
println!("Description: {value}");
|
println!("Description: {value}");
|
||||||
println!("-------------------------------------------------");
|
println!("-------------------------------------------------");
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
panic!()
|
panic!()
|
||||||
};
|
};
|
||||||
|
|
||||||
init_logger();
|
init_logger();
|
||||||
let mut editor = Editor::create();
|
let mut editor = Editor::create();
|
||||||
|
|
||||||
for (document_name, _, file_name) in crate::messages::dialog::simple_dialogs::ARTWORK {
|
// UNCOMMENT THIS FOR RUNNING UNDER MIRI
|
||||||
let document_serialized_content = std::fs::read_to_string(format!("../demo-artwork/{file_name}")).unwrap();
|
//
|
||||||
|
// let files = [
|
||||||
|
// include_str!("../../demo-artwork/isometric-fountain.graphite"),
|
||||||
|
// include_str!("../../demo-artwork/just-a-potted-cactus.graphite"),
|
||||||
|
// include_str!("../../demo-artwork/procedural-string-lights.graphite"),
|
||||||
|
// include_str!("../../demo-artwork/red-dress.graphite"),
|
||||||
|
// include_str!("../../demo-artwork/valley-of-spires.graphite"),
|
||||||
|
// ];
|
||||||
|
// for (id, document_serialized_content) in files.iter().enumerate() {
|
||||||
|
// let document_name = format!("document {id}");
|
||||||
|
|
||||||
assert_eq!(
|
for (document_name, _, file_name) in crate::messages::dialog::simple_dialogs::ARTWORK {
|
||||||
document_serialized_content.lines().count(),
|
let document_serialized_content = std::fs::read_to_string(format!("../demo-artwork/{file_name}")).unwrap();
|
||||||
1,
|
|
||||||
"Demo artwork '{document_name}' has more than 1 line (remember to open and re-save it in Graphite)",
|
|
||||||
);
|
|
||||||
|
|
||||||
let responses = editor.handle_message(PortfolioMessage::OpenDocumentFile {
|
assert_eq!(
|
||||||
document_name: document_name.into(),
|
document_serialized_content.lines().count(),
|
||||||
document_serialized_content,
|
1,
|
||||||
});
|
"Demo artwork '{document_name}' has more than 1 line (remember to open and re-save it in Graphite)",
|
||||||
println!("Responses:\n{responses:#?}");
|
);
|
||||||
|
|
||||||
// Check if the graph renders
|
let responses = editor.handle_message(PortfolioMessage::OpenDocumentFile {
|
||||||
let portfolio = &mut editor.dispatcher.message_handlers.portfolio_message_handler;
|
document_name: document_name.into(),
|
||||||
portfolio
|
document_serialized_content: document_serialized_content.into(),
|
||||||
.executor
|
});
|
||||||
.submit_node_graph_evaluation(portfolio.documents.get_mut(&portfolio.active_document_id.unwrap()).unwrap(), glam::UVec2::ONE)
|
|
||||||
.expect("submit_node_graph_evaluation failed");
|
|
||||||
crate::node_graph_executor::run_node_graph().await;
|
|
||||||
let mut messages = VecDeque::new();
|
|
||||||
editor.poll_node_graph_evaluation(&mut messages).expect("Graph should render");
|
|
||||||
|
|
||||||
for response in responses {
|
// Check if the graph renders
|
||||||
// Check for the existence of the file format incompatibility warning dialog after opening the test file
|
let portfolio = &mut editor.dispatcher.message_handlers.portfolio_message_handler;
|
||||||
if let FrontendMessage::UpdateDialogColumn1 { layout_target: _, diff } = response {
|
portfolio
|
||||||
if let DiffUpdate::SubLayout(sub_layout) = &diff[0].new_value {
|
.executor
|
||||||
if let LayoutGroup::Row { widgets } = &sub_layout[0] {
|
.submit_node_graph_evaluation(portfolio.documents.get_mut(&portfolio.active_document_id.unwrap()).unwrap(), glam::UVec2::ONE)
|
||||||
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
|
.expect("submit_node_graph_evaluation failed");
|
||||||
print_problem_to_terminal_on_failure(value);
|
crate::node_graph_executor::run_node_graph().await;
|
||||||
|
let mut messages = VecDeque::new();
|
||||||
|
editor.poll_node_graph_evaluation(&mut messages).expect("Graph should render");
|
||||||
|
|
||||||
|
for response in responses {
|
||||||
|
// Check for the existence of the file format incompatibility warning dialog after opening the test file
|
||||||
|
if let FrontendMessage::UpdateDialogColumn1 { layout_target: _, diff } = response {
|
||||||
|
if let DiffUpdate::SubLayout(sub_layout) = &diff[0].new_value {
|
||||||
|
if let LayoutGroup::Row { widgets } = &sub_layout[0] {
|
||||||
|
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
|
||||||
|
print_problem_to_terminal_on_failure(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ impl LayoutHolder for ExportDialogMessageHandler {
|
|||||||
NumberInput::new(Some(self.scale_factor))
|
NumberInput::new(Some(self.scale_factor))
|
||||||
.unit("")
|
.unit("")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.disabled(self.file_type == FileType::Svg)
|
.disabled(self.file_type == FileType::Svg)
|
||||||
.on_update(|number_input: &NumberInput| ExportDialogMessage::ScaleFactor(number_input.value.unwrap()).into())
|
.on_update(|number_input: &NumberInput| ExportDialogMessage::ScaleFactor(number_input.value.unwrap()).into())
|
||||||
.min_width(200)
|
.min_width(200)
|
||||||
|
|||||||
+2
-2
@@ -95,7 +95,7 @@ impl LayoutHolder for NewDocumentDialogMessageHandler {
|
|||||||
.label("W")
|
.label("W")
|
||||||
.unit(" px")
|
.unit(" px")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.is_integer(true)
|
.is_integer(true)
|
||||||
.disabled(self.infinite)
|
.disabled(self.infinite)
|
||||||
.min_width(100)
|
.min_width(100)
|
||||||
@@ -106,7 +106,7 @@ impl LayoutHolder for NewDocumentDialogMessageHandler {
|
|||||||
.label("H")
|
.label("H")
|
||||||
.unit(" px")
|
.unit(" px")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.is_integer(true)
|
.is_integer(true)
|
||||||
.disabled(self.infinite)
|
.disabled(self.infinite)
|
||||||
.min_width(100)
|
.min_width(100)
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ impl PreferencesDialogMessageHandler {
|
|||||||
NumberInput::new(Some(preferences.imaginate_refresh_frequency))
|
NumberInput::new(Some(preferences.imaginate_refresh_frequency))
|
||||||
.unit(" seconds")
|
.unit(" seconds")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.min_width(200)
|
.min_width(200)
|
||||||
.on_update(|number_input: &NumberInput| PreferencesMessage::ImaginateRefreshFrequency { seconds: number_input.value.unwrap() }.into())
|
.on_update(|number_input: &NumberInput| PreferencesMessage::ImaginateRefreshFrequency { seconds: number_input.value.unwrap() }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
let insert_index = parent
|
let insert_index = parent
|
||||||
.children(self.metadata())
|
.children(self.metadata())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find_map(|(index, item)| self.selected_nodes.selected_layers(self.metadata()).any(|x| x == item).then_some(index as usize))
|
.find_map(|(index, item)| self.selected_nodes.selected_layers(self.metadata()).any(|x| x == item).then_some(index))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
// Store a history step before doing anything
|
// Store a history step before doing anything
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
pub fn insert_text(&mut self, text: String, font: Font, size: f64, layer: NodeId) {
|
pub fn insert_text(&mut self, text: String, font: Font, size: f64, layer: NodeId) {
|
||||||
let text = resolve_document_node_type("Text").expect("Text node does not exist").to_document_node(
|
let text = resolve_document_node_type("Text").expect("Text node does not exist").to_document_node(
|
||||||
[
|
[
|
||||||
NodeInput::network(graph_craft::concrete!(graphene_std::wasm_application_io::WasmEditorApi), 0),
|
NodeInput::scope("editor-api"),
|
||||||
NodeInput::value(TaggedValue::String(text), false),
|
NodeInput::value(TaggedValue::String(text), false),
|
||||||
NodeInput::value(TaggedValue::Font(font), false),
|
NodeInput::value(TaggedValue::Font(font), false),
|
||||||
NodeInput::value(TaggedValue::F64(size), false),
|
NodeInput::value(TaggedValue::F64(size), false),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use graphene_core::text::Font;
|
|||||||
use graphene_core::transform::Footprint;
|
use graphene_core::transform::Footprint;
|
||||||
use graphene_core::vector::VectorData;
|
use graphene_core::vector::VectorData;
|
||||||
use graphene_core::*;
|
use graphene_core::*;
|
||||||
|
use graphene_std::application_io::RenderConfig;
|
||||||
use graphene_std::wasm_application_io::WasmEditorApi;
|
use graphene_std::wasm_application_io::WasmEditorApi;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use {gpu_executor::*, graphene_core::application_io::SurfaceHandle, wgpu_executor::WgpuExecutor};
|
use {gpu_executor::*, graphene_core::application_io::SurfaceHandle, wgpu_executor::WgpuExecutor};
|
||||||
@@ -320,23 +321,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
properties: node_properties::artboard_properties,
|
properties: node_properties::artboard_properties,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// TODO: Does this need an internal Cull node to be added to its implementation?
|
|
||||||
DocumentNodeDefinition {
|
|
||||||
name: "Input Frame",
|
|
||||||
category: "Ignore",
|
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_core::ExtractImageFrame"),
|
|
||||||
inputs: vec![DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
}],
|
|
||||||
outputs: vec![DocumentOutputType {
|
|
||||||
name: "Image Frame",
|
|
||||||
data_type: FrontendGraphDataType::Raster,
|
|
||||||
}],
|
|
||||||
properties: node_properties::node_no_properties,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
name: "Load Image",
|
name: "Load Image",
|
||||||
category: "Structural",
|
category: "Structural",
|
||||||
@@ -345,7 +329,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Load Resource".to_string(),
|
name: "Load Resource".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 0), NodeInput::network(concrete!(String), 1)],
|
inputs: vec![NodeInput::scope("editor-api"), NodeInput::network(concrete!(String), 1)],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::LoadResourceNode<_>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::LoadResourceNode<_>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -373,7 +357,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "api",
|
name: "api",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
default: NodeInput::scope("editor-api"),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "path",
|
name: "path",
|
||||||
@@ -396,7 +380,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Create Canvas".to_string(),
|
name: "Create Canvas".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 0)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
||||||
skip_deduplication: true,
|
skip_deduplication: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -415,11 +399,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
inputs: vec![DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
}],
|
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "Canvas",
|
name: "Canvas",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -440,7 +419,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
},
|
},
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Create Canvas".to_string(),
|
name: "Create Canvas".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
||||||
skip_deduplication: true,
|
skip_deduplication: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -465,18 +444,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
inputs: vec![
|
inputs: vec![DocumentInputType {
|
||||||
DocumentInputType {
|
name: "In",
|
||||||
name: "In",
|
data_type: FrontendGraphDataType::Raster,
|
||||||
data_type: FrontendGraphDataType::Raster,
|
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
}],
|
||||||
},
|
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "Canvas",
|
name: "Canvas",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -491,7 +463,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Create Canvas".to_string(),
|
name: "Create Canvas".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 2)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
||||||
skip_deduplication: true,
|
skip_deduplication: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -534,11 +506,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
properties: node_properties::rasterize_properties,
|
properties: node_properties::rasterize_properties,
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
@@ -547,136 +514,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
}],
|
}],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNodeDefinition {
|
|
||||||
// This essentially builds the concept of a closure where we store variables (`let` bindings) so they can be accessed within this scope.
|
|
||||||
name: "Begin Scope",
|
|
||||||
category: "Ignore",
|
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
|
||||||
exports: vec![NodeInput::node(NodeId(1), 0), NodeInput::node(NodeId(2), 0)],
|
|
||||||
nodes: [
|
|
||||||
DocumentNode {
|
|
||||||
name: "SetNode".to_string(),
|
|
||||||
manual_composition: Some(concrete!(WasmEditorApi)),
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::SomeNode")),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
name: "LetNode".to_string(),
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::LetNode<_>")),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
name: "RefNode".to_string(),
|
|
||||||
manual_composition: Some(concrete!(())),
|
|
||||||
inputs: vec![NodeInput::lambda(NodeId(1), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::RefNode<_, _>")),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
inputs: vec![DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::Raster,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
}],
|
|
||||||
outputs: vec![
|
|
||||||
DocumentOutputType {
|
|
||||||
name: "Scope",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
},
|
|
||||||
DocumentOutputType {
|
|
||||||
name: "Binding",
|
|
||||||
data_type: FrontendGraphDataType::Raster,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
properties: |_document_node, _node_id, _context| node_properties::string_properties("Binds the input in a local scope as a variable"),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeDefinition {
|
|
||||||
name: "End Scope",
|
|
||||||
category: "Ignore",
|
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_core::memo::EndLetNode<_, _>"),
|
|
||||||
inputs: vec![
|
|
||||||
DocumentInputType {
|
|
||||||
name: "Scope",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::value(TaggedValue::None, true),
|
|
||||||
},
|
|
||||||
DocumentInputType {
|
|
||||||
name: "Data",
|
|
||||||
data_type: FrontendGraphDataType::Raster,
|
|
||||||
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
outputs: vec![DocumentOutputType {
|
|
||||||
name: "Frame",
|
|
||||||
data_type: FrontendGraphDataType::Raster,
|
|
||||||
}],
|
|
||||||
properties: |_document_node, _node_id, _context| node_properties::string_properties("Consumes the scope opened by the Begin Scope node and evaluates the contained node network"),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeDefinition {
|
|
||||||
name: "Output",
|
|
||||||
category: "Ignore",
|
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
|
||||||
exports: vec![NodeInput::node(NodeId(3), 0)],
|
|
||||||
nodes: [
|
|
||||||
DocumentNode {
|
|
||||||
name: "Create Canvas".to_string(),
|
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
|
||||||
skip_deduplication: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
name: "Cache".to_string(),
|
|
||||||
manual_composition: Some(concrete!(())),
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
name: "Conversion".to_string(),
|
|
||||||
inputs: vec![NodeInput::network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T))), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, GraphicGroup>")),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
name: "RenderNode".to_string(),
|
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1), NodeInput::node(NodeId(2), 0), NodeInput::node(NodeId(1), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _>")),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
inputs: vec![
|
|
||||||
DocumentInputType {
|
|
||||||
name: "Output",
|
|
||||||
data_type: FrontendGraphDataType::Raster,
|
|
||||||
default: NodeInput::value(TaggedValue::GraphicGroup(GraphicGroup::default()), true),
|
|
||||||
},
|
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
outputs: vec![],
|
|
||||||
properties: node_properties::output_properties,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
name: "Image Frame",
|
name: "Image Frame",
|
||||||
category: "General",
|
category: "General",
|
||||||
@@ -1150,7 +987,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1174,18 +1011,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
inputs: vec![
|
inputs: vec![DocumentInputType {
|
||||||
DocumentInputType {
|
name: "In",
|
||||||
name: "In",
|
data_type: FrontendGraphDataType::General,
|
||||||
data_type: FrontendGraphDataType::General,
|
default: NodeInput::value(TaggedValue::F64(0.), true),
|
||||||
default: NodeInput::value(TaggedValue::F64(0.), true),
|
}],
|
||||||
},
|
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "Uniform",
|
name: "Uniform",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -1201,7 +1031,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1225,18 +1055,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
inputs: vec![
|
inputs: vec![DocumentInputType {
|
||||||
DocumentInputType {
|
name: "In",
|
||||||
name: "In",
|
data_type: FrontendGraphDataType::General,
|
||||||
data_type: FrontendGraphDataType::General,
|
default: NodeInput::value(TaggedValue::None, true),
|
||||||
default: NodeInput::value(TaggedValue::None, true),
|
}],
|
||||||
},
|
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "Storage",
|
name: "Storage",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -1252,7 +1075,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 2)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1282,11 +1105,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
default: NodeInput::value(TaggedValue::None, true),
|
default: NodeInput::value(TaggedValue::None, true),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "In",
|
name: "In",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -1309,7 +1127,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1344,11 +1162,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
default: NodeInput::network(concrete!(gpu_executor::PipelineLayout<WgpuExecutor>), 0),
|
default: NodeInput::network(concrete!(gpu_executor::PipelineLayout<WgpuExecutor>), 0),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 1),
|
|
||||||
},
|
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "In",
|
name: "In",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -1410,7 +1223,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1434,18 +1247,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
inputs: vec![
|
inputs: vec![DocumentInputType {
|
||||||
DocumentInputType {
|
name: "In",
|
||||||
name: "In",
|
data_type: FrontendGraphDataType::General,
|
||||||
data_type: FrontendGraphDataType::General,
|
default: NodeInput::value(TaggedValue::None, true),
|
||||||
default: NodeInput::value(TaggedValue::None, true),
|
}],
|
||||||
},
|
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "PipelineResult",
|
name: "PipelineResult",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -1461,7 +1267,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1485,18 +1291,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
inputs: vec![
|
inputs: vec![DocumentInputType {
|
||||||
DocumentInputType {
|
name: "In",
|
||||||
name: "In",
|
data_type: FrontendGraphDataType::General,
|
||||||
data_type: FrontendGraphDataType::General,
|
default: NodeInput::value(TaggedValue::None, true),
|
||||||
default: NodeInput::value(TaggedValue::None, true),
|
}],
|
||||||
},
|
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "Buffer",
|
name: "Buffer",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -1512,7 +1311,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Create Gpu Surface".to_string(),
|
name: "Create Gpu Surface".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 0)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("gpu_executor::CreateGpuSurfaceNode")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("gpu_executor::CreateGpuSurfaceNode")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1530,11 +1329,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
inputs: vec![DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
}],
|
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "GpuSurface",
|
name: "GpuSurface",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -1550,7 +1344,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 2)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1582,11 +1376,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
default: NodeInput::value(TaggedValue::None, true),
|
default: NodeInput::value(TaggedValue::None, true),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
|
||||||
name: "EditorApi",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "RenderedTexture",
|
name: "RenderedTexture",
|
||||||
@@ -1603,7 +1392,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
inputs: vec![NodeInput::scope("editor-api")],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1627,18 +1416,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
inputs: vec![
|
inputs: vec![DocumentInputType {
|
||||||
DocumentInputType {
|
name: "In",
|
||||||
name: "In",
|
data_type: FrontendGraphDataType::General,
|
||||||
data_type: FrontendGraphDataType::General,
|
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
}],
|
||||||
},
|
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
outputs: vec![DocumentOutputType {
|
outputs: vec![DocumentOutputType {
|
||||||
name: "Texture",
|
name: "Texture",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
@@ -1657,11 +1439,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
default: NodeInput::value(TaggedValue::DocumentNode(DocumentNode::default()), true),
|
default: NodeInput::value(TaggedValue::DocumentNode(DocumentNode::default()), true),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
|
||||||
name: "In",
|
|
||||||
data_type: FrontendGraphDataType::General,
|
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -2414,7 +2191,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
category: "Vector",
|
category: "Vector",
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_core::text::TextGeneratorNode<_, _, _>"),
|
implementation: DocumentNodeImplementation::proto("graphene_core::text::TextGeneratorNode<_, _, _>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::none(),
|
DocumentInputType::new("Editor API", FrontendGraphDataType::General, NodeInput::scope("editor-api")),
|
||||||
DocumentInputType::value("Text", TaggedValue::String("Lorem ipsum".to_string()), false),
|
DocumentInputType::value("Text", TaggedValue::String("Lorem ipsum".to_string()), false),
|
||||||
DocumentInputType::value(
|
DocumentInputType::value(
|
||||||
"Font",
|
"Font",
|
||||||
@@ -2424,7 +2201,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
DocumentInputType::value("Size", TaggedValue::F64(24.), false),
|
DocumentInputType::value("Size", TaggedValue::F64(24.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::VectorData)],
|
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::VectorData)],
|
||||||
properties: node_properties::node_section_font,
|
properties: node_properties::text_properties,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
@@ -2895,7 +2672,7 @@ pub static IMAGINATE_NODE: Lazy<DocumentNodeDefinition> = Lazy::new(|| DocumentN
|
|||||||
name: "Imaginate".into(),
|
name: "Imaginate".into(),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::node(NodeId(0), 0),
|
NodeInput::node(NodeId(0), 0),
|
||||||
NodeInput::network(concrete!(WasmEditorApi), 1),
|
NodeInput::scope("editor-api"),
|
||||||
NodeInput::network(concrete!(ImaginateController), 2),
|
NodeInput::network(concrete!(ImaginateController), 2),
|
||||||
NodeInput::network(concrete!(f64), 3),
|
NodeInput::network(concrete!(f64), 3),
|
||||||
NodeInput::network(concrete!(Option<DVec2>), 4),
|
NodeInput::network(concrete!(Option<DVec2>), 4),
|
||||||
@@ -2925,7 +2702,7 @@ pub static IMAGINATE_NODE: Lazy<DocumentNodeDefinition> = Lazy::new(|| DocumentN
|
|||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Editor Api",
|
name: "Editor Api",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
default: NodeInput::network(concrete!(WasmEditorApi), 0),
|
default: NodeInput::scope("editor-api"),
|
||||||
},
|
},
|
||||||
DocumentInputType::value("Controller", TaggedValue::ImaginateController(Default::default()), false),
|
DocumentInputType::value("Controller", TaggedValue::ImaginateController(Default::default()), false),
|
||||||
DocumentInputType::value("Seed", TaggedValue::U64(0), false), // Remember to keep index used in `ImaginateRandom` updated with this entry's index
|
DocumentInputType::value("Seed", TaggedValue::U64(0), false), // Remember to keep index used in `ImaginateRandom` updated with this entry's index
|
||||||
@@ -2992,18 +2769,9 @@ impl DocumentNodeDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap_network_in_scope(mut network: NodeNetwork, hash: u64) -> NodeNetwork {
|
pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEditorApi>) -> NodeNetwork {
|
||||||
network.generate_node_paths(&[]);
|
network.generate_node_paths(&[]);
|
||||||
|
|
||||||
let mut begin_scope = resolve_document_node_type("Begin Scope")
|
|
||||||
.expect("Begin Scope node type not found")
|
|
||||||
.to_document_node(vec![NodeInput::network(concrete!(WasmEditorApi), 0)], DocumentNodeMetadata::position((-12, -3)));
|
|
||||||
if let DocumentNodeImplementation::Network(g) = &mut begin_scope.implementation {
|
|
||||||
if let Some(node) = g.nodes.get_mut(&NodeId(0)) {
|
|
||||||
node.world_state_hash = hash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inner_network = DocumentNode {
|
let inner_network = DocumentNode {
|
||||||
name: "Scope".to_string(),
|
name: "Scope".to_string(),
|
||||||
implementation: DocumentNodeImplementation::Network(network),
|
implementation: DocumentNodeImplementation::Network(network),
|
||||||
@@ -3014,13 +2782,13 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, hash: u64) -> NodeNetwork
|
|||||||
|
|
||||||
let render_node = graph_craft::document::DocumentNode {
|
let render_node = graph_craft::document::DocumentNode {
|
||||||
name: "Output".into(),
|
name: "Output".into(),
|
||||||
inputs: vec![NodeInput::node(NodeId(1), 0), NodeInput::node(NodeId(0), 1)],
|
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(2), 0)],
|
||||||
implementation: graph_craft::document::DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: graph_craft::document::DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(2), 0)],
|
exports: vec![NodeInput::node(NodeId(2), 0)],
|
||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Create Canvas".to_string(),
|
name: "Create Canvas".to_string(),
|
||||||
inputs: vec![NodeInput::network(concrete!(WasmEditorApi), 1)],
|
inputs: vec![NodeInput::network(concrete!(&WasmEditorApi), 1)],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
|
||||||
skip_deduplication: true,
|
skip_deduplication: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -3032,10 +2800,11 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, hash: u64) -> NodeNetwork
|
|||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
// TODO: Add conversion step
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "RenderNode".to_string(),
|
name: "RenderNode".to_string(),
|
||||||
|
manual_composition: Some(concrete!(RenderConfig)),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::network(concrete!(WasmEditorApi), 1),
|
|
||||||
NodeInput::network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T))), 0),
|
NodeInput::network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T))), 0),
|
||||||
NodeInput::node(NodeId(1), 0),
|
NodeInput::node(NodeId(1), 0),
|
||||||
],
|
],
|
||||||
@@ -3055,17 +2824,20 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, hash: u64) -> NodeNetwork
|
|||||||
|
|
||||||
// wrap the inner network in a scope
|
// wrap the inner network in a scope
|
||||||
let nodes = vec![
|
let nodes = vec![
|
||||||
begin_scope,
|
|
||||||
inner_network,
|
inner_network,
|
||||||
render_node,
|
render_node,
|
||||||
resolve_document_node_type("End Scope")
|
DocumentNode {
|
||||||
.expect("End Scope node type not found")
|
name: "Editor Api".into(),
|
||||||
.to_document_node(vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(2), 0)], DocumentNodeMetadata::position((2, -3))),
|
implementation: DocumentNodeImplementation::proto("graphene_core::ops::IdentityNode"),
|
||||||
|
inputs: vec![NodeInput::value(TaggedValue::EditorApi(editor_api), false)],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
NodeNetwork {
|
NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(3), 0)],
|
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||||
nodes: nodes.into_iter().enumerate().map(|(id, node)| (NodeId(id as u64), node)).collect(),
|
nodes: nodes.into_iter().enumerate().map(|(id, node)| (NodeId(id as u64), node)).collect(),
|
||||||
|
scope_injections: [("editor-api".to_string(), (NodeId(2), concrete!(&WasmEditorApi)))].into_iter().collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3097,7 +2869,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, hash: u64) -> NodeNetwork
|
|||||||
// let mut network = NodeNetwork { ..Default::default() };
|
// let mut network = NodeNetwork { ..Default::default() };
|
||||||
// network.push_node_to_document_network(text_generator.to_document_node(
|
// network.push_node_to_document_network(text_generator.to_document_node(
|
||||||
// [
|
// [
|
||||||
// NodeInput::network(concrete!(WasmEditorApi), 0),
|
// NodeInput::scope("editor-api"),
|
||||||
// NodeInput::value(TaggedValue::String(text), false),
|
// NodeInput::value(TaggedValue::String(text), false),
|
||||||
// NodeInput::value(TaggedValue::Font(font), false),
|
// NodeInput::value(TaggedValue::Font(font), false),
|
||||||
// NodeInput::value(TaggedValue::F64(size), false),
|
// NodeInput::value(TaggedValue::F64(size), false),
|
||||||
|
|||||||
@@ -295,8 +295,8 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||||||
NumberInput::new(Some(dvec2.x))
|
NumberInput::new(Some(dvec2.x))
|
||||||
.label(x)
|
.label(x)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -304,8 +304,8 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||||||
NumberInput::new(Some(dvec2.y))
|
NumberInput::new(Some(dvec2.y))
|
||||||
.label(y)
|
.label(y)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -323,8 +323,8 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||||||
.int()
|
.int()
|
||||||
.label(x)
|
.label(x)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(update_x, node_id, index))
|
.on_update(update_value(update_x, node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -333,8 +333,8 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||||||
.int()
|
.int()
|
||||||
.label(y)
|
.label(y)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(update_y, node_id, index))
|
.on_update(update_value(update_y, node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -353,7 +353,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||||||
.label(x)
|
.label(x)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(0.))
|
.min(min.unwrap_or(0.))
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(update_x, node_id, index))
|
.on_update(update_value(update_x, node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -363,7 +363,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||||||
.label(y)
|
.label(y)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(0.))
|
.min(min.unwrap_or(0.))
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(update_y, node_id, index))
|
.on_update(update_value(update_y, node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -1769,7 +1769,7 @@ pub fn rasterize_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
|||||||
footprint_widget(document_node, node_id, 1)
|
footprint_widget(document_node, node_id, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_section_font(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn text_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let text = text_area_widget(document_node, node_id, 1, "Text", true);
|
let text = text_area_widget(document_node, node_id, 1, "Text", true);
|
||||||
let (font, style) = font_inputs(document_node, node_id, 2, "Font", true);
|
let (font, style) = font_inputs(document_node, node_id, 2, "Font", true);
|
||||||
let size = number_widget(document_node, node_id, 3, "Size", NumberInput::default().unit(" px").min(1.), true);
|
let size = number_widget(document_node, node_id, 3, "Size", NumberInput::default().unit(" px").min(1.), true);
|
||||||
|
|||||||
@@ -177,13 +177,13 @@ impl DocumentMetadata {
|
|||||||
|
|
||||||
// Should refer to output node
|
// Should refer to output node
|
||||||
|
|
||||||
let mut awaiting_horizontal_flow = vec![(NodeId(std::u64::MAX), LayerNodeIdentifier::ROOT_PARENT)];
|
let mut awaiting_horizontal_flow = vec![(NodeId(u64::MAX), LayerNodeIdentifier::ROOT_PARENT)];
|
||||||
let mut awaiting_primary_flow = vec![];
|
let mut awaiting_primary_flow = vec![];
|
||||||
|
|
||||||
while let Some((horizontal_root_node_id, mut parent_layer_node)) = awaiting_horizontal_flow.pop() {
|
while let Some((horizontal_root_node_id, mut parent_layer_node)) = awaiting_horizontal_flow.pop() {
|
||||||
let horizontal_flow_iter = graph.upstream_flow_back_from_nodes(vec![horizontal_root_node_id], FlowType::HorizontalFlow);
|
let horizontal_flow_iter = graph.upstream_flow_back_from_nodes(vec![horizontal_root_node_id], FlowType::HorizontalFlow);
|
||||||
// Skip the horizontal_root_node_id node
|
// Skip the horizontal_root_node_id node
|
||||||
for (current_node, current_node_id) in horizontal_flow_iter.skip(if horizontal_root_node_id == NodeId(std::u64::MAX) { 0 } else { 1 }) {
|
for (current_node, current_node_id) in horizontal_flow_iter.skip(if horizontal_root_node_id == NodeId(u64::MAX) { 0 } else { 1 }) {
|
||||||
if !current_node.visible {
|
if !current_node.visible {
|
||||||
self.hidden.insert(current_node_id);
|
self.hidden.insert(current_node_id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
|||||||
.unit(" px")
|
.unit(" px")
|
||||||
.label("Weight")
|
.label("Weight")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(|number_input: &NumberInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
.on_update(|number_input: &NumberInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
.widget_holder()
|
.widget_holder()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
|||||||
.unit(" px")
|
.unit(" px")
|
||||||
.label("Weight")
|
.label("Weight")
|
||||||
.min(1.)
|
.min(1.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
.on_update(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
.widget_holder()
|
.widget_holder()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
|||||||
.unit(" px")
|
.unit(" px")
|
||||||
.label("Weight")
|
.label("Weight")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
.on_update(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
.widget_holder()
|
.widget_holder()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ impl LayoutHolder for PathTool {
|
|||||||
.label("X")
|
.label("X")
|
||||||
.min_width(120)
|
.min_width(120)
|
||||||
.disabled(x.is_none())
|
.disabled(x.is_none())
|
||||||
.min(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64))
|
.min(-((1_u64 << f64::MANTISSA_DIGITS) as f64))
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(move |number_input: &NumberInput| {
|
.on_update(move |number_input: &NumberInput| {
|
||||||
if let Some(new_x) = number_input.value.or(x) {
|
if let Some(new_x) = number_input.value.or(x) {
|
||||||
PathToolMessage::SelectedPointXChanged { new_x }.into()
|
PathToolMessage::SelectedPointXChanged { new_x }.into()
|
||||||
@@ -114,8 +114,8 @@ impl LayoutHolder for PathTool {
|
|||||||
.label("Y")
|
.label("Y")
|
||||||
.min_width(120)
|
.min_width(120)
|
||||||
.disabled(y.is_none())
|
.disabled(y.is_none())
|
||||||
.min(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64))
|
.min(-((1_u64 << f64::MANTISSA_DIGITS) as f64))
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(move |number_input: &NumberInput| {
|
.on_update(move |number_input: &NumberInput| {
|
||||||
if let Some(new_y) = number_input.value.or(y) {
|
if let Some(new_y) = number_input.value.or(y) {
|
||||||
PathToolMessage::SelectedPointYChanged { new_y }.into()
|
PathToolMessage::SelectedPointYChanged { new_y }.into()
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
|||||||
.unit(" px")
|
.unit(" px")
|
||||||
.label("Weight")
|
.label("Weight")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
.on_update(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
.widget_holder()
|
.widget_holder()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
|||||||
.unit(" px")
|
.unit(" px")
|
||||||
.label("Weight")
|
.label("Weight")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(|number_input: &NumberInput| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
.on_update(|number_input: &NumberInput| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
.widget_holder()
|
.widget_holder()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
|||||||
.unit(" px")
|
.unit(" px")
|
||||||
.label("Weight")
|
.label("Weight")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(|number_input: &NumberInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
.on_update(|number_input: &NumberInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
.widget_holder()
|
.widget_holder()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
|||||||
.unit(" px")
|
.unit(" px")
|
||||||
.label("Weight")
|
.label("Weight")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
.on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
.widget_holder()
|
.widget_holder()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ fn create_text_widgets(tool: &TextTool) -> Vec<WidgetHolder> {
|
|||||||
.label("Size")
|
.label("Size")
|
||||||
.int()
|
.int()
|
||||||
.min(1.)
|
.min(1.)
|
||||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap() as u32)).into())
|
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap() as u32)).into())
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
vec![
|
vec![
|
||||||
|
|||||||
@@ -39,14 +39,11 @@ pub struct NodeRuntime {
|
|||||||
executor: DynamicExecutor,
|
executor: DynamicExecutor,
|
||||||
receiver: Receiver<NodeRuntimeMessage>,
|
receiver: Receiver<NodeRuntimeMessage>,
|
||||||
sender: InternalNodeGraphUpdateSender,
|
sender: InternalNodeGraphUpdateSender,
|
||||||
|
|
||||||
/// Font data (for rendering text) made available to the graph through the [`WasmEditorApi`].
|
|
||||||
font_cache: FontCache,
|
|
||||||
/// Imaginate preferences made available to the graph through the [`WasmEditorApi`].
|
|
||||||
imaginate_preferences: ImaginatePreferences,
|
imaginate_preferences: ImaginatePreferences,
|
||||||
|
recompile_graph: bool,
|
||||||
|
|
||||||
|
editor_api: Arc<WasmEditorApi>,
|
||||||
|
|
||||||
/// Gives access to APIs like a rendering surface (native window handle or HTML5 canvas) and WGPU (which becomes WebGPU on web).
|
|
||||||
wasm_application_io: Option<WasmApplicationIo>,
|
|
||||||
graph_hash: Option<u64>,
|
graph_hash: Option<u64>,
|
||||||
node_graph_errors: GraphErrors,
|
node_graph_errors: GraphErrors,
|
||||||
resolved_types: ResolvedDocumentNodeTypes,
|
resolved_types: ResolvedDocumentNodeTypes,
|
||||||
@@ -89,7 +86,7 @@ pub struct ExecutionRequest {
|
|||||||
pub struct ExecutionResponse {
|
pub struct ExecutionResponse {
|
||||||
execution_id: u64,
|
execution_id: u64,
|
||||||
result: Result<TaggedValue, String>,
|
result: Result<TaggedValue, String>,
|
||||||
responses: VecDeque<Message>,
|
responses: VecDeque<FrontendMessage>,
|
||||||
new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
||||||
new_vector_modify: HashMap<NodeId, VectorData>,
|
new_vector_modify: HashMap<NodeId, VectorData>,
|
||||||
new_upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
|
new_upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
|
||||||
@@ -103,6 +100,7 @@ pub enum NodeGraphUpdate {
|
|||||||
NodeGraphUpdateMessage(NodeGraphUpdateMessage),
|
NodeGraphUpdateMessage(NodeGraphUpdateMessage),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct InternalNodeGraphUpdateSender(Sender<NodeGraphUpdate>);
|
struct InternalNodeGraphUpdateSender(Sender<NodeGraphUpdate>);
|
||||||
|
|
||||||
impl InternalNodeGraphUpdateSender {
|
impl InternalNodeGraphUpdateSender {
|
||||||
@@ -126,12 +124,19 @@ impl NodeRuntime {
|
|||||||
Self {
|
Self {
|
||||||
executor: DynamicExecutor::default(),
|
executor: DynamicExecutor::default(),
|
||||||
receiver,
|
receiver,
|
||||||
sender: InternalNodeGraphUpdateSender(sender),
|
sender: InternalNodeGraphUpdateSender(sender.clone()),
|
||||||
|
imaginate_preferences: ImaginatePreferences::default(),
|
||||||
|
recompile_graph: true,
|
||||||
|
|
||||||
font_cache: FontCache::default(),
|
editor_api: WasmEditorApi {
|
||||||
imaginate_preferences: Default::default(),
|
font_cache: FontCache::default(),
|
||||||
|
imaginate_preferences: Box::new(ImaginatePreferences::default()),
|
||||||
|
node_graph_message_sender: Box::new(InternalNodeGraphUpdateSender(sender)),
|
||||||
|
|
||||||
|
application_io: None,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
|
||||||
wasm_application_io: None,
|
|
||||||
graph_hash: None,
|
graph_hash: None,
|
||||||
node_graph_errors: Vec::new(),
|
node_graph_errors: Vec::new(),
|
||||||
resolved_types: ResolvedDocumentNodeTypes::default(),
|
resolved_types: ResolvedDocumentNodeTypes::default(),
|
||||||
@@ -153,8 +158,26 @@ impl NodeRuntime {
|
|||||||
requests.reverse();
|
requests.reverse();
|
||||||
for request in requests {
|
for request in requests {
|
||||||
match request {
|
match request {
|
||||||
NodeRuntimeMessage::FontCacheUpdate(font_cache) => self.font_cache = font_cache,
|
NodeRuntimeMessage::FontCacheUpdate(font_cache) => {
|
||||||
NodeRuntimeMessage::ImaginatePreferencesUpdate(preferences) => self.imaginate_preferences = preferences,
|
self.editor_api = WasmEditorApi {
|
||||||
|
font_cache,
|
||||||
|
application_io: self.editor_api.application_io.clone(),
|
||||||
|
node_graph_message_sender: Box::new(self.sender.clone()),
|
||||||
|
imaginate_preferences: Box::new(self.imaginate_preferences.clone()),
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
self.recompile_graph = true;
|
||||||
|
}
|
||||||
|
NodeRuntimeMessage::ImaginatePreferencesUpdate(preferences) => {
|
||||||
|
self.editor_api = WasmEditorApi {
|
||||||
|
font_cache: self.editor_api.font_cache.clone(),
|
||||||
|
application_io: self.editor_api.application_io.clone(),
|
||||||
|
node_graph_message_sender: Box::new(self.sender.clone()),
|
||||||
|
imaginate_preferences: Box::new(preferences),
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
self.recompile_graph = true;
|
||||||
|
}
|
||||||
NodeRuntimeMessage::ExecutionRequest(ExecutionRequest {
|
NodeRuntimeMessage::ExecutionRequest(ExecutionRequest {
|
||||||
execution_id, graph, render_config, ..
|
execution_id, graph, render_config, ..
|
||||||
}) => {
|
}) => {
|
||||||
@@ -182,23 +205,21 @@ impl NodeRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn execute_network(&mut self, graph: NodeNetwork, render_config: RenderConfig) -> Result<TaggedValue, String> {
|
async fn execute_network(&mut self, graph: NodeNetwork, render_config: RenderConfig) -> Result<TaggedValue, String> {
|
||||||
if self.wasm_application_io.is_none() {
|
if self.editor_api.application_io.is_none() {
|
||||||
self.wasm_application_io = Some(WasmApplicationIo::new().await);
|
self.editor_api = WasmEditorApi {
|
||||||
|
application_io: Some(WasmApplicationIo::new().await.into()),
|
||||||
|
font_cache: self.editor_api.font_cache.clone(),
|
||||||
|
node_graph_message_sender: Box::new(self.sender.clone()),
|
||||||
|
imaginate_preferences: Box::new(ImaginatePreferences::default()),
|
||||||
|
}
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
let editor_api = WasmEditorApi {
|
let editor_api = &self.editor_api;
|
||||||
font_cache: &self.font_cache,
|
|
||||||
imaginate_preferences: &self.imaginate_preferences,
|
|
||||||
application_io: self.wasm_application_io.as_ref().unwrap(),
|
|
||||||
node_graph_message_sender: &self.sender,
|
|
||||||
render_config,
|
|
||||||
image_frame: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Required to ensure that the appropriate proto nodes are reinserted when the Editor API changes.
|
// Required to ensure that the appropriate proto nodes are reinserted when the Editor API changes.
|
||||||
let mut graph_input_hash = DefaultHasher::new();
|
let mut graph_input_hash = DefaultHasher::new();
|
||||||
editor_api.font_cache.hash(&mut graph_input_hash);
|
editor_api.font_cache.hash(&mut graph_input_hash);
|
||||||
let font_hash_code = graph_input_hash.finish();
|
let _font_hash_code = graph_input_hash.finish();
|
||||||
graph.hash(&mut graph_input_hash);
|
graph.hash(&mut graph_input_hash);
|
||||||
let hash_code = graph_input_hash.finish();
|
let hash_code = graph_input_hash.finish();
|
||||||
|
|
||||||
@@ -206,8 +227,8 @@ impl NodeRuntime {
|
|||||||
self.graph_hash = None;
|
self.graph_hash = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.graph_hash.is_none() {
|
if self.graph_hash.is_none() || self.recompile_graph {
|
||||||
let scoped_network = wrap_network_in_scope(graph, font_hash_code);
|
let scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
|
||||||
|
|
||||||
self.monitor_nodes = scoped_network
|
self.monitor_nodes = scoped_network
|
||||||
.recursive_nodes()
|
.recursive_nodes()
|
||||||
@@ -235,7 +256,7 @@ impl NodeRuntime {
|
|||||||
use graph_craft::graphene_compiler::Executor;
|
use graph_craft::graphene_compiler::Executor;
|
||||||
|
|
||||||
let result = match self.executor.input_type() {
|
let result = match self.executor.input_type() {
|
||||||
Some(t) if t == concrete!(WasmEditorApi) => (&self.executor).execute(editor_api).await.map_err(|e| e.to_string()),
|
Some(t) if t == concrete!(RenderConfig) => (&self.executor).execute(render_config).await.map_err(|e| e.to_string()),
|
||||||
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
|
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
|
||||||
Some(t) => Err(format!("Invalid input type {t:?}")),
|
Some(t) => Err(format!("Invalid input type {t:?}")),
|
||||||
_ => Err(format!("No input type:\n{:?}", self.node_graph_errors)),
|
_ => Err(format!("No input type:\n{:?}", self.node_graph_errors)),
|
||||||
@@ -259,7 +280,7 @@ impl NodeRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates state data
|
/// Updates state data
|
||||||
pub fn process_monitor_nodes(&mut self, responses: &mut VecDeque<Message>) {
|
pub fn process_monitor_nodes(&mut self, responses: &mut VecDeque<FrontendMessage>) {
|
||||||
// TODO: Consider optimizing this since it's currently O(m*n^2), with a sort it could be made O(m * n*log(n))
|
// TODO: Consider optimizing this since it's currently O(m*n^2), with a sort it could be made O(m * n*log(n))
|
||||||
self.thumbnail_renders.retain(|id, _| self.monitor_nodes.iter().any(|monitor_node_path| monitor_node_path.contains(id)));
|
self.thumbnail_renders.retain(|id, _| self.monitor_nodes.iter().any(|monitor_node_path| monitor_node_path.contains(id)));
|
||||||
|
|
||||||
@@ -325,7 +346,7 @@ impl NodeRuntime {
|
|||||||
let old_thumbnail_svg = self.thumbnail_renders.entry(parent_network_node_id).or_default();
|
let old_thumbnail_svg = self.thumbnail_renders.entry(parent_network_node_id).or_default();
|
||||||
|
|
||||||
if old_thumbnail_svg != &new_thumbnail_svg {
|
if old_thumbnail_svg != &new_thumbnail_svg {
|
||||||
responses.add(FrontendMessage::UpdateNodeThumbnail {
|
responses.push_back(FrontendMessage::UpdateNodeThumbnail {
|
||||||
id: parent_network_node_id,
|
id: parent_network_node_id,
|
||||||
value: new_thumbnail_svg.to_svg_string(),
|
value: new_thumbnail_svg.to_svg_string(),
|
||||||
});
|
});
|
||||||
@@ -576,7 +597,7 @@ impl NodeGraphExecutor {
|
|||||||
transform,
|
transform,
|
||||||
} = execution_response;
|
} = execution_response;
|
||||||
|
|
||||||
responses.extend(existing_responses);
|
responses.extend(existing_responses.into_iter().map(Into::into));
|
||||||
responses.add(NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors });
|
responses.add(NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors });
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
use crate::raster::ImageFrame;
|
|
||||||
use crate::text::FontCache;
|
use crate::text::FontCache;
|
||||||
use crate::transform::{Footprint, Transform, TransformMut};
|
use crate::transform::{Footprint, Transform, TransformMut};
|
||||||
use crate::vector::style::ViewMode;
|
use crate::vector::style::ViewMode;
|
||||||
use crate::{Color, Node};
|
|
||||||
|
|
||||||
use dyn_any::{StaticType, StaticTypeSized};
|
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
use core::hash::{Hash, Hasher};
|
use core::hash::{Hash, Hasher};
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
|
use core::ptr::addr_of;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
@@ -133,6 +132,12 @@ pub trait NodeGraphUpdateSender {
|
|||||||
fn send(&self, message: NodeGraphUpdateMessage);
|
fn send(&self, message: NodeGraphUpdateMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: NodeGraphUpdateSender> NodeGraphUpdateSender for std::sync::Mutex<T> {
|
||||||
|
fn send(&self, message: NodeGraphUpdateMessage) {
|
||||||
|
self.lock().as_mut().unwrap().send(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait GetImaginatePreferences {
|
pub trait GetImaginatePreferences {
|
||||||
fn get_host_name(&self) -> &str;
|
fn get_host_name(&self) -> &str;
|
||||||
}
|
}
|
||||||
@@ -148,7 +153,7 @@ pub enum ExportFormat {
|
|||||||
Canvas,
|
Canvas,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny)]
|
||||||
pub struct RenderConfig {
|
pub struct RenderConfig {
|
||||||
pub viewport: Footprint,
|
pub viewport: Footprint,
|
||||||
pub export_format: ExportFormat,
|
pub export_format: ExportFormat,
|
||||||
@@ -157,82 +162,69 @@ pub struct RenderConfig {
|
|||||||
pub for_export: bool,
|
pub for_export: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EditorApi<'a, Io> {
|
struct Logger;
|
||||||
// TODO: Is `image_frame` still used? I think it's only ever set to None.
|
|
||||||
pub image_frame: Option<ImageFrame<Color>>,
|
impl NodeGraphUpdateSender for Logger {
|
||||||
pub font_cache: &'a FontCache,
|
fn send(&self, message: NodeGraphUpdateMessage) {
|
||||||
pub application_io: &'a Io,
|
log::warn!("dispatching message with fallback node graph update sender {:?}", message);
|
||||||
pub node_graph_message_sender: &'a dyn NodeGraphUpdateSender,
|
}
|
||||||
pub imaginate_preferences: &'a dyn GetImaginatePreferences,
|
|
||||||
pub render_config: RenderConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Io> Clone for EditorApi<'a, Io> {
|
struct DummyPreferences;
|
||||||
fn clone(&self) -> Self {
|
|
||||||
|
impl GetImaginatePreferences for DummyPreferences {
|
||||||
|
fn get_host_name(&self) -> &str {
|
||||||
|
"dummy_endpoint"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EditorApi<Io> {
|
||||||
|
/// Font data (for rendering text) made available to the graph through the [`WasmEditorApi`].
|
||||||
|
pub font_cache: FontCache,
|
||||||
|
/// Gives access to APIs like a rendering surface (native window handle or HTML5 canvas) and WGPU (which becomes WebGPU on web).
|
||||||
|
pub application_io: Option<Arc<Io>>,
|
||||||
|
pub node_graph_message_sender: Box<dyn NodeGraphUpdateSender + Send + Sync>,
|
||||||
|
/// Imaginate preferences made available to the graph through the [`WasmEditorApi`].
|
||||||
|
pub imaginate_preferences: Box<dyn GetImaginatePreferences + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Io> Eq for EditorApi<Io> {}
|
||||||
|
|
||||||
|
impl<Io: Default> Default for EditorApi<Io> {
|
||||||
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
image_frame: self.image_frame.clone(),
|
font_cache: FontCache::default(),
|
||||||
font_cache: self.font_cache,
|
application_io: None,
|
||||||
application_io: self.application_io,
|
node_graph_message_sender: Box::new(Logger),
|
||||||
node_graph_message_sender: self.node_graph_message_sender,
|
imaginate_preferences: Box::new(DummyPreferences),
|
||||||
imaginate_preferences: self.imaginate_preferences,
|
|
||||||
render_config: self.render_config,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> PartialEq for EditorApi<'a, T> {
|
impl<Io> Hash for EditorApi<Io> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.image_frame == other.image_frame && self.font_cache == other.font_cache
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Hash for EditorApi<'a, T> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.image_frame.hash(state);
|
|
||||||
self.font_cache.hash(state);
|
self.font_cache.hash(state);
|
||||||
|
self.application_io.as_ref().map_or(0, |io| io.as_ref() as *const _ as usize).hash(state);
|
||||||
|
(self.node_graph_message_sender.as_ref() as *const dyn NodeGraphUpdateSender).hash(state);
|
||||||
|
(self.imaginate_preferences.as_ref() as *const dyn GetImaginatePreferences).hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Debug for EditorApi<'a, T> {
|
impl<Io> PartialEq for EditorApi<Io> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.font_cache == other.font_cache
|
||||||
|
&& self.application_io.as_ref().map_or(0, |io| addr_of!(io) as usize) == other.application_io.as_ref().map_or(0, |io| addr_of!(io) as usize)
|
||||||
|
&& std::ptr::eq(self.node_graph_message_sender.as_ref() as *const _, other.node_graph_message_sender.as_ref() as *const _)
|
||||||
|
&& std::ptr::eq(self.imaginate_preferences.as_ref() as *const _, other.imaginate_preferences.as_ref() as *const _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Debug for EditorApi<T> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
f.debug_struct("EditorApi").field("image_frame", &self.image_frame).field("font_cache", &self.font_cache).finish()
|
f.debug_struct("EditorApi").field("font_cache", &self.font_cache).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: StaticTypeSized> StaticType for EditorApi<'_, T> {
|
unsafe impl<T: StaticTypeSized> StaticType for EditorApi<T> {
|
||||||
type Static = EditorApi<'static, T::Static>;
|
type Static = EditorApi<T::Static>;
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> AsRef<EditorApi<'a, T>> for EditorApi<'a, T> {
|
|
||||||
fn as_ref(&self) -> &EditorApi<'a, T> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required for the EndLetNode
|
|
||||||
impl<'a, IO> From<EditorApi<'a, IO>> for Footprint {
|
|
||||||
fn from(value: EditorApi<'a, IO>) -> Self {
|
|
||||||
value.render_config.viewport
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required for the EndLetNode
|
|
||||||
impl<'a, IO> From<EditorApi<'a, IO>> for () {
|
|
||||||
fn from(_value: EditorApi<'a, IO>) -> Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
pub struct ExtractImageFrame;
|
|
||||||
|
|
||||||
impl<'a: 'input, 'input, T> Node<'input, EditorApi<'a, T>> for ExtractImageFrame {
|
|
||||||
type Output = ImageFrame<Color>;
|
|
||||||
fn eval(&'input self, editor_api: EditorApi<'a, T>) -> Self::Output {
|
|
||||||
editor_api.image_frame.unwrap_or(ImageFrame::identity())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtractImageFrame {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ impl ClickTarget {
|
|||||||
/// Does the click target intersect the rectangle
|
/// Does the click target intersect the rectangle
|
||||||
pub fn intersect_rectangle(&self, document_quad: Quad, layer_transform: DAffine2) -> bool {
|
pub fn intersect_rectangle(&self, document_quad: Quad, layer_transform: DAffine2) -> bool {
|
||||||
// Check if the matrix is not invertible
|
// Check if the matrix is not invertible
|
||||||
if layer_transform.matrix2.determinant().abs() <= std::f64::EPSILON {
|
if layer_transform.matrix2.determinant().abs() <= f64::EPSILON {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let quad = layer_transform.inverse() * document_quad;
|
let quad = layer_transform.inverse() * document_quad;
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<&'i (dyn NodeIO<'i, I, Output = O> +
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub use crate::application_io::{ExtractImageFrame, SurfaceFrame, SurfaceId};
|
pub use crate::application_io::{SurfaceFrame, SurfaceId};
|
||||||
#[cfg(feature = "wasm")]
|
#[cfg(feature = "wasm")]
|
||||||
pub type WasmSurfaceHandle = application_io::SurfaceHandle<web_sys::HtmlCanvasElement>;
|
pub type WasmSurfaceHandle = application_io::SurfaceHandle<web_sys::HtmlCanvasElement>;
|
||||||
#[cfg(feature = "wasm")]
|
#[cfg(feature = "wasm")]
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use core::future::Future;
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
|
|
||||||
/// Caches the output of a given Node and acts as a proxy
|
/// Caches the output of a given Node and acts as a proxy
|
||||||
@@ -46,7 +45,7 @@ impl<T, CachedNode> MemoNode<T, CachedNode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Caches the output of a given Node and acts as a proxy.
|
/// Caches the output of a given Node and acts as a proxy.
|
||||||
/// In contrast to the relgular `MemoNode`. This node ignores all input.
|
/// In contrast to the regular `MemoNode`. This node ignores all input.
|
||||||
/// Using this node might result in the document not updating properly,
|
/// Using this node might result in the document not updating properly,
|
||||||
/// use with caution.
|
/// use with caution.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -137,85 +136,3 @@ impl<I, T, N> MonitorNode<I, T, N> {
|
|||||||
MonitorNode { io: Cell::new(None), node }
|
MonitorNode { io: Cell::new(None), node }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caches the output of a given Node and acts as a proxy
|
|
||||||
/// It provides two modes of operation, it can either be set
|
|
||||||
/// when calling the node with a `Some<T>` variant or the last
|
|
||||||
/// value that was added is returned when calling it with `None`
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct LetNode<T> {
|
|
||||||
// We have to use an append only data structure to make sure the references
|
|
||||||
// to the cache entries are always valid
|
|
||||||
// TODO: We only ever access the last value so there is not really a reason for us
|
|
||||||
// to store the previous entries. This should be reworked in the future
|
|
||||||
cache: Cell<Option<T>>,
|
|
||||||
}
|
|
||||||
impl<'i, T: 'i + Clone> Node<'i, Option<T>> for LetNode<T> {
|
|
||||||
type Output = T;
|
|
||||||
fn eval(&'i self, input: Option<T>) -> Self::Output {
|
|
||||||
if let Some(input) = input {
|
|
||||||
self.cache.set(Some(input.clone()));
|
|
||||||
input
|
|
||||||
} else {
|
|
||||||
let value = self.cache.take();
|
|
||||||
self.cache.set(value.clone());
|
|
||||||
value.expect("LetNode was not initialized. This can happen if you try to evaluate a node that depends on the EditorApi in the node_registry")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn reset(&self) {
|
|
||||||
self.cache.set(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> LetNode<T> {
|
|
||||||
pub fn new() -> LetNode<T> {
|
|
||||||
LetNode { cache: Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Caches the output of a given Node and acts as a proxy
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
|
||||||
pub struct EndLetNode<Input, Parameter> {
|
|
||||||
input: Input,
|
|
||||||
parameter: PhantomData<Parameter>,
|
|
||||||
}
|
|
||||||
impl<'i, T: 'i, Parameter: 'i + From<T>, Input> Node<'i, T> for EndLetNode<Input, Parameter>
|
|
||||||
where
|
|
||||||
Input: Node<'i, Parameter>,
|
|
||||||
{
|
|
||||||
type Output = <Input>::Output;
|
|
||||||
fn eval(&'i self, t: T) -> Self::Output {
|
|
||||||
let result = self.input.eval(Parameter::from(t));
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Input, Parameter> EndLetNode<Input, Parameter> {
|
|
||||||
pub const fn new(input: Input) -> EndLetNode<Input, Parameter> {
|
|
||||||
EndLetNode { input, parameter: PhantomData }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use crate::ops::SomeNode as InitNode;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
|
||||||
pub struct RefNode<T, Let> {
|
|
||||||
let_node: Let,
|
|
||||||
_t: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'i, T: 'i, Let> Node<'i, ()> for RefNode<T, Let>
|
|
||||||
where
|
|
||||||
Let: for<'a> Node<'a, Option<T>>,
|
|
||||||
{
|
|
||||||
type Output = <Let as Node<'i, Option<T>>>::Output;
|
|
||||||
fn eval(&'i self, _: ()) -> Self::Output {
|
|
||||||
self.let_node.eval(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Let, T> RefNode<T, Let> {
|
|
||||||
pub const fn new(let_node: Let) -> RefNode<T, Let> {
|
|
||||||
RefNode { let_node, _t: PhantomData }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ fn levels_node(color: Color, input_start: f64, input_mid: f64, input_end: f64, o
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Input levels (Range: 0-1)
|
// Input levels (Range: 0-1)
|
||||||
let highlights_minus_shadows = (input_highlights - input_shadows).max(f32::EPSILON).min(1.);
|
let highlights_minus_shadows = (input_highlights - input_shadows).clamp(f32::EPSILON, 1.);
|
||||||
let color = color.map_rgb(|c| ((c - input_shadows).max(0.) / highlights_minus_shadows).min(1.));
|
let color = color.map_rgb(|c| ((c - input_shadows).max(0.) / highlights_minus_shadows).min(1.));
|
||||||
|
|
||||||
// Midtones (Range: 0-1)
|
// Midtones (Range: 0-1)
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ impl CubicSplines {
|
|||||||
|
|
||||||
// Eliminate the current column in all rows below the current one
|
// Eliminate the current column in all rows below the current one
|
||||||
for row_below_current in row + 1..4 {
|
for row_below_current in row + 1..4 {
|
||||||
assert!(augmented_matrix[row][row].abs() > core::f32::EPSILON);
|
assert!(augmented_matrix[row][row].abs() > f32::EPSILON);
|
||||||
|
|
||||||
let scale_factor = augmented_matrix[row_below_current][row] / augmented_matrix[row][row];
|
let scale_factor = augmented_matrix[row_below_current][row] / augmented_matrix[row][row];
|
||||||
for col in row..5 {
|
for col in row..5 {
|
||||||
@@ -122,7 +122,7 @@ impl CubicSplines {
|
|||||||
// Gaussian elimination: back substitution
|
// Gaussian elimination: back substitution
|
||||||
let mut solutions = [0.; 4];
|
let mut solutions = [0.; 4];
|
||||||
for col in (0..4).rev() {
|
for col in (0..4).rev() {
|
||||||
assert!(augmented_matrix[col][col].abs() > core::f32::EPSILON);
|
assert!(augmented_matrix[col][col].abs() > f32::EPSILON);
|
||||||
|
|
||||||
solutions[col] = augmented_matrix[col][4] / augmented_matrix[col][col];
|
solutions[col] = augmented_matrix[col][4] / augmented_matrix[col][col];
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub struct TextGeneratorNode<Text, FontName, Size> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_fn(TextGeneratorNode)]
|
#[node_fn(TextGeneratorNode)]
|
||||||
fn generate_text<'a: 'input, T>(editor: EditorApi<'a, T>, text: String, font_name: Font, font_size: f64) -> crate::vector::VectorData {
|
fn generate_text<'a: 'input, T: 'a>(editor: &'a EditorApi<T>, text: String, font_name: Font, font_size: f64) -> crate::vector::VectorData {
|
||||||
let buzz_face = editor.font_cache.get(&font_name).map(|data| load_face(data));
|
let buzz_face = editor.font_cache.get(&font_name).map(|data| load_face(data));
|
||||||
crate::vector::VectorData::from_subpaths(to_path(&text, buzz_face, font_size, None), false)
|
crate::vector::VectorData::from_subpaths(to_path(&text, buzz_face, font_size, None), false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ impl Default for Font {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// A cache of all loaded font data and preview urls along with the default font (send from `init_app` in `editor_api.rs`)
|
/// A cache of all loaded font data and preview urls along with the default font (send from `init_app` in `editor_api.rs`)
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default, PartialEq)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default, PartialEq, DynAny)]
|
||||||
pub struct FontCache {
|
pub struct FontCache {
|
||||||
/// Actual font file data used for rendering a font with ttf_parser and rustybuzz
|
/// Actual font file data used for rendering a font with ttf_parser and rustybuzz
|
||||||
font_file_data: HashMap<Font, Vec<u8>>,
|
font_file_data: HashMap<Font, Vec<u8>>,
|
||||||
|
|||||||
@@ -39,6 +39,23 @@ impl<T> From<T> for ValueNode<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
pub struct AsRefNode<T: AsRef<U>, U>(pub T, PhantomData<U>);
|
||||||
|
|
||||||
|
impl<'i, T: 'i + AsRef<U>, U: 'i> Node<'i, ()> for AsRefNode<T, U> {
|
||||||
|
type Output = &'i U;
|
||||||
|
#[inline(always)]
|
||||||
|
fn eval(&'i self, _input: ()) -> Self::Output {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<U>, U> AsRefNode<T, U> {
|
||||||
|
pub const fn new(value: T) -> AsRefNode<T, U> {
|
||||||
|
AsRefNode(value, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct RefCellMutNode<T>(pub RefCell<T>);
|
pub struct RefCellMutNode<T>(pub RefCell<T>);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use graph_craft::{proto::ProtoNetwork, Type};
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
pub fn compile_spirv(request: &CompileRequest, compile_dir: Option<&str>, manifest_path: &str) -> anyhow::Result<Vec<u8>> {
|
pub fn compile_spirv(request: &CompileRequest, compile_dir: Option<&str>, manifest_path: &str) -> anyhow::Result<Vec<u8>> {
|
||||||
let serialized_graph = serde_json::to_string(&gpu_executor::CompileRequest {
|
let serialized_graph = serde_json::to_string(&graph_craft::graphene_compiler::CompileRequest {
|
||||||
networks: request.networks.clone(),
|
networks: request.networks.clone(),
|
||||||
io: request.shader_io.clone(),
|
io: request.shader_io.clone(),
|
||||||
})?;
|
})?;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ node-macro = { path = "../node-macro" }
|
|||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
graphene-core = { workspace = true, features = ["std", "alloc", "gpu"] }
|
graphene-core = { workspace = true, features = ["std", "alloc", "gpu"] }
|
||||||
graph-craft = { workspace = true }
|
|
||||||
dyn-any = { workspace = true, features = ["log-bad-types", "rc", "glam"] }
|
dyn-any = { workspace = true, features = ["log-bad-types", "rc", "glam"] }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
use bytemuck::{Pod, Zeroable};
|
use dyn_any::{StaticType, StaticTypeSized};
|
||||||
use graph_craft::proto::ProtoNetwork;
|
use graphene_core::application_io::{ApplicationIo, EditorApi, SurfaceHandle};
|
||||||
|
use graphene_core::raster::{Image, ImageFrame, Pixel, SRGBA8};
|
||||||
use graphene_core::*;
|
use graphene_core::*;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use dyn_any::{StaticType, StaticTypeSized};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use glam::{DAffine2, UVec3};
|
use glam::{DAffine2, UVec3};
|
||||||
use graphene_core::application_io::{ApplicationIo, EditorApi, SurfaceHandle};
|
|
||||||
use graphene_core::raster::{Image, ImageFrame, Pixel, SRGBA8};
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -61,15 +59,9 @@ pub trait GpuExecutor {
|
|||||||
fn create_surface(&self, window: SurfaceHandle<Self::Window>) -> Result<SurfaceHandle<Self::Surface<'_>>>;
|
fn create_surface(&self, window: SurfaceHandle<Self::Window>) -> Result<SurfaceHandle<Self::Surface<'_>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SpirVCompiler {
|
// pub trait SpirVCompiler {
|
||||||
fn compile(&self, network: &[ProtoNetwork], io: &ShaderIO) -> Result<Shader>;
|
// fn compile(&self, network: &[ProtoNetwork], io: &ShaderIO) -> Result<Shader>;
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct CompileRequest {
|
|
||||||
pub networks: Vec<ProtoNetwork>,
|
|
||||||
pub io: ShaderIO,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
/// GPU constants that can be used as inputs to a shader.
|
/// GPU constants that can be used as inputs to a shader.
|
||||||
@@ -496,9 +488,9 @@ async fn read_output_buffer_node<'a: 'input, E: 'a + GpuExecutor>(buffer: Arc<Sh
|
|||||||
pub struct CreateGpuSurfaceNode {}
|
pub struct CreateGpuSurfaceNode {}
|
||||||
|
|
||||||
#[node_macro::node_fn(CreateGpuSurfaceNode)]
|
#[node_macro::node_fn(CreateGpuSurfaceNode)]
|
||||||
async fn create_gpu_surface<'a: 'input, E: 'a + GpuExecutor<Window = Io::Surface>, Io: ApplicationIo<Executor = E>>(editor_api: EditorApi<'a, Io>) -> Arc<SurfaceHandle<E::Surface<'a>>> {
|
async fn create_gpu_surface<'a: 'input, E: 'a + GpuExecutor<Window = Io::Surface>, Io: ApplicationIo<Executor = E> + 'input>(editor_api: &'a EditorApi<Io>) -> Arc<SurfaceHandle<E::Surface<'a>>> {
|
||||||
let canvas = editor_api.application_io.create_surface();
|
let canvas = editor_api.application_io.as_ref().unwrap().create_surface();
|
||||||
let executor = editor_api.application_io.gpu_executor().unwrap();
|
let executor = editor_api.application_io.as_ref().unwrap().gpu_executor().unwrap();
|
||||||
Arc::new(executor.create_surface(canvas).unwrap())
|
Arc::new(executor.create_surface(canvas).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
default = ["dealloc_nodes"]
|
default = ["dealloc_nodes"]
|
||||||
serde = ["dep:serde", "graphene-core/serde", "glam/serde", "bezier-rs/serde"]
|
serde = ["dep:serde", "graphene-core/serde", "glam/serde", "bezier-rs/serde"]
|
||||||
dealloc_nodes = []
|
dealloc_nodes = []
|
||||||
|
wgpu = ["wgpu-executor"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
@@ -27,6 +28,19 @@ bezier-rs = { workspace = true }
|
|||||||
specta = { workspace = true }
|
specta = { workspace = true }
|
||||||
bytemuck = { workspace = true }
|
bytemuck = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
|
url = { workspace = true }
|
||||||
|
reqwest = { workspace = true }
|
||||||
|
|
||||||
# Optional workspace dependencies
|
# Optional workspace dependencies
|
||||||
|
wgpu-executor = { workspace = true, optional = true }
|
||||||
serde = { workspace = true, optional = true }
|
serde = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
# Workspace dependencies
|
||||||
|
web-sys = { workspace = true }
|
||||||
|
js-sys = { workspace = true }
|
||||||
|
wasm-bindgen = { workspace = true }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
# Workspace dependencies
|
||||||
|
winit = { workspace = true }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
|||||||
|
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
pub use graphene_core::uuid::generate_uuid;
|
pub use graphene_core::uuid::generate_uuid;
|
||||||
use graphene_core::{ProtoNodeIdentifier, Type};
|
use graphene_core::{Cow, ProtoNodeIdentifier, Type};
|
||||||
|
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
@@ -365,6 +365,7 @@ impl DocumentNode {
|
|||||||
}
|
}
|
||||||
NodeInput::Network { import_type, .. } => (ProtoNodeInput::ManualComposition(import_type), ConstructionArgs::Nodes(vec![])),
|
NodeInput::Network { import_type, .. } => (ProtoNodeInput::ManualComposition(import_type), ConstructionArgs::Nodes(vec![])),
|
||||||
NodeInput::Inline(inline) => (ProtoNodeInput::None, ConstructionArgs::Inline(inline)),
|
NodeInput::Inline(inline) => (ProtoNodeInput::None, ConstructionArgs::Inline(inline)),
|
||||||
|
NodeInput::Scope(_) => unreachable!("Scope input was not resolved"),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network { .. })), "received non resolved parameter");
|
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network { .. })), "received non resolved parameter");
|
||||||
@@ -452,6 +453,9 @@ pub enum NodeInput {
|
|||||||
/// Input that is provided by the parent network to this document node, instead of from a hardcoded value or another node within the same network.
|
/// Input that is provided by the parent network to this document node, instead of from a hardcoded value or another node within the same network.
|
||||||
Network { import_type: Type, import_index: usize },
|
Network { import_type: Type, import_index: usize },
|
||||||
|
|
||||||
|
/// Input that is extracted from the parent scopes the node resides in. The string argument is the key.
|
||||||
|
Scope(Cow<'static, str>),
|
||||||
|
|
||||||
/// A Rust source code string. Allows us to insert literal Rust code. Only used for GPU compilation.
|
/// A Rust source code string. Allows us to insert literal Rust code. Only used for GPU compilation.
|
||||||
/// We can use this whenever we spin up Rustc. Sort of like inline assembly, but because our language is Rust, it acts as inline Rust.
|
/// We can use this whenever we spin up Rustc. Sort of like inline assembly, but because our language is Rust, it acts as inline Rust.
|
||||||
Inline(InlineRust),
|
Inline(InlineRust),
|
||||||
@@ -474,15 +478,23 @@ impl NodeInput {
|
|||||||
pub const fn node(node_id: NodeId, output_index: usize) -> Self {
|
pub const fn node(node_id: NodeId, output_index: usize) -> Self {
|
||||||
Self::Node { node_id, output_index, lambda: false }
|
Self::Node { node_id, output_index, lambda: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn lambda(node_id: NodeId, output_index: usize) -> Self {
|
pub const fn lambda(node_id: NodeId, output_index: usize) -> Self {
|
||||||
Self::Node { node_id, output_index, lambda: true }
|
Self::Node { node_id, output_index, lambda: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn value(tagged_value: TaggedValue, exposed: bool) -> Self {
|
pub const fn value(tagged_value: TaggedValue, exposed: bool) -> Self {
|
||||||
Self::Value { tagged_value, exposed }
|
Self::Value { tagged_value, exposed }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn network(import_type: Type, import_index: usize) -> Self {
|
pub const fn network(import_type: Type, import_index: usize) -> Self {
|
||||||
Self::Network { import_type, import_index }
|
Self::Network { import_type, import_index }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scope(key: impl Into<Cow<'static, str>>) -> Self {
|
||||||
|
Self::Scope(key.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
|
fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
|
||||||
if let &mut NodeInput::Node { node_id, output_index, lambda } = self {
|
if let &mut NodeInput::Node { node_id, output_index, lambda } = self {
|
||||||
*self = NodeInput::Node {
|
*self = NodeInput::Node {
|
||||||
@@ -492,22 +504,27 @@ impl NodeInput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_exposed(&self) -> bool {
|
pub fn is_exposed(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
NodeInput::Node { .. } => true,
|
NodeInput::Node { .. } => true,
|
||||||
NodeInput::Value { exposed, .. } => *exposed,
|
NodeInput::Value { exposed, .. } => *exposed,
|
||||||
NodeInput::Network { .. } => true,
|
NodeInput::Network { .. } => true,
|
||||||
NodeInput::Inline(_) => false,
|
NodeInput::Inline(_) => false,
|
||||||
|
NodeInput::Scope(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ty(&self) -> Type {
|
pub fn ty(&self) -> Type {
|
||||||
match self {
|
match self {
|
||||||
NodeInput::Node { .. } => unreachable!("ty() called on NodeInput::Node"),
|
NodeInput::Node { .. } => unreachable!("ty() called on NodeInput::Node"),
|
||||||
NodeInput::Value { tagged_value, .. } => tagged_value.ty(),
|
NodeInput::Value { tagged_value, .. } => tagged_value.ty(),
|
||||||
NodeInput::Network { import_type, .. } => import_type.clone(),
|
NodeInput::Network { import_type, .. } => import_type.clone(),
|
||||||
NodeInput::Inline(_) => panic!("ty() called on NodeInput::Inline"),
|
NodeInput::Inline(_) => panic!("ty() called on NodeInput::Inline"),
|
||||||
|
NodeInput::Scope(_) => unreachable!("ty() called on NodeInput::Scope"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_value(&self) -> Option<&TaggedValue> {
|
pub fn as_value(&self) -> Option<&TaggedValue> {
|
||||||
if let NodeInput::Value { tagged_value, .. } = self {
|
if let NodeInput::Value { tagged_value, .. } = self {
|
||||||
Some(tagged_value)
|
Some(tagged_value)
|
||||||
@@ -515,6 +532,7 @@ impl NodeInput {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_node(&self) -> Option<NodeId> {
|
pub fn as_node(&self) -> Option<NodeId> {
|
||||||
if let NodeInput::Node { node_id, .. } = self {
|
if let NodeInput::Node { node_id, .. } = self {
|
||||||
Some(*node_id)
|
Some(*node_id)
|
||||||
@@ -666,6 +684,10 @@ pub struct NodeNetwork {
|
|||||||
pub imports_metadata: (NodeId, IVec2),
|
pub imports_metadata: (NodeId, IVec2),
|
||||||
#[serde(default = "default_export_metadata")]
|
#[serde(default = "default_export_metadata")]
|
||||||
pub exports_metadata: (NodeId, IVec2),
|
pub exports_metadata: (NodeId, IVec2),
|
||||||
|
|
||||||
|
/// A network may expose nodes as constants which can by used by other nodes using a `NodeInput::Scope(key)`.
|
||||||
|
#[serde(default)]
|
||||||
|
pub scope_injections: HashMap<String, (NodeId, Type)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::Hash for NodeNetwork {
|
impl std::hash::Hash for NodeNetwork {
|
||||||
@@ -688,6 +710,7 @@ impl Default for NodeNetwork {
|
|||||||
previewing: Default::default(),
|
previewing: Default::default(),
|
||||||
imports_metadata: default_import_metadata(),
|
imports_metadata: default_import_metadata(),
|
||||||
exports_metadata: default_export_metadata(),
|
exports_metadata: default_export_metadata(),
|
||||||
|
scope_injections: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -963,7 +986,7 @@ impl<'a> Iterator for FlowIter<'a> {
|
|||||||
let mut node_id = self.stack.pop()?;
|
let mut node_id = self.stack.pop()?;
|
||||||
|
|
||||||
// Special handling for iterating from ROOT_PARENT in load_structure`
|
// Special handling for iterating from ROOT_PARENT in load_structure`
|
||||||
if node_id == NodeId(std::u64::MAX) {
|
if node_id == NodeId(u64::MAX) {
|
||||||
if let Some(root_node) = self.network.get_root_node() {
|
if let Some(root_node) = self.network.get_root_node() {
|
||||||
node_id = root_node.id
|
node_id = root_node.id
|
||||||
} else {
|
} else {
|
||||||
@@ -1000,6 +1023,7 @@ impl NodeNetwork {
|
|||||||
root_node_to_restore.id = f(root_node_to_restore.id);
|
root_node_to_restore.id = f(root_node_to_restore.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.scope_injections.values_mut().for_each(|(id, _ty)| *id = f(*id));
|
||||||
let nodes = std::mem::take(&mut self.nodes);
|
let nodes = std::mem::take(&mut self.nodes);
|
||||||
self.nodes = nodes
|
self.nodes = nodes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -1110,6 +1134,18 @@ impl NodeNetwork {
|
|||||||
are_inputs_used
|
are_inputs_used
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_scope_inputs(&mut self) {
|
||||||
|
for node in self.nodes.values_mut() {
|
||||||
|
for input in node.inputs.iter_mut() {
|
||||||
|
if let NodeInput::Scope(key) = input {
|
||||||
|
let (import_id, _ty) = self.scope_injections.get(key.as_ref()).expect("Tried to import a non existent key from scope");
|
||||||
|
// TODO use correct output index
|
||||||
|
*input = NodeInput::node(*import_id, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove all nodes that contain [`DocumentNodeImplementation::Network`] by moving the nested nodes into the parent network.
|
/// Remove all nodes that contain [`DocumentNodeImplementation::Network`] by moving the nested nodes into the parent network.
|
||||||
pub fn flatten(&mut self, node_id: NodeId) {
|
pub fn flatten(&mut self, node_id: NodeId) {
|
||||||
self.flatten_with_fns(node_id, merge_ids, || NodeId(generate_uuid()))
|
self.flatten_with_fns(node_id, merge_ids, || NodeId(generate_uuid()))
|
||||||
@@ -1204,6 +1240,17 @@ impl NodeNetwork {
|
|||||||
inner_network.map_ids(|inner_id| map_ids(id, inner_id));
|
inner_network.map_ids(|inner_id| map_ids(id, inner_id));
|
||||||
let new_nodes = inner_network.nodes.keys().cloned().collect::<Vec<_>>();
|
let new_nodes = inner_network.nodes.keys().cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (key, value) in inner_network.scope_injections.into_iter() {
|
||||||
|
match self.scope_injections.entry(key) {
|
||||||
|
std::collections::hash_map::Entry::Occupied(o) => {
|
||||||
|
log::warn!("Found duplicate scope injection for key {}, ignoring", o.key());
|
||||||
|
}
|
||||||
|
std::collections::hash_map::Entry::Vacant(v) => {
|
||||||
|
v.insert(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Match the document node input and the inputs of the inner network
|
// Match the document node input and the inputs of the inner network
|
||||||
for (nested_node_id, mut nested_node) in inner_network.nodes.into_iter() {
|
for (nested_node_id, mut nested_node) in inner_network.nodes.into_iter() {
|
||||||
if nested_node.name == "To Artboard" {
|
if nested_node.name == "To Artboard" {
|
||||||
@@ -1232,6 +1279,12 @@ impl NodeNetwork {
|
|||||||
}
|
}
|
||||||
NodeInput::Value { .. } => unreachable!("Value inputs should have been replaced with value nodes"),
|
NodeInput::Value { .. } => unreachable!("Value inputs should have been replaced with value nodes"),
|
||||||
NodeInput::Inline(_) => (),
|
NodeInput::Inline(_) => (),
|
||||||
|
NodeInput::Scope(ref key) => {
|
||||||
|
log::debug!("flattening scope: {}", key);
|
||||||
|
let (import_id, _ty) = self.scope_injections.get(key.as_ref()).expect("Tried to import a non existent key from scope");
|
||||||
|
// TODO use correct output index
|
||||||
|
nested_node.inputs[nested_input_index] = NodeInput::node(*import_id, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use super::DocumentNode;
|
|||||||
use crate::graphene_compiler::Any;
|
use crate::graphene_compiler::Any;
|
||||||
pub use crate::imaginate_input::{ImaginateCache, ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod};
|
pub use crate::imaginate_input::{ImaginateCache, ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod};
|
||||||
use crate::proto::{Any as DAny, FutureAny};
|
use crate::proto::{Any as DAny, FutureAny};
|
||||||
|
use crate::wasm_application_io::WasmEditorApi;
|
||||||
|
|
||||||
use graphene_core::raster::brush_cache::BrushCache;
|
use graphene_core::raster::brush_cache::BrushCache;
|
||||||
use graphene_core::raster::{BlendMode, LuminanceCalculation};
|
use graphene_core::raster::{BlendMode, LuminanceCalculation};
|
||||||
@@ -12,6 +13,7 @@ pub use dyn_any::StaticType;
|
|||||||
pub use glam::{DAffine2, DVec2, IVec2, UVec2};
|
pub use glam::{DAffine2, DVec2, IVec2, UVec2};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::marker::PhantomData;
|
||||||
pub use std::sync::Arc;
|
pub use std::sync::Arc;
|
||||||
|
|
||||||
/// Macro to generate the tagged value enum.
|
/// Macro to generate the tagged value enum.
|
||||||
@@ -25,6 +27,8 @@ macro_rules! tagged_value {
|
|||||||
$( $(#[$meta] ) *$identifier( $ty ), )*
|
$( $(#[$meta] ) *$identifier( $ty ), )*
|
||||||
RenderOutput(RenderOutput),
|
RenderOutput(RenderOutput),
|
||||||
SurfaceFrame(graphene_core::SurfaceFrame),
|
SurfaceFrame(graphene_core::SurfaceFrame),
|
||||||
|
#[serde(skip)]
|
||||||
|
EditorApi(Arc<WasmEditorApi>)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We must manually implement hashing because some values are floats and so do not reproducibly hash (see FakeHash below)
|
// We must manually implement hashing because some values are floats and so do not reproducibly hash (see FakeHash below)
|
||||||
@@ -37,6 +41,7 @@ macro_rules! tagged_value {
|
|||||||
$( Self::$identifier(x) => {x.hash(state)}),*
|
$( Self::$identifier(x) => {x.hash(state)}),*
|
||||||
Self::RenderOutput(x) => x.hash(state),
|
Self::RenderOutput(x) => x.hash(state),
|
||||||
Self::SurfaceFrame(x) => x.hash(state),
|
Self::SurfaceFrame(x) => x.hash(state),
|
||||||
|
Self::EditorApi(x) => x.hash(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,6 +53,7 @@ macro_rules! tagged_value {
|
|||||||
$( Self::$identifier(x) => Box::new(x), )*
|
$( Self::$identifier(x) => Box::new(x), )*
|
||||||
Self::RenderOutput(x) => Box::new(x),
|
Self::RenderOutput(x) => Box::new(x),
|
||||||
Self::SurfaceFrame(x) => Box::new(x),
|
Self::SurfaceFrame(x) => Box::new(x),
|
||||||
|
Self::EditorApi(x) => Box::new(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates a graphene_core::Type::Concrete(TypeDescriptor { .. }) with the type of the value inside the tagged value
|
/// Creates a graphene_core::Type::Concrete(TypeDescriptor { .. }) with the type of the value inside the tagged value
|
||||||
@@ -57,6 +63,7 @@ macro_rules! tagged_value {
|
|||||||
$( Self::$identifier(_) => concrete!($ty), )*
|
$( Self::$identifier(_) => concrete!($ty), )*
|
||||||
Self::RenderOutput(_) => concrete!(RenderOutput),
|
Self::RenderOutput(_) => concrete!(RenderOutput),
|
||||||
Self::SurfaceFrame(_) => concrete!(graphene_core::SurfaceFrame),
|
Self::SurfaceFrame(_) => concrete!(graphene_core::SurfaceFrame),
|
||||||
|
Self::EditorApi(_) => concrete!(&WasmEditorApi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Attempts to downcast the dynamic type to a tagged value
|
/// Attempts to downcast the dynamic type to a tagged value
|
||||||
@@ -172,6 +179,7 @@ tagged_value! {
|
|||||||
VectorModification(graphene_core::vector::VectorModification),
|
VectorModification(graphene_core::vector::VectorModification),
|
||||||
CentroidType(graphene_core::vector::misc::CentroidType),
|
CentroidType(graphene_core::vector::misc::CentroidType),
|
||||||
BooleanOperation(graphene_core::vector::misc::BooleanOperation),
|
BooleanOperation(graphene_core::vector::misc::BooleanOperation),
|
||||||
|
FontCache(Arc<graphene_core::text::FontCache>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaggedValue {
|
impl TaggedValue {
|
||||||
@@ -218,6 +226,22 @@ impl UpcastNode {
|
|||||||
Self { value }
|
Self { value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
pub struct UpcastAsRefNode<T: AsRef<U>, U>(pub T, PhantomData<U>);
|
||||||
|
|
||||||
|
impl<'i, T: 'i + AsRef<U>, U: 'i + StaticType> Node<'i, DAny<'i>> for UpcastAsRefNode<T, U> {
|
||||||
|
type Output = FutureAny<'i>;
|
||||||
|
#[inline(always)]
|
||||||
|
fn eval(&'i self, _: DAny<'i>) -> Self::Output {
|
||||||
|
Box::pin(async move { Box::new(self.0.as_ref()) as DAny<'i> })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<U>, U> UpcastAsRefNode<T, U> {
|
||||||
|
pub const fn new(value: T) -> UpcastAsRefNode<T, U> {
|
||||||
|
UpcastAsRefNode(value, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, Hash)]
|
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ impl Compiler {
|
|||||||
for id in node_ids {
|
for id in node_ids {
|
||||||
network.flatten(id);
|
network.flatten(id);
|
||||||
}
|
}
|
||||||
|
network.resolve_scope_inputs();
|
||||||
network.remove_redundant_id_nodes();
|
network.remove_redundant_id_nodes();
|
||||||
network.remove_dead_nodes(0);
|
network.remove_dead_nodes(0);
|
||||||
let proto_networks = network.into_proto_networks();
|
let proto_networks = network.into_proto_networks();
|
||||||
@@ -40,3 +41,9 @@ pub type Any<'a> = Box<dyn DynAny<'a> + 'a>;
|
|||||||
pub trait Executor<I, O> {
|
pub trait Executor<I, O> {
|
||||||
fn execute(&self, input: I) -> LocalFuture<Result<O, Box<dyn Error>>>;
|
fn execute(&self, input: I) -> LocalFuture<Result<O, Box<dyn Error>>>;
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
pub struct CompileRequest {
|
||||||
|
pub networks: Vec<ProtoNetwork>,
|
||||||
|
pub io: wgpu_executor::ShaderIO,
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ pub mod proto;
|
|||||||
|
|
||||||
pub mod graphene_compiler;
|
pub mod graphene_compiler;
|
||||||
pub mod imaginate_input;
|
pub mod imaginate_input;
|
||||||
|
|
||||||
|
pub mod wasm_application_io;
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 94 B |
@@ -0,0 +1,228 @@
|
|||||||
|
use dyn_any::StaticType;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use graphene_core::application_io::SurfaceHandleFrame;
|
||||||
|
use graphene_core::application_io::{ApplicationError, ApplicationIo, ResourceFuture, SurfaceHandle, SurfaceId};
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
use wgpu_executor::WgpuExecutor;
|
||||||
|
|
||||||
|
use core::future::Future;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use js_sys::{Object, Reflect};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::pin::Pin;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use std::sync::atomic::AtomicU64;
|
||||||
|
use std::sync::Arc;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use std::sync::Mutex;
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasm_bindgen::JsValue;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use web_sys::window;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use web_sys::HtmlCanvasElement;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct WasmApplicationIo {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
ids: AtomicU64,
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
pub(crate) gpu_executor: Option<WgpuExecutor>,
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
windows: Mutex<Vec<Arc<winit::window::Window>>>,
|
||||||
|
pub resources: HashMap<String, Arc<[u8]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WasmApplicationIo {
|
||||||
|
pub async fn new() -> Self {
|
||||||
|
#[cfg(all(feature = "wgpu", target_arch = "wasm32"))]
|
||||||
|
let executor = if let Some(gpu) = web_sys::window().map(|w| w.navigator().gpu()) {
|
||||||
|
let request_adapter = || {
|
||||||
|
let request_adapter = js_sys::Reflect::get(&gpu, &wasm_bindgen::JsValue::from_str("requestAdapter")).ok()?;
|
||||||
|
let function = request_adapter.dyn_ref::<js_sys::Function>()?;
|
||||||
|
Some(function.call0(&gpu).ok())
|
||||||
|
};
|
||||||
|
let result = request_adapter();
|
||||||
|
match result {
|
||||||
|
None => None,
|
||||||
|
Some(_) => WgpuExecutor::new().await,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
#[cfg(all(feature = "wgpu", not(target_arch = "wasm32")))]
|
||||||
|
let executor = WgpuExecutor::new().await;
|
||||||
|
let mut io = Self {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
ids: AtomicU64::new(0),
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
gpu_executor: executor,
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
windows: Vec::new().into(),
|
||||||
|
resources: HashMap::new(),
|
||||||
|
};
|
||||||
|
io.resources.insert("null".to_string(), Arc::from(include_bytes!("null.png").to_vec()));
|
||||||
|
io
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl StaticType for WasmApplicationIo {
|
||||||
|
type Static = WasmApplicationIo;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a WasmEditorApi> for &'a WasmApplicationIo {
|
||||||
|
fn from(editor_api: &'a WasmEditorApi) -> Self {
|
||||||
|
editor_api.application_io.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
impl<'a> From<&'a WasmApplicationIo> for &'a WgpuExecutor {
|
||||||
|
fn from(app_io: &'a WasmApplicationIo) -> Self {
|
||||||
|
app_io.gpu_executor.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type WasmEditorApi = graphene_core::application_io::EditorApi<WasmApplicationIo>;
|
||||||
|
|
||||||
|
impl ApplicationIo for WasmApplicationIo {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
type Surface = HtmlCanvasElement;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
type Surface = Arc<winit::window::Window>;
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
type Executor = WgpuExecutor;
|
||||||
|
#[cfg(not(feature = "wgpu"))]
|
||||||
|
type Executor = ();
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn create_surface(&self) -> SurfaceHandle<Self::Surface> {
|
||||||
|
let wrapper = || {
|
||||||
|
let document = window().expect("should have a window in this context").document().expect("window should have a document");
|
||||||
|
|
||||||
|
let canvas: HtmlCanvasElement = document.create_element("canvas")?.dyn_into::<HtmlCanvasElement>()?;
|
||||||
|
let id = self.ids.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
// store the canvas in the global scope so it doesn't get garbage collected
|
||||||
|
let window = window().expect("should have a window in this context");
|
||||||
|
let window = Object::from(window);
|
||||||
|
|
||||||
|
let image_canvases_key = JsValue::from_str("imageCanvases");
|
||||||
|
|
||||||
|
let mut canvases = Reflect::get(&window, &image_canvases_key);
|
||||||
|
if canvases.is_err() {
|
||||||
|
Reflect::set(&JsValue::from(web_sys::window().unwrap()), &image_canvases_key, &Object::new()).unwrap();
|
||||||
|
canvases = Reflect::get(&window, &image_canvases_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert key and value to JsValue
|
||||||
|
let js_key = JsValue::from_str(format!("canvas{}", id).as_str());
|
||||||
|
let js_value = JsValue::from(canvas.clone());
|
||||||
|
|
||||||
|
let canvases = Object::from(canvases.unwrap());
|
||||||
|
|
||||||
|
// Use Reflect API to set property
|
||||||
|
Reflect::set(&canvases, &js_key, &js_value)?;
|
||||||
|
Ok::<_, JsValue>(SurfaceHandle {
|
||||||
|
surface_id: graphene_core::SurfaceId(id),
|
||||||
|
surface: canvas,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper().expect("should be able to set canvas in global scope")
|
||||||
|
}
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn create_surface(&self) -> SurfaceHandle<Self::Surface> {
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
use winit::platform::wayland::EventLoopBuilderExtWayland;
|
||||||
|
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
let event_loop = winit::event_loop::EventLoopBuilder::new().with_any_thread(true).build().unwrap();
|
||||||
|
#[cfg(not(feature = "wayland"))]
|
||||||
|
let event_loop = winit::event_loop::EventLoop::new().unwrap();
|
||||||
|
let window = winit::window::WindowBuilder::new()
|
||||||
|
.with_title("Graphite")
|
||||||
|
.with_inner_size(winit::dpi::PhysicalSize::new(800, 600))
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
let window = Arc::new(window);
|
||||||
|
self.windows.lock().as_mut().unwrap().push(window.clone());
|
||||||
|
SurfaceHandle {
|
||||||
|
surface_id: SurfaceId(window.id().into()),
|
||||||
|
surface: window,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn destroy_surface(&self, surface_id: SurfaceId) {
|
||||||
|
let window = window().expect("should have a window in this context");
|
||||||
|
let window = Object::from(window);
|
||||||
|
|
||||||
|
let image_canvases_key = JsValue::from_str("imageCanvases");
|
||||||
|
|
||||||
|
let wrapper = || {
|
||||||
|
if let Ok(canvases) = Reflect::get(&window, &image_canvases_key) {
|
||||||
|
// Convert key and value to JsValue
|
||||||
|
let js_key = JsValue::from_str(format!("canvas{}", surface_id.0).as_str());
|
||||||
|
|
||||||
|
// Use Reflect API to set property
|
||||||
|
Reflect::delete_property(&canvases.into(), &js_key)?;
|
||||||
|
}
|
||||||
|
Ok::<_, JsValue>(())
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper().expect("should be able to set canvas in global scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn destroy_surface(&self, _surface_id: SurfaceId) {}
|
||||||
|
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
fn gpu_executor(&self) -> Option<&Self::Executor> {
|
||||||
|
self.gpu_executor.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_resource(&self, url: impl AsRef<str>) -> Result<ResourceFuture, ApplicationError> {
|
||||||
|
let url = url::Url::parse(url.as_ref()).map_err(|_| ApplicationError::InvalidUrl)?;
|
||||||
|
log::trace!("Loading resource: {url:?}");
|
||||||
|
match url.scheme() {
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
"file" => {
|
||||||
|
let path = url.to_file_path().map_err(|_| ApplicationError::NotFound)?;
|
||||||
|
let path = path.to_str().ok_or(ApplicationError::NotFound)?;
|
||||||
|
let path = path.to_owned();
|
||||||
|
Ok(Box::pin(async move {
|
||||||
|
let file = tokio::fs::File::open(path).await.map_err(|_| ApplicationError::NotFound)?;
|
||||||
|
let mut reader = tokio::io::BufReader::new(file);
|
||||||
|
let mut data = Vec::new();
|
||||||
|
reader.read_to_end(&mut data).await.map_err(|_| ApplicationError::NotFound)?;
|
||||||
|
Ok(Arc::from(data))
|
||||||
|
}) as Pin<Box<dyn Future<Output = Result<Arc<[u8]>, _>>>>)
|
||||||
|
}
|
||||||
|
"http" | "https" => {
|
||||||
|
let url = url.to_string();
|
||||||
|
Ok(Box::pin(async move {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let response = client.get(url).send().await.map_err(|_| ApplicationError::NotFound)?;
|
||||||
|
let data = response.bytes().await.map_err(|_| ApplicationError::NotFound)?;
|
||||||
|
Ok(Arc::from(data.to_vec()))
|
||||||
|
}) as Pin<Box<dyn Future<Output = Result<Arc<[u8]>, _>>>>)
|
||||||
|
}
|
||||||
|
"graphite" => {
|
||||||
|
let path = url.path();
|
||||||
|
let path = path.to_owned();
|
||||||
|
log::trace!("Loading local resource: {path}");
|
||||||
|
let data = self.resources.get(&path).ok_or(ApplicationError::NotFound)?.clone();
|
||||||
|
Ok(Box::pin(async move { Ok(data.clone()) }) as Pin<Box<dyn Future<Output = Result<Arc<[u8]>, _>>>>)
|
||||||
|
}
|
||||||
|
_ => Err(ApplicationError::NotFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub type WasmSurfaceHandle = SurfaceHandle<HtmlCanvasElement>;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub type WasmSurfaceHandleFrame = SurfaceHandleFrame<HtmlCanvasElement>;
|
||||||
@@ -42,17 +42,16 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
device.poll(wgpu::Maintain::Poll);
|
device.poll(wgpu::Maintain::Poll);
|
||||||
});
|
});
|
||||||
|
|
||||||
let editor_api = WasmEditorApi {
|
let _editor_api = WasmEditorApi {
|
||||||
image_frame: None,
|
font_cache: FontCache::default(),
|
||||||
font_cache: &FontCache::default(),
|
application_io: Some(application_io.into()),
|
||||||
application_io: &application_io,
|
node_graph_message_sender: Box::new(UpdateLogger {}),
|
||||||
node_graph_message_sender: &UpdateLogger {},
|
imaginate_preferences: Box::new(ImaginatePreferences::default()),
|
||||||
imaginate_preferences: &ImaginatePreferences::default(),
|
|
||||||
render_config: graphene_core::application_io::RenderConfig::default(),
|
|
||||||
};
|
};
|
||||||
|
let render_config = graphene_core::application_io::RenderConfig::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let _result = (&executor).execute(editor_api.clone()).await?;
|
let _result = (&executor).execute(render_config).await?;
|
||||||
std::thread::sleep(std::time::Duration::from_millis(16));
|
std::thread::sleep(std::time::Duration::from_millis(16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ gpu = [
|
|||||||
"gpu-executor",
|
"gpu-executor",
|
||||||
]
|
]
|
||||||
vulkan = ["gpu", "vulkan-executor"]
|
vulkan = ["gpu", "vulkan-executor"]
|
||||||
wgpu = ["gpu", "wgpu-executor", "dep:wgpu"]
|
wgpu = ["gpu", "wgpu-executor", "dep:wgpu", "graph-craft/wgpu"]
|
||||||
quantization = ["autoquant"]
|
quantization = ["autoquant"]
|
||||||
wasm = ["wasm-bindgen", "web-sys", "js-sys"]
|
wasm = ["wasm-bindgen", "web-sys", "js-sys"]
|
||||||
imaginate = ["image/png", "base64", "js-sys", "web-sys", "wasm-bindgen-futures"]
|
imaginate = ["image/png", "base64", "js-sys", "web-sys", "wasm-bindgen-futures"]
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use gpu_executor::{GpuExecutor, ShaderIO, ShaderInput};
|
|||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::*;
|
use graph_craft::document::*;
|
||||||
use graph_craft::proto::*;
|
use graph_craft::proto::*;
|
||||||
|
use graphene_core::application_io::ApplicationIo;
|
||||||
use graphene_core::quantization::QuantizationChannels;
|
use graphene_core::quantization::QuantizationChannels;
|
||||||
use graphene_core::raster::*;
|
use graphene_core::raster::*;
|
||||||
use graphene_core::*;
|
use graphene_core::*;
|
||||||
@@ -67,9 +68,9 @@ impl<T: GpuExecutor> Clone for ComputePass<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_impl(MapGpuNode)]
|
#[node_macro::node_impl(MapGpuNode)]
|
||||||
async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, editor_api: graphene_core::application_io::EditorApi<'a, WasmApplicationIo>) -> ImageFrame<Color> {
|
async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, editor_api: &'a graphene_core::application_io::EditorApi<WasmApplicationIo>) -> ImageFrame<Color> {
|
||||||
log::debug!("Executing gpu node");
|
log::debug!("Executing gpu node");
|
||||||
let executor = &editor_api.application_io.gpu_executor.as_ref().unwrap();
|
let executor = &editor_api.application_io.as_ref().and_then(|io| io.gpu_executor()).unwrap();
|
||||||
|
|
||||||
#[cfg(feature = "quantization")]
|
#[cfg(feature = "quantization")]
|
||||||
let quantization = crate::quantization::generate_quantization_from_image_frame(&image);
|
let quantization = crate::quantization::generate_quantization_from_image_frame(&image);
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ impl core::fmt::Debug for ImaginatePersistentData {
|
|||||||
impl Default for ImaginatePersistentData {
|
impl Default for ImaginatePersistentData {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut status = ImaginateServerStatus::default();
|
let mut status = ImaginateServerStatus::default();
|
||||||
|
#[cfg(not(miri))]
|
||||||
let client = new_client().map_err(|err| status = ImaginateServerStatus::Failed(err.to_string())).ok();
|
let client = new_client().map_err(|err| status = ImaginateServerStatus::Failed(err.to_string())).ok();
|
||||||
|
#[cfg(miri)]
|
||||||
|
let client = None;
|
||||||
let ImaginatePreferences { host_name } = Default::default();
|
let ImaginatePreferences { host_name } = Default::default();
|
||||||
Self {
|
Self {
|
||||||
pending_server_check: None,
|
pending_server_check: None,
|
||||||
@@ -263,7 +266,7 @@ struct ImaginateCommonImageRequest<'a> {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn imaginate<'a, P: Pixel>(
|
pub async fn imaginate<'a, P: Pixel>(
|
||||||
image: Image<P>,
|
image: Image<P>,
|
||||||
editor_api: impl Future<Output = WasmEditorApi<'a>>,
|
editor_api: impl Future<Output = &'a WasmEditorApi>,
|
||||||
controller: ImaginateController,
|
controller: ImaginateController,
|
||||||
seed: impl Future<Output = f64>,
|
seed: impl Future<Output = f64>,
|
||||||
res: impl Future<Output = Option<DVec2>>,
|
res: impl Future<Output = Option<DVec2>>,
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 241 B |
@@ -479,7 +479,7 @@ macro_rules! generate_imaginate_node {
|
|||||||
|
|
||||||
impl<'e, P: Pixel, E, C, $($t,)*> ImaginateNode<P, E, C, $($t,)*>
|
impl<'e, P: Pixel, E, C, $($t,)*> ImaginateNode<P, E, C, $($t,)*>
|
||||||
where $($t: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, $o>>,)*
|
where $($t: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, $o>>,)*
|
||||||
E: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, WasmEditorApi<'e>>>,
|
E: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, &'e WasmEditorApi>>,
|
||||||
C: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, ImaginateController>>,
|
C: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, ImaginateController>>,
|
||||||
{
|
{
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@@ -490,7 +490,7 @@ macro_rules! generate_imaginate_node {
|
|||||||
|
|
||||||
impl<'i, 'e: 'i, P: Pixel + 'i + Hash + Default, E: 'i, C: 'i, $($t: 'i,)*> Node<'i, ImageFrame<P>> for ImaginateNode<P, E, C, $($t,)*>
|
impl<'i, 'e: 'i, P: Pixel + 'i + Hash + Default, E: 'i, C: 'i, $($t: 'i,)*> Node<'i, ImageFrame<P>> for ImaginateNode<P, E, C, $($t,)*>
|
||||||
where $($t: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, $o>>,)*
|
where $($t: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, $o>>,)*
|
||||||
E: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, WasmEditorApi<'e>>>,
|
E: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, &'e WasmEditorApi>>,
|
||||||
C: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, ImaginateController>>,
|
C: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, ImaginateController>>,
|
||||||
{
|
{
|
||||||
type Output = DynFuture<'i, ImageFrame<P>>;
|
type Output = DynFuture<'i, ImageFrame<P>>;
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ fn boolean_operation_node(graphic_group: GraphicGroup, boolean_operation: Boolea
|
|||||||
let transform_of_lower_into_space_of_upper = result.transform.inverse() * lower_vector_data.transform;
|
let transform_of_lower_into_space_of_upper = result.transform.inverse() * lower_vector_data.transform;
|
||||||
|
|
||||||
let upper_path_string = to_svg_string(&result, DAffine2::IDENTITY);
|
let upper_path_string = to_svg_string(&result, DAffine2::IDENTITY);
|
||||||
let lower_path_string = to_svg_string(&lower_vector_data, transform_of_lower_into_space_of_upper);
|
let lower_path_string = to_svg_string(lower_vector_data, transform_of_lower_into_space_of_upper);
|
||||||
|
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
let boolean_operation_string = unsafe { boolean_subtract(upper_path_string, lower_path_string) };
|
let boolean_operation_string = unsafe { boolean_subtract(upper_path_string, lower_path_string) };
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use dyn_any::StaticType;
|
use graphene_core::application_io::{ApplicationIo, ExportFormat, RenderConfig, SurfaceHandle, SurfaceHandleFrame};
|
||||||
use graphene_core::application_io::{ApplicationError, ApplicationIo, ExportFormat, RenderConfig, ResourceFuture, SurfaceHandle, SurfaceHandleFrame, SurfaceId};
|
|
||||||
use graphene_core::raster::bbox::Bbox;
|
use graphene_core::raster::bbox::Bbox;
|
||||||
use graphene_core::raster::Image;
|
use graphene_core::raster::Image;
|
||||||
use graphene_core::raster::{color::SRGBA8, ImageFrame};
|
use graphene_core::raster::{color::SRGBA8, ImageFrame};
|
||||||
@@ -7,226 +6,17 @@ use graphene_core::renderer::{format_transform_matrix, GraphicElementRendered, I
|
|||||||
use graphene_core::transform::{Footprint, TransformMut};
|
use graphene_core::transform::{Footprint, TransformMut};
|
||||||
use graphene_core::Color;
|
use graphene_core::Color;
|
||||||
use graphene_core::Node;
|
use graphene_core::Node;
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
use wgpu_executor::WgpuExecutor;
|
|
||||||
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
|
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use js_sys::{Object, Reflect};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[cfg(feature = "tokio")]
|
|
||||||
use tokio::io::AsyncReadExt;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use wasm_bindgen::JsValue;
|
|
||||||
use wasm_bindgen::{Clamped, JsCast};
|
use wasm_bindgen::{Clamped, JsCast};
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use web_sys::window;
|
|
||||||
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
|
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
|
||||||
|
|
||||||
#[cfg(any(feature = "resvg", feature = "vello"))]
|
pub use graph_craft::wasm_application_io::*;
|
||||||
pub struct Canvas(());
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct WasmApplicationIo {
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
ids: RefCell<u64>,
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
pub(crate) gpu_executor: Option<WgpuExecutor>,
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
windows: RefCell<Vec<Arc<winit::window::Window>>>,
|
|
||||||
pub resources: HashMap<String, Arc<[u8]>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WasmApplicationIo {
|
|
||||||
pub async fn new() -> Self {
|
|
||||||
#[cfg(all(feature = "wgpu", target_arch = "wasm32"))]
|
|
||||||
let executor = if let Some(gpu) = web_sys::window().map(|w| w.navigator().gpu()) {
|
|
||||||
let request_adapter = || {
|
|
||||||
let request_adapter = js_sys::Reflect::get(&gpu, &wasm_bindgen::JsValue::from_str("requestAdapter")).ok()?;
|
|
||||||
let function = request_adapter.dyn_ref::<js_sys::Function>()?;
|
|
||||||
Some(function.call0(&gpu).ok())
|
|
||||||
};
|
|
||||||
let result = request_adapter();
|
|
||||||
match result {
|
|
||||||
None => None,
|
|
||||||
Some(_) => WgpuExecutor::new().await,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
#[cfg(all(feature = "wgpu", not(target_arch = "wasm32")))]
|
|
||||||
let executor = WgpuExecutor::new().await;
|
|
||||||
let mut io = Self {
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
ids: RefCell::new(0),
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
gpu_executor: executor,
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
windows: RefCell::new(Vec::new()),
|
|
||||||
resources: HashMap::new(),
|
|
||||||
};
|
|
||||||
io.resources.insert("null".to_string(), Arc::from(include_bytes!("null.png").to_vec()));
|
|
||||||
io
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl StaticType for WasmApplicationIo {
|
|
||||||
type Static = WasmApplicationIo;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<WasmEditorApi<'a>> for &'a WasmApplicationIo {
|
|
||||||
fn from(editor_api: WasmEditorApi<'a>) -> Self {
|
|
||||||
editor_api.application_io
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
impl<'a> From<&'a WasmApplicationIo> for &'a WgpuExecutor {
|
|
||||||
fn from(app_io: &'a WasmApplicationIo) -> Self {
|
|
||||||
app_io.gpu_executor.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type WasmEditorApi<'a> = graphene_core::application_io::EditorApi<'a, WasmApplicationIo>;
|
|
||||||
|
|
||||||
impl ApplicationIo for WasmApplicationIo {
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
type Surface = HtmlCanvasElement;
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
type Surface = Arc<winit::window::Window>;
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
type Executor = WgpuExecutor;
|
|
||||||
#[cfg(not(feature = "wgpu"))]
|
|
||||||
type Executor = ();
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
fn create_surface(&self) -> SurfaceHandle<Self::Surface> {
|
|
||||||
let wrapper = || {
|
|
||||||
let document = window().expect("should have a window in this context").document().expect("window should have a document");
|
|
||||||
|
|
||||||
let canvas: HtmlCanvasElement = document.create_element("canvas")?.dyn_into::<HtmlCanvasElement>()?;
|
|
||||||
let mut guard = self.ids.borrow_mut();
|
|
||||||
let id = SurfaceId(*guard);
|
|
||||||
*guard += 1;
|
|
||||||
// store the canvas in the global scope so it doesn't get garbage collected
|
|
||||||
let window = window().expect("should have a window in this context");
|
|
||||||
let window = Object::from(window);
|
|
||||||
|
|
||||||
let image_canvases_key = JsValue::from_str("imageCanvases");
|
|
||||||
|
|
||||||
let mut canvases = Reflect::get(&window, &image_canvases_key);
|
|
||||||
if canvases.is_err() {
|
|
||||||
Reflect::set(&JsValue::from(web_sys::window().unwrap()), &image_canvases_key, &Object::new()).unwrap();
|
|
||||||
canvases = Reflect::get(&window, &image_canvases_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert key and value to JsValue
|
|
||||||
let js_key = JsValue::from_str(format!("canvas{}", id.0).as_str());
|
|
||||||
let js_value = JsValue::from(canvas.clone());
|
|
||||||
|
|
||||||
let canvases = Object::from(canvases.unwrap());
|
|
||||||
|
|
||||||
// Use Reflect API to set property
|
|
||||||
Reflect::set(&canvases, &js_key, &js_value)?;
|
|
||||||
Ok::<_, JsValue>(SurfaceHandle { surface_id: id, surface: canvas })
|
|
||||||
};
|
|
||||||
|
|
||||||
wrapper().expect("should be able to set canvas in global scope")
|
|
||||||
}
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
fn create_surface(&self) -> SurfaceHandle<Self::Surface> {
|
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
use winit::platform::wayland::EventLoopBuilderExtWayland;
|
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
let event_loop = winit::event_loop::EventLoopBuilder::new().with_any_thread(true).build().unwrap();
|
|
||||||
#[cfg(not(feature = "wayland"))]
|
|
||||||
let event_loop = winit::event_loop::EventLoop::new().unwrap();
|
|
||||||
let window = winit::window::WindowBuilder::new()
|
|
||||||
.with_title("Graphite")
|
|
||||||
.with_inner_size(winit::dpi::PhysicalSize::new(800, 600))
|
|
||||||
.build(&event_loop)
|
|
||||||
.unwrap();
|
|
||||||
let window = Arc::new(window);
|
|
||||||
self.windows.borrow_mut().push(window.clone());
|
|
||||||
SurfaceHandle {
|
|
||||||
surface_id: SurfaceId(window.id().into()),
|
|
||||||
surface: window,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
fn destroy_surface(&self, surface_id: SurfaceId) {
|
|
||||||
let window = window().expect("should have a window in this context");
|
|
||||||
let window = Object::from(window);
|
|
||||||
|
|
||||||
let image_canvases_key = JsValue::from_str("imageCanvases");
|
|
||||||
|
|
||||||
let wrapper = || {
|
|
||||||
if let Ok(canvases) = Reflect::get(&window, &image_canvases_key) {
|
|
||||||
// Convert key and value to JsValue
|
|
||||||
let js_key = JsValue::from_str(format!("canvas{}", surface_id.0).as_str());
|
|
||||||
|
|
||||||
// Use Reflect API to set property
|
|
||||||
Reflect::delete_property(&canvases.into(), &js_key)?;
|
|
||||||
}
|
|
||||||
Ok::<_, JsValue>(())
|
|
||||||
};
|
|
||||||
|
|
||||||
wrapper().expect("should be able to set canvas in global scope")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
fn destroy_surface(&self, _surface_id: SurfaceId) {}
|
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
fn gpu_executor(&self) -> Option<&Self::Executor> {
|
|
||||||
self.gpu_executor.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_resource(&self, url: impl AsRef<str>) -> Result<ResourceFuture, ApplicationError> {
|
|
||||||
let url = url::Url::parse(url.as_ref()).map_err(|_| ApplicationError::InvalidUrl)?;
|
|
||||||
log::trace!("Loading resource: {url:?}");
|
|
||||||
match url.scheme() {
|
|
||||||
#[cfg(feature = "tokio")]
|
|
||||||
"file" => {
|
|
||||||
let path = url.to_file_path().map_err(|_| ApplicationError::NotFound)?;
|
|
||||||
let path = path.to_str().ok_or(ApplicationError::NotFound)?;
|
|
||||||
let path = path.to_owned();
|
|
||||||
Ok(Box::pin(async move {
|
|
||||||
let file = tokio::fs::File::open(path).await.map_err(|_| ApplicationError::NotFound)?;
|
|
||||||
let mut reader = tokio::io::BufReader::new(file);
|
|
||||||
let mut data = Vec::new();
|
|
||||||
reader.read_to_end(&mut data).await.map_err(|_| ApplicationError::NotFound)?;
|
|
||||||
Ok(Arc::from(data))
|
|
||||||
}) as Pin<Box<dyn Future<Output = Result<Arc<[u8]>, _>>>>)
|
|
||||||
}
|
|
||||||
"http" | "https" => {
|
|
||||||
let url = url.to_string();
|
|
||||||
Ok(Box::pin(async move {
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
let response = client.get(url).send().await.map_err(|_| ApplicationError::NotFound)?;
|
|
||||||
let data = response.bytes().await.map_err(|_| ApplicationError::NotFound)?;
|
|
||||||
Ok(Arc::from(data.to_vec()))
|
|
||||||
}) as Pin<Box<dyn Future<Output = Result<Arc<[u8]>, _>>>>)
|
|
||||||
}
|
|
||||||
"graphite" => {
|
|
||||||
let path = url.path();
|
|
||||||
let path = path.to_owned();
|
|
||||||
log::trace!("Loading local resource: {path}");
|
|
||||||
let data = self.resources.get(&path).ok_or(ApplicationError::NotFound)?.clone();
|
|
||||||
Ok(Box::pin(async move { Ok(data.clone()) }) as Pin<Box<dyn Future<Output = Result<Arc<[u8]>, _>>>>)
|
|
||||||
}
|
|
||||||
_ => Err(ApplicationError::NotFound),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type WasmSurfaceHandle = SurfaceHandle<HtmlCanvasElement>;
|
pub type WasmSurfaceHandle = SurfaceHandle<HtmlCanvasElement>;
|
||||||
pub type WasmSurfaceHandleFrame = SurfaceHandleFrame<HtmlCanvasElement>;
|
pub type WasmSurfaceHandleFrame = SurfaceHandleFrame<HtmlCanvasElement>;
|
||||||
@@ -234,8 +24,8 @@ pub type WasmSurfaceHandleFrame = SurfaceHandleFrame<HtmlCanvasElement>;
|
|||||||
pub struct CreateSurfaceNode {}
|
pub struct CreateSurfaceNode {}
|
||||||
|
|
||||||
#[node_macro::node_fn(CreateSurfaceNode)]
|
#[node_macro::node_fn(CreateSurfaceNode)]
|
||||||
async fn create_surface_node<'a: 'input>(editor: WasmEditorApi<'a>) -> Arc<SurfaceHandle<<WasmApplicationIo as ApplicationIo>::Surface>> {
|
async fn create_surface_node<'a: 'input>(editor: &'a WasmEditorApi) -> Arc<SurfaceHandle<<WasmApplicationIo as ApplicationIo>::Surface>> {
|
||||||
editor.application_io.create_surface().into()
|
editor.application_io.as_ref().unwrap().create_surface().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrawImageFrameNode<Surface> {
|
pub struct DrawImageFrameNode<Surface> {
|
||||||
@@ -266,8 +56,8 @@ pub struct LoadResourceNode<Url> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(LoadResourceNode)]
|
#[node_macro::node_fn(LoadResourceNode)]
|
||||||
async fn load_resource_node<'a: 'input>(editor: WasmEditorApi<'a>, url: String) -> Arc<[u8]> {
|
async fn load_resource_node<'a: 'input>(editor: &'a WasmEditorApi, url: String) -> Arc<[u8]> {
|
||||||
editor.application_io.load_resource(url).unwrap().await.unwrap()
|
editor.application_io.as_ref().unwrap().load_resource(url).unwrap().await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DecodeImageNode;
|
pub struct DecodeImageNode;
|
||||||
@@ -321,7 +111,7 @@ fn _render_canvas(
|
|||||||
mut render: SvgRender,
|
mut render: SvgRender,
|
||||||
render_params: RenderParams,
|
render_params: RenderParams,
|
||||||
footprint: Footprint,
|
footprint: Footprint,
|
||||||
editor: WasmEditorApi<'_>,
|
editor: &'_ WasmEditorApi,
|
||||||
surface_handle: Arc<SurfaceHandle<HtmlCanvasElement>>,
|
surface_handle: Arc<SurfaceHandle<HtmlCanvasElement>>,
|
||||||
) -> RenderOutput {
|
) -> RenderOutput {
|
||||||
let resolution = footprint.resolution;
|
let resolution = footprint.resolution;
|
||||||
@@ -337,7 +127,7 @@ fn _render_canvas(
|
|||||||
canvas.set_height(resolution.y);
|
canvas.set_height(resolution.y);
|
||||||
let usvg_tree = data.to_usvg_tree(resolution, [min, max]);
|
let usvg_tree = data.to_usvg_tree(resolution, [min, max]);
|
||||||
|
|
||||||
if let Some(_exec) = editor.application_io.gpu_executor() {
|
if let Some(_exec) = editor.application_io.as_ref().unwrap().gpu_executor() {
|
||||||
todo!()
|
todo!()
|
||||||
} else {
|
} else {
|
||||||
let pixmap_size = usvg_tree.size.to_int_size();
|
let pixmap_size = usvg_tree.size.to_int_size();
|
||||||
@@ -421,7 +211,7 @@ async fn rasterize<_T: GraphicElementRendered + TransformMut>(mut data: _T, foot
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render with the data node taking in Footprint.
|
// Render with the data node taking in Footprint.
|
||||||
impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future<Output = T>, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, WasmEditorApi<'a>>
|
impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future<Output = T>, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, RenderConfig>
|
||||||
for RenderNode<Data, Surface, Footprint>
|
for RenderNode<Data, Surface, Footprint>
|
||||||
where
|
where
|
||||||
Data: Node<'input, Footprint, Output = F>,
|
Data: Node<'input, Footprint, Output = F>,
|
||||||
@@ -431,14 +221,14 @@ where
|
|||||||
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = RenderOutput> + 'input>>;
|
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = RenderOutput> + 'input>>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eval(&'input self, editor: WasmEditorApi<'a>) -> Self::Output {
|
fn eval(&'input self, render_config: RenderConfig) -> Self::Output {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let footprint = editor.render_config.viewport;
|
let footprint = render_config.viewport;
|
||||||
|
|
||||||
let RenderConfig { hide_artboards, for_export, .. } = editor.render_config;
|
let RenderConfig { hide_artboards, for_export, .. } = render_config;
|
||||||
let render_params = RenderParams::new(editor.render_config.view_mode, ImageRenderMode::Base64, None, false, hide_artboards, for_export);
|
let render_params = RenderParams::new(render_config.view_mode, ImageRenderMode::Base64, None, false, hide_artboards, for_export);
|
||||||
|
|
||||||
let output_format = editor.render_config.export_format;
|
let output_format = render_config.export_format;
|
||||||
match output_format {
|
match output_format {
|
||||||
ExportFormat::Svg => render_svg(self.data.eval(footprint).await, SvgRender::new(), render_params, footprint),
|
ExportFormat::Svg => render_svg(self.data.eval(footprint).await, SvgRender::new(), render_params, footprint),
|
||||||
#[cfg(all(any(feature = "resvg", feature = "vello"), target_arch = "wasm32"))]
|
#[cfg(all(any(feature = "resvg", feature = "vello"), target_arch = "wasm32"))]
|
||||||
@@ -450,7 +240,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render with the data node taking in ().
|
// Render with the data node taking in ().
|
||||||
impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future<Output = T>, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, WasmEditorApi<'a>>
|
impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future<Output = T>, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, RenderConfig>
|
||||||
for RenderNode<Data, Surface, ()>
|
for RenderNode<Data, Surface, ()>
|
||||||
where
|
where
|
||||||
Data: Node<'input, (), Output = F>,
|
Data: Node<'input, (), Output = F>,
|
||||||
@@ -459,14 +249,14 @@ where
|
|||||||
{
|
{
|
||||||
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = RenderOutput> + 'input>>;
|
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = RenderOutput> + 'input>>;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eval(&'input self, editor: WasmEditorApi<'a>) -> Self::Output {
|
fn eval(&'input self, render_config: RenderConfig) -> Self::Output {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let footprint = editor.render_config.viewport;
|
let footprint = render_config.viewport;
|
||||||
|
|
||||||
let RenderConfig { hide_artboards, for_export, .. } = editor.render_config;
|
let RenderConfig { hide_artboards, for_export, .. } = render_config;
|
||||||
let render_params = RenderParams::new(editor.render_config.view_mode, ImageRenderMode::Base64, None, false, hide_artboards, for_export);
|
let render_params = RenderParams::new(render_config.view_mode, ImageRenderMode::Base64, None, false, hide_artboards, for_export);
|
||||||
|
|
||||||
let output_format = editor.render_config.export_format;
|
let output_format = render_config.export_format;
|
||||||
match output_format {
|
match output_format {
|
||||||
ExportFormat::Svg => render_svg(self.data.eval(()).await, SvgRender::new(), render_params, footprint),
|
ExportFormat::Svg => render_svg(self.data.eval(()).await, SvgRender::new(), render_params, footprint),
|
||||||
#[cfg(all(any(feature = "resvg", feature = "vello"), target_arch = "wasm32"))]
|
#[cfg(all(any(feature = "resvg", feature = "vello"), target_arch = "wasm32"))]
|
||||||
@@ -478,11 +268,11 @@ where
|
|||||||
}
|
}
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl<Data, Surface, Parameter> RenderNode<Data, Surface, Parameter> {
|
impl<Data, Surface, Parameter> RenderNode<Data, Surface, Parameter> {
|
||||||
pub fn new(data: Data, surface_handle: Surface) -> Self {
|
pub fn new(data: Data, _surface_handle: Surface) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
#[cfg(all(any(feature = "resvg", feature = "vello"), target_arch = "wasm32"))]
|
#[cfg(all(any(feature = "resvg", feature = "vello"), target_arch = "wasm32"))]
|
||||||
surface_handle,
|
surface_handle: _surface_handle,
|
||||||
#[cfg(not(all(any(feature = "resvg", feature = "vello"), target_arch = "wasm32")))]
|
#[cfg(not(all(any(feature = "resvg", feature = "vello"), target_arch = "wasm32")))]
|
||||||
surface_handle: PhantomData,
|
surface_handle: PhantomData,
|
||||||
parameter: PhantomData,
|
parameter: PhantomData,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::node_registry;
|
use crate::node_registry;
|
||||||
|
|
||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use graph_craft::document::value::{TaggedValue, UpcastNode};
|
use graph_craft::document::value::{TaggedValue, UpcastAsRefNode, UpcastNode};
|
||||||
use graph_craft::document::{NodeId, Source};
|
use graph_craft::document::{NodeId, Source};
|
||||||
use graph_craft::graphene_compiler::Executor;
|
use graph_craft::graphene_compiler::Executor;
|
||||||
use graph_craft::proto::{ConstructionArgs, GraphError, LocalFuture, NodeContainer, ProtoNetwork, ProtoNode, SharedNodeContainer, TypeErasedBox, TypingContext};
|
use graph_craft::proto::{ConstructionArgs, GraphError, LocalFuture, NodeContainer, ProtoNetwork, ProtoNode, SharedNodeContainer, TypeErasedBox, TypingContext};
|
||||||
@@ -102,7 +102,7 @@ impl DynamicExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I: StaticType + 'a> Executor<I, TaggedValue> for &'a DynamicExecutor {
|
impl<'a, I: StaticType + 'static> Executor<I, TaggedValue> for &'a DynamicExecutor {
|
||||||
fn execute(&self, input: I) -> LocalFuture<Result<TaggedValue, Box<dyn Error>>> {
|
fn execute(&self, input: I) -> LocalFuture<Result<TaggedValue, Box<dyn Error>>> {
|
||||||
Box::pin(async move { self.tree.eval_tagged_value(self.output, input).await.map_err(|e| e.into()) })
|
Box::pin(async move { self.tree.eval_tagged_value(self.output, input).await.map_err(|e| e.into()) })
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ impl BorrowTree {
|
|||||||
}
|
}
|
||||||
/// Evaluate the output node of the [`BorrowTree`] and cast it to a tagged value.
|
/// Evaluate the output node of the [`BorrowTree`] and cast it to a tagged value.
|
||||||
/// This ensures that no borrowed data can escape the node graph.
|
/// This ensures that no borrowed data can escape the node graph.
|
||||||
pub async fn eval_tagged_value<'i, I: StaticType + 'i>(&'i self, id: NodeId, input: I) -> Result<TaggedValue, String> {
|
pub async fn eval_tagged_value<I: StaticType + 'static>(&self, id: NodeId, input: I) -> Result<TaggedValue, String> {
|
||||||
let node = self.nodes.get(&id).cloned().ok_or("Output node not found in executor")?;
|
let node = self.nodes.get(&id).cloned().ok_or("Output node not found in executor")?;
|
||||||
let output = node.eval(Box::new(input));
|
let output = node.eval(Box::new(input));
|
||||||
TaggedValue::try_from_any(output.await)
|
TaggedValue::try_from_any(output.await)
|
||||||
@@ -207,9 +207,15 @@ impl BorrowTree {
|
|||||||
|
|
||||||
match &proto_node.construction_args {
|
match &proto_node.construction_args {
|
||||||
ConstructionArgs::Value(value) => {
|
ConstructionArgs::Value(value) => {
|
||||||
let upcasted = UpcastNode::new(value.to_owned());
|
let node: std::rc::Rc<NodeContainer> = if let TaggedValue::EditorApi(api) = value {
|
||||||
let node = Box::new(upcasted) as TypeErasedBox<'_>;
|
let editor_api = UpcastAsRefNode::new(api.clone());
|
||||||
let node: std::rc::Rc<NodeContainer> = NodeContainer::new(node);
|
let node = Box::new(editor_api) as TypeErasedBox<'_>;
|
||||||
|
NodeContainer::new(node)
|
||||||
|
} else {
|
||||||
|
let upcasted = UpcastNode::new(value.to_owned());
|
||||||
|
let node = Box::new(upcasted) as TypeErasedBox<'_>;
|
||||||
|
NodeContainer::new(node)
|
||||||
|
};
|
||||||
self.store_node(node, id);
|
self.store_node(node, id);
|
||||||
}
|
}
|
||||||
ConstructionArgs::Inline(_) => unimplemented!("Inline nodes are not supported yet"),
|
ConstructionArgs::Inline(_) => unimplemented!("Inline nodes are not supported yet"),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use graphene_core::{fn_type, raster::*};
|
|||||||
use graphene_core::{Cow, ProtoNodeIdentifier, Type};
|
use graphene_core::{Cow, ProtoNodeIdentifier, Type};
|
||||||
use graphene_core::{Node, NodeIO, NodeIOTypes};
|
use graphene_core::{Node, NodeIO, NodeIOTypes};
|
||||||
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode};
|
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode};
|
||||||
|
use graphene_std::application_io::RenderConfig;
|
||||||
use graphene_std::wasm_application_io::*;
|
use graphene_std::wasm_application_io::*;
|
||||||
|
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
@@ -182,7 +183,6 @@ macro_rules! raster_node {
|
|||||||
// TODO: turn into hashmap
|
// TODO: turn into hashmap
|
||||||
fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> {
|
fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> {
|
||||||
let node_types: Vec<Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)>> = vec![
|
let node_types: Vec<Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)>> = vec![
|
||||||
// register_node!(graphene_core::ops::IdentityNode, input: Any<'_>, params: []),
|
|
||||||
vec![(
|
vec![(
|
||||||
ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"),
|
ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"),
|
||||||
|_| Box::pin(async move { FutureWrapperNode::new(IdentityNode::new()).into_type_erased() }),
|
|_| Box::pin(async move { FutureWrapperNode::new(IdentityNode::new()).into_type_erased() }),
|
||||||
@@ -196,7 +196,6 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
register_node!(graphene_core::ops::AddPairNode, input: (u32, u32), params: []),
|
register_node!(graphene_core::ops::AddPairNode, input: (u32, u32), params: []),
|
||||||
register_node!(graphene_core::ops::AddPairNode, input: (u32, &u32), params: []),
|
register_node!(graphene_core::ops::AddPairNode, input: (u32, &u32), params: []),
|
||||||
register_node!(graphene_core::ops::CloneNode<_>, input: &ImageFrame<Color>, params: []),
|
register_node!(graphene_core::ops::CloneNode<_>, input: &ImageFrame<Color>, params: []),
|
||||||
register_node!(graphene_core::ops::CloneNode<_>, input: &WasmEditorApi, params: []),
|
|
||||||
register_node!(graphene_core::ops::AddNode<_>, input: u32, params: [u32]),
|
register_node!(graphene_core::ops::AddNode<_>, input: u32, params: [u32]),
|
||||||
register_node!(graphene_core::ops::AddNode<_>, input: &u32, params: [u32]),
|
register_node!(graphene_core::ops::AddNode<_>, input: &u32, params: [u32]),
|
||||||
register_node!(graphene_core::ops::AddNode<_>, input: u32, params: [&u32]),
|
register_node!(graphene_core::ops::AddNode<_>, input: u32, params: [&u32]),
|
||||||
@@ -271,7 +270,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
register_node!(graphene_core::ops::ModuloNode<_>, input: f64, params: [&f64]),
|
register_node!(graphene_core::ops::ModuloNode<_>, input: f64, params: [&f64]),
|
||||||
register_node!(graphene_core::ops::ModuloNode<_>, input: &f64, params: [&f64]),
|
register_node!(graphene_core::ops::ModuloNode<_>, input: &f64, params: [&f64]),
|
||||||
register_node!(graphene_core::ops::ConstructVector2<_, _>, input: (), params: [f64, f64]),
|
register_node!(graphene_core::ops::ConstructVector2<_, _>, input: (), params: [f64, f64]),
|
||||||
register_node!(graphene_core::ops::SomeNode, input: WasmEditorApi, params: []),
|
register_node!(graphene_core::ops::SomeNode, input: &WasmEditorApi, params: []),
|
||||||
register_node!(graphene_core::logic::LogToConsoleNode, input: bool, params: []),
|
register_node!(graphene_core::logic::LogToConsoleNode, input: bool, params: []),
|
||||||
register_node!(graphene_core::logic::LogToConsoleNode, input: f64, params: []),
|
register_node!(graphene_core::logic::LogToConsoleNode, input: f64, params: []),
|
||||||
register_node!(graphene_core::logic::LogToConsoleNode, input: f64, params: []),
|
register_node!(graphene_core::logic::LogToConsoleNode, input: f64, params: []),
|
||||||
@@ -292,7 +291,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: GraphicGroup, output: GraphicGroup, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: GraphicGroup, output: GraphicGroup, params: []),
|
||||||
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: Artboard, output: GraphicGroup, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: Artboard, output: GraphicGroup, params: []),
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
async_node!(graphene_core::ops::IntoNode<_, &WgpuExecutor>, input: WasmEditorApi, output: &WgpuExecutor, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, &WgpuExecutor>, input: &WasmEditorApi, output: &WgpuExecutor, params: []),
|
||||||
register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
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::MaskImageNode<_, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Luma>]),
|
||||||
register_node!(graphene_std::raster::InsertChannelNode<_, _, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Color>, RedGreenBlue]),
|
register_node!(graphene_std::raster::InsertChannelNode<_, _, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Color>, RedGreenBlue]),
|
||||||
@@ -348,9 +347,9 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicGroup, fn_params: [Footprint => graphene_core::GraphicGroup]),
|
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicGroup, fn_params: [Footprint => graphene_core::GraphicGroup]),
|
||||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicElement, fn_params: [Footprint => graphene_core::GraphicElement]),
|
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicElement, fn_params: [Footprint => graphene_core::GraphicElement]),
|
||||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: Artboard, fn_params: [Footprint => Artboard]),
|
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: Artboard, fn_params: [Footprint => Artboard]),
|
||||||
async_node!(graphene_std::wasm_application_io::LoadResourceNode<_>, input: WasmEditorApi, output: Arc<[u8]>, params: [String]),
|
async_node!(graphene_std::wasm_application_io::LoadResourceNode<_>, input: &WasmEditorApi, output: Arc<[u8]>, params: [String]),
|
||||||
register_node!(graphene_std::wasm_application_io::DecodeImageNode, input: Arc<[u8]>, params: []),
|
register_node!(graphene_std::wasm_application_io::DecodeImageNode, input: Arc<[u8]>, params: []),
|
||||||
async_node!(graphene_std::wasm_application_io::CreateSurfaceNode, input: WasmEditorApi, output: Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>, params: []),
|
async_node!(graphene_std::wasm_application_io::CreateSurfaceNode, input: &WasmEditorApi, output: Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>, params: []),
|
||||||
async_node!(
|
async_node!(
|
||||||
graphene_std::wasm_application_io::DrawImageFrameNode<_>,
|
graphene_std::wasm_application_io::DrawImageFrameNode<_>,
|
||||||
input: ImageFrame<SRGBA8>,
|
input: ImageFrame<SRGBA8>,
|
||||||
@@ -384,7 +383,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
async_node!(gpu_executor::ReadOutputBufferNode<_, _>, input: Arc<ShaderInput<WgpuExecutor>>, output: Vec<u8>, params: [&WgpuExecutor, ()]),
|
async_node!(gpu_executor::ReadOutputBufferNode<_, _>, input: Arc<ShaderInput<WgpuExecutor>>, output: Vec<u8>, params: [&WgpuExecutor, ()]),
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
async_node!(gpu_executor::CreateGpuSurfaceNode, input: WasmEditorApi, output: Arc<SurfaceHandle<<WgpuExecutor as GpuExecutor>::Surface<'_>>>, params: []),
|
async_node!(gpu_executor::CreateGpuSurfaceNode, input: &WasmEditorApi, output: Arc<SurfaceHandle<<WgpuExecutor as GpuExecutor>::Surface<'_>>>, params: []),
|
||||||
// todo!(gpu) get this to compie without saying that one type is more general than the other
|
// todo!(gpu) get this to compie without saying that one type is more general than the other
|
||||||
// #[cfg(feature = "gpu")]
|
// #[cfg(feature = "gpu")]
|
||||||
// async_node!(gpu_executor::RenderTextureNode<_, _>, input: ShaderInputFrame<WgpuExecutor>, output: SurfaceFrame, params: [Arc<SurfaceHandle<<WgpuExecutor as GpuExecutor>::Surface<'_>>>, &WgpuExecutor]),
|
// async_node!(gpu_executor::RenderTextureNode<_, _>, input: ShaderInputFrame<WgpuExecutor>, output: SurfaceFrame, params: [Arc<SurfaceHandle<<WgpuExecutor as GpuExecutor>::Surface<'_>>>, &WgpuExecutor]),
|
||||||
@@ -401,7 +400,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
|args| {
|
|args| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let document_node: DowncastBothNode<(), graph_craft::document::DocumentNode> = DowncastBothNode::new(args[0].clone());
|
let document_node: DowncastBothNode<(), graph_craft::document::DocumentNode> = DowncastBothNode::new(args[0].clone());
|
||||||
let editor_api: DowncastBothNode<(), WasmEditorApi> = DowncastBothNode::new(args[1].clone());
|
let editor_api: DowncastBothNode<(), &WasmEditorApi> = DowncastBothNode::new(args[1].clone());
|
||||||
// let document_node = ClonedNode::new(document_node.eval(()));
|
// let document_node = ClonedNode::new(document_node.eval(()));
|
||||||
let node = graphene_std::gpu_nodes::MapGpuNode::new(document_node, editor_api);
|
let node = graphene_std::gpu_nodes::MapGpuNode::new(document_node, editor_api);
|
||||||
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(node);
|
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||||
@@ -569,99 +568,42 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
register_node!(graphene_core::raster::BlendModeNode<_>, input: ImageFrame<Color>, params: [BlendMode]),
|
register_node!(graphene_core::raster::BlendModeNode<_>, input: ImageFrame<Color>, params: [BlendMode]),
|
||||||
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
|
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
|
||||||
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
|
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
|
||||||
register_node!(graphene_core::memo::LetNode<_>, input: Option<ImageFrame<Color>>, params: []),
|
vec![(
|
||||||
register_node!(graphene_core::memo::LetNode<_>, input: Option<WasmEditorApi>, params: []),
|
ProtoNodeIdentifier::new("graphene_std::raster::ImaginateNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>"),
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
|args: Vec<graph_craft::proto::SharedNodeContainer>| {
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: VectorData, params: [VectorData]),
|
Box::pin(async move {
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [RenderOutput]),
|
use graphene_std::raster::ImaginateNode;
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [f32]),
|
macro_rules! instantiate_imaginate_node {
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [f64]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [bool]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [String]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [Option<Color>]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [Vec<Color>]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [DVec2]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => VectorData]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ImageFrame<Color>]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Option<Color>]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Vec<Color>]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => GraphicGroup]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Artboard]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => f32]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => f64]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => bool]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => String]),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => DVec2]),
|
|
||||||
async_node!(
|
|
||||||
graphene_core::memo::EndLetNode<_, _>,
|
|
||||||
input: WasmEditorApi,
|
|
||||||
output: GraphicGroup,
|
|
||||||
params: [GraphicGroup]
|
|
||||||
),
|
|
||||||
async_node!(
|
|
||||||
graphene_core::memo::EndLetNode<_, _>,
|
|
||||||
input: WasmEditorApi,
|
|
||||||
output: Artboard,
|
|
||||||
params: [Artboard]
|
|
||||||
),
|
|
||||||
async_node!(
|
|
||||||
graphene_core::memo::EndLetNode<_, _>,
|
|
||||||
input: WasmEditorApi,
|
|
||||||
output: WasmSurfaceHandleFrame,
|
|
||||||
params: [WasmSurfaceHandleFrame]
|
|
||||||
),
|
|
||||||
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: SurfaceFrame, params: [SurfaceFrame]),
|
|
||||||
vec![
|
|
||||||
(
|
|
||||||
ProtoNodeIdentifier::new("graphene_core::memo::RefNode<_, _>"),
|
|
||||||
|args| {
|
|
||||||
Box::pin(async move {
|
|
||||||
let node: DowncastBothNode<Option<WasmEditorApi>, WasmEditorApi> = graphene_std::any::DowncastBothNode::new(args[0].clone());
|
|
||||||
let node = <graphene_core::memo::RefNode<_, _>>::new(node);
|
|
||||||
let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(node);
|
|
||||||
|
|
||||||
any.into_type_erased()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
NodeIOTypes::new(concrete!(()), concrete!(WasmEditorApi), vec![fn_type!(Option<WasmEditorApi>, WasmEditorApi)]),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
ProtoNodeIdentifier::new("graphene_std::raster::ImaginateNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>"),
|
|
||||||
|args: Vec<graph_craft::proto::SharedNodeContainer>| {
|
|
||||||
Box::pin(async move {
|
|
||||||
use graphene_std::raster::ImaginateNode;
|
|
||||||
macro_rules! instantiate_imaginate_node {
|
|
||||||
($($i:expr,)*) => { ImaginateNode::new($(graphene_std::any::input_node(args[$i].clone()),)* ) };
|
($($i:expr,)*) => { ImaginateNode::new($(graphene_std::any::input_node(args[$i].clone()),)* ) };
|
||||||
}
|
}
|
||||||
let node: ImaginateNode<Color, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _> = instantiate_imaginate_node!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,);
|
let node: ImaginateNode<Color, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _> = instantiate_imaginate_node!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,);
|
||||||
let any = graphene_std::any::DynAnyNode::new(node);
|
let any = graphene_std::any::DynAnyNode::new(node);
|
||||||
any.into_type_erased()
|
any.into_type_erased()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
NodeIOTypes::new(
|
NodeIOTypes::new(
|
||||||
concrete!(ImageFrame<Color>),
|
concrete!(ImageFrame<Color>),
|
||||||
concrete!(ImageFrame<Color>),
|
concrete!(ImageFrame<Color>),
|
||||||
vec![
|
vec![
|
||||||
fn_type!(WasmEditorApi),
|
fn_type!(WasmEditorApi),
|
||||||
fn_type!(ImaginateController),
|
fn_type!(ImaginateController),
|
||||||
fn_type!(u64),
|
fn_type!(u64),
|
||||||
fn_type!(Option<DVec2>),
|
fn_type!(Option<DVec2>),
|
||||||
fn_type!(u32),
|
fn_type!(u32),
|
||||||
fn_type!(ImaginateSamplingMethod),
|
fn_type!(ImaginateSamplingMethod),
|
||||||
fn_type!(f64),
|
fn_type!(f64),
|
||||||
fn_type!(String),
|
fn_type!(String),
|
||||||
fn_type!(String),
|
fn_type!(String),
|
||||||
fn_type!(bool),
|
fn_type!(bool),
|
||||||
fn_type!(f64),
|
fn_type!(f64),
|
||||||
fn_type!(bool),
|
fn_type!(bool),
|
||||||
fn_type!(f64),
|
fn_type!(f64),
|
||||||
fn_type!(ImaginateMaskStartingFill),
|
fn_type!(ImaginateMaskStartingFill),
|
||||||
fn_type!(bool),
|
fn_type!(bool),
|
||||||
fn_type!(bool),
|
fn_type!(bool),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
)],
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Image<Color>, params: [Image<Color>]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Image<Color>, params: [Image<Color>]),
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: QuantizationChannels, params: [QuantizationChannels]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: QuantizationChannels, params: [QuantizationChannels]),
|
||||||
@@ -683,23 +625,23 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
register_node!(graphene_core::quantization::QuantizeNode<_>, input: Color, params: [QuantizationChannels]),
|
register_node!(graphene_core::quantization::QuantizeNode<_>, input: Color, params: [QuantizationChannels]),
|
||||||
register_node!(graphene_core::quantization::DeQuantizeNode<_>, input: PackedPixel, params: [QuantizationChannels]),
|
register_node!(graphene_core::quantization::DeQuantizeNode<_>, input: PackedPixel, params: [QuantizationChannels]),
|
||||||
register_node!(graphene_core::ops::CloneNode<_>, input: &QuantizationChannels, params: []),
|
register_node!(graphene_core::ops::CloneNode<_>, input: &QuantizationChannels, params: []),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ImageFrame<Color>, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, fn_params: [Footprint => ImageFrame<Color>, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => VectorData, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, fn_params: [Footprint => VectorData, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Artboard, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, fn_params: [Footprint => Artboard, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ArtboardGroup, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, fn_params: [Footprint => ArtboardGroup, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Option<Color>, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, fn_params: [Footprint => Option<Color>, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Vec<Color>, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, fn_params: [Footprint => Vec<Color>, () => Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [ImageFrame<Color>, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [ImageFrame<Color>, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [VectorData, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [VectorData, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [GraphicGroup, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [GraphicGroup, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [Artboard, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [Artboard, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [bool, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [bool, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [f32, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [f32, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [f64, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [f64, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [String, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [String, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [Option<Color>, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [Option<Color>, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, params: [Vec<Color>, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: RenderConfig, output: RenderOutput, params: [Vec<Color>, Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RasterizeNode<_, _>, input: VectorData, output: ImageFrame<Color>, params: [Footprint, Arc<WasmSurfaceHandle>]),
|
async_node!(graphene_std::wasm_application_io::RasterizeNode<_, _>, input: VectorData, output: ImageFrame<Color>, params: [Footprint, Arc<WasmSurfaceHandle>]),
|
||||||
async_node!(graphene_std::wasm_application_io::RasterizeNode<_, _>, input: GraphicGroup, output: ImageFrame<Color>, params: [Footprint, Arc<WasmSurfaceHandle>]),
|
async_node!(graphene_std::wasm_application_io::RasterizeNode<_, _>, input: GraphicGroup, output: ImageFrame<Color>, params: [Footprint, Arc<WasmSurfaceHandle>]),
|
||||||
async_node!(graphene_core::transform::TransformNode<_, _, _, _, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, () => DVec2, () => f64, () => DVec2, () => DVec2, () => DVec2]),
|
async_node!(graphene_core::transform::TransformNode<_, _, _, _, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, () => DVec2, () => f64, () => DVec2, () => DVec2, () => DVec2]),
|
||||||
@@ -805,9 +747,8 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
params: [Vec<graphene_core::vector::PointId>]
|
params: [Vec<graphene_core::vector::PointId>]
|
||||||
),
|
),
|
||||||
register_node!(graphene_core::vector::PathModify<_>, input: VectorData, params: [graphene_core::vector::VectorModification]),
|
register_node!(graphene_core::vector::PathModify<_>, input: VectorData, params: [graphene_core::vector::VectorModification]),
|
||||||
register_node!(graphene_core::text::TextGeneratorNode<_, _, _>, input: WasmEditorApi, params: [String, graphene_core::text::Font, f64]),
|
register_node!(graphene_core::text::TextGeneratorNode<_, _, _>, input: &WasmEditorApi, params: [String, graphene_core::text::Font, f64]),
|
||||||
register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []),
|
register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []),
|
||||||
register_node!(graphene_core::ExtractImageFrame, input: WasmEditorApi, params: []),
|
|
||||||
async_node!(graphene_core::ConstructLayerNode<_, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => GraphicGroup, Footprint => graphene_core::GraphicElement]),
|
async_node!(graphene_core::ConstructLayerNode<_, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => GraphicGroup, Footprint => graphene_core::GraphicElement]),
|
||||||
register_node!(graphene_core::ToGraphicElementNode, input: graphene_core::vector::VectorData, params: []),
|
register_node!(graphene_core::ToGraphicElementNode, input: graphene_core::vector::VectorData, params: []),
|
||||||
register_node!(graphene_core::ToGraphicElementNode, input: ImageFrame<Color>, params: []),
|
register_node!(graphene_core::ToGraphicElementNode, input: ImageFrame<Color>, params: []),
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ gpu-executor = { path = "../gpu-executor" }
|
|||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
graphene-core = { workspace = true, features = ["std", "alloc", "gpu"] }
|
graphene-core = { workspace = true, features = ["std", "alloc", "gpu"] }
|
||||||
graph-craft = { workspace = true }
|
|
||||||
dyn-any = { workspace = true, features = ["log-bad-types", "rc", "glam"] }
|
dyn-any = { workspace = true, features = ["log-bad-types", "rc", "glam"] }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
|
use super::context::Context;
|
||||||
|
|
||||||
|
use dyn_any::StaticTypeSized;
|
||||||
|
|
||||||
|
use bytemuck::Pod;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{borrow::Cow, error::Error};
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use super::context::Context;
|
pub type LocalFuture<'n, T> = Pin<Box<dyn core::future::Future<Output = T> + 'n>>;
|
||||||
use bytemuck::Pod;
|
|
||||||
use dyn_any::StaticTypeSized;
|
|
||||||
use graph_craft::{graphene_compiler::Executor, proto::LocalFuture};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GpuExecutor<'a, I: StaticTypeSized, O> {
|
pub struct GpuExecutor<'a, I: StaticTypeSized, O> {
|
||||||
@@ -15,7 +19,7 @@ pub struct GpuExecutor<'a, I: StaticTypeSized, O> {
|
|||||||
_phantom: std::marker::PhantomData<(I, O)>,
|
_phantom: std::marker::PhantomData<(I, O)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I: StaticTypeSized, O> GpuExecutor<'a, I, O> {
|
impl<'a, I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> GpuExecutor<'a, I, O> {
|
||||||
pub fn new(context: Context, shader: Cow<'a, [u32]>, entry_point: String) -> anyhow::Result<Self> {
|
pub fn new(context: Context, shader: Cow<'a, [u32]>, entry_point: String) -> anyhow::Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
context,
|
context,
|
||||||
@@ -24,10 +28,8 @@ impl<'a, I: StaticTypeSized, O> GpuExecutor<'a, I, O> {
|
|||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> Executor<Vec<I>, Vec<O>> for GpuExecutor<'a, I, O> {
|
pub fn execute(&self, input: Vec<I>) -> LocalFuture<Result<Vec<O>, Box<dyn Error>>> {
|
||||||
fn execute(&self, input: Vec<I>) -> LocalFuture<Result<Vec<O>, Box<dyn Error>>> {
|
|
||||||
let context = &self.context;
|
let context = &self.context;
|
||||||
let future = execute_shader(context.device.clone(), context.queue.clone(), self.shader.to_vec(), input, self.entry_point.clone());
|
let future = execute_shader(context.device.clone(), context.queue.clone(), self.shader.to_vec(), input, self.entry_point.clone());
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ mod executor;
|
|||||||
pub use context::Context;
|
pub use context::Context;
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
pub use executor::GpuExecutor;
|
pub use executor::GpuExecutor;
|
||||||
|
pub use gpu_executor::ShaderIO;
|
||||||
use gpu_executor::{ComputePassDimensions, Shader, ShaderInput, StorageBufferOptions, TextureBufferOptions, TextureBufferType, ToStorageBuffer, ToUniformBuffer};
|
use gpu_executor::{ComputePassDimensions, Shader, ShaderInput, StorageBufferOptions, TextureBufferOptions, TextureBufferType, ToStorageBuffer, ToUniformBuffer};
|
||||||
use graph_craft::Type;
|
use graphene_core::Type;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use graphene_core::application_io::{ApplicationIo, EditorApi, SurfaceHandle};
|
use graphene_core::application_io::{ApplicationIo, EditorApi, SurfaceHandle};
|
||||||
|
|
||||||
use std::cell::Cell;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use wgpu::{Buffer, BufferDescriptor, CommandBuffer, ShaderModule, SurfaceConfiguration, SurfaceError, Texture, TextureView};
|
use wgpu::{Buffer, BufferDescriptor, CommandBuffer, ShaderModule, SurfaceError, Texture, TextureView};
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use web_sys::HtmlCanvasElement;
|
use web_sys::HtmlCanvasElement;
|
||||||
@@ -25,7 +25,6 @@ use web_sys::HtmlCanvasElement;
|
|||||||
pub struct WgpuExecutor {
|
pub struct WgpuExecutor {
|
||||||
pub context: Context,
|
pub context: Context,
|
||||||
render_configuration: RenderConfiguration,
|
render_configuration: RenderConfiguration,
|
||||||
surface_config: Cell<Option<SurfaceConfiguration>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for WgpuExecutor {
|
impl std::fmt::Debug for WgpuExecutor {
|
||||||
@@ -37,9 +36,9 @@ impl std::fmt::Debug for WgpuExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ApplicationIo<Executor = WgpuExecutor>> From<EditorApi<'a, T>> for &'a WgpuExecutor {
|
impl<'a, T: ApplicationIo<Executor = WgpuExecutor>> From<&'a EditorApi<T>> for &'a WgpuExecutor {
|
||||||
fn from(editor_api: EditorApi<'a, T>) -> Self {
|
fn from(editor_api: &'a EditorApi<T>) -> Self {
|
||||||
editor_api.application_io.gpu_executor().unwrap()
|
editor_api.application_io.as_ref().unwrap().gpu_executor().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,9 +295,8 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
log::warn!("No surface formats available");
|
log::warn!("No surface formats available");
|
||||||
// return Ok(());
|
// return Ok(());
|
||||||
}
|
}
|
||||||
let Some(config) = self.surface_config.take() else { return Ok(()) };
|
// let new_config = config.clone();
|
||||||
let new_config = config.clone();
|
// self.surface_config.replace(Some(config));
|
||||||
self.surface_config.replace(Some(config));
|
|
||||||
let output = match result {
|
let output = match result {
|
||||||
Err(SurfaceError::Timeout) => {
|
Err(SurfaceError::Timeout) => {
|
||||||
log::warn!("Timeout when getting current texture");
|
log::warn!("Timeout when getting current texture");
|
||||||
@@ -307,7 +305,7 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
Err(SurfaceError::Lost) => {
|
Err(SurfaceError::Lost) => {
|
||||||
log::warn!("Surface lost");
|
log::warn!("Surface lost");
|
||||||
|
|
||||||
surface.configure(&self.context.device, &new_config);
|
// surface.configure(&self.context.device, &new_config);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(SurfaceError::OutOfMemory) => {
|
Err(SurfaceError::OutOfMemory) => {
|
||||||
@@ -316,7 +314,7 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
}
|
}
|
||||||
Err(SurfaceError::Outdated) => {
|
Err(SurfaceError::Outdated) => {
|
||||||
log::warn!("Surface outdated");
|
log::warn!("Surface outdated");
|
||||||
surface.configure(&self.context.device, &new_config);
|
// surface.configure(&self.context.device, &new_config);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Ok(surface) => surface,
|
Ok(surface) => surface,
|
||||||
@@ -472,7 +470,6 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
desired_maximum_frame_latency: 2,
|
desired_maximum_frame_latency: 2,
|
||||||
};
|
};
|
||||||
surface.configure(&self.context.device, &config);
|
surface.configure(&self.context.device, &config);
|
||||||
self.surface_config.set(Some(config));
|
|
||||||
|
|
||||||
let surface_id = window.surface_id;
|
let surface_id = window.surface_id;
|
||||||
Ok(SurfaceHandle { surface_id, surface })
|
Ok(SurfaceHandle { surface_id, surface })
|
||||||
@@ -591,11 +588,7 @@ impl WgpuExecutor {
|
|||||||
sampler,
|
sampler,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Self {
|
Some(Self { context, render_configuration })
|
||||||
context,
|
|
||||||
render_configuration,
|
|
||||||
surface_config: Cell::new(None),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ let
|
|||||||
rustc-wasm = pkgs.rust-bin.stable.latest.default.override {
|
rustc-wasm = pkgs.rust-bin.stable.latest.default.override {
|
||||||
targets = [ "wasm32-unknown-unknown" ];
|
targets = [ "wasm32-unknown-unknown" ];
|
||||||
# wasm-pack needs this
|
# wasm-pack needs this
|
||||||
extensions = [ "rust-src" "rust-analyzer" "clippy" ];
|
extensions = [ "rust-src" "rust-analyzer" "clippy"];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
# Make a shell with the dependencies we need
|
# Make a shell with the dependencies we need
|
||||||
@@ -50,11 +50,14 @@ in
|
|||||||
pkgs.webkitgtk
|
pkgs.webkitgtk
|
||||||
|
|
||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
|
pkgs.openssl
|
||||||
|
|
||||||
# Use Mold as a Linke
|
# Use Mold as a Linke
|
||||||
pkgs.mold
|
pkgs.mold
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [pkgs.openssl];
|
||||||
# Hacky way to run cago through Mold
|
# Hacky way to run cago through Mold
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
alias cargo='mold --run cargo'
|
alias cargo='mold --run cargo'
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub fn init() {
|
|||||||
log::set_logger(&LOGGER).expect("Failed to set logger");
|
log::set_logger(&LOGGER).expect("Failed to set logger");
|
||||||
log::set_max_level(log::LevelFilter::Trace);
|
log::set_max_level(log::LevelFilter::Trace);
|
||||||
|
|
||||||
fn panic_hook(info: &core::panic::PanicInfo) {
|
fn panic_hook(info: &std::panic::PanicInfo<'_>) {
|
||||||
// Skip if we have already panicked
|
// Skip if we have already panicked
|
||||||
if HAS_CRASHED.with(|cell| cell.replace(true)) {
|
if HAS_CRASHED.with(|cell| cell.replace(true)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user