mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Refactor the node macro and simply most of the node implementations (#1942)
* Add support structure for new node macro to gcore * Fix compile issues and code generation * Implement new node_fn macro * Implement property translation * Fix NodeIO type generation * Start translating math nodes * Move node implementation to outer scope to allow usage of local imports * Add expose attribute to allow controlling the parameter exposure * Add rust analyzer support for #[implementations] attribute * Migrate logic nodes * Handle where clause properly * Implement argument ident pattern preservation * Implement adjustment layer mapping * Fix node registry types * Fix module paths * Improve demo artwork comptibility * Improve macro error reporting * Fix handling of impl node implementations * Fix nodeio type computation * Fix opacity node and graph type resolution * Fix loading of demo artworks * Fix eslint * Fix typo in macro test * Remove node definitions for Adjustment Nodes * Fix type alias property generation and make adjustments footprint aware * Convert vector nodes * Implement path overrides * Fix stroke node * Fix painted dreams * Implement experimental type level specialization * Fix poisson disk sampling -> all demo artworks should work again * Port text node + make node macro more robust by implementing lifetime substitution * Fix vector node tests * Fix red dress demo + ci * Fix clippy warnings * Code review * Fix primary input issues * Improve math nodes and audit others * Set no_properties when no automatic properties are derived * Port vector generator nodes (could not derive all definitions yet) * Various QA changes and add min/max/mode_range to number parameters * Add min and max for f64 and u32 * Convert gpu nodes and clean up unused nodes * Partially port transform node * Allow implementations on call arg * Port path modify node * Start porting graphic element nodes * Transform nodes in graphic_element.rs * Port brush node * Port nodes in wasm_executior * Rename node macro * Fix formatting * Fix Mandelbrot node * Formatting * Fix Load Image and Load Resource nodes, add scope input to node macro * Remove unnecessary underscores * Begin attemping to make nodes resolution-aware * Infer a generic manual compositon type on generic call arg * Various fixes and work towards merging * Final changes for merge! * Fix tests, probably * More free line removals! --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -4,30 +4,21 @@ use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::*;
|
||||
use graph_craft::proto::*;
|
||||
use graphene_core::application_io::ApplicationIo;
|
||||
use graphene_core::quantization::QuantizationChannels;
|
||||
use graphene_core::raster::*;
|
||||
use graphene_core::*;
|
||||
use wgpu_executor::{Bindgroup, PipelineLayout, Shader, ShaderIO, ShaderInput, WgpuExecutor, WgpuShaderInput};
|
||||
|
||||
use glam::{DAffine2, DVec2, Mat2, Vec2};
|
||||
|
||||
#[cfg(feature = "quantization")]
|
||||
use graphene_core::quantization::PackedPixel;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::wasm_application_io::WasmApplicationIo;
|
||||
|
||||
pub struct GpuCompiler<TypingContext, ShaderIO> {
|
||||
typing_context: TypingContext,
|
||||
io: ShaderIO,
|
||||
}
|
||||
|
||||
// TODO: Move to graph-craft
|
||||
#[node_macro::node_fn(GpuCompiler)]
|
||||
async fn compile_gpu(node: &'input DocumentNode, typing_context: TypingContext, io: ShaderIO) -> Result<compilation_client::Shader, String> {
|
||||
#[node_macro::node(category("Debug: GPU"))]
|
||||
async fn compile_gpu<'a: 'n>(_: (), node: &'a DocumentNode, typing_context: TypingContext, io: ShaderIO) -> Result<compilation_client::Shader, String> {
|
||||
let mut typing_context = typing_context;
|
||||
let compiler = graph_craft::graphene_compiler::Compiler {};
|
||||
let DocumentNodeImplementation::Network(ref network) = node.implementation else { panic!() };
|
||||
@@ -69,40 +60,22 @@ impl Clone for ComputePass {
|
||||
}
|
||||
}
|
||||
|
||||
#[node_macro::node_impl(MapGpuNode)]
|
||||
#[node_macro::old_node_impl(MapGpuNode)]
|
||||
async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, editor_api: &'a graphene_core::application_io::EditorApi<WasmApplicationIo>) -> ImageFrame<Color> {
|
||||
log::debug!("Executing gpu node");
|
||||
let executor = &editor_api.application_io.as_ref().and_then(|io| io.gpu_executor()).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 = "image-compare")]
|
||||
let img: image::DynamicImage = image::Rgba32FImage::from_raw(image.image.width, image.image.height, bytemuck::cast_vec(image.image.data.clone()))
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
#[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,
|
||||
base64_string: None,
|
||||
},
|
||||
transform: image.transform,
|
||||
alpha_blending: image.alpha_blending,
|
||||
};
|
||||
|
||||
// TODO: The cache should be based on the network topology not the node name
|
||||
let compute_pass_descriptor = if self.cache.lock().as_ref().unwrap().contains_key("placeholder") {
|
||||
self.cache.lock().as_ref().unwrap().get("placeholder").unwrap().clone()
|
||||
} else {
|
||||
let name = "placeholder".to_string();
|
||||
let Ok(compute_pass_descriptor) = create_compute_pass_descriptor(node, &image, executor, quantization).await else {
|
||||
let Ok(compute_pass_descriptor) = create_compute_pass_descriptor(node, &image, executor).await else {
|
||||
log::error!("Error creating compute pass descriptor in 'map_gpu()");
|
||||
return ImageFrame::empty();
|
||||
};
|
||||
@@ -122,13 +95,6 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
|
||||
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]);
|
||||
|
||||
@@ -161,32 +127,19 @@ impl<Node, EditorApi> MapGpuNode<Node, EditorApi> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
|
||||
node: DocumentNode,
|
||||
image: &ImageFrame<T>,
|
||||
executor: &&WgpuExecutor,
|
||||
quantization: QuantizationChannels,
|
||||
) -> Result<ComputePass, String> {
|
||||
async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(node: DocumentNode, image: &ImageFrame<T>, executor: &&WgpuExecutor) -> Result<ComputePass, String> {
|
||||
let compiler = graph_craft::graphene_compiler::Compiler {};
|
||||
let inner_network = NodeNetwork::value_network(node);
|
||||
|
||||
log::debug!("inner_network: {inner_network:?}");
|
||||
let network = NodeNetwork {
|
||||
#[cfg(feature = "quantization")]
|
||||
exports: vec![NodeInput::node(NodeId(5), 0)],
|
||||
#[cfg(not(feature = "quantization"))]
|
||||
exports: vec![NodeInput::node(NodeId(3), 0)],
|
||||
exports: vec![NodeInput::node(NodeId(2), 0)],
|
||||
nodes: [
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::Inline(InlineRust::new("i1[(_global_index.y * i0 + _global_index.x) as usize]".into(), concrete![Color]))],
|
||||
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::value::CopiedNode".into()),
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::network(concrete!(quantization::Quantization), 1)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into()),
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::network(concrete!(u32), 0)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into()),
|
||||
@@ -201,34 +154,19 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
|
||||
},*/
|
||||
/*
|
||||
DocumentNode {
|
||||
name: "GetNode".into(),
|
||||
name: "Get Node".into(),
|
||||
inputs: vec![NodeInput::node(NodeId(1), 0), NodeInput::node(NodeId(0), 0)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::storage::GetNode".into()),
|
||||
..Default::default()
|
||||
},*/
|
||||
#[cfg(feature = "quantization")]
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(1), 0)],
|
||||
implementation: DocumentNodeImplementation::proto("graphene_core::quantization::DeQuantizeNode"),
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
#[cfg(feature = "quantization")]
|
||||
inputs: vec![NodeInput::node(NodeId(3), 0)],
|
||||
#[cfg(not(feature = "quantization"))]
|
||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
||||
implementation: DocumentNodeImplementation::Network(inner_network),
|
||||
..Default::default()
|
||||
},
|
||||
#[cfg(feature = "quantization")]
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::node(NodeId(4), 0), NodeInput::node(NodeId(1), 0)],
|
||||
implementation: DocumentNodeImplementation::proto("graphene_core::quantization::QuantizeNode"),
|
||||
..Default::default()
|
||||
},
|
||||
/*
|
||||
DocumentNode {
|
||||
name: "SaveNode".into(),
|
||||
name: "Save Node".into(),
|
||||
inputs: vec![
|
||||
NodeInput::node(NodeId(5), 0),
|
||||
NodeInput::Inline(InlineRust::new(
|
||||
@@ -256,23 +194,11 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
|
||||
vec![concrete!(u32), concrete!(Color)],
|
||||
vec![concrete!(Color)],
|
||||
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![
|
||||
ShaderInput::UniformBuffer((), concrete!(u32)),
|
||||
ShaderInput::StorageBuffer((), concrete!(Color)),
|
||||
ShaderInput::OutputBuffer((), concrete!(Color)),
|
||||
],
|
||||
#[cfg(feature = "quantization")]
|
||||
output: ShaderInput::OutputBuffer((), concrete!(PackedPixel)),
|
||||
#[cfg(not(feature = "quantization"))]
|
||||
output: ShaderInput::OutputBuffer((), concrete!(Color)),
|
||||
},
|
||||
)
|
||||
@@ -281,6 +207,17 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
|
||||
// return ImageFrame::empty();
|
||||
let len: usize = image.image.data.len();
|
||||
|
||||
let storage_buffer = executor
|
||||
.create_storage_buffer(
|
||||
image.image.data.clone(),
|
||||
StorageBufferOptions {
|
||||
cpu_writable: false,
|
||||
gpu_writable: true,
|
||||
cpu_readable: false,
|
||||
storage: true,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
/*
|
||||
let canvas = editor_api.application_io.create_surface();
|
||||
|
||||
@@ -298,25 +235,7 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
|
||||
return frame;*/
|
||||
log::debug!("creating buffer");
|
||||
let width_uniform = executor.create_uniform_buffer(image.image.width).unwrap();
|
||||
#[cfg(not(feature = "quantization"))]
|
||||
core::hint::black_box(quantization);
|
||||
|
||||
#[cfg(feature = "quantization")]
|
||||
let quantization_uniform = executor.create_uniform_buffer(quantization).unwrap();
|
||||
let storage_buffer = executor
|
||||
.create_storage_buffer(
|
||||
image.image.data.clone(),
|
||||
StorageBufferOptions {
|
||||
cpu_writable: false,
|
||||
gpu_writable: true,
|
||||
cpu_readable: false,
|
||||
storage: true,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let width_uniform = Arc::new(width_uniform);
|
||||
#[cfg(feature = "quantization")]
|
||||
let quantization_uniform = Arc::new(quantization_uniform);
|
||||
let storage_buffer = Arc::new(storage_buffer);
|
||||
let output_buffer = executor.create_output_buffer(len, concrete!(Color), false).unwrap();
|
||||
let output_buffer = Arc::new(output_buffer);
|
||||
@@ -324,10 +243,7 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
|
||||
let readback_buffer = Arc::new(readback_buffer);
|
||||
log::debug!("created buffer");
|
||||
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, storage_buffer],
|
||||
buffers: vec![width_uniform.into(), storage_buffer],
|
||||
};
|
||||
|
||||
let shader = Shader {
|
||||
@@ -351,72 +267,9 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
|
||||
readback_buffer: Some(readback_buffer),
|
||||
})
|
||||
}
|
||||
/*
|
||||
#[node_macro::node_fn(MapGpuNode)]
|
||||
async fn map_gpu(inputs: Vec<ShaderInput<<NewExecutor as GpuExecutor>::BufferHandle>>, shader: &'any_input compilation_client::Shader) {
|
||||
use graph_craft::executor::Executor;
|
||||
let executor = NewExecutor::new().unwrap();
|
||||
for input in shader.io.inputs.iter() {
|
||||
let buffer = executor.create_storage_buffer(&self, data, options)
|
||||
let buffer = executor.create_buffer(input.size).unwrap();
|
||||
executor.write_buffer(buffer, input.data).unwrap();
|
||||
}
|
||||
todo!();
|
||||
/*
|
||||
let executor: GpuExecutor = GpuExecutor::new(Context::new().await.unwrap(), shader.into(), "gpu::eval".into()).unwrap();
|
||||
let data: Vec<_> = input.into_iter().collect();
|
||||
let result = executor.execute(Box::new(data)).unwrap();
|
||||
let result = dyn_any::downcast::<Vec<_O>>(result).unwrap();
|
||||
*result
|
||||
*/
|
||||
}
|
||||
|
||||
pub struct MapGpuSingleImageNode<N> {
|
||||
node: N,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(MapGpuSingleImageNode)]
|
||||
fn map_gpu_single_image(input: Image<Color>, node: String) -> Image<Color> {
|
||||
use graph_craft::document::*;
|
||||
use graph_craft::ProtoNodeIdentifier;
|
||||
|
||||
let identifier = ProtoNodeIdentifier { name: std::borrow::Cow::Owned(node) };
|
||||
|
||||
let network = NodeNetwork {
|
||||
inputs: vec![NodeId(0)],
|
||||
disabled: vec![],
|
||||
previous_outputs: None,
|
||||
outputs: vec![NodeInput::node(NodeId(0), 0)],
|
||||
nodes: [(
|
||||
NodeId(0),
|
||||
DocumentNode {
|
||||
name: "Image Filter".into(),
|
||||
inputs: vec![NodeInput::Network(concrete!(Color))],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(identifier),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
..Default::default()
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let value_network = ValueNode::new(network);
|
||||
let map_node = MapGpuNode::new(value_network);
|
||||
let data = map_node.eval(input.data.clone());
|
||||
Image { data, ..input }
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BlendGpuImageNode<Background, B, O> {
|
||||
background: Background,
|
||||
blend_mode: B,
|
||||
opacity: O,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(BlendGpuImageNode)]
|
||||
async fn blend_gpu_image(foreground: ImageFrame<Color>, background: ImageFrame<Color>, blend_mode: BlendMode, opacity: f64) -> ImageFrame<Color> {
|
||||
#[node_macro::node(category("Debug: GPU"))]
|
||||
async fn blend_gpu_image(_: (), foreground: ImageFrame<Color>, background: ImageFrame<Color>, blend_mode: BlendMode, opacity: f64) -> ImageFrame<Color> {
|
||||
let foreground_size = DVec2::new(foreground.image.width as f64, foreground.image.height as f64);
|
||||
let background_size = DVec2::new(background.image.width as f64, background.image.height as f64);
|
||||
// Transforms a point from the background image to the foreground image
|
||||
|
||||
Reference in New Issue
Block a user