diff --git a/Cargo.lock b/Cargo.lock index 24637bbd89..d06a58e46e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4549,6 +4549,7 @@ dependencies = [ "graphene-std", "interpreted-executor", "log", + "wgpu-executor", ] [[package]] @@ -7159,6 +7160,8 @@ dependencies = [ "futures", "glam", "graphene-application-io", + "graphic-types", + "log", "node-macro", "raster-types", "rendering", diff --git a/node-graph/graph-craft/src/proto.rs b/node-graph/graph-craft/src/proto.rs index 7cbc80b7a8..3284183cf3 100644 --- a/node-graph/graph-craft/src/proto.rs +++ b/node-graph/graph-craft/src/proto.rs @@ -344,7 +344,7 @@ impl ProtoNetwork { let (_, node) = &self.nodes[old_node_id.0 as usize]; let mut path = node.original_location.path.clone(); - log::debug!("Inserting context nullification after {:?} with context features: {:?}", node.identifier, context_deps); + // log::debug!("Inserting context nullification after {:?} with context features: {:?}", node.identifier, context_deps); // Add a path extension with a placeholder value which should not conflict with existing paths if let Some(p) = path.as_mut() { diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 50edcb76f7..3a44bf264b 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -4,7 +4,6 @@ use graph_craft::document::DocumentNode; use graph_craft::document::value::RenderOutput; use graph_craft::proto::{NodeConstructor, TypeErasedBox}; use graphene_std::Artboard; -use graphene_std::Context; use graphene_std::Graphic; use graphene_std::any::DynAnyNode; use graphene_std::application_io::{ImageTexture, SurfaceFrame}; @@ -25,6 +24,7 @@ use graphene_std::vector::Vector; use graphene_std::wasm_application_io::WasmEditorApi; #[cfg(feature = "gpu")] use graphene_std::wasm_application_io::WasmSurfaceHandle; +use graphene_std::{Context, ContextFeatures}; use graphene_std::{Cow, ProtoNodeIdentifier}; use graphene_std::{NodeIO, NodeIOTypes}; use graphene_std::{fn_type_fut, future}; @@ -45,9 +45,9 @@ fn node_registry() -> HashMap, to: Table), into_node!(from: Table, to: Table), - into_node!(from: Table>, to: Table>), - #[cfg(feature = "gpu")] - into_node!(from: Table>, to: Table>), + // into_node!(from: Table>, to: Table>), + // #[cfg(feature = "gpu")] + // into_node!(from: Table>, to: Table>), convert_node!(from: Table, to: Table), convert_node!(from: Table>, to: Table), #[cfg(feature = "gpu")] @@ -69,6 +69,8 @@ fn node_registry() -> HashMap>, to: Table>, converter: &WgpuExecutor), #[cfg(feature = "gpu")] convert_node!(from: Table>, to: Table>, converter: &WgpuExecutor), + #[cfg(feature = "gpu")] + convert_node!(from: Table, to: Table>, converter: &WgpuExecutor), // ============= // MONITOR NODES // ============= @@ -186,6 +188,8 @@ fn node_registry() -> HashMap, input: Context, fn_params: [Context => WgpuSurface]), #[cfg(feature = "gpu")] + async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => &WgpuExecutor]), + #[cfg(feature = "gpu")] async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table>]), async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Option]), async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Color]), @@ -396,7 +400,6 @@ mod node_registry_macros { ( ProtoNodeIdentifier::new(concat!["graphene_core::ops::ConvertNode<", stringify!($to), ">"]), |mut args| { - log::debug!("registering convert from {:?} to {:?} with {:?}", stringify!($from), stringify!($to), stringify!($convert)); Box::pin(async move { let mut args = args.drain(..); let node = graphene_std::ops::ConvertNode::new( @@ -417,7 +420,6 @@ mod node_registry_macros { ); let params = vec![fn_type_fut!(Context, $from), fn_type_fut!(Context, $convert)]; let node_io = NodeIO::<'_, Context>::to_async_node_io(&node, params); - // log::debug!("node io: {:?}", node_io); node_io }, ) diff --git a/node-graph/libraries/wgpu-executor/Cargo.toml b/node-graph/libraries/wgpu-executor/Cargo.toml index 046ff6ad99..96158a2e6d 100644 --- a/node-graph/libraries/wgpu-executor/Cargo.toml +++ b/node-graph/libraries/wgpu-executor/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Local dependencies core-types = { workspace = true } +graphic-types = { workspace = true } raster-types = { workspace = true, features = ["wgpu"] } graphene-application-io = { workspace = true, features = ["wgpu"] } rendering = { workspace = true } @@ -22,3 +23,4 @@ web-sys = { workspace = true } winit = { workspace = true } vello = { workspace = true } bytemuck = { workspace = true } +log = { workspace = true } diff --git a/node-graph/libraries/wgpu-executor/src/lib.rs b/node-graph/libraries/wgpu-executor/src/lib.rs index 868c73f60e..a02869cec1 100644 --- a/node-graph/libraries/wgpu-executor/src/lib.rs +++ b/node-graph/libraries/wgpu-executor/src/lib.rs @@ -1,6 +1,7 @@ mod context; pub mod shader_runtime; pub mod texture_conversion; +pub mod vector_to_raster; use crate::shader_runtime::ShaderRuntime; use anyhow::Result; diff --git a/node-graph/libraries/wgpu-executor/src/vector_to_raster.rs b/node-graph/libraries/wgpu-executor/src/vector_to_raster.rs new file mode 100644 index 0000000000..af237ef701 --- /dev/null +++ b/node-graph/libraries/wgpu-executor/src/vector_to_raster.rs @@ -0,0 +1,75 @@ +use crate::WgpuExecutor; +use core_types::ops::Convert; +use core_types::table::Table; +use core_types::transform::Footprint; +use graphic_types::Vector; +use graphic_types::raster_types::{GPU, Raster}; +use rendering::{Render, RenderOutputType, RenderParams}; +use wgpu::{CommandEncoderDescriptor, TextureFormat, TextureViewDescriptor}; + +/// Converts Table to Table> by rendering each vector to Vello scene and then to texture +impl<'i> Convert>, &'i WgpuExecutor> for Table { + async fn convert(self, footprint: Footprint, executor: &'i WgpuExecutor) -> Table> { + // Create render parameters for Vello rendering + let render_params = RenderParams { + render_mode: graphic_types::vector_types::vector::style::RenderMode::Normal, + hide_artboards: false, + for_export: false, + render_output_type: RenderOutputType::Vello, + footprint, + ..Default::default() + }; + log::debug!("rasterizing vector data with footprint {footprint:?}"); + + let vector = &self; + log::debug!("{:?}", vector); + + // Create a Vello scene for this vector + let mut scene = vello::Scene::new(); + let mut context = crate::RenderContext::default(); + + // Render the vector to the Vello scene with the row's transform + vector.render_to_vello(&mut scene, footprint.transform, &mut context, &render_params); + + // Render the scene to a GPU texture + let resolution = footprint.resolution; + let background = core_types::Color::BLACK; + + // Use async rendering to get the texture + let texture = async { + executor + .render_vello_scene_to_texture(&scene, resolution, &context, background) + .await + .expect("Failed to render Vello scene to texture") + }; + + // Block on the texture creation (we're in an async context) + let texture = futures::executor::block_on(texture); + let device = &executor.context.device; + let queue = &executor.context.queue; + let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor::default()); + let blitter = wgpu::util::TextureBlitter::new(device, TextureFormat::Rgba8UnormSrgb); + let view = texture.create_view(&TextureViewDescriptor::default()); + let new_texture = device.create_texture(&wgpu::wgt::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: texture.width(), + height: texture.height(), + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, + format: TextureFormat::Rgba8UnormSrgb, + view_formats: &[], + }); + let new_view = new_texture.create_view(&TextureViewDescriptor::default()); + + blitter.copy(device, &mut encoder, &view, &new_view); + let command_buffer = encoder.finish(); + queue.submit([command_buffer]); + + Table::new_from_element(Raster::new_gpu(new_texture)) + } +} diff --git a/node-graph/nodes/gstd/src/render_node.rs b/node-graph/nodes/gstd/src/render_node.rs index 462ee85985..671ded4005 100644 --- a/node-graph/nodes/gstd/src/render_node.rs +++ b/node-graph/nodes/gstd/src/render_node.rs @@ -9,6 +9,7 @@ use graphene_application_io::{ApplicationIo, ExportFormat, ImageTexture, RenderC use graphic_types::Artboard; use graphic_types::Graphic; use graphic_types::Vector; +use graphic_types::raster_types::GPU; use graphic_types::raster_types::Image; use graphic_types::raster_types::{CPU, Raster}; use rendering::{Render, RenderOutputType as RenderOutputTypeRequest, RenderParams, RenderSvgSegmentList, SvgRender, format_transform_matrix}; @@ -40,6 +41,7 @@ async fn render_intermediate<'a: 'n, T: 'static + Render + WasmNotSend + Send + Context -> Table, Context -> Table, Context -> Table>, + Context -> Table>, Context -> Table, Context -> Table, )] diff --git a/node-graph/preprocessor/Cargo.toml b/node-graph/preprocessor/Cargo.toml index 08d03c8524..41ccc03704 100644 --- a/node-graph/preprocessor/Cargo.toml +++ b/node-graph/preprocessor/Cargo.toml @@ -11,5 +11,6 @@ log = { workspace = true } # Workspace dependencies graphene-std = { workspace = true, features = ["gpu"] } +wgpu-executor = { workspace = true } graph-craft = { workspace = true } interpreted-executor = { workspace = true } diff --git a/node-graph/preprocessor/src/lib.rs b/node-graph/preprocessor/src/lib.rs index fa610eaffa..71dff3adbb 100644 --- a/node-graph/preprocessor/src/lib.rs +++ b/node-graph/preprocessor/src/lib.rs @@ -8,6 +8,7 @@ use graph_craft::{ProtoNodeIdentifier, concrete}; use graphene_std::registry::*; use graphene_std::*; use std::collections::{HashMap, HashSet}; +use wgpu_executor::WgpuExecutor; pub fn expand_network(network: &mut NodeNetwork, substitutions: &HashMap) { if network.generated { @@ -76,12 +77,20 @@ pub fn generate_node_substitutions() -> HashMap", input_ty.clone()).into(), }; + let mut context_features = ContextDependencies::default(); let proto_node = if into_node_registry.keys().any(|ident: &ProtoNodeIdentifier| ident.name.as_ref() == into_node_identifier.name.as_ref()) { generated_nodes += 1; into_node_identifier - } else if into_node_registry.keys().any(|ident| ident.name.as_ref() == convert_node_identifier.name.as_ref()) { + } else if let Some(impls) = into_node_registry.get(&convert_node_identifier) { generated_nodes += 1; - inputs.push(NodeInput::value(TaggedValue::None, false)); + let converter = match impls.keys().next() { + Some(node_io) if node_io.inputs[1].nested_type() == &concrete!(&WgpuExecutor) => { + context_features.extract |= ContextFeatures::FOOTPRINT; + NodeInput::scope("wgpu-executor") + } + _ => NodeInput::value(TaggedValue::None, false), + }; + inputs.push(converter); convert_node_identifier } else { identity_node.clone() @@ -93,6 +102,7 @@ pub fn generate_node_substitutions() -> HashMap