Graphene CLI + quantization research (#1320)
* Implement skeleton for graphene-cli * Configure gpu surface on non wasm32 targets * Create window with full hd size * Create window using the graphen-cli * Use window size for surface creation * Reuse surface configuration * Reduce window size for native applications to 800x600 * Add compute pipeline test * Poll wgpu execution externally * Remove cache node after texture upload * Add profiling instructions * Add more debug markers * Evaluate extract node before flattening the network * Reenable hue saturation node for compilation * Make hue saturation node work on the gpu + make f32 default for user inputs * Add version of test files without caching * Only dispatch each workgroup not pixel * ICE * Add quantization to gpu code * Fix quantization * Load images at graph runtime * Fix quantization calculation * Feature gate quantization * Use git version of autoquant * Add license to `graphene-cli` * Fix graphene-cli test case * Ignore tests on non unix platforms * Fix flattening test
@@ -8,6 +8,7 @@ members = [
|
|||||||
"node-graph/gcore",
|
"node-graph/gcore",
|
||||||
"node-graph/gstd",
|
"node-graph/gstd",
|
||||||
"node-graph/graph-craft",
|
"node-graph/graph-craft",
|
||||||
|
"node-graph/graphene-cli",
|
||||||
"node-graph/interpreted-executor",
|
"node-graph/interpreted-executor",
|
||||||
"node-graph/node-macro",
|
"node-graph/node-macro",
|
||||||
"node-graph/compilation-server",
|
"node-graph/compilation-server",
|
||||||
|
|||||||
@@ -111,12 +111,12 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
fn stroke_set(&mut self, stroke: Stroke) {
|
fn stroke_set(&mut self, stroke: Stroke) {
|
||||||
self.modify_inputs("Stroke", false, |inputs| {
|
self.modify_inputs("Stroke", false, |inputs| {
|
||||||
inputs[1] = NodeInput::value(TaggedValue::OptionalColor(stroke.color), false);
|
inputs[1] = NodeInput::value(TaggedValue::OptionalColor(stroke.color), false);
|
||||||
inputs[2] = NodeInput::value(TaggedValue::F64(stroke.weight), false);
|
inputs[2] = NodeInput::value(TaggedValue::F32(stroke.weight as f32), false);
|
||||||
inputs[3] = NodeInput::value(TaggedValue::VecF32(stroke.dash_lengths), false);
|
inputs[3] = NodeInput::value(TaggedValue::VecF32(stroke.dash_lengths), false);
|
||||||
inputs[4] = NodeInput::value(TaggedValue::F64(stroke.dash_offset), false);
|
inputs[4] = NodeInput::value(TaggedValue::F32(stroke.dash_offset as f32), false);
|
||||||
inputs[5] = NodeInput::value(TaggedValue::LineCap(stroke.line_cap), false);
|
inputs[5] = NodeInput::value(TaggedValue::LineCap(stroke.line_cap), false);
|
||||||
inputs[6] = NodeInput::value(TaggedValue::LineJoin(stroke.line_join), false);
|
inputs[6] = NodeInput::value(TaggedValue::LineJoin(stroke.line_join), false);
|
||||||
inputs[7] = NodeInput::value(TaggedValue::F64(stroke.line_join_miter_limit), false);
|
inputs[7] = NodeInput::value(TaggedValue::F32(stroke.line_join_miter_limit as f32), false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ pub fn update_transform(inputs: &mut [NodeInput], transform: DAffine2) {
|
|||||||
let (scale, angle, translation, shear) = compute_scale_angle_translation_shear(transform);
|
let (scale, angle, translation, shear) = compute_scale_angle_translation_shear(transform);
|
||||||
|
|
||||||
inputs[1] = NodeInput::value(TaggedValue::DVec2(translation), false);
|
inputs[1] = NodeInput::value(TaggedValue::DVec2(translation), false);
|
||||||
inputs[2] = NodeInput::value(TaggedValue::F64(angle), false);
|
inputs[2] = NodeInput::value(TaggedValue::F32(angle as f32), false);
|
||||||
inputs[3] = NodeInput::value(TaggedValue::DVec2(scale), false);
|
inputs[3] = NodeInput::value(TaggedValue::DVec2(scale), false);
|
||||||
inputs[4] = NodeInput::value(TaggedValue::DVec2(shear), false);
|
inputs[4] = NodeInput::value(TaggedValue::DVec2(shear), false);
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let angle = if let NodeInput::Value {
|
let angle = if let NodeInput::Value {
|
||||||
tagged_value: TaggedValue::F64(angle),
|
tagged_value: TaggedValue::F32(angle),
|
||||||
..
|
..
|
||||||
} = inputs[2]
|
} = inputs[2]
|
||||||
{
|
{
|
||||||
@@ -116,7 +116,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||||||
DVec2::ZERO
|
DVec2::ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
DAffine2::from_scale_angle_translation(scale, angle, translation) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.])
|
DAffine2::from_scale_angle_translation(scale, angle as f64, translation) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the current normalized pivot from the layer
|
/// Extract the current normalized pivot from the layer
|
||||||
|
|||||||
@@ -254,6 +254,50 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
}],
|
}],
|
||||||
properties: node_properties::input_properties,
|
properties: node_properties::input_properties,
|
||||||
},
|
},
|
||||||
|
DocumentNodeType {
|
||||||
|
name: "Load Image",
|
||||||
|
category: "Structural",
|
||||||
|
identifier: NodeImplementation::DocumentNode(NodeNetwork {
|
||||||
|
inputs: vec![0, 0],
|
||||||
|
outputs: vec![NodeOutput::new(1, 0)],
|
||||||
|
nodes: [
|
||||||
|
DocumentNode {
|
||||||
|
name: "Load Resource".to_string(),
|
||||||
|
inputs: vec![NodeInput::Network(concrete!(WasmEditorApi)), NodeInput::Network(concrete!(String))],
|
||||||
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::LoadResourceNode<_>")),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
DocumentNode {
|
||||||
|
name: "Decode Image".to_string(),
|
||||||
|
inputs: vec![NodeInput::node(0, 0)],
|
||||||
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::DecodeImageNode")),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(id, node)| (id as NodeId, node))
|
||||||
|
.collect(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
inputs: vec![
|
||||||
|
DocumentInputType {
|
||||||
|
name: "api",
|
||||||
|
data_type: FrontendGraphDataType::General,
|
||||||
|
default: NodeInput::Network(concrete!(WasmEditorApi)),
|
||||||
|
},
|
||||||
|
DocumentInputType {
|
||||||
|
name: "path",
|
||||||
|
data_type: FrontendGraphDataType::General,
|
||||||
|
default: NodeInput::value(TaggedValue::String("graphite:null".to_string()), false),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
outputs: vec![DocumentOutputType {
|
||||||
|
name: "Image Frame",
|
||||||
|
data_type: FrontendGraphDataType::Raster,
|
||||||
|
}],
|
||||||
|
properties: node_properties::load_image_properties,
|
||||||
|
},
|
||||||
DocumentNodeType {
|
DocumentNodeType {
|
||||||
name: "Create Canvas",
|
name: "Create Canvas",
|
||||||
category: "Structural",
|
category: "Structural",
|
||||||
@@ -289,7 +333,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
name: "Canvas",
|
name: "Canvas",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
}],
|
}],
|
||||||
properties: node_properties::input_properties,
|
properties: node_properties::no_properties,
|
||||||
},
|
},
|
||||||
DocumentNodeType {
|
DocumentNodeType {
|
||||||
name: "Draw Canvas",
|
name: "Draw Canvas",
|
||||||
@@ -345,7 +389,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
name: "Canvas",
|
name: "Canvas",
|
||||||
data_type: FrontendGraphDataType::General,
|
data_type: FrontendGraphDataType::General,
|
||||||
}],
|
}],
|
||||||
properties: node_properties::input_properties,
|
properties: node_properties::no_properties,
|
||||||
},
|
},
|
||||||
DocumentNodeType {
|
DocumentNodeType {
|
||||||
name: "Begin Scope",
|
name: "Begin Scope",
|
||||||
@@ -490,7 +534,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("Second", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Second", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("BlendMode", TaggedValue::BlendMode(BlendMode::Normal), false),
|
DocumentInputType::value("BlendMode", TaggedValue::BlendMode(BlendMode::Normal), false),
|
||||||
DocumentInputType::value("Opacity", TaggedValue::F64(100.), false),
|
DocumentInputType::value("Opacity", TaggedValue::F32(100.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::blend_properties,
|
properties: node_properties::blend_properties,
|
||||||
@@ -508,27 +552,27 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Shadows",
|
name: "Shadows",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(0.), false),
|
default: NodeInput::value(TaggedValue::F32(0.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Midtones",
|
name: "Midtones",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(50.), false),
|
default: NodeInput::value(TaggedValue::F32(50.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Highlights",
|
name: "Highlights",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(100.), false),
|
default: NodeInput::value(TaggedValue::F32(100.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Output Minimums",
|
name: "Output Minimums",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(0.), false),
|
default: NodeInput::value(TaggedValue::F32(0.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Output Maximums",
|
name: "Output Maximums",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(100.), false),
|
default: NodeInput::value(TaggedValue::F32(100.), false),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
@@ -552,32 +596,32 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Reds",
|
name: "Reds",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(40.), false),
|
default: NodeInput::value(TaggedValue::F32(40.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Yellows",
|
name: "Yellows",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(60.), false),
|
default: NodeInput::value(TaggedValue::F32(60.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Greens",
|
name: "Greens",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(40.), false),
|
default: NodeInput::value(TaggedValue::F32(40.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Cyans",
|
name: "Cyans",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(60.), false),
|
default: NodeInput::value(TaggedValue::F32(60.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Blues",
|
name: "Blues",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(20.), false),
|
default: NodeInput::value(TaggedValue::F32(20.), false),
|
||||||
},
|
},
|
||||||
DocumentInputType {
|
DocumentInputType {
|
||||||
name: "Magentas",
|
name: "Magentas",
|
||||||
data_type: FrontendGraphDataType::Number,
|
data_type: FrontendGraphDataType::Number,
|
||||||
default: NodeInput::value(TaggedValue::F64(80.), false),
|
default: NodeInput::value(TaggedValue::F32(80.), false),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
@@ -713,7 +757,10 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
name: "Memoize",
|
name: "Memoize",
|
||||||
category: "Structural",
|
category: "Structural",
|
||||||
identifier: NodeImplementation::proto("graphene_core::memo::MemoNode<_, _>"),
|
identifier: NodeImplementation::proto("graphene_core::memo::MemoNode<_, _>"),
|
||||||
inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)],
|
inputs: vec![
|
||||||
|
DocumentInputType::value("ShortCircut", TaggedValue::None, false),
|
||||||
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::no_properties,
|
properties: node_properties::no_properties,
|
||||||
},
|
},
|
||||||
@@ -1136,7 +1183,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
category: "Gpu",
|
category: "Gpu",
|
||||||
identifier: NodeImplementation::DocumentNode(NodeNetwork {
|
identifier: NodeImplementation::DocumentNode(NodeNetwork {
|
||||||
inputs: vec![1, 1, 0],
|
inputs: vec![1, 1, 0],
|
||||||
outputs: vec![NodeOutput::new(2, 0)],
|
outputs: vec![NodeOutput::new(1, 0)],
|
||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Extract Executor".to_string(),
|
name: "Extract Executor".to_string(),
|
||||||
@@ -1154,12 +1201,6 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("gpu_executor::RenderTextureNode<_, _>")),
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("gpu_executor::RenderTextureNode<_, _>")),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNode {
|
|
||||||
name: "Cache".to_string(),
|
|
||||||
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(1, 0)],
|
|
||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -1367,9 +1408,9 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
identifier: NodeImplementation::proto("graphene_core::raster::HueSaturationNode<_, _, _>"),
|
identifier: NodeImplementation::proto("graphene_core::raster::HueSaturationNode<_, _, _>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("Hue Shift", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Hue Shift", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Saturation Shift", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Saturation Shift", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Lightness Shift", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Lightness Shift", TaggedValue::F32(0.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::adjust_hsl_properties,
|
properties: node_properties::adjust_hsl_properties,
|
||||||
@@ -1380,8 +1421,8 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _, _>"),
|
identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _, _>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("Brightness", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Brightness", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Contrast", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Contrast", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Use Legacy", TaggedValue::Bool(false), false),
|
DocumentInputType::value("Use Legacy", TaggedValue::Bool(false), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
@@ -1393,8 +1434,8 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_, _, _>"),
|
identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_, _, _>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("Min Luminance", TaggedValue::F64(50.), false),
|
DocumentInputType::value("Min Luminance", TaggedValue::F32(50.), false),
|
||||||
DocumentInputType::value("Max Luminance", TaggedValue::F64(100.), false),
|
DocumentInputType::value("Max Luminance", TaggedValue::F32(100.), false),
|
||||||
DocumentInputType::value("Luminance Calc", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false),
|
DocumentInputType::value("Luminance Calc", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
@@ -1406,7 +1447,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
identifier: NodeImplementation::proto("graphene_core::raster::VibranceNode<_>"),
|
identifier: NodeImplementation::proto("graphene_core::raster::VibranceNode<_>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("Vibrance", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Vibrance", TaggedValue::F32(0.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::adjust_vibrance_properties,
|
properties: node_properties::adjust_vibrance_properties,
|
||||||
@@ -1420,25 +1461,25 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
// Monochrome toggle
|
// Monochrome toggle
|
||||||
DocumentInputType::value("Monochrome", TaggedValue::Bool(false), false),
|
DocumentInputType::value("Monochrome", TaggedValue::Bool(false), false),
|
||||||
// Monochrome
|
// Monochrome
|
||||||
DocumentInputType::value("Red", TaggedValue::F64(40.), false),
|
DocumentInputType::value("Red", TaggedValue::F32(40.), false),
|
||||||
DocumentInputType::value("Green", TaggedValue::F64(40.), false),
|
DocumentInputType::value("Green", TaggedValue::F32(40.), false),
|
||||||
DocumentInputType::value("Blue", TaggedValue::F64(20.), false),
|
DocumentInputType::value("Blue", TaggedValue::F32(20.), false),
|
||||||
DocumentInputType::value("Constant", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Constant", TaggedValue::F32(0.), false),
|
||||||
// Red output channel
|
// Red output channel
|
||||||
DocumentInputType::value("(Red) Red", TaggedValue::F64(100.), false),
|
DocumentInputType::value("(Red) Red", TaggedValue::F32(100.), false),
|
||||||
DocumentInputType::value("(Red) Green", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Red) Green", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Red) Blue", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Red) Blue", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Red) Constant", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Red) Constant", TaggedValue::F32(0.), false),
|
||||||
// Green output channel
|
// Green output channel
|
||||||
DocumentInputType::value("(Green) Red", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Green) Red", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Green) Green", TaggedValue::F64(100.), false),
|
DocumentInputType::value("(Green) Green", TaggedValue::F32(100.), false),
|
||||||
DocumentInputType::value("(Green) Blue", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Green) Blue", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Green) Constant", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Green) Constant", TaggedValue::F32(0.), false),
|
||||||
// Blue output channel
|
// Blue output channel
|
||||||
DocumentInputType::value("(Blue) Red", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blue) Red", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Blue) Green", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blue) Green", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Blue) Blue", TaggedValue::F64(100.), false),
|
DocumentInputType::value("(Blue) Blue", TaggedValue::F32(100.), false),
|
||||||
DocumentInputType::value("(Blue) Constant", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blue) Constant", TaggedValue::F32(0.), false),
|
||||||
// Display-only properties (not used within the node)
|
// Display-only properties (not used within the node)
|
||||||
DocumentInputType::value("Output Channel", TaggedValue::RedGreenBlue(RedGreenBlue::Red), false),
|
DocumentInputType::value("Output Channel", TaggedValue::RedGreenBlue(RedGreenBlue::Red), false),
|
||||||
],
|
],
|
||||||
@@ -1456,50 +1497,50 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
// Mode
|
// Mode
|
||||||
DocumentInputType::value("Mode", TaggedValue::RelativeAbsolute(RelativeAbsolute::Relative), false),
|
DocumentInputType::value("Mode", TaggedValue::RelativeAbsolute(RelativeAbsolute::Relative), false),
|
||||||
// Reds
|
// Reds
|
||||||
DocumentInputType::value("(Reds) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Reds) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Reds) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Reds) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Reds) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Reds) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Reds) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Reds) Black", TaggedValue::F32(0.), false),
|
||||||
// Yellows
|
// Yellows
|
||||||
DocumentInputType::value("(Yellows) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Yellows) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Yellows) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Yellows) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Yellows) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Yellows) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Yellows) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Yellows) Black", TaggedValue::F32(0.), false),
|
||||||
// Greens
|
// Greens
|
||||||
DocumentInputType::value("(Greens) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Greens) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Greens) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Greens) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Greens) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Greens) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Greens) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Greens) Black", TaggedValue::F32(0.), false),
|
||||||
// Cyans
|
// Cyans
|
||||||
DocumentInputType::value("(Cyans) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Cyans) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Cyans) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Cyans) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Cyans) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Cyans) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Cyans) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Cyans) Black", TaggedValue::F32(0.), false),
|
||||||
// Blues
|
// Blues
|
||||||
DocumentInputType::value("(Blues) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blues) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Blues) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blues) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Blues) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blues) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Blues) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blues) Black", TaggedValue::F32(0.), false),
|
||||||
// Magentas
|
// Magentas
|
||||||
DocumentInputType::value("(Magentas) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Magentas) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Magentas) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Magentas) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Magentas) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Magentas) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Magentas) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Magentas) Black", TaggedValue::F32(0.), false),
|
||||||
// Whites
|
// Whites
|
||||||
DocumentInputType::value("(Whites) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Whites) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Whites) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Whites) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Whites) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Whites) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Whites) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Whites) Black", TaggedValue::F32(0.), false),
|
||||||
// Neutrals
|
// Neutrals
|
||||||
DocumentInputType::value("(Neutrals) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Neutrals) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Neutrals) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Neutrals) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Neutrals) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Neutrals) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Neutrals) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Neutrals) Black", TaggedValue::F32(0.), false),
|
||||||
// Blacks
|
// Blacks
|
||||||
DocumentInputType::value("(Blacks) Cyan", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blacks) Cyan", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Blacks) Magenta", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blacks) Magenta", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Blacks) Yellow", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blacks) Yellow", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("(Blacks) Black", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blacks) Black", TaggedValue::F32(0.), false),
|
||||||
// Display-only properties (not used within the node)
|
// Display-only properties (not used within the node)
|
||||||
DocumentInputType::value("Colors", TaggedValue::SelectiveColorChoice(SelectiveColorChoice::Reds), false),
|
DocumentInputType::value("Colors", TaggedValue::SelectiveColorChoice(SelectiveColorChoice::Reds), false),
|
||||||
],
|
],
|
||||||
@@ -1512,7 +1553,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
identifier: NodeImplementation::proto("graphene_core::raster::OpacityNode<_>"),
|
identifier: NodeImplementation::proto("graphene_core::raster::OpacityNode<_>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("Factor", TaggedValue::F64(100.), false),
|
DocumentInputType::value("Factor", TaggedValue::F32(100.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::multiply_opacity,
|
properties: node_properties::multiply_opacity,
|
||||||
@@ -1523,7 +1564,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
identifier: NodeImplementation::proto("graphene_core::raster::PosterizeNode<_>"),
|
identifier: NodeImplementation::proto("graphene_core::raster::PosterizeNode<_>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("Value", TaggedValue::F64(4.), false),
|
DocumentInputType::value("Value", TaggedValue::F32(4.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::posterize_properties,
|
properties: node_properties::posterize_properties,
|
||||||
@@ -1534,9 +1575,9 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
identifier: NodeImplementation::proto("graphene_core::raster::ExposureNode<_, _, _>"),
|
identifier: NodeImplementation::proto("graphene_core::raster::ExposureNode<_, _, _>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
DocumentInputType::value("Exposure", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Exposure", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Offset", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Offset", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Gamma Correction", TaggedValue::F64(1.), false),
|
DocumentInputType::value("Gamma Correction", TaggedValue::F32(1.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::exposure_properties,
|
properties: node_properties::exposure_properties,
|
||||||
@@ -1546,8 +1587,8 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
category: "Math",
|
category: "Math",
|
||||||
identifier: NodeImplementation::proto("graphene_core::ops::AddParameterNode<_>"),
|
identifier: NodeImplementation::proto("graphene_core::ops::AddParameterNode<_>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Input", TaggedValue::F64(0.), true),
|
DocumentInputType::value("Input", TaggedValue::F32(0.), true),
|
||||||
DocumentInputType::value("Addend", TaggedValue::F64(0.), true),
|
DocumentInputType::value("Addend", TaggedValue::F32(0.), true),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Output", FrontendGraphDataType::Number)],
|
outputs: vec![DocumentOutputType::new("Output", FrontendGraphDataType::Number)],
|
||||||
properties: node_properties::add_properties,
|
properties: node_properties::add_properties,
|
||||||
@@ -1580,7 +1621,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
DocumentInputType::none(),
|
DocumentInputType::none(),
|
||||||
DocumentInputType::value("Text", TaggedValue::String("hello world".to_string()), false),
|
DocumentInputType::value("Text", TaggedValue::String("hello world".to_string()), false),
|
||||||
DocumentInputType::value("Font", TaggedValue::Font(Font::new(DEFAULT_FONT_FAMILY.into(), DEFAULT_FONT_STYLE.into())), false),
|
DocumentInputType::value("Font", TaggedValue::Font(Font::new(DEFAULT_FONT_FAMILY.into(), DEFAULT_FONT_STYLE.into())), false),
|
||||||
DocumentInputType::value("Size", TaggedValue::F64(24.), false),
|
DocumentInputType::value("Size", TaggedValue::F32(24.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||||
properties: node_properties::node_section_font,
|
properties: node_properties::node_section_font,
|
||||||
@@ -1592,7 +1633,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
DocumentInputType::value("Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
||||||
DocumentInputType::value("Translation", TaggedValue::DVec2(DVec2::ZERO), false),
|
DocumentInputType::value("Translation", TaggedValue::DVec2(DVec2::ZERO), false),
|
||||||
DocumentInputType::value("Rotation", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Rotation", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Scale", TaggedValue::DVec2(DVec2::ONE), false),
|
DocumentInputType::value("Scale", TaggedValue::DVec2(DVec2::ONE), false),
|
||||||
DocumentInputType::value("Skew", TaggedValue::DVec2(DVec2::ZERO), false),
|
DocumentInputType::value("Skew", TaggedValue::DVec2(DVec2::ZERO), false),
|
||||||
DocumentInputType::value("Pivot", TaggedValue::DVec2(DVec2::splat(0.5)), false),
|
DocumentInputType::value("Pivot", TaggedValue::DVec2(DVec2::splat(0.5)), false),
|
||||||
@@ -1635,12 +1676,12 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
||||||
DocumentInputType::value("Color", TaggedValue::OptionalColor(Some(Color::BLACK)), false),
|
DocumentInputType::value("Color", TaggedValue::OptionalColor(Some(Color::BLACK)), false),
|
||||||
DocumentInputType::value("Weight", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Weight", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Dash Lengths", TaggedValue::VecF32(Vec::new()), false),
|
DocumentInputType::value("Dash Lengths", TaggedValue::VecF32(Vec::new()), false),
|
||||||
DocumentInputType::value("Dash Offset", TaggedValue::F64(0.), false),
|
DocumentInputType::value("Dash Offset", TaggedValue::F32(0.), false),
|
||||||
DocumentInputType::value("Line Cap", TaggedValue::LineCap(graphene_core::vector::style::LineCap::Butt), false),
|
DocumentInputType::value("Line Cap", TaggedValue::LineCap(graphene_core::vector::style::LineCap::Butt), false),
|
||||||
DocumentInputType::value("Line Join", TaggedValue::LineJoin(graphene_core::vector::style::LineJoin::Miter), false),
|
DocumentInputType::value("Line Join", TaggedValue::LineJoin(graphene_core::vector::style::LineJoin::Miter), false),
|
||||||
DocumentInputType::value("Miter Limit", TaggedValue::F64(4.), false),
|
DocumentInputType::value("Miter Limit", TaggedValue::F32(4.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||||
properties: node_properties::stroke_properties,
|
properties: node_properties::stroke_properties,
|
||||||
@@ -1727,18 +1768,18 @@ pub static IMAGINATE_NODE: Lazy<DocumentNodeType> = Lazy::new(|| DocumentNodeTyp
|
|||||||
default: NodeInput::Network(concrete!(WasmEditorApi)),
|
default: NodeInput::Network(concrete!(WasmEditorApi)),
|
||||||
},
|
},
|
||||||
DocumentInputType::value("Controller", TaggedValue::ImaginateController(Default::default()), false),
|
DocumentInputType::value("Controller", TaggedValue::ImaginateController(Default::default()), false),
|
||||||
DocumentInputType::value("Seed", TaggedValue::F64(0.), false), // Remember to keep index used in `ImaginateRandom` updated with this entry's index
|
DocumentInputType::value("Seed", TaggedValue::F32(0.), false), // Remember to keep index used in `ImaginateRandom` updated with this entry's index
|
||||||
DocumentInputType::value("Resolution", TaggedValue::OptionalDVec2(None), false),
|
DocumentInputType::value("Resolution", TaggedValue::OptionalDVec2(None), false),
|
||||||
DocumentInputType::value("Samples", TaggedValue::U32(30), false),
|
DocumentInputType::value("Samples", TaggedValue::U32(30), false),
|
||||||
DocumentInputType::value("Sampling Method", TaggedValue::ImaginateSamplingMethod(ImaginateSamplingMethod::EulerA), false),
|
DocumentInputType::value("Sampling Method", TaggedValue::ImaginateSamplingMethod(ImaginateSamplingMethod::EulerA), false),
|
||||||
DocumentInputType::value("Prompt Guidance", TaggedValue::F64(7.5), false),
|
DocumentInputType::value("Prompt Guidance", TaggedValue::F32(7.5), false),
|
||||||
DocumentInputType::value("Prompt", TaggedValue::String(String::new()), false),
|
DocumentInputType::value("Prompt", TaggedValue::String(String::new()), false),
|
||||||
DocumentInputType::value("Negative Prompt", TaggedValue::String(String::new()), false),
|
DocumentInputType::value("Negative Prompt", TaggedValue::String(String::new()), false),
|
||||||
DocumentInputType::value("Adapt Input Image", TaggedValue::Bool(false), false),
|
DocumentInputType::value("Adapt Input Image", TaggedValue::Bool(false), false),
|
||||||
DocumentInputType::value("Image Creativity", TaggedValue::F64(66.), false),
|
DocumentInputType::value("Image Creativity", TaggedValue::F32(66.), false),
|
||||||
DocumentInputType::value("Masking Layer", TaggedValue::LayerPath(None), false),
|
DocumentInputType::value("Masking Layer", TaggedValue::LayerPath(None), false),
|
||||||
DocumentInputType::value("Inpaint", TaggedValue::Bool(true), false),
|
DocumentInputType::value("Inpaint", TaggedValue::Bool(true), false),
|
||||||
DocumentInputType::value("Mask Blur", TaggedValue::F64(4.), false),
|
DocumentInputType::value("Mask Blur", TaggedValue::F32(4.), false),
|
||||||
DocumentInputType::value("Mask Starting Fill", TaggedValue::ImaginateMaskStartingFill(ImaginateMaskStartingFill::Fill), false),
|
DocumentInputType::value("Mask Starting Fill", TaggedValue::ImaginateMaskStartingFill(ImaginateMaskStartingFill::Fill), false),
|
||||||
DocumentInputType::value("Improve Faces", TaggedValue::Bool(false), false),
|
DocumentInputType::value("Improve Faces", TaggedValue::Bool(false), false),
|
||||||
DocumentInputType::value("Tiling", TaggedValue::Bool(false), false),
|
DocumentInputType::value("Tiling", TaggedValue::Bool(false), false),
|
||||||
@@ -1767,7 +1808,9 @@ impl DocumentNodeType {
|
|||||||
|
|
||||||
let inner_network = match &self.identifier {
|
let inner_network = match &self.identifier {
|
||||||
NodeImplementation::DocumentNode(network) => network.clone(),
|
NodeImplementation::DocumentNode(network) => network.clone(),
|
||||||
NodeImplementation::ProtoNode(ident) => {
|
|
||||||
|
NodeImplementation::ProtoNode(ident) => return DocumentNodeImplementation::Unresolved(ident.clone()),
|
||||||
|
/*
|
||||||
NodeNetwork {
|
NodeNetwork {
|
||||||
inputs: (0..num_inputs).map(|_| 0).collect(),
|
inputs: (0..num_inputs).map(|_| 0).collect(),
|
||||||
outputs: vec![NodeOutput::new(0, 0)],
|
outputs: vec![NodeOutput::new(0, 0)],
|
||||||
@@ -1785,23 +1828,10 @@ impl DocumentNodeType {
|
|||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
NodeImplementation::Extract => NodeNetwork {
|
*/
|
||||||
inputs: (0..num_inputs).map(|_| 0).collect(),
|
NodeImplementation::Extract => return DocumentNodeImplementation::Extract,
|
||||||
outputs: vec![NodeOutput::new(0, 0)],
|
|
||||||
nodes: [(
|
|
||||||
0,
|
|
||||||
DocumentNode {
|
|
||||||
name: "ExtractNode".to_string(),
|
|
||||||
implementation: DocumentNodeImplementation::Extract,
|
|
||||||
inputs: self.inputs.iter().map(|i| NodeInput::Network(i.default.ty())).collect(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DocumentNodeImplementation::Network(inner_network)
|
DocumentNodeImplementation::Network(inner_network)
|
||||||
@@ -1830,9 +1860,9 @@ impl DocumentNodeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
|
pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
|
||||||
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
|
||||||
|
|
||||||
network.generate_node_paths(&[]);
|
network.generate_node_paths(&[]);
|
||||||
|
|
||||||
|
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
||||||
for id in node_ids {
|
for id in node_ids {
|
||||||
network.flatten(id);
|
network.flatten(id);
|
||||||
}
|
}
|
||||||
@@ -1927,7 +1957,7 @@ pub fn new_vector_network(subpaths: Vec<bezier_rs::Subpath<uuid::ManipulatorGrou
|
|||||||
network
|
network
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_text_network(text: String, font: Font, size: f64) -> NodeNetwork {
|
pub fn new_text_network(text: String, font: Font, size: f32) -> NodeNetwork {
|
||||||
let text_generator = resolve_document_node_type("Text").expect("Text node does not exist");
|
let text_generator = resolve_document_node_type("Text").expect("Text node does not exist");
|
||||||
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist");
|
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist");
|
||||||
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist");
|
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist");
|
||||||
@@ -1944,7 +1974,7 @@ pub fn new_text_network(text: String, font: Font, size: f64) -> NodeNetwork {
|
|||||||
NodeInput::Network(concrete!(WasmEditorApi)),
|
NodeInput::Network(concrete!(WasmEditorApi)),
|
||||||
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::F32(size), false),
|
||||||
],
|
],
|
||||||
DocumentNodeMetadata::position((0, 4)),
|
DocumentNodeMetadata::position((0, 4)),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -624,6 +624,12 @@ pub fn blend_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
|||||||
vec![backdrop, blend_mode, LayoutGroup::Row { widgets: opacity }]
|
vec![backdrop, blend_mode, LayoutGroup::Row { widgets: opacity }]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
|
let url = text_widget(document_node, node_id, 1, "Url", true);
|
||||||
|
|
||||||
|
vec![LayoutGroup::Row { widgets: url }]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn output_properties(_document_node: &DocumentNode, _node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn output_properties(_document_node: &DocumentNode, _node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let output_type = context.executor.previous_output_type(context.layer_path);
|
let output_type = context.executor.previous_output_type(context.layer_path);
|
||||||
let raster_output_type = concrete!(ImageFrame<Color>);
|
let raster_output_type = concrete!(ImageFrame<Color>);
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ impl TextToolData {
|
|||||||
else if let Some(editing_text) = self.editing_text.as_ref().filter(|_| state == TextToolFsmState::Ready) {
|
else if let Some(editing_text) = self.editing_text.as_ref().filter(|_| state == TextToolFsmState::Ready) {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
let network = new_text_network(String::new(), editing_text.font.clone(), editing_text.font_size);
|
let network = new_text_network(String::new(), editing_text.font.clone(), editing_text.font_size as f32);
|
||||||
|
|
||||||
responses.add(Operation::AddFrame {
|
responses.add(Operation::AddFrame {
|
||||||
path: self.layer_path.clone(),
|
path: self.layer_path.clone(),
|
||||||
|
|||||||
@@ -156,9 +156,6 @@ unsafe impl<T: StaticTypeSized> StaticType for *const [T] {
|
|||||||
unsafe impl<T: StaticTypeSized> StaticType for *mut [T] {
|
unsafe impl<T: StaticTypeSized> StaticType for *mut [T] {
|
||||||
type Static = *mut [<T as StaticTypeSized>::Static];
|
type Static = *mut [<T as StaticTypeSized>::Static];
|
||||||
}
|
}
|
||||||
unsafe impl<'a, T: StaticTypeSized> StaticType for &'a [T] {
|
|
||||||
type Static = &'static [<T as StaticTypeSized>::Static];
|
|
||||||
}
|
|
||||||
macro_rules! impl_slice {
|
macro_rules! impl_slice {
|
||||||
($($id:ident),*) => {
|
($($id:ident),*) => {
|
||||||
$(
|
$(
|
||||||
@@ -192,6 +189,9 @@ unsafe impl<'a, T: 'a + StaticType + ?Sized> StaticType for &'a T {
|
|||||||
unsafe impl<T: StaticTypeSized, const N: usize> StaticType for [T; N] {
|
unsafe impl<T: StaticTypeSized, const N: usize> StaticType for [T; N] {
|
||||||
type Static = [<T as StaticTypeSized>::Static; N];
|
type Static = [<T as StaticTypeSized>::Static; N];
|
||||||
}
|
}
|
||||||
|
unsafe impl<T: StaticTypeSized> StaticType for [T] {
|
||||||
|
type Static = [<T as StaticTypeSized>::Static];
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl StaticType for dyn for<'i> DynAny<'_> + '_ {
|
unsafe impl StaticType for dyn for<'i> DynAny<'_> + '_ {
|
||||||
type Static = dyn DynAny<'static>;
|
type Static = dyn DynAny<'static>;
|
||||||
@@ -271,7 +271,9 @@ impl_type!(Rc<T>);
|
|||||||
#[cfg(all(feature = "rc", feature = "alloc"))]
|
#[cfg(all(feature = "rc", feature = "alloc"))]
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[cfg(all(feature = "rc", feature = "alloc"))]
|
#[cfg(all(feature = "rc", feature = "alloc"))]
|
||||||
impl_type!(Arc<T>);
|
unsafe impl<T: StaticType + ?Sized> StaticType for Arc<T> {
|
||||||
|
type Static = Arc<<T as StaticType>::Static>;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "glam")]
|
#[cfg(feature = "glam")]
|
||||||
use glam::*;
|
use glam::*;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ async fn post_compile_spirv(State(state): State<Arc<AppState>>, Json(compile_req
|
|||||||
let result = compile_request.compile(state.compile_dir.path().to_str().expect("non utf8 tempdir path"), &path).map_err(|e| {
|
let result = compile_request.compile(state.compile_dir.path().to_str().expect("non utf8 tempdir path"), &path).map_err(|e| {
|
||||||
eprintln!("compilation failed: {}", e);
|
eprintln!("compilation failed: {}", e);
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
});
|
})?;
|
||||||
state.cache.write().unwrap().insert(compile_request, result.clone());
|
state.cache.write().unwrap().insert(compile_request, Ok(result.clone()));
|
||||||
result
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,24 +9,10 @@ license = "MIT OR Apache-2.0"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = ["dyn-any", "dyn-any/std", "alloc", "glam/std", "specta", "num-traits/std", "rustybuzz"]
|
||||||
"dyn-any",
|
|
||||||
"dyn-any/std",
|
|
||||||
"alloc",
|
|
||||||
"glam/std",
|
|
||||||
"specta",
|
|
||||||
"num-traits/std",
|
|
||||||
"rustybuzz",
|
|
||||||
]
|
|
||||||
default = ["async", "serde", "kurbo", "log", "std", "rand_chacha", "wasm"]
|
default = ["async", "serde", "kurbo", "log", "std", "rand_chacha", "wasm"]
|
||||||
log = ["dep:log"]
|
log = ["dep:log"]
|
||||||
serde = [
|
serde = ["dep:serde", "glam/serde", "bezier-rs/serde", "bezier-rs/serde", "base64"]
|
||||||
"dep:serde",
|
|
||||||
"glam/serde",
|
|
||||||
"bezier-rs/serde",
|
|
||||||
"bezier-rs/serde",
|
|
||||||
"base64",
|
|
||||||
]
|
|
||||||
gpu = ["spirv-std", "glam/bytemuck", "dyn-any", "glam/libm"]
|
gpu = ["spirv-std", "glam/bytemuck", "dyn-any", "glam/libm"]
|
||||||
async = ["async-trait", "alloc"]
|
async = ["async-trait", "alloc"]
|
||||||
nightly = []
|
nightly = []
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ use dyn_any::StaticType;
|
|||||||
use dyn_any::StaticTypeSized;
|
use dyn_any::StaticTypeSized;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
|
|
||||||
|
use core::any::Any;
|
||||||
|
use core::future::Future;
|
||||||
use core::hash::{Hash, Hasher};
|
use core::hash::{Hash, Hasher};
|
||||||
|
use core::pin::Pin;
|
||||||
|
|
||||||
use crate::text::FontCache;
|
use crate::text::FontCache;
|
||||||
|
|
||||||
@@ -93,6 +96,7 @@ pub trait ApplicationIo {
|
|||||||
fn gpu_executor(&self) -> Option<&Self::Executor> {
|
fn gpu_executor(&self) -> Option<&Self::Executor> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
fn load_resource<'a>(&self, url: impl AsRef<str>) -> Result<Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>>>>, ApplicationError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ApplicationIo> ApplicationIo for &T {
|
impl<T: ApplicationIo> ApplicationIo for &T {
|
||||||
@@ -110,6 +114,16 @@ impl<T: ApplicationIo> ApplicationIo for &T {
|
|||||||
fn gpu_executor(&self) -> Option<&T::Executor> {
|
fn gpu_executor(&self) -> Option<&T::Executor> {
|
||||||
(**self).gpu_executor()
|
(**self).gpu_executor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_resource<'a>(&self, url: impl AsRef<str>) -> Result<Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>>>>, ApplicationError> {
|
||||||
|
(**self).load_resource(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ApplicationError {
|
||||||
|
NotFound,
|
||||||
|
InvalidUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::Node;
|
use crate::{Node, NodeMut};
|
||||||
pub struct FnNode<T: Fn(I) -> O, I, O>(T, PhantomData<(I, O)>);
|
pub struct FnNode<T: Fn(I) -> O, I, O>(T, PhantomData<(I, O)>);
|
||||||
|
|
||||||
impl<'i, T: Fn(I) -> O + 'i, O: 'i, I: 'i> Node<'i, I> for FnNode<T, I, O> {
|
impl<'i, T: Fn(I) -> O + 'i, O: 'i, I: 'i> Node<'i, I> for FnNode<T, I, O> {
|
||||||
@@ -16,6 +16,21 @@ impl<T: Fn(I) -> O, I, O> FnNode<T, I, O> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FnMutNode<T: FnMut(I) -> O, I, O>(T, PhantomData<(I, O)>);
|
||||||
|
|
||||||
|
impl<'i, T: FnMut(I) -> O + 'i, O: 'i, I: 'i> NodeMut<'i, I> for FnMutNode<T, I, O> {
|
||||||
|
type MutOutput = O;
|
||||||
|
fn eval_mut(&'i mut self, input: I) -> Self::MutOutput {
|
||||||
|
self.0(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T: FnMut(I) -> O + 'i, I: 'i, O: 'i> FnMutNode<T, I, O> {
|
||||||
|
pub fn new(f: T) -> Self {
|
||||||
|
FnMutNode(f, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FnNodeWithState<'i, T: Fn(I, &'i State) -> O, I, O, State: 'i>(T, State, PhantomData<(&'i O, I)>);
|
pub struct FnNodeWithState<'i, T: Fn(I, &'i State) -> O, I, O, State: 'i>(T, State, PhantomData<(&'i O, I)>);
|
||||||
impl<'i, I: 'i, O: 'i, State, T: Fn(I, &'i State) -> O + 'i> Node<'i, I> for FnNodeWithState<'i, T, I, O, State> {
|
impl<'i, I: 'i, O: 'i, State, T: Fn(I, &'i State) -> O + 'i> Node<'i, I> for FnNodeWithState<'i, T, I, O, State> {
|
||||||
type Output = O;
|
type Output = O;
|
||||||
|
|||||||
@@ -55,6 +55,32 @@ pub trait Node<'i, Input: 'i>: 'i {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait NodeMut<'i, Input: 'i>: 'i {
|
||||||
|
type MutOutput: 'i;
|
||||||
|
fn eval_mut(&'i mut self, input: Input) -> Self::MutOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait NodeOnce<'i, Input>
|
||||||
|
where
|
||||||
|
Input: 'i,
|
||||||
|
{
|
||||||
|
type OnceOutput: 'i;
|
||||||
|
fn eval_once(self, input: Input) -> Self::OnceOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T: Node<'i, I>, I: 'i> NodeOnce<'i, I> for &'i T {
|
||||||
|
type OnceOutput = T::Output;
|
||||||
|
fn eval_once(self, input: I) -> Self::OnceOutput {
|
||||||
|
(self).eval(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'i, T: Node<'i, I> + ?Sized, I: 'i> NodeMut<'i, I> for &'i T {
|
||||||
|
type MutOutput = T::Output;
|
||||||
|
fn eval_mut(&'i mut self, input: I) -> Self::MutOutput {
|
||||||
|
(*self).eval(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
mod types;
|
mod types;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
@@ -98,52 +124,40 @@ where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'i, 's: 'i, I: 'i, O: 'i, N: Node<'i, I, Output = O>> Node<'i, I> for &'s N {
|
impl<'i, 's: 'i, I: 'i, N: Node<'i, I> + ?Sized> Node<'i, I> for &'i N {
|
||||||
|
type Output = N::Output;
|
||||||
|
fn eval(&'i self, input: I) -> N::Output {
|
||||||
|
(*self).eval(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<'i, 's: 'i, I: 'i, O: 'i, N: Node<'i, I, Output = O> + ?Sized> Node<'i, I> for Box<N> {
|
||||||
type Output = O;
|
type Output = O;
|
||||||
|
fn eval(&'i self, input: I) -> O {
|
||||||
fn eval(&'i self, input: I) -> Self::Output {
|
|
||||||
(**self).eval(input)
|
(**self).eval(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
impl<'i, 's: 'i, I: 'i, O: 'i, N: Node<'i, I, Output = O>> Node<'i, I> for Box<N> {
|
impl<'i, 's: 'i, I: 'i, O: 'i, N: Node<'i, I, Output = O> + ?Sized> Node<'i, I> for alloc::sync::Arc<N> {
|
||||||
type Output = O;
|
type Output = O;
|
||||||
|
fn eval(&'i self, input: I) -> O {
|
||||||
fn eval(&'i self, input: I) -> Self::Output {
|
|
||||||
(**self).eval(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
impl<'i, 's: 'i, I: 'i, O: 'i, N: Node<'i, I, Output = O>> Node<'i, I> for alloc::sync::Arc<N> {
|
|
||||||
type Output = O;
|
|
||||||
|
|
||||||
fn eval(&'i self, input: I) -> Self::Output {
|
|
||||||
(**self).eval(input)
|
(**self).eval(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'i, I: 'i, O: 'i> Node<'i, I> for &'i dyn Node<'i, I, Output = O> {
|
|
||||||
type Output = O;
|
|
||||||
|
|
||||||
fn eval(&'i self, input: I) -> Self::Output {
|
|
||||||
(**self).eval(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
|
|
||||||
use dyn_any::StaticTypeSized;
|
use dyn_any::StaticTypeSized;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<Box<dyn Node<'i, I, Output = O> + 'i>> {
|
impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<Box<dyn Node<'i, I, Output = O> + 'i>> {
|
||||||
type Output = O;
|
type Output = O;
|
||||||
|
fn eval(&'i self, input: I) -> O {
|
||||||
fn eval(&'i self, input: I) -> Self::Output {
|
|
||||||
(**self).eval(input)
|
(**self).eval(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<&'i (dyn NodeIO<'i, I, Output = O> + 'i)> {
|
impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<&'i (dyn NodeIO<'i, I, Output = O> + 'i)> {
|
||||||
type Output = O;
|
type Output = O;
|
||||||
|
fn eval(&'i self, input: I) -> O {
|
||||||
fn eval(&'i self, input: I) -> Self::Output {
|
|
||||||
(**self).eval(input)
|
(**self).eval(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ where
|
|||||||
// TODO: This should return a reference to the cached cached_value
|
// TODO: This should return a reference to the cached cached_value
|
||||||
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty acurate xD
|
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty acurate xD
|
||||||
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
|
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
|
||||||
fn eval(&'i self, input: ()) -> Self::Output {
|
fn eval(&'i self, input: ()) -> Pin<Box<dyn Future<Output = T> + 'i>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
if let Some(cached_value) = self.cache.take() {
|
if let Some(cached_value) = self.cache.take() {
|
||||||
self.cache.set(Some(cached_value.clone()));
|
self.cache.set(Some(cached_value.clone()));
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ pub struct IntoNode<I, O> {
|
|||||||
_i: PhantomData<I>,
|
_i: PhantomData<I>,
|
||||||
_o: PhantomData<O>,
|
_o: PhantomData<O>,
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
#[node_macro::node_fn(IntoNode<_I, _O>)]
|
#[node_macro::node_fn(IntoNode<_I, _O>)]
|
||||||
async fn into<_I, _O>(input: _I) -> _O
|
async fn into<_I, _O>(input: _I) -> _O
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -1,58 +1,121 @@
|
|||||||
use crate::raster::Color;
|
use crate::raster::{Color, Pixel};
|
||||||
use crate::Node;
|
use crate::Node;
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
|
|
||||||
|
use num_traits::CheckedShr;
|
||||||
#[cfg(target_arch = "spirv")]
|
#[cfg(target_arch = "spirv")]
|
||||||
use spirv_std::num_traits::Float;
|
use spirv_std::num_traits::Float;
|
||||||
|
|
||||||
#[derive(Clone, Debug, DynAny, PartialEq)]
|
#[derive(Clone, Copy, DynAny, PartialEq, Pod, Zeroable)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[repr(C, align(16))]
|
||||||
pub struct Quantization {
|
pub struct Quantization {
|
||||||
pub fn_index: usize,
|
|
||||||
pub a: f32,
|
pub a: f32,
|
||||||
pub b: f32,
|
pub b: f32,
|
||||||
pub c: f32,
|
pub bits: u32,
|
||||||
pub d: f32,
|
_padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for Quantization {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("Quantization").field("a", &self.a).field("b", &self.b()).field("bits", &self.bits()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Quantization {
|
||||||
|
pub fn new(a: f32, b: f32, bits: u32) -> Self {
|
||||||
|
Self { a, b, bits, _padding: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn a(&self) -> f32 {
|
||||||
|
self.a
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn b(&self) -> f32 {
|
||||||
|
self.b
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bits(&self) -> u32 {
|
||||||
|
self.bits
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::hash::Hash for Quantization {
|
impl core::hash::Hash for Quantization {
|
||||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.fn_index.hash(state);
|
self.bits().hash(state);
|
||||||
self.a.to_bits().hash(state);
|
self.a().to_bits().hash(state);
|
||||||
self.b.to_bits().hash(state);
|
self.b().to_bits().hash(state);
|
||||||
self.c.to_bits().hash(state);
|
|
||||||
self.d.to_bits().hash(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Quantization {
|
impl Default for Quantization {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::new(1., 0., 8)
|
||||||
fn_index: Default::default(),
|
|
||||||
a: 1.,
|
|
||||||
b: Default::default(),
|
|
||||||
c: Default::default(),
|
|
||||||
d: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type QuantizationChannels = [Quantization; 4];
|
pub type QuantizationChannels = [Quantization; 4];
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(DynAny, Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
|
||||||
|
pub struct PackedPixel(pub u32);
|
||||||
|
|
||||||
fn quantize(value: f32, quantization: &Quantization) -> f32 {
|
impl Pixel for PackedPixel {}
|
||||||
let Quantization { fn_index, a, b, c, d } = quantization;
|
|
||||||
match fn_index {
|
/*
|
||||||
1 => ((value + a) * d).abs().ln() * b + c,
|
#[inline(always)]
|
||||||
_ => a * value + b,
|
fn quantize(value: f32, offset: u32, quantization: Quantization) -> u32 {
|
||||||
}
|
let a = quantization.a();
|
||||||
|
let bits = quantization.bits();
|
||||||
|
let b = quantization.b();
|
||||||
|
let value = (((a * value) * ((1 << bits) - 1) as f32) as i32 + b) as u32;
|
||||||
|
value.checked_shl(32 - bits - offset).unwrap_or(0)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn quantize(value: f32, offset: u32, quantization: Quantization) -> u32 {
|
||||||
|
let a = quantization.a();
|
||||||
|
let b = quantization.b();
|
||||||
|
let bits = quantization.bits();
|
||||||
|
|
||||||
|
// Calculate the quantized value
|
||||||
|
// Scale the value by 'a' and the maximum quantization range
|
||||||
|
let scaled_value = ((a * value) + b) * ((1 << bits) - 1) as f32;
|
||||||
|
// Round the scaled value to the nearest integer
|
||||||
|
let rounded_value = scaled_value.clamp(0., (1 << bits) as f32 - 1.) as u32;
|
||||||
|
|
||||||
|
// Shift the quantized value to the appropriate position based on the offset
|
||||||
|
let shifted_value = rounded_value.checked_shl(32 - bits - offset).unwrap();
|
||||||
|
|
||||||
|
shifted_value as u32
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
#[inline(always)]
|
||||||
|
fn decode(value: u32, offset: u32, quantization: Quantization) -> f32 {
|
||||||
|
let a = quantization.a();
|
||||||
|
let bits = quantization.bits();
|
||||||
|
let b = quantization.b();
|
||||||
|
let value = (value << offset) >> (31 - bits);
|
||||||
|
let value = value as i32 - b;
|
||||||
|
(value as f32 / ((1 << bits) - 1) as f32) / a
|
||||||
|
}*/
|
||||||
|
|
||||||
fn decode(value: f32, quantization: &Quantization) -> f32 {
|
#[inline(always)]
|
||||||
let Quantization { fn_index, a, b, c, d } = quantization;
|
fn decode(value: u32, offset: u32, quantization: Quantization) -> f32 {
|
||||||
match fn_index {
|
let a = quantization.a();
|
||||||
1 => -(-c / b).exp() * (a * d * (c / b).exp() - (value / b).exp()) / d,
|
let bits = quantization.bits();
|
||||||
_ => (value - b) / a,
|
let b = quantization.b();
|
||||||
}
|
|
||||||
|
// Shift the value to the appropriate position based on the offset
|
||||||
|
let shifted_value = value.checked_shr(32 - bits - offset).unwrap();
|
||||||
|
|
||||||
|
// Unpack the quantized value
|
||||||
|
let unpacked_value = shifted_value & ((1 << bits) - 1); // Mask out the unnecessary bits
|
||||||
|
let normalized_value = unpacked_value as f32 / ((1 << bits) - 1) as f32; // Normalize the value based on the quantization range
|
||||||
|
let decoded_value = normalized_value - b;
|
||||||
|
let original_value = decoded_value / a;
|
||||||
|
|
||||||
|
original_value
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct QuantizeNode<Quantization> {
|
pub struct QuantizeNode<Quantization> {
|
||||||
@@ -60,14 +123,22 @@ pub struct QuantizeNode<Quantization> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(QuantizeNode)]
|
#[node_macro::node_fn(QuantizeNode)]
|
||||||
fn quantize_fn<'a>(color: Color, quantization: [Quantization; 4]) -> Color {
|
fn quantize_fn<'a>(color: Color, quantization: [Quantization; 4]) -> PackedPixel {
|
||||||
let quant = quantization.as_slice();
|
let quant = quantization;
|
||||||
let r = quantize(color.r(), &quant[0]);
|
quantize_color(color, quant)
|
||||||
let g = quantize(color.g(), &quant[1]);
|
}
|
||||||
let b = quantize(color.b(), &quant[2]);
|
|
||||||
let a = quantize(color.a(), &quant[3]);
|
|
||||||
|
|
||||||
Color::from_rgbaf32_unchecked(r, g, b, a)
|
pub fn quantize_color(color: Color, quant: [Quantization; 4]) -> PackedPixel {
|
||||||
|
let mut offset = 0;
|
||||||
|
let r = quantize(color.r(), offset, quant[0]);
|
||||||
|
offset += quant[0].bits();
|
||||||
|
let g = quantize(color.g(), offset, quant[1]);
|
||||||
|
offset += quant[1].bits();
|
||||||
|
let b = quantize(color.b(), offset, quant[2]);
|
||||||
|
offset += quant[2].bits();
|
||||||
|
let a = quantize(color.a(), offset, quant[3]);
|
||||||
|
|
||||||
|
PackedPixel(r | g | b | a)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DeQuantizeNode<Quantization> {
|
pub struct DeQuantizeNode<Quantization> {
|
||||||
@@ -75,12 +146,53 @@ pub struct DeQuantizeNode<Quantization> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(DeQuantizeNode)]
|
#[node_macro::node_fn(DeQuantizeNode)]
|
||||||
fn dequantize_fn<'a>(color: Color, quantization: [Quantization; 4]) -> Color {
|
fn dequantize_fn<'a>(color: PackedPixel, quantization: [Quantization; 4]) -> Color {
|
||||||
let quant = quantization.as_slice();
|
let quant = quantization;
|
||||||
let r = decode(color.r(), &quant[0]);
|
dequantize_color(color, quant)
|
||||||
let g = decode(color.g(), &quant[1]);
|
}
|
||||||
let b = decode(color.b(), &quant[2]);
|
|
||||||
let a = decode(color.a(), &quant[3]);
|
pub fn dequantize_color(color: PackedPixel, quant: [Quantization; 4]) -> Color {
|
||||||
|
let mut offset = 0;
|
||||||
|
let r = decode(color.0, offset, quant[0]);
|
||||||
|
offset += quant[0].bits();
|
||||||
|
let g = decode(color.0, offset, quant[1]);
|
||||||
|
offset += quant[1].bits();
|
||||||
|
let b = decode(color.0, offset, quant[2]);
|
||||||
|
offset += quant[2].bits();
|
||||||
|
let a = decode(color.0, offset, quant[3]);
|
||||||
|
|
||||||
Color::from_rgbaf32_unchecked(r, g, b, a)
|
Color::from_rgbaf32_unchecked(r, g, b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quantize() {
|
||||||
|
let quant = Quantization::new(1., 0., 8);
|
||||||
|
let color = Color::from_rgbaf32_unchecked(0.5, 0.5, 0.5, 0.5);
|
||||||
|
let quantized = quantize_color(color, [quant; 4]);
|
||||||
|
assert_eq!(quantized.0, 0x7f7f7f7f);
|
||||||
|
let dequantized = dequantize_color(quantized, [quant; 4]);
|
||||||
|
//assert_eq!(color, dequantized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quantize_black() {
|
||||||
|
let quant = Quantization::new(1., 0., 8);
|
||||||
|
let color = Color::from_rgbaf32_unchecked(0., 0., 0., 1.);
|
||||||
|
let quantized = quantize_color(color, [quant; 4]);
|
||||||
|
assert_eq!(quantized.0, 0xff);
|
||||||
|
let dequantized = dequantize_color(quantized, [quant; 4]);
|
||||||
|
assert_eq!(color, dequantized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_getters() {
|
||||||
|
let quant = Quantization::new(1., 3., 8);
|
||||||
|
assert_eq!(quant.a(), 1.);
|
||||||
|
assert_eq!(quant.b(), 3.);
|
||||||
|
assert_eq!(quant.bits(), 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub mod adjustments;
|
|||||||
pub mod bbox;
|
pub mod bbox;
|
||||||
#[cfg(not(target_arch = "spirv"))]
|
#[cfg(not(target_arch = "spirv"))]
|
||||||
pub mod brightness_contrast;
|
pub mod brightness_contrast;
|
||||||
|
#[cfg(not(target_arch = "spirv"))]
|
||||||
pub mod brush_cache;
|
pub mod brush_cache;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod discrete_srgb;
|
pub mod discrete_srgb;
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ pub struct LevelsNode<InputStart, InputMid, InputEnd, OutputStart, OutputEnd> {
|
|||||||
|
|
||||||
// From https://stackoverflow.com/questions/39510072/algorithm-for-adjustment-of-image-levels
|
// From https://stackoverflow.com/questions/39510072/algorithm-for-adjustment-of-image-levels
|
||||||
#[node_macro::node_fn(LevelsNode)]
|
#[node_macro::node_fn(LevelsNode)]
|
||||||
fn levels_node(color: Color, input_start: f64, input_mid: f64, input_end: f64, output_start: f64, output_end: f64) -> Color {
|
fn levels_node(color: Color, input_start: f32, input_mid: f32, input_end: f32, output_start: f32, output_end: f32) -> Color {
|
||||||
let color = color.to_gamma_srgb();
|
let color = color.to_gamma_srgb();
|
||||||
|
|
||||||
// Input Range (Range: 0-1)
|
// Input Range (Range: 0-1)
|
||||||
@@ -238,8 +238,8 @@ fn levels_node(color: Color, input_start: f64, input_mid: f64, input_end: f64, o
|
|||||||
let input_highlights = (input_end / 100.) as f32;
|
let input_highlights = (input_end / 100.) as f32;
|
||||||
|
|
||||||
// Output Range (Range: 0-1)
|
// Output Range (Range: 0-1)
|
||||||
let output_minimums = (output_start / 100.) as f32;
|
let output_minimums = output_start / 100.;
|
||||||
let output_maximums = (output_end / 100.) as f32;
|
let output_maximums = output_end / 100.;
|
||||||
|
|
||||||
// Midtones interpolation factor between minimums and maximums (Range: 0-1)
|
// Midtones interpolation factor between minimums and maximums (Range: 0-1)
|
||||||
let midtones = output_minimums + (output_maximums - output_minimums) * input_midtones;
|
let midtones = output_minimums + (output_maximums - output_minimums) * input_midtones;
|
||||||
@@ -286,7 +286,7 @@ pub struct GrayscaleNode<Tint, Reds, Yellows, Greens, Cyans, Blues, Magentas> {
|
|||||||
// From <https://stackoverflow.com/a/55233732/775283>
|
// From <https://stackoverflow.com/a/55233732/775283>
|
||||||
// Works the same for gamma and linear color
|
// Works the same for gamma and linear color
|
||||||
#[node_macro::node_fn(GrayscaleNode)]
|
#[node_macro::node_fn(GrayscaleNode)]
|
||||||
fn grayscale_color_node(color: Color, tint: Color, reds: f64, yellows: f64, greens: f64, cyans: f64, blues: f64, magentas: f64) -> Color {
|
fn grayscale_color_node(color: Color, tint: Color, reds: f32, yellows: f32, greens: f32, cyans: f32, blues: f32, magentas: f32) -> Color {
|
||||||
let color = color.to_gamma_srgb();
|
let color = color.to_gamma_srgb();
|
||||||
|
|
||||||
let reds = reds as f32 / 100.;
|
let reds = reds as f32 / 100.;
|
||||||
@@ -321,38 +321,29 @@ fn grayscale_color_node(color: Color, tint: Color, reds: f64, yellows: f64, gree
|
|||||||
color.to_linear_srgb()
|
color.to_linear_srgb()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "spirv"))]
|
#[derive(Debug)]
|
||||||
pub use hue_shift::HueSaturationNode;
|
pub struct HueSaturationNode<Hue, Saturation, Lightness> {
|
||||||
|
hue_shift: Hue,
|
||||||
|
saturation_shift: Saturation,
|
||||||
|
lightness_shift: Lightness,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Make this work on GPU so it can be removed from the wrapper module that excludes GPU (it doesn't work because of the modulo)
|
#[node_macro::node_fn(HueSaturationNode)]
|
||||||
#[cfg(not(target_arch = "spirv"))]
|
fn hue_shift_color_node(color: Color, hue_shift: f32, saturation_shift: f32, lightness_shift: f32) -> Color {
|
||||||
mod hue_shift {
|
let color = color.to_gamma_srgb();
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
let [hue, saturation, lightness, alpha] = color.to_hsla();
|
||||||
pub struct HueSaturationNode<Hue, Saturation, Lightness> {
|
|
||||||
hue_shift: Hue,
|
|
||||||
saturation_shift: Saturation,
|
|
||||||
lightness_shift: Lightness,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node_fn(HueSaturationNode)]
|
let color = Color::from_hsla(
|
||||||
fn hue_shift_color_node(color: Color, hue_shift: f64, saturation_shift: f64, lightness_shift: f64) -> Color {
|
(hue + hue_shift / 360.) % 1.,
|
||||||
let color = color.to_gamma_srgb();
|
// TODO: Improve the way saturation works (it's slightly off)
|
||||||
|
(saturation + saturation_shift / 100.).clamp(0., 1.),
|
||||||
|
// TODO: Fix the way lightness works (it's very off)
|
||||||
|
(lightness + lightness_shift / 100.).clamp(0., 1.),
|
||||||
|
alpha,
|
||||||
|
);
|
||||||
|
|
||||||
let [hue, saturation, lightness, alpha] = color.to_hsla();
|
color.to_linear_srgb()
|
||||||
|
|
||||||
let color = Color::from_hsla(
|
|
||||||
(hue + hue_shift as f32 / 360.) % 1.,
|
|
||||||
// TODO: Improve the way saturation works (it's slightly off)
|
|
||||||
(saturation + saturation_shift as f32 / 100.).clamp(0., 1.),
|
|
||||||
// TODO: Fix the way lightness works (it's very off)
|
|
||||||
(lightness + lightness_shift as f32 / 100.).clamp(0., 1.),
|
|
||||||
alpha,
|
|
||||||
);
|
|
||||||
|
|
||||||
color.to_linear_srgb()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -388,9 +379,9 @@ pub struct ThresholdNode<MinLuminance, MaxLuminance, LuminanceCalc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(ThresholdNode)]
|
#[node_macro::node_fn(ThresholdNode)]
|
||||||
fn threshold_node(color: Color, min_luminance: f64, max_luminance: f64, luminance_calc: LuminanceCalculation) -> Color {
|
fn threshold_node(color: Color, min_luminance: f32, max_luminance: f32, luminance_calc: LuminanceCalculation) -> Color {
|
||||||
let min_luminance = Color::srgb_to_linear(min_luminance as f32 / 100.);
|
let min_luminance = Color::srgb_to_linear(min_luminance / 100.);
|
||||||
let max_luminance = Color::srgb_to_linear(max_luminance as f32 / 100.);
|
let max_luminance = Color::srgb_to_linear(max_luminance / 100.);
|
||||||
|
|
||||||
let luminance = match luminance_calc {
|
let luminance = match luminance_calc {
|
||||||
LuminanceCalculation::SRGB => color.luminance_srgb(),
|
LuminanceCalculation::SRGB => color.luminance_srgb(),
|
||||||
@@ -414,7 +405,7 @@ pub struct BlendNode<BlendMode, Opacity> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(BlendNode)]
|
#[node_macro::node_fn(BlendNode)]
|
||||||
fn blend_node(input: (Color, Color), blend_mode: BlendMode, opacity: f64) -> Color {
|
fn blend_node(input: (Color, Color), blend_mode: BlendMode, opacity: f32) -> Color {
|
||||||
blend_colors(input.0, input.1, blend_mode, opacity as f32 / 100.)
|
blend_colors(input.0, input.1, blend_mode, opacity as f32 / 100.)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,8 +461,8 @@ pub struct VibranceNode<Vibrance> {
|
|||||||
// Modified from https://stackoverflow.com/questions/33966121/what-is-the-algorithm-for-vibrance-filters
|
// Modified from https://stackoverflow.com/questions/33966121/what-is-the-algorithm-for-vibrance-filters
|
||||||
// The results of this implementation are very close to correct, but not quite perfect
|
// The results of this implementation are very close to correct, but not quite perfect
|
||||||
#[node_macro::node_fn(VibranceNode)]
|
#[node_macro::node_fn(VibranceNode)]
|
||||||
fn vibrance_node(color: Color, vibrance: f64) -> Color {
|
fn vibrance_node(color: Color, vibrance: f32) -> Color {
|
||||||
let vibrance = vibrance as f32 / 100.;
|
let vibrance = vibrance / 100.;
|
||||||
// Slow the effect down by half when it's negative, since artifacts begin appearing past -50%.
|
// Slow the effect down by half when it's negative, since artifacts begin appearing past -50%.
|
||||||
// So this scales the 0% to -50% range to 0% to -100%.
|
// So this scales the 0% to -50% range to 0% to -100%.
|
||||||
let slowed_vibrance = if vibrance >= 0. { vibrance } else { vibrance * 0.5 };
|
let slowed_vibrance = if vibrance >= 0. { vibrance } else { vibrance * 0.5 };
|
||||||
@@ -562,22 +553,22 @@ pub struct ChannelMixerNode<Monochrome, MonochromeR, MonochromeG, MonochromeB, M
|
|||||||
fn channel_mixer_node(
|
fn channel_mixer_node(
|
||||||
color: Color,
|
color: Color,
|
||||||
monochrome: bool,
|
monochrome: bool,
|
||||||
monochrome_r: f64,
|
monochrome_r: f32,
|
||||||
monochrome_g: f64,
|
monochrome_g: f32,
|
||||||
monochrome_b: f64,
|
monochrome_b: f32,
|
||||||
monochrome_c: f64,
|
monochrome_c: f32,
|
||||||
red_r: f64,
|
red_r: f32,
|
||||||
red_g: f64,
|
red_g: f32,
|
||||||
red_b: f64,
|
red_b: f32,
|
||||||
red_c: f64,
|
red_c: f32,
|
||||||
green_r: f64,
|
green_r: f32,
|
||||||
green_g: f64,
|
green_g: f32,
|
||||||
green_b: f64,
|
green_b: f32,
|
||||||
green_c: f64,
|
green_c: f32,
|
||||||
blue_r: f64,
|
blue_r: f32,
|
||||||
blue_g: f64,
|
blue_g: f32,
|
||||||
blue_b: f64,
|
blue_b: f32,
|
||||||
blue_c: f64,
|
blue_c: f32,
|
||||||
) -> Color {
|
) -> Color {
|
||||||
let color = color.to_gamma_srgb();
|
let color = color.to_gamma_srgb();
|
||||||
|
|
||||||
@@ -699,42 +690,42 @@ pub struct SelectiveColorNode<Absolute, RC, RM, RY, RK, YC, YM, YY, YK, GC, GM,
|
|||||||
fn selective_color_node(
|
fn selective_color_node(
|
||||||
color: Color,
|
color: Color,
|
||||||
mode: RelativeAbsolute,
|
mode: RelativeAbsolute,
|
||||||
r_c: f64,
|
r_c: f32,
|
||||||
r_m: f64,
|
r_m: f32,
|
||||||
r_y: f64,
|
r_y: f32,
|
||||||
r_k: f64,
|
r_k: f32,
|
||||||
y_c: f64,
|
y_c: f32,
|
||||||
y_m: f64,
|
y_m: f32,
|
||||||
y_y: f64,
|
y_y: f32,
|
||||||
y_k: f64,
|
y_k: f32,
|
||||||
g_c: f64,
|
g_c: f32,
|
||||||
g_m: f64,
|
g_m: f32,
|
||||||
g_y: f64,
|
g_y: f32,
|
||||||
g_k: f64,
|
g_k: f32,
|
||||||
c_c: f64,
|
c_c: f32,
|
||||||
c_m: f64,
|
c_m: f32,
|
||||||
c_y: f64,
|
c_y: f32,
|
||||||
c_k: f64,
|
c_k: f32,
|
||||||
b_c: f64,
|
b_c: f32,
|
||||||
b_m: f64,
|
b_m: f32,
|
||||||
b_y: f64,
|
b_y: f32,
|
||||||
b_k: f64,
|
b_k: f32,
|
||||||
m_c: f64,
|
m_c: f32,
|
||||||
m_m: f64,
|
m_m: f32,
|
||||||
m_y: f64,
|
m_y: f32,
|
||||||
m_k: f64,
|
m_k: f32,
|
||||||
w_c: f64,
|
w_c: f32,
|
||||||
w_m: f64,
|
w_m: f32,
|
||||||
w_y: f64,
|
w_y: f32,
|
||||||
w_k: f64,
|
w_k: f32,
|
||||||
n_c: f64,
|
n_c: f32,
|
||||||
n_m: f64,
|
n_m: f32,
|
||||||
n_y: f64,
|
n_y: f32,
|
||||||
n_k: f64,
|
n_k: f32,
|
||||||
k_c: f64,
|
k_c: f32,
|
||||||
k_m: f64,
|
k_m: f32,
|
||||||
k_y: f64,
|
k_y: f32,
|
||||||
k_k: f64,
|
k_k: f32,
|
||||||
) -> Color {
|
) -> Color {
|
||||||
let color = color.to_gamma_srgb();
|
let color = color.to_gamma_srgb();
|
||||||
|
|
||||||
@@ -784,7 +775,7 @@ fn selective_color_node(
|
|||||||
// Skip this color parameter group...
|
// Skip this color parameter group...
|
||||||
// ...if it's unchanged from the default of zero offset on all CMYK paramters, or...
|
// ...if it's unchanged from the default of zero offset on all CMYK paramters, or...
|
||||||
// ...if this pixel's color isn't in the range affected by this color parameter group
|
// ...if this pixel's color isn't in the range affected by this color parameter group
|
||||||
if (c < f64::EPSILON && m < f64::EPSILON && y < f64::EPSILON && k < f64::EPSILON) || (!pixel_color_range(color_parameter_group)) {
|
if (c < f32::EPSILON && m < f32::EPSILON && y < f32::EPSILON && k < f32::EPSILON) || (!pixel_color_range(color_parameter_group)) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,7 +807,7 @@ pub struct OpacityNode<O> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(OpacityNode)]
|
#[node_macro::node_fn(OpacityNode)]
|
||||||
fn image_opacity(color: Color, opacity_multiplier: f64) -> Color {
|
fn image_opacity(color: Color, opacity_multiplier: f32) -> Color {
|
||||||
let opacity_multiplier = opacity_multiplier as f32 / 100.;
|
let opacity_multiplier = opacity_multiplier as f32 / 100.;
|
||||||
Color::from_rgbaf32_unchecked(color.r(), color.g(), color.b(), color.a() * opacity_multiplier)
|
Color::from_rgbaf32_unchecked(color.r(), color.g(), color.b(), color.a() * opacity_multiplier)
|
||||||
}
|
}
|
||||||
@@ -829,7 +820,7 @@ pub struct PosterizeNode<P> {
|
|||||||
// Based on http://www.axiomx.com/posterize.htm
|
// Based on http://www.axiomx.com/posterize.htm
|
||||||
// This algorithm is perfectly accurate.
|
// This algorithm is perfectly accurate.
|
||||||
#[node_macro::node_fn(PosterizeNode)]
|
#[node_macro::node_fn(PosterizeNode)]
|
||||||
fn posterize(color: Color, posterize_value: f64) -> Color {
|
fn posterize(color: Color, posterize_value: f32) -> Color {
|
||||||
let color = color.to_gamma_srgb();
|
let color = color.to_gamma_srgb();
|
||||||
|
|
||||||
let posterize_value = posterize_value as f32;
|
let posterize_value = posterize_value as f32;
|
||||||
@@ -850,7 +841,7 @@ pub struct ExposureNode<Exposure, Offset, GammaCorrection> {
|
|||||||
|
|
||||||
// Based on https://geraldbakker.nl/psnumbers/exposure.html
|
// Based on https://geraldbakker.nl/psnumbers/exposure.html
|
||||||
#[node_macro::node_fn(ExposureNode)]
|
#[node_macro::node_fn(ExposureNode)]
|
||||||
fn exposure(color: Color, exposure: f64, offset: f64, gamma_correction: f64) -> Color {
|
fn exposure(color: Color, exposure: f32, offset: f32, gamma_correction: f32) -> Color {
|
||||||
let adjusted = color
|
let adjusted = color
|
||||||
// Exposure
|
// Exposure
|
||||||
.map_rgb(|c: f32| c * 2_f32.powf(exposure as f32))
|
.map_rgb(|c: f32| c * 2_f32.powf(exposure as f32))
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ pub struct BrushPlan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, DynAny, Default)]
|
#[derive(Debug, DynAny, Default)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct BrushCache {
|
pub struct BrushCache {
|
||||||
inner: Arc<Mutex<BrushCacheImpl>>,
|
inner: Arc<Mutex<BrushCacheImpl>>,
|
||||||
proto: bool,
|
proto: bool,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::Node;
|
use crate::{Node, NodeMut};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct ComposeNode<First, Second, I> {
|
pub struct ComposeNode<First, Second, I> {
|
||||||
first: First,
|
first: First,
|
||||||
second: Second,
|
second: Second,
|
||||||
@@ -21,12 +21,20 @@ where
|
|||||||
second.eval(arg)
|
second.eval(arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<'i, 'f: 'i, 's: 'i, Input: 'i, First, Second> NodeMut<'i, Input> for ComposeNode<First, Second, Input>
|
||||||
impl<'i, First, Second, Input: 'i> ComposeNode<First, Second, Input>
|
|
||||||
where
|
where
|
||||||
First: Node<'i, Input>,
|
First: Node<'i, Input>,
|
||||||
Second: Node<'i, <First as Node<'i, Input>>::Output>,
|
Second: NodeMut<'i, <First as Node<'i, Input>>::Output> + 'i,
|
||||||
{
|
{
|
||||||
|
type MutOutput = <Second as NodeMut<'i, <First as Node<'i, Input>>::Output>>::MutOutput;
|
||||||
|
fn eval_mut(&'i mut self, input: Input) -> Self::MutOutput {
|
||||||
|
let arg = self.first.eval(input);
|
||||||
|
let second = &mut self.second;
|
||||||
|
second.eval_mut(arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, First, Second, Input: 'i> ComposeNode<First, Second, Input> {
|
||||||
pub const fn new(first: First, second: Second) -> Self {
|
pub const fn new(first: First, second: Second) -> Self {
|
||||||
ComposeNode::<First, Second, Input> { first, second, phantom: PhantomData }
|
ComposeNode::<First, Second, Input> { first, second, phantom: PhantomData }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub struct TextGenerator<Text, FontName, Size> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_fn(TextGenerator)]
|
#[node_fn(TextGenerator)]
|
||||||
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>(editor: EditorApi<'a, T>, text: String, font_name: Font, font_size: f32) -> 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))
|
crate::vector::VectorData::from_subpaths(to_path(&text, buzz_face, font_size as f64, None))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,10 +78,10 @@ pub struct TransformNode<Translation, Rotation, Scale, Shear, Pivot> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(TransformNode)]
|
#[node_macro::node_fn(TransformNode)]
|
||||||
pub(crate) fn transform_vector_data<Data: TransformMut>(mut data: Data, translate: DVec2, rotate: f64, scale: DVec2, shear: DVec2, pivot: DVec2) -> Data {
|
pub(crate) fn transform_vector_data<Data: TransformMut>(mut data: Data, translate: DVec2, rotate: f32, scale: DVec2, shear: DVec2, pivot: DVec2) -> Data {
|
||||||
let pivot = DAffine2::from_translation(data.local_pivot(pivot));
|
let pivot = DAffine2::from_translation(data.local_pivot(pivot));
|
||||||
|
|
||||||
let modification = pivot * DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.]) * pivot.inverse();
|
let modification = pivot * DAffine2::from_scale_angle_translation(scale, rotate as f64, translate) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.]) * pivot.inverse();
|
||||||
let data_transform = data.transform_mut();
|
let data_transform = data.transform_mut();
|
||||||
*data_transform = modification * (*data_transform);
|
*data_transform = modification * (*data_transform);
|
||||||
|
|
||||||
|
|||||||
@@ -53,21 +53,21 @@ pub struct SetStrokeNode<Color, Weight, DashLengths, DashOffset, LineCap, LineJo
|
|||||||
fn set_vector_data_stroke(
|
fn set_vector_data_stroke(
|
||||||
mut vector_data: VectorData,
|
mut vector_data: VectorData,
|
||||||
color: Option<Color>,
|
color: Option<Color>,
|
||||||
weight: f64,
|
weight: f32,
|
||||||
dash_lengths: Vec<f32>,
|
dash_lengths: Vec<f32>,
|
||||||
dash_offset: f64,
|
dash_offset: f32,
|
||||||
line_cap: super::style::LineCap,
|
line_cap: super::style::LineCap,
|
||||||
line_join: super::style::LineJoin,
|
line_join: super::style::LineJoin,
|
||||||
miter_limit: f64,
|
miter_limit: f32,
|
||||||
) -> VectorData {
|
) -> VectorData {
|
||||||
vector_data.style.set_stroke(Stroke {
|
vector_data.style.set_stroke(Stroke {
|
||||||
color,
|
color,
|
||||||
weight,
|
weight: weight as f64,
|
||||||
dash_lengths,
|
dash_lengths,
|
||||||
dash_offset,
|
dash_offset: dash_offset as f64,
|
||||||
line_cap,
|
line_cap,
|
||||||
line_join,
|
line_join,
|
||||||
line_join_miter_limit: miter_limit,
|
line_join_miter_limit: miter_limit as f64,
|
||||||
});
|
});
|
||||||
vector_data
|
vector_data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -597,7 +597,6 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
"js-sys",
|
|
||||||
"kurbo",
|
"kurbo",
|
||||||
"log",
|
"log",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
@@ -610,7 +609,6 @@ dependencies = [
|
|||||||
"specta",
|
"specta",
|
||||||
"spin",
|
"spin",
|
||||||
"spirv-std",
|
"spirv-std",
|
||||||
"wasm-bindgen",
|
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ impl SpirVCompiler for GpuCompiler {
|
|||||||
|
|
||||||
pub fn serialize_gpu(networks: &[ProtoNetwork], io: &ShaderIO) -> anyhow::Result<String> {
|
pub fn serialize_gpu(networks: &[ProtoNetwork], io: &ShaderIO) -> anyhow::Result<String> {
|
||||||
fn nid(id: &u64) -> String {
|
fn nid(id: &u64) -> String {
|
||||||
format!("n{id}")
|
format!("n{id:0x}")
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(&io);
|
dbg!(&io);
|
||||||
@@ -151,13 +151,13 @@ pub fn serialize_gpu(networks: &[ProtoNetwork], io: &ShaderIO) -> anyhow::Result
|
|||||||
}
|
}
|
||||||
for (i, id) in network.inputs.iter().enumerate() {
|
for (i, id) in network.inputs.iter().enumerate() {
|
||||||
let Some((_, node)) = network.nodes.iter().find(|(i, _)| i == id) else {
|
let Some((_, node)) = network.nodes.iter().find(|(i, _)| i == id) else {
|
||||||
anyhow::bail!("Input node not found");
|
anyhow::bail!("Input node not found");
|
||||||
};
|
};
|
||||||
let fqn = &node.identifier.name;
|
let fqn = &node.identifier.name;
|
||||||
let id = nid(id);
|
let id = nid(id);
|
||||||
let node = Node {
|
let node = Node {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
index: i,
|
index: i + 2,
|
||||||
fqn: fqn.to_string().split('<').next().unwrap().to_owned(),
|
fqn: fqn.to_string().split('<').next().unwrap().to_owned(),
|
||||||
args: node.construction_args.new_function_args(),
|
args: node.construction_args.new_function_args(),
|
||||||
};
|
};
|
||||||
@@ -202,7 +202,7 @@ pub fn serialize_gpu(networks: &[ProtoNetwork], io: &ShaderIO) -> anyhow::Result
|
|||||||
context.insert("input_nodes", &input_nodes);
|
context.insert("input_nodes", &input_nodes);
|
||||||
context.insert("output_nodes", &output_nodes);
|
context.insert("output_nodes", &output_nodes);
|
||||||
context.insert("nodes", &nodes);
|
context.insert("nodes", &nodes);
|
||||||
context.insert("compute_threads", &64);
|
context.insert("compute_threads", "12, 8");
|
||||||
Ok(tera.render("spirv", &context)?)
|
Ok(tera.render("spirv", &context)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,9 +215,13 @@ pub fn compile(dir: &Path) -> Result<spirv_builder::CompileResult, spirv_builder
|
|||||||
.preserve_bindings(true)
|
.preserve_bindings(true)
|
||||||
.release(true)
|
.release(true)
|
||||||
.spirv_metadata(SpirvMetadata::Full)
|
.spirv_metadata(SpirvMetadata::Full)
|
||||||
//.extra_arg("no-early-report-zombies")
|
//.scalar_block_layout(true)
|
||||||
//.extra_arg("no-infer-storage-classes")
|
.relax_logical_pointer(true)
|
||||||
//.extra_arg("spirt-passes=qptr")
|
//.capability(spirv_builder::Capability::Float64)
|
||||||
|
//.capability(spirv_builder::Capability::VariablePointersStorageBuffer)
|
||||||
|
.extra_arg("no-early-report-zombies")
|
||||||
|
.extra_arg("no-infer-storage-classes")
|
||||||
|
.extra_arg("spirt-passes=qptr")
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
|||||||
@@ -19,20 +19,18 @@ extern crate spirv_std;
|
|||||||
{{input}},
|
{{input}},
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
) {
|
) {
|
||||||
use graphene_core::Node;
|
use graphene_core::{Node, NodeMut};
|
||||||
use graphene_core::raster::adjustments::{BlendMode, BlendNode};
|
use graphene_core::raster::adjustments::{BlendMode, BlendNode};
|
||||||
use graphene_core::Color;
|
use graphene_core::Color;
|
||||||
|
|
||||||
/*
|
|
||||||
{% for input in input_nodes %}
|
{% for input in input_nodes %}
|
||||||
let i{{input.index}} = graphene_core::value::CopiedNode::new(i{{input.index}});
|
let _i{{input.index}} = graphene_core::value::CopiedNode::new(*i{{input.index}});
|
||||||
let _{{input.id}} = {{input.fqn}}::new({% for arg in input.args %}{{arg}}, {% endfor %});
|
let _{{input.id}} = {{input.fqn}}::new({% for arg in input.args %}{{arg}}, {% endfor %});
|
||||||
let {{input.id}} = graphene_core::structural::ComposeNode::new(i{{input.index}}, _{{input.id}});
|
let {{input.id}} = graphene_core::structural::ComposeNode::new(_i{{input.index}}, _{{input.id}});
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
*/
|
|
||||||
|
|
||||||
{% for node in nodes %}
|
{% for node in nodes %}
|
||||||
let {{node.id}} = {{node.fqn}}::new({% for arg in node.args %}{{arg}}, {% endfor %});
|
let mut {{node.id}} = {{node.fqn}}::new({% for arg in node.args %}{{arg}}, {% endfor %});
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% for output in output_nodes %}
|
{% for output in output_nodes %}
|
||||||
|
|||||||
@@ -357,12 +357,23 @@ where
|
|||||||
|
|
||||||
/// A struct representing a compute pipeline.
|
/// A struct representing a compute pipeline.
|
||||||
pub struct PipelineLayout<E: GpuExecutor + ?Sized> {
|
pub struct PipelineLayout<E: GpuExecutor + ?Sized> {
|
||||||
pub shader: E::ShaderHandle,
|
pub shader: Arc<E::ShaderHandle>,
|
||||||
pub entry_point: String,
|
pub entry_point: String,
|
||||||
pub bind_group: Bindgroup<E>,
|
pub bind_group: Arc<Bindgroup<E>>,
|
||||||
pub output_buffer: Arc<ShaderInput<E>>,
|
pub output_buffer: Arc<ShaderInput<E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: GpuExecutor + ?Sized> Clone for PipelineLayout<E> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
shader: self.shader.clone(),
|
||||||
|
entry_point: self.entry_point.clone(),
|
||||||
|
bind_group: self.bind_group.clone(),
|
||||||
|
output_buffer: self.output_buffer.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl<E: GpuExecutor + ?Sized + StaticType> StaticType for PipelineLayout<E>
|
unsafe impl<E: GpuExecutor + ?Sized + StaticType> StaticType for PipelineLayout<E>
|
||||||
where
|
where
|
||||||
E::Static: GpuExecutor,
|
E::Static: GpuExecutor,
|
||||||
@@ -457,9 +468,9 @@ pub struct CreatePipelineLayoutNode<_E, EntryPoint, Bindgroup, OutputBuffer> {
|
|||||||
#[node_macro::node_fn(CreatePipelineLayoutNode<_E>)]
|
#[node_macro::node_fn(CreatePipelineLayoutNode<_E>)]
|
||||||
async fn create_pipeline_layout_node<_E: GpuExecutor>(shader: _E::ShaderHandle, entry_point: String, bind_group: Bindgroup<_E>, output_buffer: Arc<ShaderInput<_E>>) -> PipelineLayout<_E> {
|
async fn create_pipeline_layout_node<_E: GpuExecutor>(shader: _E::ShaderHandle, entry_point: String, bind_group: Bindgroup<_E>, output_buffer: Arc<ShaderInput<_E>>) -> PipelineLayout<_E> {
|
||||||
PipelineLayout {
|
PipelineLayout {
|
||||||
shader,
|
shader: shader.into(),
|
||||||
entry_point,
|
entry_point,
|
||||||
bind_group,
|
bind_group: bind_group.into(),
|
||||||
output_buffer,
|
output_buffer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,6 +531,7 @@ where
|
|||||||
#[node_macro::node_fn(RenderTextureNode)]
|
#[node_macro::node_fn(RenderTextureNode)]
|
||||||
async fn render_texture_node<'a: 'input, E: 'a + GpuExecutor>(image: ShaderInputFrame<E>, surface: Arc<SurfaceHandle<E::Surface>>, executor: &'a E) -> SurfaceFrame {
|
async fn render_texture_node<'a: 'input, E: 'a + GpuExecutor>(image: ShaderInputFrame<E>, surface: Arc<SurfaceHandle<E::Surface>>, executor: &'a E) -> SurfaceFrame {
|
||||||
let surface_id = surface.surface_id;
|
let surface_id = surface.surface_id;
|
||||||
|
log::trace!("rendering to surface {:?}", surface_id);
|
||||||
|
|
||||||
executor.create_render_pass(image.shader_input, surface).unwrap();
|
executor.create_render_pass(image.shader_input, surface).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ impl DocumentNode {
|
|||||||
document_node_path: self.path.unwrap_or(Vec::new()),
|
document_node_path: self.path.unwrap_or(Vec::new()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unreachable!("tried to resolve not flattened node on resolved node");
|
unreachable!("tried to resolve not flattened node on resolved node {:?}", self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,10 +654,13 @@ impl NodeNetwork {
|
|||||||
|
|
||||||
/// Recursively dissolve non-primitive document nodes and return a single flattened network of nodes.
|
/// Recursively dissolve non-primitive document nodes and return a single flattened network of nodes.
|
||||||
pub fn flatten_with_fns(&mut self, node: NodeId, map_ids: impl Fn(NodeId, NodeId) -> NodeId + Copy, gen_id: impl Fn() -> NodeId + Copy) {
|
pub fn flatten_with_fns(&mut self, node: NodeId, map_ids: impl Fn(NodeId, NodeId) -> NodeId + Copy, gen_id: impl Fn() -> NodeId + Copy) {
|
||||||
let (id, mut node) = self
|
self.resolve_extract_nodes();
|
||||||
|
let Some((id, mut node)) = self
|
||||||
.nodes
|
.nodes
|
||||||
.remove_entry(&node)
|
.remove_entry(&node) else {
|
||||||
.unwrap_or_else(|| panic!("The node which was supposed to be flattened does not exist in the network, id {} network {:#?}", node, self));
|
warn!("The node which was supposed to be flattened does not exist in the network, id {} network {:#?}", node, self);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
if self.disabled.contains(&id) {
|
if self.disabled.contains(&id) {
|
||||||
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
|
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
|
||||||
@@ -665,11 +668,12 @@ impl NodeNetwork {
|
|||||||
self.nodes.insert(id, node);
|
self.nodes.insert(id, node);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
log::debug!("Flattening node {:?}", &node.name);
|
||||||
|
|
||||||
// replace value inputs with value nodes
|
// replace value inputs with value nodes
|
||||||
for input in &mut node.inputs {
|
for input in &mut node.inputs {
|
||||||
// Skip inputs that are already value nodes
|
// Skip inputs that are already value nodes
|
||||||
if node.implementation == DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()) {
|
if node.implementation == DocumentNodeImplementation::Unresolved("graphene_core::value::ClonedNode".into()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,7 +694,7 @@ impl NodeNetwork {
|
|||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Value".into(),
|
name: "Value".into(),
|
||||||
inputs: vec![NodeInput::Value { tagged_value, exposed }],
|
inputs: vec![NodeInput::Value { tagged_value, exposed }],
|
||||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
|
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ClonedNode".into()),
|
||||||
path,
|
path,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -706,6 +710,8 @@ impl NodeNetwork {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let DocumentNodeImplementation::Network(mut inner_network) = node.implementation {
|
if let DocumentNodeImplementation::Network(mut inner_network) = node.implementation {
|
||||||
|
// Resolve all extract nodes in the inner network
|
||||||
|
inner_network.resolve_extract_nodes();
|
||||||
// Connect all network inputs to either the parent network nodes, or newly created value nodes.
|
// Connect all network inputs to either the parent network nodes, or newly created value nodes.
|
||||||
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<_>>();
|
||||||
@@ -717,8 +723,10 @@ impl NodeNetwork {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
node.inputs.len(),
|
node.inputs.len(),
|
||||||
inner_network.inputs.len(),
|
inner_network.inputs.len(),
|
||||||
"The number of inputs to the node and the inner network must be the same {}",
|
"The number of inputs to the node and the inner network must be the same for {}. The node has {:?} inputs, the network has {:?} inputs.",
|
||||||
node.name
|
node.name,
|
||||||
|
node.inputs,
|
||||||
|
inner_network.inputs
|
||||||
);
|
);
|
||||||
// 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 (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) {
|
for (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) {
|
||||||
@@ -829,21 +837,30 @@ impl NodeNetwork {
|
|||||||
self.nodes.retain(|_, node| !matches!(node.implementation, DocumentNodeImplementation::Extract));
|
self.nodes.retain(|_, node| !matches!(node.implementation, DocumentNodeImplementation::Extract));
|
||||||
|
|
||||||
for (_, node) in &mut extraction_nodes {
|
for (_, node) in &mut extraction_nodes {
|
||||||
|
log::info!("extraction network: {:#?}", &self);
|
||||||
if let DocumentNodeImplementation::Extract = node.implementation {
|
if let DocumentNodeImplementation::Extract = node.implementation {
|
||||||
assert_eq!(node.inputs.len(), 1);
|
assert_eq!(node.inputs.len(), 1);
|
||||||
|
log::debug!("Resolving extract node {:?}", node);
|
||||||
let NodeInput::Node { node_id, output_index, .. } = node.inputs.pop().unwrap() else {
|
let NodeInput::Node { node_id, output_index, .. } = node.inputs.pop().unwrap() else {
|
||||||
panic!("Extract node has no input");
|
panic!("Extract node has no input, inputs: {:?}", node.inputs);
|
||||||
};
|
};
|
||||||
assert_eq!(output_index, 0);
|
assert_eq!(output_index, 0);
|
||||||
// TODO: check if we can readd lambda checking
|
// TODO: check if we can readd lambda checking
|
||||||
let mut input_node = self.nodes.remove(&node_id).unwrap();
|
let mut input_node = self.nodes.remove(&node_id).unwrap();
|
||||||
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into());
|
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::value::ClonedNode".into());
|
||||||
|
if let Some(input) = input_node.inputs.get_mut(0) {
|
||||||
|
*input = match &input {
|
||||||
|
NodeInput::Node { .. } => NodeInput::Network(generic!(T)),
|
||||||
|
ni => NodeInput::Network(ni.ty()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for input in input_node.inputs.iter_mut() {
|
for input in input_node.inputs.iter_mut() {
|
||||||
match input {
|
if let NodeInput::Node { .. } = input {
|
||||||
NodeInput::Node { .. } | NodeInput::Value { .. } => *input = NodeInput::Network(generic!(T)),
|
*input = NodeInput::Network(generic!(T))
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log::debug!("Extract node {:?} resolved to {:?}", node, input_node);
|
||||||
node.inputs = vec![NodeInput::value(TaggedValue::DocumentNode(input_node), false)];
|
node.inputs = vec![NodeInput::value(TaggedValue::DocumentNode(input_node), false)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1108,7 +1125,7 @@ mod test {
|
|||||||
tagged_value: TaggedValue::U32(2),
|
tagged_value: TaggedValue::U32(2),
|
||||||
exposed: false,
|
exposed: false,
|
||||||
}],
|
}],
|
||||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
|
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ClonedNode".into()),
|
||||||
path: Some(vec![1, 4]),
|
path: Some(vec![1, 4]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ pub enum TaggedValue {
|
|||||||
ManipulatorGroupIds(Vec<graphene_core::uuid::ManipulatorGroupId>),
|
ManipulatorGroupIds(Vec<graphene_core::uuid::ManipulatorGroupId>),
|
||||||
Font(graphene_core::text::Font),
|
Font(graphene_core::text::Font),
|
||||||
BrushStrokes(Vec<graphene_core::vector::brush_stroke::BrushStroke>),
|
BrushStrokes(Vec<graphene_core::vector::brush_stroke::BrushStroke>),
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
BrushCache(BrushCache),
|
BrushCache(BrushCache),
|
||||||
Segments(Vec<graphene_core::raster::ImageFrame<Color>>),
|
Segments(Vec<graphene_core::raster::ImageFrame<Color>>),
|
||||||
DocumentNode(DocumentNode),
|
DocumentNode(DocumentNode),
|
||||||
@@ -193,6 +194,7 @@ impl<'a> TaggedValue {
|
|||||||
TaggedValue::F64(x) => x.to_string() + "_f64",
|
TaggedValue::F64(x) => x.to_string() + "_f64",
|
||||||
TaggedValue::Bool(x) => x.to_string(),
|
TaggedValue::Bool(x) => x.to_string(),
|
||||||
TaggedValue::BlendMode(blend_mode) => "BlendMode::".to_string() + &blend_mode.to_string(),
|
TaggedValue::BlendMode(blend_mode) => "BlendMode::".to_string() + &blend_mode.to_string(),
|
||||||
|
TaggedValue::Color(color) => "graphene_core::Color::from_rgbaf32_unchecked(0.,0.,0.,1.)".to_string(),
|
||||||
_ => panic!("Cannot convert to primitive string"),
|
_ => panic!("Cannot convert to primitive string"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,12 @@ pub struct Compiler {}
|
|||||||
|
|
||||||
impl Compiler {
|
impl Compiler {
|
||||||
pub fn compile(&self, mut network: NodeNetwork, resolve_inputs: bool) -> impl Iterator<Item = ProtoNetwork> {
|
pub fn compile(&self, mut network: NodeNetwork, resolve_inputs: bool) -> impl Iterator<Item = ProtoNetwork> {
|
||||||
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
|
||||||
println!("flattening");
|
println!("flattening");
|
||||||
|
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
||||||
for id in node_ids {
|
for id in node_ids {
|
||||||
network.flatten(id);
|
network.flatten(id);
|
||||||
}
|
}
|
||||||
network.remove_redundant_id_nodes();
|
network.remove_redundant_id_nodes();
|
||||||
network.resolve_extract_nodes();
|
|
||||||
network.remove_dead_nodes();
|
network.remove_dead_nodes();
|
||||||
let proto_networks = network.into_proto_networks();
|
let proto_networks = network.into_proto_networks();
|
||||||
proto_networks.map(move |mut proto_network| {
|
proto_networks.map(move |mut proto_network| {
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ pub type LocalFuture<'n, T> = Pin<Box<dyn core::future::Future<Output = T> + 'n>
|
|||||||
pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
|
pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
|
||||||
pub type FutureAny<'n> = DynFuture<'n, Any<'n>>;
|
pub type FutureAny<'n> = DynFuture<'n, Any<'n>>;
|
||||||
pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n;
|
pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n;
|
||||||
pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n)>;
|
pub type TypeErasedPinnedRef<'n> = Pin<&'n TypeErasedNode<'n>>;
|
||||||
pub type TypeErasedRef<'n> = &'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n);
|
pub type TypeErasedRef<'n> = &'n TypeErasedNode<'n>;
|
||||||
pub type TypeErasedBox<'n> = Box<dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n>;
|
pub type TypeErasedBox<'n> = Box<TypeErasedNode<'n>>;
|
||||||
pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n>>;
|
pub type TypeErasedPinned<'n> = Pin<Box<TypeErasedNode<'n>>>;
|
||||||
|
|
||||||
pub type NodeConstructor = for<'a> fn(Vec<Arc<NodeContainer>>) -> DynFuture<'static, TypeErasedBox<'static>>;
|
pub type NodeConstructor = for<'a> fn(Vec<Arc<NodeContainer>>) -> DynFuture<'static, TypeErasedBox<'static>>;
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ impl Hash for ConstructionArgs {
|
|||||||
impl ConstructionArgs {
|
impl ConstructionArgs {
|
||||||
pub fn new_function_args(&self) -> Vec<String> {
|
pub fn new_function_args(&self) -> Vec<String> {
|
||||||
match self {
|
match self {
|
||||||
ConstructionArgs::Nodes(nodes) => nodes.iter().map(|n| format!("&n{}", n.0)).collect(),
|
ConstructionArgs::Nodes(nodes) => nodes.iter().map(|n| format!("n{:0x}", n.0)).collect(),
|
||||||
ConstructionArgs::Value(value) => vec![value.to_primitive_string()],
|
ConstructionArgs::Value(value) => vec![value.to_primitive_string()],
|
||||||
ConstructionArgs::Inline(inline) => vec![inline.expr.clone()],
|
ConstructionArgs::Inline(inline) => vec![inline.expr.clone()],
|
||||||
}
|
}
|
||||||
@@ -248,7 +248,7 @@ impl ProtoNode {
|
|||||||
|
|
||||||
pub fn value(value: ConstructionArgs, path: Vec<NodeId>) -> Self {
|
pub fn value(value: ConstructionArgs, path: Vec<NodeId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identifier: NodeIdentifier::new("graphene_core::value::ValueNode"),
|
identifier: NodeIdentifier::new("graphene_core::value::ClonedNode"),
|
||||||
construction_args: value,
|
construction_args: value,
|
||||||
input: ProtoNodeInput::None,
|
input: ProtoNodeInput::None,
|
||||||
document_node_path: path,
|
document_node_path: path,
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
[package]
|
||||||
|
name = "graphene-cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "CLI interface for the graphene language"
|
||||||
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
gpu = [
|
||||||
|
"interpreted-executor/gpu",
|
||||||
|
"graphene-std/gpu",
|
||||||
|
"graphene-core/gpu",
|
||||||
|
"wgpu-executor",
|
||||||
|
"gpu-executor",
|
||||||
|
]
|
||||||
|
default = ["wgpu"]
|
||||||
|
wgpu = ["wgpu-executor", "gpu", "graphene-std/wgpu"]
|
||||||
|
wayland = ["graphene-std/wayland"]
|
||||||
|
profiling = ["wgpu-executor/profiling"]
|
||||||
|
passthrough = ["wgpu-executor/passthrough"]
|
||||||
|
quantization = ["graphene-std/quantization"]
|
||||||
|
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
bitflags = "1.2.1"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = { version = "1.0" }
|
||||||
|
bezier-rs = { path = "../../libraries/bezier-rs" }
|
||||||
|
glam = { version = "0.22", features = ["serde"] }
|
||||||
|
|
||||||
|
# Node graph
|
||||||
|
graphene-std = { path = "../gstd" }
|
||||||
|
image = { version = "0.24", default-features = false, features = [
|
||||||
|
"bmp",
|
||||||
|
"png",
|
||||||
|
] }
|
||||||
|
graph-craft = { path = "../graph-craft" }
|
||||||
|
wgpu-executor = { path = "../wgpu-executor", optional = true }
|
||||||
|
gpu-executor = { path = "../gpu-executor", optional = true }
|
||||||
|
interpreted-executor = { path = "../interpreted-executor" }
|
||||||
|
dyn-any = { path = "../../libraries/dyn-any" }
|
||||||
|
graphene-core = { path = "../gcore" }
|
||||||
|
future-executor = { path = "../future-executor", optional = true }
|
||||||
|
|
||||||
|
wasm-bindgen = { version = "0.2.86", optional = true }
|
||||||
|
futures = "0.3.28"
|
||||||
|
fern = { version = "0.6.2", features = ["colored"] }
|
||||||
|
chrono = "0.4.26"
|
||||||
|
tokio = { version = "1.28.2", features = ["macros", "rt"] }
|
||||||
|
wgpu = "0.16.1"
|
||||||
|
|
||||||
|
[dependencies.document-legacy]
|
||||||
|
path = "../../document-legacy"
|
||||||
|
package = "graphite-document-legacy"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
env_logger = "0.8.4"
|
||||||
|
test-case = "2.1"
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
use fern::colors::{Color, ColoredLevelConfig};
|
||||||
|
use std::{collections::HashMap, error::Error, sync::Arc};
|
||||||
|
|
||||||
|
use document_legacy::{
|
||||||
|
document::Document,
|
||||||
|
layers::layer_info::{LayerData, LayerDataType},
|
||||||
|
};
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use graph_craft::{
|
||||||
|
concrete,
|
||||||
|
document::{value::TaggedValue, *},
|
||||||
|
graphene_compiler::{Compiler, Executor},
|
||||||
|
imaginate_input::ImaginatePreferences,
|
||||||
|
NodeIdentifier, Type, TypeDescriptor,
|
||||||
|
};
|
||||||
|
use graphene_core::{
|
||||||
|
application_io::{self, ApplicationIo, NodeGraphUpdateSender},
|
||||||
|
raster::ImageFrame,
|
||||||
|
text::FontCache,
|
||||||
|
Cow,
|
||||||
|
};
|
||||||
|
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
|
||||||
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
|
|
||||||
|
struct UpdateLogger {}
|
||||||
|
|
||||||
|
impl NodeGraphUpdateSender for UpdateLogger {
|
||||||
|
fn send(&self, message: graphene_core::application_io::NodeGraphUpdateMessage) {
|
||||||
|
println!("{:?}", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
init_logging();
|
||||||
|
|
||||||
|
let document_path = std::env::args().nth(1).expect("No document path provided");
|
||||||
|
|
||||||
|
let image_path = std::env::args().nth(2);
|
||||||
|
|
||||||
|
let document_string = std::fs::read_to_string(&document_path).expect("Failed to read document");
|
||||||
|
|
||||||
|
let executor = create_executor(document_string)?;
|
||||||
|
println!("creating gpu context",);
|
||||||
|
let mut application_io = block_on(WasmApplicationIo::new());
|
||||||
|
if let Some(image_path) = image_path {
|
||||||
|
application_io.resources.insert("null".to_string(), Arc::from(std::fs::read(image_path).expect("Failed to read image")));
|
||||||
|
}
|
||||||
|
|
||||||
|
let device = application_io.gpu_executor().unwrap().context.device.clone();
|
||||||
|
std::thread::spawn(move || loop {
|
||||||
|
std::thread::sleep(std::time::Duration::from_nanos(10));
|
||||||
|
device.poll(wgpu::Maintain::Poll);
|
||||||
|
});
|
||||||
|
|
||||||
|
let editor_api = WasmEditorApi {
|
||||||
|
image_frame: None,
|
||||||
|
font_cache: &FontCache::default(),
|
||||||
|
application_io: &application_io,
|
||||||
|
node_graph_message_sender: &UpdateLogger {},
|
||||||
|
imaginate_preferences: &ImaginatePreferences::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
//println!("executing");
|
||||||
|
let result = (&executor).execute(editor_api.clone()).await?;
|
||||||
|
//println!("result: {:?}", result);
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_logging() {
|
||||||
|
let colors = ColoredLevelConfig::new().debug(Color::Magenta).info(Color::Green).error(Color::Red);
|
||||||
|
fern::Dispatch::new()
|
||||||
|
.chain(std::io::stdout())
|
||||||
|
.level_for("iced", log::LevelFilter::Trace)
|
||||||
|
.level_for("wgpu", log::LevelFilter::Debug)
|
||||||
|
.level(log::LevelFilter::Trace)
|
||||||
|
.format(move |out, message, record| {
|
||||||
|
out.finish(format_args!(
|
||||||
|
"[{}]{} {}",
|
||||||
|
// This will color the log level only, not the whole line. Just a touch.
|
||||||
|
colors.color(record.level()),
|
||||||
|
chrono::Utc::now().format("[%Y-%m-%d %H:%M:%S]"),
|
||||||
|
message
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.apply()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_executor(document_string: String) -> Result<DynamicExecutor, Box<dyn Error>> {
|
||||||
|
let document: serde_json::Value = serde_json::from_str(&document_string).expect("Failed to parse document");
|
||||||
|
let document = serde_json::from_value::<Document>(document["document_legacy"].clone()).expect("Failed to parse document");
|
||||||
|
let Some(LayerDataType::Layer(ref node_graph)) = document.root.iter().find(|layer| matches!(layer.data, LayerDataType::Layer(_))).map(|x|&x.data) else { panic!("failed to extract node graph from docmuent") };
|
||||||
|
let network = &node_graph.network;
|
||||||
|
let wrapped_network = wrap_network_in_scope(network.clone());
|
||||||
|
let compiler = Compiler {};
|
||||||
|
let protograph = compiler.compile_single(wrapped_network, true)?;
|
||||||
|
let executor = block_on(DynamicExecutor::new(protograph))?;
|
||||||
|
Ok(executor)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[cfg_attr(not(feature = "wayland"), ignore)]
|
||||||
|
async fn grays_scale() {
|
||||||
|
let document_string = include_str!("../test_files/gray.graphite");
|
||||||
|
let executor = create_executor(document_string.to_string()).unwrap();
|
||||||
|
let editor_api = WasmEditorApi {
|
||||||
|
image_frame: None,
|
||||||
|
font_cache: &FontCache::default(),
|
||||||
|
application_io: &block_on(WasmApplicationIo::new()),
|
||||||
|
node_graph_message_sender: &UpdateLogger {},
|
||||||
|
imaginate_preferences: &ImaginatePreferences::default(),
|
||||||
|
};
|
||||||
|
let result = (&executor).execute(editor_api.clone()).await.unwrap();
|
||||||
|
println!("result: {:?}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[cfg_attr(not(feature = "wayland"), ignore)]
|
||||||
|
async fn hue() {
|
||||||
|
let document_string = include_str!("../test_files/hue.graphite");
|
||||||
|
let executor = create_executor(document_string.to_string()).unwrap();
|
||||||
|
let editor_api = WasmEditorApi {
|
||||||
|
image_frame: None,
|
||||||
|
font_cache: &FontCache::default(),
|
||||||
|
application_io: &block_on(WasmApplicationIo::new()),
|
||||||
|
node_graph_message_sender: &UpdateLogger {},
|
||||||
|
imaginate_preferences: &ImaginatePreferences::default(),
|
||||||
|
};
|
||||||
|
let result = (&executor).execute(editor_api.clone()).await.unwrap();
|
||||||
|
println!("result: {:?}", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
|
||||||
|
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
network.generate_node_paths(&[]);
|
||||||
|
for id in node_ids {
|
||||||
|
network.flatten(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut network_inputs = Vec::new();
|
||||||
|
let mut input_type = None;
|
||||||
|
for (id, node) in network.nodes.iter() {
|
||||||
|
for input in node.inputs.iter() {
|
||||||
|
if let NodeInput::Network(_) = input {
|
||||||
|
if input_type.is_none() {
|
||||||
|
input_type = Some(input.clone());
|
||||||
|
}
|
||||||
|
assert_eq!(input, input_type.as_ref().unwrap(), "Networks wrapped in scope must have the same input type");
|
||||||
|
network_inputs.push(*id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let len = network_inputs.len();
|
||||||
|
network.inputs = network_inputs;
|
||||||
|
|
||||||
|
// if the network has no inputs, it doesn't need to be wrapped in a scope
|
||||||
|
if len == 0 {
|
||||||
|
return network;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inner_network = DocumentNode {
|
||||||
|
name: "Scope".to_string(),
|
||||||
|
implementation: DocumentNodeImplementation::Network(network),
|
||||||
|
inputs: core::iter::repeat(NodeInput::node(0, 1)).take(len).collect(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// wrap the inner network in a scope
|
||||||
|
let nodes = vec![
|
||||||
|
begin_scope(),
|
||||||
|
inner_network,
|
||||||
|
DocumentNode {
|
||||||
|
name: "End Scope".to_string(),
|
||||||
|
implementation: DocumentNodeImplementation::proto("graphene_core::memo::EndLetNode<_>"),
|
||||||
|
inputs: vec![NodeInput::node(0, 0), NodeInput::node(1, 0)],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
];
|
||||||
|
NodeNetwork {
|
||||||
|
inputs: vec![0],
|
||||||
|
outputs: vec![NodeOutput::new(2, 0)],
|
||||||
|
nodes: nodes.into_iter().enumerate().map(|(id, node)| (id as NodeId, node)).collect(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn begin_scope() -> DocumentNode {
|
||||||
|
DocumentNode {
|
||||||
|
name: "Begin Scope".to_string(),
|
||||||
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
|
inputs: vec![0],
|
||||||
|
outputs: vec![NodeOutput::new(1, 0), NodeOutput::new(2, 0)],
|
||||||
|
nodes: [
|
||||||
|
DocumentNode {
|
||||||
|
name: "SetNode".to_string(),
|
||||||
|
inputs: vec![NodeInput::ShortCircut(concrete!(WasmEditorApi))],
|
||||||
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::SomeNode")),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
DocumentNode {
|
||||||
|
name: "LetNode".to_string(),
|
||||||
|
inputs: vec![NodeInput::node(0, 0)],
|
||||||
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::LetNode<_>")),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
DocumentNode {
|
||||||
|
name: "RefNode".to_string(),
|
||||||
|
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::lambda(1, 0)],
|
||||||
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::RefNode<_, _>")),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(id, node)| (id as NodeId, node))
|
||||||
|
.collect(),
|
||||||
|
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
inputs: vec![NodeInput::Network(concrete!(WasmEditorApi))],
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 334 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 2.4 MiB |
@@ -21,7 +21,7 @@ wgpu = ["gpu", "wgpu-executor"]
|
|||||||
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"]
|
||||||
|
wayland = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
autoquant = { git = "https://github.com/truedoctor/autoquant", optional = true, features = [
|
autoquant = { git = "https://github.com/truedoctor/autoquant", optional = true, features = [
|
||||||
@@ -42,7 +42,7 @@ gpu-compiler-bin-wrapper = { path = "../gpu-compiler/gpu-compiler-bin-wrapper",
|
|||||||
compilation-client = { path = "../compilation-client", optional = true }
|
compilation-client = { path = "../compilation-client", optional = true }
|
||||||
bytemuck = { version = "1.8" }
|
bytemuck = { version = "1.8" }
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
image = { version = "*", default-features = false }
|
image = { version = "*", default-features = false, features = ["png", "jpeg"] }
|
||||||
base64 = { version = "0.21", optional = true }
|
base64 = { version = "0.21", optional = true }
|
||||||
dyn-clone = "1.0"
|
dyn-clone = "1.0"
|
||||||
|
|
||||||
@@ -62,6 +62,9 @@ js-sys = { version = "0.3.63", optional = true }
|
|||||||
wgpu-types = "0.16.0"
|
wgpu-types = "0.16.0"
|
||||||
wgpu = "0.16.1"
|
wgpu = "0.16.1"
|
||||||
wasm-bindgen-futures = { version = "0.4.36", optional = true }
|
wasm-bindgen-futures = { version = "0.4.36", optional = true }
|
||||||
|
winit = "0.28.6"
|
||||||
|
url = "2.4.0"
|
||||||
|
tokio = { version = "1.29.0", optional = true, features = ["fs", "io-std"] }
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
|
use dyn_any::{StaticType, StaticTypeSized};
|
||||||
use glam::{DAffine2, DVec2, Mat2, Vec2};
|
use glam::{DAffine2, DVec2, Mat2, Vec2};
|
||||||
use gpu_executor::{Bindgroup, ComputePassDimensions, PipelineLayout, StorageBufferOptions};
|
use gpu_executor::{Bindgroup, ComputePassDimensions, PipelineLayout, StorageBufferOptions};
|
||||||
use gpu_executor::{GpuExecutor, ShaderIO, ShaderInput};
|
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::quantization::{PackedPixel, QuantizationChannels};
|
||||||
use graphene_core::raster::*;
|
use graphene_core::raster::*;
|
||||||
use graphene_core::*;
|
use graphene_core::*;
|
||||||
use wgpu_executor::WgpuExecutor;
|
use wgpu_executor::WgpuExecutor;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::wasm_application_io::WasmApplicationIo;
|
use crate::wasm_application_io::WasmApplicationIo;
|
||||||
@@ -42,18 +46,110 @@ async fn compile_gpu(node: &'input DocumentNode, mut typing_context: TypingConte
|
|||||||
pub struct MapGpuNode<Node, EditorApi> {
|
pub struct MapGpuNode<Node, EditorApi> {
|
||||||
node: Node,
|
node: Node,
|
||||||
editor_api: EditorApi,
|
editor_api: EditorApi,
|
||||||
|
cache: RefCell<HashMap<String, ComputePass<WgpuExecutor>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(MapGpuNode)]
|
struct ComputePass<T: GpuExecutor> {
|
||||||
|
pipeline_layout: PipelineLayout<T>,
|
||||||
|
readback_buffer: Option<Arc<ShaderInput<T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: GpuExecutor> Clone for ComputePass<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
pipeline_layout: self.pipeline_layout.clone(),
|
||||||
|
readback_buffer: self.readback_buffer.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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: graphene_core::application_io::EditorApi<'a, WasmApplicationIo>) -> ImageFrame<Color> {
|
||||||
log::debug!("Executing gpu node");
|
log::debug!("Executing gpu node");
|
||||||
|
let executor = &editor_api.application_io.gpu_executor.as_ref().unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
let quantization = crate::quantization::generate_quantization_from_image_frame(&image);
|
||||||
|
#[cfg(not(feature = "quantization"))]
|
||||||
|
let quantization = QuantizationChannels::default();
|
||||||
|
log::debug!("quantization: {:?}", quantization);
|
||||||
|
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
let image = ImageFrame {
|
||||||
|
image: Image {
|
||||||
|
data: image.image.data.iter().map(|c| quantization::quantize_color(*c, quantization)).collect(),
|
||||||
|
width: image.image.width,
|
||||||
|
height: image.image.height,
|
||||||
|
},
|
||||||
|
transform: image.transform,
|
||||||
|
};
|
||||||
|
// TODO: The cache should be based on the network topology not the node name
|
||||||
|
let compute_pass_descriptor = if self.cache.borrow().contains_key(&node.name) {
|
||||||
|
self.cache.borrow().get(&node.name).unwrap().clone()
|
||||||
|
} else {
|
||||||
|
let name = node.name.clone();
|
||||||
|
let compute_pass_descriptor = create_compute_pass_descriptor(node, &image, executor, quantization).await;
|
||||||
|
self.cache.borrow_mut().insert(name, compute_pass_descriptor.clone());
|
||||||
|
log::error!("created compute pass");
|
||||||
|
compute_pass_descriptor
|
||||||
|
};
|
||||||
|
|
||||||
|
let compute_pass = executor
|
||||||
|
.create_compute_pass(
|
||||||
|
&compute_pass_descriptor.pipeline_layout,
|
||||||
|
compute_pass_descriptor.readback_buffer.clone(),
|
||||||
|
ComputePassDimensions::XY(image.image.width / 12 + 1, image.image.height / 8 + 1),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
executor.execute_compute_pipeline(compute_pass).unwrap();
|
||||||
|
log::debug!("executed pipeline");
|
||||||
|
log::debug!("reading buffer");
|
||||||
|
let result = executor.read_output_buffer(compute_pass_descriptor.readback_buffer.clone().unwrap()).await.unwrap();
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
let colors = bytemuck::pod_collect_to_vec::<u8, PackedPixel>(result.as_slice());
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
log::debug!("first color: {:b}", colors[0].0);
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
let colors: Vec<_> = colors.iter().map(|c| quantization::dequantize_color(*c, quantization)).collect();
|
||||||
|
#[cfg(not(feature = "quantization"))]
|
||||||
|
let colors = bytemuck::pod_collect_to_vec::<u8, Color>(result.as_slice());
|
||||||
|
log::debug!("first color: {:?}", colors[0]);
|
||||||
|
ImageFrame {
|
||||||
|
image: Image {
|
||||||
|
data: colors,
|
||||||
|
width: image.image.width,
|
||||||
|
height: image.image.height,
|
||||||
|
},
|
||||||
|
transform: image.transform,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Node, EditorApi> MapGpuNode<Node, EditorApi> {
|
||||||
|
pub fn new(node: Node, editor_api: EditorApi) -> Self {
|
||||||
|
Self {
|
||||||
|
node,
|
||||||
|
editor_api,
|
||||||
|
cache: RefCell::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
|
||||||
|
node: DocumentNode,
|
||||||
|
image: &ImageFrame<T>,
|
||||||
|
executor: &&WgpuExecutor,
|
||||||
|
quantization: QuantizationChannels,
|
||||||
|
) -> ComputePass<WgpuExecutor> {
|
||||||
let compiler = graph_craft::graphene_compiler::Compiler {};
|
let compiler = graph_craft::graphene_compiler::Compiler {};
|
||||||
let inner_network = NodeNetwork::value_network(node);
|
let inner_network = NodeNetwork::value_network(node);
|
||||||
|
|
||||||
log::debug!("inner_network: {:?}", inner_network);
|
log::debug!("inner_network: {:?}", inner_network);
|
||||||
let network = NodeNetwork {
|
let network = NodeNetwork {
|
||||||
inputs: vec![], //vec![0, 1],
|
inputs: vec![2, 1], //vec![0, 1],
|
||||||
outputs: vec![NodeOutput::new(1, 0)],
|
#[cfg(feature = "quantization")]
|
||||||
|
outputs: vec![NodeOutput::new(5, 0)],
|
||||||
|
#[cfg(not(feature = "quantization"))]
|
||||||
|
outputs: vec![NodeOutput::new(3, 0)],
|
||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "Slice".into(),
|
name: "Slice".into(),
|
||||||
@@ -61,6 +157,18 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
|||||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::CopiedNode".into()),
|
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::CopiedNode".into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
DocumentNode {
|
||||||
|
name: "Quantization".into(),
|
||||||
|
inputs: vec![NodeInput::Network(concrete!(quantization::Quantization))],
|
||||||
|
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
DocumentNode {
|
||||||
|
name: "Width".into(),
|
||||||
|
inputs: vec![NodeInput::Network(concrete!(u32))],
|
||||||
|
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
/*DocumentNode {
|
/*DocumentNode {
|
||||||
name: "Index".into(),
|
name: "Index".into(),
|
||||||
//inputs: vec![NodeInput::Network(concrete!(UVec3))],
|
//inputs: vec![NodeInput::Network(concrete!(UVec3))],
|
||||||
@@ -68,30 +176,48 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
|||||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::CopiedNode".into()),
|
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::CopiedNode".into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},*/
|
},*/
|
||||||
/*
|
/*
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "GetNode".into(),
|
name: "GetNode".into(),
|
||||||
inputs: vec![NodeInput::node(1, 0), NodeInput::node(0, 0)],
|
inputs: vec![NodeInput::node(1, 0), NodeInput::node(0, 0)],
|
||||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::storage::GetNode".into()),
|
implementation: DocumentNodeImplementation::Unresolved("graphene_core::storage::GetNode".into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},*/
|
},*/
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
DocumentNode {
|
||||||
|
name: "Dequantize".into(),
|
||||||
|
inputs: vec![NodeInput::node(0, 0), NodeInput::node(1, 0)],
|
||||||
|
implementation: DocumentNodeImplementation::proto("graphene_core::quantization::DeQuantizeNode"),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "MapNode".into(),
|
name: "MapNode".into(),
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
inputs: vec![NodeInput::node(3, 0)],
|
||||||
|
#[cfg(not(feature = "quantization"))]
|
||||||
inputs: vec![NodeInput::node(0, 0)],
|
inputs: vec![NodeInput::node(0, 0)],
|
||||||
implementation: DocumentNodeImplementation::Network(inner_network),
|
implementation: DocumentNodeImplementation::Network(inner_network),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
DocumentNode {
|
||||||
|
name: "Quantize".into(),
|
||||||
|
inputs: vec![NodeInput::node(4, 0), NodeInput::node(1, 0)],
|
||||||
|
implementation: DocumentNodeImplementation::proto("graphene_core::quantization::QuantizeNode"),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
/*
|
/*
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
name: "SaveNode".into(),
|
name: "SaveNode".into(),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
//NodeInput::node(0, 0),
|
NodeInput::node(5, 0),
|
||||||
NodeInput::Inline(InlineRust::new(
|
NodeInput::Inline(InlineRust::new(
|
||||||
"o0[_global_index.x as usize] = i0[_global_index.x as usize]".into(),
|
"|x| o0[(_global_index.y * i1 + _global_index.x) as usize] = x".into(),
|
||||||
Type::Fn(Box::new(concrete!(Color)), Box::new(concrete!(()))),
|
//"|x|()".into(),
|
||||||
|
Type::Fn(Box::new(concrete!(PackedPixel)), Box::new(concrete!(()))),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
|
implementation: DocumentNodeImplementation::Unresolved("graphene_core::generic::FnMutNode".into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
@@ -110,12 +236,23 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
|||||||
vec![concrete!(u32), concrete!(Color)], //, concrete!(u32)],
|
vec![concrete!(u32), concrete!(Color)], //, concrete!(u32)],
|
||||||
vec![concrete!(Color)],
|
vec![concrete!(Color)],
|
||||||
ShaderIO {
|
ShaderIO {
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
inputs: vec![
|
||||||
|
ShaderInput::UniformBuffer((), concrete!(u32)),
|
||||||
|
ShaderInput::StorageBuffer((), concrete!(PackedPixel)),
|
||||||
|
ShaderInput::UniformBuffer((), concrete!(quantization::QuantizationChannels)),
|
||||||
|
//ShaderInput::Constant(gpu_executor::GPUConstant::GlobalInvocationId),
|
||||||
|
ShaderInput::OutputBuffer((), concrete!(PackedPixel)),
|
||||||
|
],
|
||||||
|
#[cfg(not(feature = "quantization"))]
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
ShaderInput::UniformBuffer((), concrete!(u32)),
|
ShaderInput::UniformBuffer((), concrete!(u32)),
|
||||||
ShaderInput::StorageBuffer((), concrete!(Color)),
|
ShaderInput::StorageBuffer((), concrete!(Color)),
|
||||||
//ShaderInput::Constant(gpu_executor::GPUConstant::GlobalInvocationId),
|
|
||||||
ShaderInput::OutputBuffer((), concrete!(Color)),
|
ShaderInput::OutputBuffer((), concrete!(Color)),
|
||||||
],
|
],
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
output: ShaderInput::OutputBuffer((), concrete!(PackedPixel)),
|
||||||
|
#[cfg(not(feature = "quantization"))]
|
||||||
output: ShaderInput::OutputBuffer((), concrete!(Color)),
|
output: ShaderInput::OutputBuffer((), concrete!(Color)),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -124,8 +261,6 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
|||||||
//return ImageFrame::empty();
|
//return ImageFrame::empty();
|
||||||
let len: usize = image.image.data.len();
|
let len: usize = image.image.data.len();
|
||||||
|
|
||||||
let executor = &editor_api.application_io.gpu_executor.as_ref().unwrap();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let canvas = editor_api.application_io.create_surface();
|
let canvas = editor_api.application_io.create_surface();
|
||||||
|
|
||||||
@@ -144,6 +279,7 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
|||||||
return frame;*/
|
return frame;*/
|
||||||
log::debug!("creating buffer");
|
log::debug!("creating buffer");
|
||||||
let width_uniform = executor.create_uniform_buffer(image.image.width).unwrap();
|
let width_uniform = executor.create_uniform_buffer(image.image.width).unwrap();
|
||||||
|
let quantization_uniform = executor.create_uniform_buffer(quantization).unwrap();
|
||||||
let storage_buffer = executor
|
let storage_buffer = executor
|
||||||
.create_storage_buffer(
|
.create_storage_buffer(
|
||||||
image.image.data.clone(),
|
image.image.data.clone(),
|
||||||
@@ -156,6 +292,7 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let width_uniform = Arc::new(width_uniform);
|
let width_uniform = Arc::new(width_uniform);
|
||||||
|
let quantization_uniform = Arc::new(quantization_uniform);
|
||||||
let storage_buffer = Arc::new(storage_buffer);
|
let storage_buffer = Arc::new(storage_buffer);
|
||||||
let output_buffer = executor.create_output_buffer(len, concrete!(Color), false).unwrap();
|
let output_buffer = executor.create_output_buffer(len, concrete!(Color), false).unwrap();
|
||||||
let output_buffer = Arc::new(output_buffer);
|
let output_buffer = Arc::new(output_buffer);
|
||||||
@@ -163,6 +300,9 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
|||||||
let readback_buffer = Arc::new(readback_buffer);
|
let readback_buffer = Arc::new(readback_buffer);
|
||||||
log::debug!("created buffer");
|
log::debug!("created buffer");
|
||||||
let bind_group = Bindgroup {
|
let bind_group = Bindgroup {
|
||||||
|
#[cfg(feature = "quantization")]
|
||||||
|
buffers: vec![width_uniform.clone(), storage_buffer.clone(), quantization_uniform.clone()],
|
||||||
|
#[cfg(not(feature = "quantization"))]
|
||||||
buffers: vec![width_uniform.clone(), storage_buffer.clone()],
|
buffers: vec![width_uniform.clone(), storage_buffer.clone()],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -175,36 +315,18 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
|||||||
let shader = executor.load_shader(shader).unwrap();
|
let shader = executor.load_shader(shader).unwrap();
|
||||||
log::debug!("loaded shader");
|
log::debug!("loaded shader");
|
||||||
let pipeline = PipelineLayout {
|
let pipeline = PipelineLayout {
|
||||||
shader,
|
shader: shader.into(),
|
||||||
entry_point: "eval".to_string(),
|
entry_point: "eval".to_string(),
|
||||||
bind_group,
|
bind_group: bind_group.into(),
|
||||||
output_buffer: output_buffer.clone(),
|
output_buffer: output_buffer.clone(),
|
||||||
};
|
};
|
||||||
log::debug!("created pipeline");
|
log::debug!("created pipeline");
|
||||||
let compute_pass = executor
|
|
||||||
.create_compute_pass(&pipeline, Some(readback_buffer.clone()), ComputePassDimensions::XY(image.image.width, image.image.height))
|
|
||||||
.unwrap();
|
|
||||||
executor.execute_compute_pipeline(compute_pass).unwrap();
|
|
||||||
log::debug!("executed pipeline");
|
|
||||||
log::debug!("reading buffer");
|
|
||||||
let result = executor.read_output_buffer(readback_buffer).await.unwrap();
|
|
||||||
let colors = bytemuck::pod_collect_to_vec::<u8, Color>(result.as_slice());
|
|
||||||
ImageFrame {
|
|
||||||
image: Image {
|
|
||||||
data: colors,
|
|
||||||
width: image.image.width,
|
|
||||||
height: image.image.height,
|
|
||||||
},
|
|
||||||
transform: image.transform,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
let compute_pass_descriptor = ComputePass {
|
||||||
let executor: GpuExecutor = GpuExecutor::new(Context::new().await.unwrap(), shader.into(), "gpu::eval".into()).unwrap();
|
pipeline_layout: pipeline,
|
||||||
let data: Vec<_> = input.into_iter().collect();
|
readback_buffer: Some(readback_buffer.clone()),
|
||||||
let result = executor.execute(Box::new(data)).unwrap();
|
};
|
||||||
let result = dyn_any::downcast::<Vec<_O>>(result).unwrap();
|
compute_pass_descriptor
|
||||||
*result
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
#[node_macro::node_fn(MapGpuNode)]
|
#[node_macro::node_fn(MapGpuNode)]
|
||||||
@@ -414,9 +536,9 @@ async fn blend_gpu_image(foreground: ImageFrame<Color>, background: ImageFrame<C
|
|||||||
let shader = executor.load_shader(shader).unwrap();
|
let shader = executor.load_shader(shader).unwrap();
|
||||||
log::debug!("loaded shader");
|
log::debug!("loaded shader");
|
||||||
let pipeline = PipelineLayout {
|
let pipeline = PipelineLayout {
|
||||||
shader,
|
shader: shader.into(),
|
||||||
entry_point: "eval".to_string(),
|
entry_point: "eval".to_string(),
|
||||||
bind_group,
|
bind_group: bind_group.into(),
|
||||||
output_buffer: output_buffer.clone(),
|
output_buffer: output_buffer.clone(),
|
||||||
};
|
};
|
||||||
log::debug!("created pipeline");
|
log::debug!("created pipeline");
|
||||||
|
|||||||
|
After Width: | Height: | Size: 241 B |
@@ -1,3 +1,4 @@
|
|||||||
|
use autoquant::packing::ErrorFunction;
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
use graphene_core::quantization::*;
|
use graphene_core::quantization::*;
|
||||||
use graphene_core::raster::{Color, ImageFrame};
|
use graphene_core::raster::{Color, ImageFrame};
|
||||||
@@ -13,47 +14,93 @@ pub struct GenerateQuantizationNode<N, M> {
|
|||||||
|
|
||||||
#[node_macro::node_fn(GenerateQuantizationNode)]
|
#[node_macro::node_fn(GenerateQuantizationNode)]
|
||||||
fn generate_quantization_fn(image_frame: ImageFrame<Color>, samples: u32, function: u32) -> [Quantization; 4] {
|
fn generate_quantization_fn(image_frame: ImageFrame<Color>, samples: u32, function: u32) -> [Quantization; 4] {
|
||||||
let image = image_frame.image;
|
generate_quantization_from_image_frame(&image_frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_quantization_from_image_frame(image_frame: &ImageFrame<Color>) -> [Quantization; 4] {
|
||||||
|
let image = &image_frame.image;
|
||||||
|
|
||||||
let len = image.data.len().min(10000);
|
let len = image.data.len().min(10000);
|
||||||
let mut channels: Vec<_> = (0..4).map(|_| Vec::with_capacity(image.data.len())).collect();
|
let data = image
|
||||||
image
|
|
||||||
.data
|
.data
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(i, _)| i % (image.data.len() / len) == 0)
|
.filter(|(i, _)| i % (image.data.len() / len) == 0)
|
||||||
.map(|(_, x)| vec![x.r() as f64, x.g() as f64, x.b() as f64, x.a() as f64])
|
.flat_map(|(_, x)| vec![x.r() as f64, x.g() as f64, x.b() as f64, x.a() as f64])
|
||||||
.for_each(|x| x.into_iter().enumerate().for_each(|(i, value)| channels[i].push(value)));
|
.collect::<Vec<_>>();
|
||||||
let quantization: Vec<Quantization> = channels.into_iter().map(|x| generate_quantization_per_channel(x, samples)).collect();
|
generate_quantization(data, len)
|
||||||
core::array::from_fn(|i| quantization[i].clone())
|
}
|
||||||
|
fn generate_quantization(data: Vec<f64>, samples: usize) -> [Quantization; 4] {
|
||||||
|
let red = create_distribution(data.clone(), samples, 0);
|
||||||
|
let green = create_distribution(data.clone(), samples, 1);
|
||||||
|
let blue = create_distribution(data.clone(), samples, 2);
|
||||||
|
let alpha = create_distribution(data, samples, 3);
|
||||||
|
|
||||||
|
let fit_red = autoquant::calculate_error_function(&red, 1, &red);
|
||||||
|
let fit_green = autoquant::calculate_error_function(&green, 1, &green);
|
||||||
|
let fit_blue = autoquant::calculate_error_function(&blue, 1, &blue);
|
||||||
|
let fit_alpha = autoquant::calculate_error_function(&alpha, 1, &alpha);
|
||||||
|
let red_error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit_red.as_slice());
|
||||||
|
let green_error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit_green.as_slice());
|
||||||
|
let blue_error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit_blue.as_slice());
|
||||||
|
let alpha_error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit_alpha.as_slice());
|
||||||
|
let merged: ErrorFunction<20> = autoquant::packing::merge_error_functions(&red_error, &green_error);
|
||||||
|
let merged: ErrorFunction<30> = autoquant::packing::merge_error_functions(&merged, &blue_error);
|
||||||
|
let merged: ErrorFunction<40> = autoquant::packing::merge_error_functions(&merged, &alpha_error);
|
||||||
|
|
||||||
|
let bin_size = 32;
|
||||||
|
let mut distributions = [red, green, blue, alpha].into_iter();
|
||||||
|
|
||||||
|
let bits = &merged.bits[bin_size];
|
||||||
|
|
||||||
|
core::array::from_fn(|i| {
|
||||||
|
let fit = autoquant::models::OptimizedLin::new(distributions.next().unwrap(), (1 << bits[i]) - 1);
|
||||||
|
let parameters = fit.parameters();
|
||||||
|
Quantization::new(parameters[0] as f32, parameters[1] as f32, bits[i] as u32)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_quantization_per_channel(data: Vec<f64>, samples: u32) -> Quantization {
|
/*
|
||||||
|
// TODO: make this work with generic size parameters
|
||||||
|
fn generate_quantization<const N: usize>(data: Vec<f64>, samples: usize, channels: usize) -> [Quantization; N] {
|
||||||
|
let mut quantizations = Vec::new();
|
||||||
|
let mut merged_error: Option<ErrorFunction<10>> = None;
|
||||||
|
let bin_size = 32;
|
||||||
|
|
||||||
|
for i in 0..channels {
|
||||||
|
let channel_data = create_distribution(data.clone(), samples, i);
|
||||||
|
|
||||||
|
let fit = autoquant::calculate_error_function(&channel_data, 0, &channel_data);
|
||||||
|
let error: ErrorFunction<10> = autoquant::packing::ErrorFunction::new(fit.as_slice());
|
||||||
|
|
||||||
|
// Merge current error function with previous ones
|
||||||
|
merged_error = match merged_error {
|
||||||
|
Some(prev_error) => Some(autoquant::packing::merge_error_functions(&prev_error, &error)),
|
||||||
|
None => Some(error.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Merged: {:?}", merged_error);
|
||||||
|
|
||||||
|
let bits = merged_error.as_ref().unwrap().bits.iter().map(|x| x[i]).collect::<Vec<_>>();
|
||||||
|
let model_fit = autoquant::models::OptimizedLin::new(channel_data, 1 << bits[bin_size]);
|
||||||
|
let parameters = model_fit.parameters();
|
||||||
|
let quantization = Quantization::new(parameters[0] as f32, parameters[1] as u32, bits[bin_size] as u32);
|
||||||
|
|
||||||
|
quantizations.push(quantization);
|
||||||
|
}
|
||||||
|
|
||||||
|
core::array::from_fn(|x| quantizations[x])
|
||||||
|
}*/
|
||||||
|
|
||||||
|
fn create_distribution(data: Vec<f64>, samples: usize, channel: usize) -> Vec<(f64, f64)> {
|
||||||
|
let data: Vec<f64> = data.chunks(4 * (data.len() / (4 * samples.min(data.len() / 4)))).map(|x| x[channel] as f64).collect();
|
||||||
|
let max = *data.iter().max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)).unwrap();
|
||||||
|
let data: Vec<f64> = data.iter().map(|x| x / max).collect();
|
||||||
|
dbg!(max);
|
||||||
|
//let data = autoquant::generate_normal_distribution(3.0, 1.1, 1000);
|
||||||
|
//data.iter_mut().for_each(|x| *x = x.abs());
|
||||||
let mut dist = autoquant::integrate_distribution(data);
|
let mut dist = autoquant::integrate_distribution(data);
|
||||||
autoquant::drop_duplicates(&mut dist);
|
autoquant::drop_duplicates(&mut dist);
|
||||||
let dist = autoquant::normalize_distribution(dist.as_slice());
|
let dist = autoquant::normalize_distribution(dist.as_slice());
|
||||||
let max = dist.last().unwrap().0;
|
dist
|
||||||
/*let linear = Box::new(autoquant::SimpleFitFn {
|
|
||||||
function: move |x| x / max,
|
|
||||||
inverse: move |x| x * max,
|
|
||||||
name: "identity",
|
|
||||||
});*/
|
|
||||||
|
|
||||||
let linear = Quantization {
|
|
||||||
fn_index: 0,
|
|
||||||
a: max as f32,
|
|
||||||
b: 0.,
|
|
||||||
c: 0.,
|
|
||||||
d: 0.,
|
|
||||||
};
|
|
||||||
let log_fit = autoquant::models::OptimizedLog::new(dist, samples as u64);
|
|
||||||
let parameters = log_fit.parameters();
|
|
||||||
let log_fit = Quantization {
|
|
||||||
fn_index: 1,
|
|
||||||
a: parameters[0] as f32,
|
|
||||||
b: parameters[1] as f32,
|
|
||||||
c: parameters[2] as f32,
|
|
||||||
d: parameters[3] as f32,
|
|
||||||
};
|
|
||||||
log_fit
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ use dyn_any::DynAny;
|
|||||||
pub struct AnyRefNode<'n, N: Node<'n>>(N, PhantomData<&'n ()>);
|
pub struct AnyRefNode<'n, N: Node<'n>>(N, PhantomData<&'n ()>);
|
||||||
|
|
||||||
impl<'n, N: Node<'n, Output = &'n O>, O: DynAny<'n> + 'n> Node<'n> for AnyRefNode<'n, N> {
|
impl<'n, N: Node<'n, Output = &'n O>, O: DynAny<'n> + 'n> Node<'n> for AnyRefNode<'n, N> {
|
||||||
type Output = &'n (dyn DynAny<'n>);
|
fn eval(&'n self) -> &'n (dyn DynAny<'n>) {
|
||||||
fn eval(&'n self) -> Self::Output {
|
|
||||||
let value: &O = self.0.eval();
|
let value: &O = self.0.eval();
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
@@ -22,8 +21,7 @@ impl<'n, N: Node<'n, Output = &'n O>, O: 'n + ?Sized> AnyRefNode<'n, N> {
|
|||||||
pub struct StorageNode<'n>(&'n dyn Node<'n, Output = &'n dyn DynAny<'n>>);
|
pub struct StorageNode<'n>(&'n dyn Node<'n, Output = &'n dyn DynAny<'n>>);
|
||||||
|
|
||||||
impl<'n> Node<'n> for StorageNode<'n> {
|
impl<'n> Node<'n> for StorageNode<'n> {
|
||||||
type Output = &'n (dyn DynAny<'n>);
|
fn eval(&'n self) -> &'n (dyn DynAny<'n>) {
|
||||||
fn eval(&'n self) -> Self::Output {
|
|
||||||
self.0.eval()
|
self.0.eval()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,7 +34,6 @@ impl<'n> StorageNode<'n> {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct AnyValueNode<'n, T>(T, PhantomData<&'n ()>);
|
pub struct AnyValueNode<'n, T>(T, PhantomData<&'n ()>);
|
||||||
impl<'n, T: 'n + DynAny<'n>> Node<'n> for AnyValueNode<'n, T> {
|
impl<'n, T: 'n + DynAny<'n>> Node<'n> for AnyValueNode<'n, T> {
|
||||||
type Output = &'n dyn DynAny<'n>;
|
|
||||||
fn eval(&'n self) -> &'n dyn DynAny<'n> {
|
fn eval(&'n self) -> &'n dyn DynAny<'n> {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use core::future::Future;
|
||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use graphene_core::application_io::{ApplicationIo, SurfaceHandle, SurfaceHandleFrame, SurfaceId};
|
use graphene_core::application_io::{ApplicationError, ApplicationIo, SurfaceHandle, SurfaceHandleFrame, SurfaceId};
|
||||||
|
use graphene_core::raster::Image;
|
||||||
|
use graphene_core::Color;
|
||||||
use graphene_core::{
|
use graphene_core::{
|
||||||
raster::{color::SRGBA8, ImageFrame},
|
raster::{color::SRGBA8, ImageFrame},
|
||||||
Node,
|
Node,
|
||||||
};
|
};
|
||||||
use js_sys::{Object, Reflect};
|
use js_sys::{Object, Reflect};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
use wasm_bindgen::{Clamped, JsCast, JsValue};
|
use wasm_bindgen::{Clamped, JsCast, JsValue};
|
||||||
use web_sys::{window, CanvasRenderingContext2d, HtmlCanvasElement};
|
use web_sys::{window, CanvasRenderingContext2d, HtmlCanvasElement};
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
@@ -20,15 +28,23 @@ pub struct WasmApplicationIo {
|
|||||||
ids: RefCell<u64>,
|
ids: RefCell<u64>,
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
pub(crate) gpu_executor: Option<WgpuExecutor>,
|
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 {
|
impl WasmApplicationIo {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
Self {
|
let mut io = Self {
|
||||||
ids: RefCell::new(0),
|
ids: RefCell::new(0),
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
gpu_executor: WgpuExecutor::new().await,
|
gpu_executor: WgpuExecutor::new().await,
|
||||||
}
|
#[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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,12 +67,16 @@ impl<'a> From<&'a WasmApplicationIo> for &'a WgpuExecutor {
|
|||||||
pub type WasmEditorApi<'a> = graphene_core::application_io::EditorApi<'a, WasmApplicationIo>;
|
pub type WasmEditorApi<'a> = graphene_core::application_io::EditorApi<'a, WasmApplicationIo>;
|
||||||
|
|
||||||
impl ApplicationIo for WasmApplicationIo {
|
impl ApplicationIo for WasmApplicationIo {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
type Surface = HtmlCanvasElement;
|
type Surface = HtmlCanvasElement;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
type Surface = Arc<winit::window::Window>;
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
type Executor = WgpuExecutor;
|
type Executor = WgpuExecutor;
|
||||||
#[cfg(not(feature = "wgpu"))]
|
#[cfg(not(feature = "wgpu"))]
|
||||||
type Executor = ();
|
type Executor = ();
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
fn create_surface(&self) -> SurfaceHandle<Self::Surface> {
|
fn create_surface(&self) -> SurfaceHandle<Self::Surface> {
|
||||||
let wrapper = || {
|
let wrapper = || {
|
||||||
let document = window().expect("should have a window in this context").document().expect("window should have a document");
|
let document = window().expect("should have a window in this context").document().expect("window should have a document");
|
||||||
@@ -90,7 +110,29 @@ impl ApplicationIo for WasmApplicationIo {
|
|||||||
|
|
||||||
wrapper().expect("should be able to set canvas in global scope")
|
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();
|
||||||
|
#[cfg(not(feature = "wayland"))]
|
||||||
|
let event_loop = winit::event_loop::EventLoop::new();
|
||||||
|
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) {
|
fn destroy_surface(&self, surface_id: SurfaceId) {
|
||||||
let window = window().expect("should have a window in this context");
|
let window = window().expect("should have a window in this context");
|
||||||
let window = Object::from(window);
|
let window = Object::from(window);
|
||||||
@@ -111,10 +153,50 @@ impl ApplicationIo for WasmApplicationIo {
|
|||||||
wrapper().expect("should be able to set canvas in global scope")
|
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")]
|
#[cfg(feature = "wgpu")]
|
||||||
fn gpu_executor(&self) -> Option<&Self::Executor> {
|
fn gpu_executor(&self) -> Option<&Self::Executor> {
|
||||||
self.gpu_executor.as_ref()
|
self.gpu_executor.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_resource(&self, url: impl AsRef<str>) -> Result<Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>>>>, ApplicationError> {
|
||||||
|
let url = url::Url::parse(url.as_ref()).map_err(|_| ApplicationError::InvalidUrl)?;
|
||||||
|
log::info!("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::info!("Loading 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>;
|
||||||
@@ -123,7 +205,7 @@ 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<HtmlCanvasElement>> {
|
async fn create_surface_node<'a: 'input>(editor: WasmEditorApi<'a>) -> Arc<SurfaceHandle<<WasmApplicationIo as ApplicationIo>::Surface>> {
|
||||||
editor.application_io.create_surface().into()
|
editor.application_io.create_surface().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,3 +231,29 @@ async fn draw_image_frame_node<'a: 'input>(image: ImageFrame<SRGBA8>, surface_ha
|
|||||||
transform: image.transform,
|
transform: image.transform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct LoadResourceNode<Url> {
|
||||||
|
url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node_fn(LoadResourceNode)]
|
||||||
|
async fn load_resource_node<'a: 'input>(editor: WasmEditorApi<'a>, url: String) -> Arc<[u8]> {
|
||||||
|
editor.application_io.load_resource(url).unwrap().await.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DecodeImageNode;
|
||||||
|
|
||||||
|
#[node_macro::node_fn(DecodeImageNode)]
|
||||||
|
fn decode_image_node<'a: 'input>(data: Arc<[u8]>) -> ImageFrame<Color> {
|
||||||
|
let image = image::load_from_memory(data.as_ref()).expect("Failed to decode image");
|
||||||
|
let image = image.to_rgba32f();
|
||||||
|
let image = ImageFrame {
|
||||||
|
image: Image {
|
||||||
|
data: image.chunks(4).map(|pixel| Color::from_unassociated_alpha(pixel[0], pixel[1], pixel[2], pixel[3])).collect(),
|
||||||
|
width: image.width(),
|
||||||
|
height: image.height(),
|
||||||
|
},
|
||||||
|
transform: glam::DAffine2::IDENTITY,
|
||||||
|
};
|
||||||
|
image
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use graph_craft::imaginate_input::{ImaginateCache, ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod};
|
use graph_craft::imaginate_input::{ImaginateCache, ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod};
|
||||||
use graph_craft::proto::{NodeConstructor, TypeErasedBox};
|
use graph_craft::proto::{NodeConstructor, TypeErasedBox};
|
||||||
use graphene_core::ops::IdNode;
|
use graphene_core::ops::IdNode;
|
||||||
use graphene_core::quantization::QuantizationChannels;
|
use graphene_core::quantization::{PackedPixel, QuantizationChannels};
|
||||||
|
|
||||||
use graphene_core::raster::brush_cache::BrushCache;
|
use graphene_core::raster::brush_cache::BrushCache;
|
||||||
use graphene_core::raster::color::Color;
|
use graphene_core::raster::color::Color;
|
||||||
@@ -191,10 +191,10 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]),
|
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]),
|
||||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]),
|
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]),
|
||||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [&u32]),
|
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [&u32]),
|
||||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [f64]),
|
register_node!(graphene_core::ops::AddParameterNode<_>, input: f32, params: [f32]),
|
||||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]),
|
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f32, params: [f32]),
|
||||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]),
|
register_node!(graphene_core::ops::AddParameterNode<_>, input: f32, params: [&f32]),
|
||||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]),
|
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f32, params: [&f32]),
|
||||||
register_node!(graphene_core::ops::SomeNode, input: WasmEditorApi, params: []),
|
register_node!(graphene_core::ops::SomeNode, input: WasmEditorApi, params: []),
|
||||||
async_node!(graphene_core::ops::IntoNode<_, ImageFrame<SRGBA8>>, input: ImageFrame<Color>, output: ImageFrame<SRGBA8>, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, ImageFrame<SRGBA8>>, input: ImageFrame<Color>, output: ImageFrame<SRGBA8>, params: []),
|
||||||
async_node!(graphene_core::ops::IntoNode<_, ImageFrame<Color>>, input: ImageFrame<SRGBA8>, output: ImageFrame<Color>, params: []),
|
async_node!(graphene_core::ops::IntoNode<_, ImageFrame<Color>>, input: ImageFrame<SRGBA8>, output: ImageFrame<Color>, params: []),
|
||||||
@@ -251,6 +251,8 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
register_node!(graphene_std::raster::EmptyImageNode<_, _>, input: DAffine2, params: [Color]),
|
register_node!(graphene_std::raster::EmptyImageNode<_, _>, input: DAffine2, params: [Color]),
|
||||||
register_node!(graphene_core::memo::MonitorNode<_>, input: ImageFrame<Color>, params: []),
|
register_node!(graphene_core::memo::MonitorNode<_>, input: ImageFrame<Color>, params: []),
|
||||||
register_node!(graphene_core::memo::MonitorNode<_>, input: graphene_core::GraphicGroup, params: []),
|
register_node!(graphene_core::memo::MonitorNode<_>, input: graphene_core::GraphicGroup, params: []),
|
||||||
|
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: []),
|
||||||
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<_>,
|
||||||
@@ -284,7 +286,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
),
|
),
|
||||||
#[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(all(feature = "gpu", target_arch = "wasm32"))]
|
#[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: []),
|
||||||
#[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]),
|
||||||
@@ -310,7 +312,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
},
|
},
|
||||||
NodeIOTypes::new(
|
NodeIOTypes::new(
|
||||||
concrete!(ImageFrame<Color>),
|
concrete!(ImageFrame<Color>),
|
||||||
concrete!(SurfaceFrame),
|
concrete!(ImageFrame<Color>),
|
||||||
vec![fn_type!(graph_craft::document::DocumentNode), fn_type!(WasmEditorApi)],
|
vec![fn_type!(graph_craft::document::DocumentNode), fn_type!(WasmEditorApi)],
|
||||||
),
|
),
|
||||||
)],
|
)],
|
||||||
@@ -355,7 +357,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
raster_node!(graphene_core::raster::ExtractChannelNode<_>, params: [RedGreenBlue]),
|
raster_node!(graphene_core::raster::ExtractChannelNode<_>, params: [RedGreenBlue]),
|
||||||
raster_node!(graphene_core::raster::ExtractAlphaNode<>, params: []),
|
raster_node!(graphene_core::raster::ExtractAlphaNode<>, params: []),
|
||||||
raster_node!(graphene_core::raster::ExtractOpaqueNode<>, params: []),
|
raster_node!(graphene_core::raster::ExtractOpaqueNode<>, params: []),
|
||||||
raster_node!(graphene_core::raster::LevelsNode<_, _, _, _, _>, params: [f64, f64, f64, f64, f64]),
|
raster_node!(graphene_core::raster::LevelsNode<_, _, _, _, _>, params: [f32, f32, f32, f32, f32]),
|
||||||
register_node!(graphene_std::image_segmentation::ImageSegmentationNode<_>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
register_node!(graphene_std::image_segmentation::ImageSegmentationNode<_>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
||||||
register_node!(graphene_core::raster::IndexNode<_>, input: Vec<ImageFrame<Color>>, params: [u32]),
|
register_node!(graphene_core::raster::IndexNode<_>, input: Vec<ImageFrame<Color>>, params: [u32]),
|
||||||
vec![(
|
vec![(
|
||||||
@@ -364,7 +366,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0].clone());
|
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0].clone());
|
||||||
let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1].clone());
|
let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1].clone());
|
||||||
let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2].clone());
|
let opacity: DowncastBothNode<(), f32> = DowncastBothNode::new(args[2].clone());
|
||||||
let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(blend_mode.eval(()).await), CopiedNode::new(opacity.eval(()).await));
|
let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(blend_mode.eval(()).await), CopiedNode::new(opacity.eval(()).await));
|
||||||
let node = graphene_std::raster::BlendImageNode::new(image, blend_node);
|
let node = graphene_std::raster::BlendImageNode::new(image, blend_node);
|
||||||
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(node);
|
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||||
@@ -374,21 +376,21 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
NodeIOTypes::new(
|
NodeIOTypes::new(
|
||||||
concrete!(ImageFrame<Color>),
|
concrete!(ImageFrame<Color>),
|
||||||
concrete!(ImageFrame<Color>),
|
concrete!(ImageFrame<Color>),
|
||||||
vec![fn_type!(ImageFrame<Color>), fn_type!(BlendMode), fn_type!(f64)],
|
vec![fn_type!(ImageFrame<Color>), fn_type!(BlendMode), fn_type!(f32)],
|
||||||
),
|
),
|
||||||
)],
|
)],
|
||||||
raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]),
|
raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f32, f32, f32, f32, f32, f32]),
|
||||||
raster_node!(graphene_core::raster::HueSaturationNode<_, _, _>, params: [f64, f64, f64]),
|
raster_node!(graphene_core::raster::HueSaturationNode<_, _, _>, params: [f32, f32, f32]),
|
||||||
raster_node!(graphene_core::raster::InvertRGBNode, params: []),
|
raster_node!(graphene_core::raster::InvertRGBNode, params: []),
|
||||||
raster_node!(graphene_core::raster::ThresholdNode<_, _, _>, params: [f64, f64, LuminanceCalculation]),
|
raster_node!(graphene_core::raster::ThresholdNode<_, _, _>, params: [f32, f32, LuminanceCalculation]),
|
||||||
raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]),
|
raster_node!(graphene_core::raster::VibranceNode<_>, params: [f32]),
|
||||||
raster_node!(
|
raster_node!(
|
||||||
graphene_core::raster::ChannelMixerNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
|
graphene_core::raster::ChannelMixerNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
|
||||||
params: [bool, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64]
|
params: [bool, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32]
|
||||||
),
|
),
|
||||||
raster_node!(
|
raster_node!(
|
||||||
graphene_core::raster::SelectiveColorNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
|
graphene_core::raster::SelectiveColorNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
|
||||||
params: [RelativeAbsolute, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64]
|
params: [RelativeAbsolute, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32]
|
||||||
),
|
),
|
||||||
vec![(
|
vec![(
|
||||||
NodeIdentifier::new("graphene_core::raster::BrightnessContrastNode<_, _, _>"),
|
NodeIdentifier::new("graphene_core::raster::BrightnessContrastNode<_, _, _>"),
|
||||||
@@ -396,9 +398,9 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
use graphene_core::raster::brightness_contrast::*;
|
use graphene_core::raster::brightness_contrast::*;
|
||||||
|
|
||||||
let brightness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[0].clone());
|
let brightness: DowncastBothNode<(), f32> = DowncastBothNode::new(args[0].clone());
|
||||||
let brightness = ClonedNode::new(brightness.eval(()).await as f32);
|
let brightness = ClonedNode::new(brightness.eval(()).await as f32);
|
||||||
let contrast: DowncastBothNode<(), f64> = DowncastBothNode::new(args[1].clone());
|
let contrast: DowncastBothNode<(), f32> = DowncastBothNode::new(args[1].clone());
|
||||||
let contrast = ClonedNode::new(contrast.eval(()).await as f32);
|
let contrast = ClonedNode::new(contrast.eval(()).await as f32);
|
||||||
let use_legacy: DowncastBothNode<(), bool> = DowncastBothNode::new(args[2].clone());
|
let use_legacy: DowncastBothNode<(), bool> = DowncastBothNode::new(args[2].clone());
|
||||||
|
|
||||||
@@ -417,11 +419,11 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![fn_type!(f64), fn_type!(f64), fn_type!(bool)]),
|
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![fn_type!(f32), fn_type!(f32), fn_type!(bool)]),
|
||||||
)],
|
)],
|
||||||
raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]),
|
raster_node!(graphene_core::raster::OpacityNode<_>, params: [f32]),
|
||||||
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
|
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f32]),
|
||||||
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
|
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f32, f32, f32]),
|
||||||
register_node!(graphene_core::memo::LetNode<_>, input: Option<ImageFrame<Color>>, params: []),
|
register_node!(graphene_core::memo::LetNode<_>, input: Option<ImageFrame<Color>>, params: []),
|
||||||
register_node!(graphene_core::memo::LetNode<_>, input: Option<WasmEditorApi>, params: []),
|
register_node!(graphene_core::memo::LetNode<_>, input: Option<WasmEditorApi>, params: []),
|
||||||
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
||||||
@@ -479,18 +481,18 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
vec![
|
vec![
|
||||||
fn_type!(WasmEditorApi),
|
fn_type!(WasmEditorApi),
|
||||||
fn_type!(ImaginateController),
|
fn_type!(ImaginateController),
|
||||||
fn_type!(f64),
|
fn_type!(f32),
|
||||||
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!(f32),
|
||||||
fn_type!(String),
|
fn_type!(String),
|
||||||
fn_type!(String),
|
fn_type!(String),
|
||||||
fn_type!(bool),
|
fn_type!(bool),
|
||||||
fn_type!(f64),
|
fn_type!(f32),
|
||||||
fn_type!(Option<Vec<u64>>),
|
fn_type!(Option<Vec<u64>>),
|
||||||
fn_type!(bool),
|
fn_type!(bool),
|
||||||
fn_type!(f64),
|
fn_type!(f32),
|
||||||
fn_type!(ImaginateMaskStartingFill),
|
fn_type!(ImaginateMaskStartingFill),
|
||||||
fn_type!(bool),
|
fn_type!(bool),
|
||||||
fn_type!(bool),
|
fn_type!(bool),
|
||||||
@@ -513,25 +515,25 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image<Color>, params: [DAffine2]),
|
register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image<Color>, params: [DAffine2]),
|
||||||
#[cfg(feature = "quantization")]
|
#[cfg(feature = "quantization")]
|
||||||
register_node!(graphene_std::quantization::GenerateQuantizationNode<_, _>, input: ImageFrame<Color>, params: [u32, u32]),
|
register_node!(graphene_std::quantization::GenerateQuantizationNode<_, _>, input: ImageFrame<Color>, params: [u32, u32]),
|
||||||
raster_node!(graphene_core::quantization::QuantizeNode<_>, params: [QuantizationChannels]),
|
register_node!(graphene_core::quantization::QuantizeNode<_>, input: Color, params: [QuantizationChannels]),
|
||||||
raster_node!(graphene_core::quantization::DeQuantizeNode<_>, 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: []),
|
||||||
register_node!(graphene_core::transform::TransformNode<_, _, _, _, _>, input: VectorData, params: [DVec2, f64, DVec2, DVec2, DVec2]),
|
register_node!(graphene_core::transform::TransformNode<_, _, _, _, _>, input: VectorData, params: [DVec2, f32, DVec2, DVec2, DVec2]),
|
||||||
register_node!(graphene_core::transform::TransformNode<_, _, _, _, _>, input: ImageFrame<Color>, params: [DVec2, f64, DVec2, DVec2, DVec2]),
|
register_node!(graphene_core::transform::TransformNode<_, _, _, _, _>, input: ImageFrame<Color>, params: [DVec2, f32, DVec2, DVec2, DVec2]),
|
||||||
register_node!(graphene_core::transform::TransformNode<_, _, _, _, _>, input: WasmSurfaceHandleFrame, params: [DVec2, f64, DVec2, DVec2, DVec2]),
|
register_node!(graphene_core::transform::TransformNode<_, _, _, _, _>, input: WasmSurfaceHandleFrame, params: [DVec2, f32, DVec2, DVec2, DVec2]),
|
||||||
register_node!(graphene_core::transform::SetTransformNode<_>, input: VectorData, params: [VectorData]),
|
register_node!(graphene_core::transform::SetTransformNode<_>, input: VectorData, params: [VectorData]),
|
||||||
register_node!(graphene_core::transform::SetTransformNode<_>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
register_node!(graphene_core::transform::SetTransformNode<_>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
||||||
register_node!(graphene_core::transform::SetTransformNode<_>, input: VectorData, params: [DAffine2]),
|
register_node!(graphene_core::transform::SetTransformNode<_>, input: VectorData, params: [DAffine2]),
|
||||||
register_node!(graphene_core::transform::SetTransformNode<_>, input: ImageFrame<Color>, params: [DAffine2]),
|
register_node!(graphene_core::transform::SetTransformNode<_>, input: ImageFrame<Color>, params: [DAffine2]),
|
||||||
register_node!(graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>, input: VectorData, params: [graphene_core::vector::style::FillType, Option<graphene_core::Color>, graphene_core::vector::style::GradientType, DVec2, DVec2, DAffine2, Vec<(f64, Option<graphene_core::Color>)>]),
|
register_node!(graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>, input: VectorData, params: [graphene_core::vector::style::FillType, Option<graphene_core::Color>, graphene_core::vector::style::GradientType, DVec2, DVec2, DAffine2, Vec<(f64, Option<graphene_core::Color>)>]),
|
||||||
register_node!(graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>, input: VectorData, params: [Option<graphene_core::Color>, f64, Vec<f32>, f64, graphene_core::vector::style::LineCap, graphene_core::vector::style::LineJoin, f64]),
|
register_node!(graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>, input: VectorData, params: [Option<graphene_core::Color>, f32, Vec<f32>, f32, graphene_core::vector::style::LineCap, graphene_core::vector::style::LineJoin, f32]),
|
||||||
register_node!(graphene_core::vector::generator_nodes::UnitCircleGenerator, input: (), params: []),
|
register_node!(graphene_core::vector::generator_nodes::UnitCircleGenerator, input: (), params: []),
|
||||||
register_node!(
|
register_node!(
|
||||||
graphene_core::vector::generator_nodes::PathGenerator<_>,
|
graphene_core::vector::generator_nodes::PathGenerator<_>,
|
||||||
input: Vec<graphene_core::vector::bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>,
|
input: Vec<graphene_core::vector::bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>,
|
||||||
params: [Vec<graphene_core::uuid::ManipulatorGroupId>]
|
params: [Vec<graphene_core::uuid::ManipulatorGroupId>]
|
||||||
),
|
),
|
||||||
register_node!(graphene_core::text::TextGenerator<_, _, _>, input: WasmEditorApi, params: [String, graphene_core::text::Font, f64]),
|
register_node!(graphene_core::text::TextGenerator<_, _, _>, input: WasmEditorApi, params: [String, graphene_core::text::Font, f32]),
|
||||||
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: []),
|
register_node!(graphene_core::ExtractImageFrame, input: WasmEditorApi, params: []),
|
||||||
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::vector::VectorData, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
|
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::vector::VectorData, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ license = "MIT OR Apache-2.0"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
profiling = ["nvtx"]
|
||||||
|
passthrough = []
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -38,3 +40,4 @@ futures-intrusive = "0.5.0"
|
|||||||
futures = "0.3.25"
|
futures = "0.3.25"
|
||||||
web-sys = { version = "0.3.4", features = ["HtmlCanvasElement"] }
|
web-sys = { version = "0.3.4", features = ["HtmlCanvasElement"] }
|
||||||
winit = "0.28.6"
|
winit = "0.28.6"
|
||||||
|
nvtx = { version = "1.1.1", optional = true }
|
||||||
|
|||||||
@@ -12,23 +12,25 @@ pub struct Context {
|
|||||||
impl Context {
|
impl Context {
|
||||||
pub async fn new() -> Option<Self> {
|
pub async fn new() -> Option<Self> {
|
||||||
// Instantiates instance of WebGPU
|
// Instantiates instance of WebGPU
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default());
|
let mut instance_descriptor = wgpu::InstanceDescriptor::default();
|
||||||
|
instance_descriptor.backends = wgpu::Backends::VULKAN | wgpu::Backends::BROWSER_WEBGPU;
|
||||||
|
let instance = wgpu::Instance::new(instance_descriptor);
|
||||||
|
|
||||||
// `request_adapter` instantiates the general connection to the GPU
|
// `request_adapter` instantiates the general connection to the GPU
|
||||||
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await?;
|
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await?;
|
||||||
|
|
||||||
//let limits = adapter.limits();
|
let limits = adapter.limits();
|
||||||
|
|
||||||
//log::trace!("Adapter limits: {:?}", limits);
|
|
||||||
|
|
||||||
// `request_device` instantiates the feature specific connection to the GPU, defining some parameters,
|
// `request_device` instantiates the feature specific connection to the GPU, defining some parameters,
|
||||||
// `features` being the available features.
|
// `features` being the available features.
|
||||||
let (device, queue) = adapter
|
let (device, queue) = adapter
|
||||||
.request_device(
|
.request_device(
|
||||||
&wgpu::DeviceDescriptor {
|
&wgpu::DeviceDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
|
#[cfg(not(feature = "passthrough"))]
|
||||||
features: wgpu::Features::empty(),
|
features: wgpu::Features::empty(),
|
||||||
limits: Default::default(),
|
#[cfg(feature = "passthrough")]
|
||||||
|
features: wgpu::Features::SPIRV_SHADER_PASSTHROUGH,
|
||||||
|
limits: limits,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,19 +11,30 @@ 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, Texture, TextureView};
|
use wgpu::{Buffer, BufferDescriptor, CommandBuffer, ShaderModule, SurfaceConfiguration, SurfaceError, Texture, TextureView};
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use web_sys::HtmlCanvasElement;
|
use web_sys::HtmlCanvasElement;
|
||||||
|
|
||||||
#[derive(Debug, dyn_any::DynAny)]
|
#[derive(dyn_any::DynAny)]
|
||||||
pub struct WgpuExecutor {
|
pub struct WgpuExecutor {
|
||||||
context: Context,
|
pub context: Context,
|
||||||
render_configuration: RenderConfiguration,
|
render_configuration: RenderConfiguration,
|
||||||
|
surface_config: Cell<Option<SurfaceConfiguration>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for WgpuExecutor {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("WgpuExecutor")
|
||||||
|
.field("context", &self.context)
|
||||||
|
.field("render_configuration", &self.render_configuration)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ApplicationIo<Executor = WgpuExecutor>> From<EditorApi<'a, T>> for &'a WgpuExecutor {
|
impl<'a, T: ApplicationIo<Executor = WgpuExecutor>> From<EditorApi<'a, T>> for &'a WgpuExecutor {
|
||||||
@@ -104,13 +115,21 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
type Window = HtmlCanvasElement;
|
type Window = HtmlCanvasElement;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
type Window = winit::window::Window;
|
type Window = Arc<winit::window::Window>;
|
||||||
|
|
||||||
fn load_shader(&self, shader: Shader) -> Result<Self::ShaderHandle> {
|
fn load_shader(&self, shader: Shader) -> Result<Self::ShaderHandle> {
|
||||||
|
#[cfg(not(feature = "passthrough"))]
|
||||||
let shader_module = self.context.device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let shader_module = self.context.device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some(shader.name),
|
label: Some(shader.name),
|
||||||
source: wgpu::ShaderSource::SpirV(shader.source),
|
source: wgpu::ShaderSource::SpirV(shader.source),
|
||||||
});
|
});
|
||||||
|
#[cfg(feature = "passthrough")]
|
||||||
|
let shader_module = unsafe {
|
||||||
|
self.context.device.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV {
|
||||||
|
label: Some(shader.name),
|
||||||
|
source: shader.source,
|
||||||
|
})
|
||||||
|
};
|
||||||
Ok(ShaderModuleWrapper(shader_module))
|
Ok(ShaderModuleWrapper(shader_module))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,14 +253,16 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
entries: entries.as_slice(),
|
entries: entries.as_slice(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut encoder = self.context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
let mut encoder = self.context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("compute encoder") });
|
||||||
{
|
{
|
||||||
let dimensions = instances.get();
|
let dimensions = instances.get();
|
||||||
let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: None });
|
let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: None });
|
||||||
cpass.set_pipeline(&compute_pipeline);
|
cpass.set_pipeline(&compute_pipeline);
|
||||||
cpass.set_bind_group(0, &bind_group, &[]);
|
cpass.set_bind_group(0, &bind_group, &[]);
|
||||||
cpass.insert_debug_marker("compute node network evaluation");
|
cpass.insert_debug_marker("compute node network evaluation");
|
||||||
|
cpass.push_debug_group("compute shader");
|
||||||
cpass.dispatch_workgroups(dimensions.0, dimensions.1, dimensions.2); // Number of cells to run, the (x,y,z) size of item being processed
|
cpass.dispatch_workgroups(dimensions.0, dimensions.1, dimensions.2); // Number of cells to run, the (x,y,z) size of item being processed
|
||||||
|
cpass.pop_debug_group();
|
||||||
}
|
}
|
||||||
// Sets adds copy operation to command encoder.
|
// Sets adds copy operation to command encoder.
|
||||||
// Will copy data from storage buffer on GPU to staging buffer on CPU.
|
// Will copy data from storage buffer on GPU to staging buffer on CPU.
|
||||||
@@ -265,7 +286,40 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
fn create_render_pass(&self, texture: Arc<ShaderInput<Self>>, canvas: Arc<SurfaceHandle<wgpu::Surface>>) -> Result<()> {
|
fn create_render_pass(&self, texture: Arc<ShaderInput<Self>>, canvas: Arc<SurfaceHandle<wgpu::Surface>>) -> Result<()> {
|
||||||
let texture = texture.texture().expect("Expected texture input");
|
let texture = texture.texture().expect("Expected texture input");
|
||||||
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
let output = canvas.as_ref().surface.get_current_texture()?;
|
let result = canvas.as_ref().surface.get_current_texture();
|
||||||
|
|
||||||
|
let surface = &canvas.as_ref().surface;
|
||||||
|
let surface_caps = surface.get_capabilities(&self.context.adapter);
|
||||||
|
println!("{:?}", surface_caps);
|
||||||
|
if surface_caps.formats.is_empty() {
|
||||||
|
log::warn!("No surface formats available");
|
||||||
|
//return Ok(());
|
||||||
|
}
|
||||||
|
let Some(config) = self.surface_config.take() else {return Ok(())};
|
||||||
|
let new_config = config.clone();
|
||||||
|
self.surface_config.replace(Some(config));
|
||||||
|
let output = match result {
|
||||||
|
Err(SurfaceError::Timeout) => {
|
||||||
|
log::warn!("Timeout when getting current texture");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(SurfaceError::Lost) => {
|
||||||
|
log::warn!("Surface lost");
|
||||||
|
|
||||||
|
surface.configure(&self.context.device, &new_config);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(SurfaceError::OutOfMemory) => {
|
||||||
|
log::warn!("Out of memory");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(SurfaceError::Outdated) => {
|
||||||
|
log::warn!("Surface outdated");
|
||||||
|
surface.configure(&self.context.device, &new_config);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Ok(surface) => surface,
|
||||||
|
};
|
||||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor {
|
let view = output.texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
format: Some(wgpu::TextureFormat::Bgra8Unorm),
|
format: Some(wgpu::TextureFormat::Bgra8Unorm),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -306,10 +360,16 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
render_pass.set_vertex_buffer(0, self.render_configuration.vertex_buffer.slice(..));
|
render_pass.set_vertex_buffer(0, self.render_configuration.vertex_buffer.slice(..));
|
||||||
render_pass.set_index_buffer(self.render_configuration.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
render_pass.set_index_buffer(self.render_configuration.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
||||||
render_pass.draw_indexed(0..self.render_configuration.num_indices, 0, 0..1);
|
render_pass.draw_indexed(0..self.render_configuration.num_indices, 0, 0..1);
|
||||||
|
render_pass.insert_debug_marker("render node network");
|
||||||
}
|
}
|
||||||
|
|
||||||
let encoder = encoder.finish();
|
let encoder = encoder.finish();
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
nvtx::range_push!("render");
|
||||||
self.context.queue.submit(Some(encoder));
|
self.context.queue.submit(Some(encoder));
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
nvtx::range_pop!();
|
||||||
|
log::trace!("Submitted render pass");
|
||||||
output.present();
|
output.present();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -318,10 +378,6 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
fn execute_compute_pipeline(&self, encoder: Self::CommandBuffer) -> Result<()> {
|
fn execute_compute_pipeline(&self, encoder: Self::CommandBuffer) -> Result<()> {
|
||||||
self.context.queue.submit(Some(encoder.0));
|
self.context.queue.submit(Some(encoder.0));
|
||||||
|
|
||||||
// Poll the device in a blocking manner so that our future resolves.
|
|
||||||
// In an actual application, `device.poll(...)` should
|
|
||||||
// be called in an event loop or on another thread.
|
|
||||||
self.context.device.poll(wgpu::Maintain::Wait);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,8 +450,25 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn create_surface(&self, window: SurfaceHandle<winit::window::Window>) -> Result<SurfaceHandle<wgpu::Surface>> {
|
fn create_surface(&self, window: SurfaceHandle<Self::Window>) -> Result<SurfaceHandle<wgpu::Surface>> {
|
||||||
let surface = unsafe { self.context.instance.create_surface(&window.surface) }?;
|
let surface = unsafe { self.context.instance.create_surface(window.surface.as_ref()) }?;
|
||||||
|
|
||||||
|
let size = window.surface.inner_size();
|
||||||
|
let surface_caps = surface.get_capabilities(&self.context.adapter);
|
||||||
|
println!("{:?}", surface_caps);
|
||||||
|
let surface_format = wgpu::TextureFormat::Bgra8Unorm;
|
||||||
|
let config = wgpu::SurfaceConfiguration {
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
format: surface_format,
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
present_mode: surface_caps.present_modes[0],
|
||||||
|
alpha_mode: surface_caps.alpha_modes[0],
|
||||||
|
view_formats: vec![],
|
||||||
|
};
|
||||||
|
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 })
|
||||||
}
|
}
|
||||||
@@ -404,6 +477,7 @@ impl gpu_executor::GpuExecutor for WgpuExecutor {
|
|||||||
impl WgpuExecutor {
|
impl WgpuExecutor {
|
||||||
pub async fn new() -> Option<Self> {
|
pub async fn new() -> Option<Self> {
|
||||||
let context = Context::new().await?;
|
let context = Context::new().await?;
|
||||||
|
println!("wgpu executor created");
|
||||||
|
|
||||||
let texture_bind_group_layout = context.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
let texture_bind_group_layout = context.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
entries: &[
|
entries: &[
|
||||||
@@ -512,7 +586,11 @@ impl WgpuExecutor {
|
|||||||
sampler,
|
sampler,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Self { context, render_configuration })
|
Some(Self {
|
||||||
|
context,
|
||||||
|
render_configuration,
|
||||||
|
surface_config: Cell::new(None),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||