diff --git a/Cargo.lock b/Cargo.lock index 4dc1552d..6de83f14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,11 +266,11 @@ checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" [[package]] name = "ash" -version = "0.37.3+1.3.251" +version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading 0.7.4", + "libloading", ] [[package]] @@ -600,18 +600,18 @@ dependencies = [ [[package]] name = "bit-set" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" [[package]] name = "bitflags" @@ -1240,12 +1240,12 @@ checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] name = "d3d12" -version = "0.20.0" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" +checksum = "bdbd1f579714e3c809ebd822c81ef148b1ceaeb3d535352afc73fd0c4c6a0017" dependencies = [ "bitflags 2.6.0", - "libloading 0.8.5", + "libloading", "winapi", ] @@ -1378,7 +1378,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.5", + "libloading", ] [[package]] @@ -2178,9 +2178,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" dependencies = [ "gl_generator", ] @@ -2217,9 +2217,9 @@ dependencies = [ [[package]] name = "gpu-allocator" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7" dependencies = [ "log", "presser", @@ -2361,6 +2361,7 @@ dependencies = [ "vello", "wasm-bindgen", "web-sys", + "wgpu", ] [[package]] @@ -2631,7 +2632,7 @@ dependencies = [ "bitflags 2.6.0", "com", "libc", - "libloading 0.8.5", + "libloading", "thiserror", "widestring", "winapi", @@ -3222,7 +3223,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.5", + "libloading", "pkg-config", ] @@ -3268,16 +3269,6 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "libloading" version = "0.8.5" @@ -3485,9 +3476,9 @@ dependencies = [ [[package]] name = "metal" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ "bitflags 2.6.0", "block", @@ -3501,11 +3492,10 @@ dependencies = [ [[package]] name = "meval" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9" +source = "git+https://github.com/Titaniumtown/meval-rs#6bf579fd402928745cf4f24e5c975bece3285179" dependencies = [ "fnv", - "nom 1.2.4", + "nom", ] [[package]] @@ -3543,18 +3533,18 @@ dependencies = [ [[package]] name = "naga" -version = "0.20.0" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" dependencies = [ "arrayvec", "bit-set", "bitflags 2.6.0", + "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", "indexmap 2.2.6", "log", - "num-traits", "petgraph", "rustc-hash 1.1.0", "spirv", @@ -3734,12 +3724,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "nom" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" - [[package]] name = "nom" version = "7.1.3" @@ -6715,7 +6699,7 @@ dependencies = [ "fnv", "home", "memchr", - "nom 7.1.3", + "nom", "once_cell", "petgraph", ] @@ -6919,9 +6903,8 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vello" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "861c12258ed7e72762765e2c88a07bb528040ec4e5f87514d65b19b29a7cccf0" +version = "0.2.0" +source = "git+https://github.com/linebender/vello#5d56854ff871ee98dc53cdf8a0e3f7ec628cb90f" dependencies = [ "bytemuck", "futures-intrusive", @@ -6938,9 +6921,8 @@ dependencies = [ [[package]] name = "vello_encoding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d73777327877fa824a45c7195f850390dd3f91feb15f47d331db1fc01abf6d" +version = "0.2.0" +source = "git+https://github.com/linebender/vello#5d56854ff871ee98dc53cdf8a0e3f7ec628cb90f" dependencies = [ "bytemuck", "guillotiere", @@ -6951,9 +6933,8 @@ dependencies = [ [[package]] name = "vello_shaders" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13ab6bcb2b079c3cf57e964d1ba0b1f08901284be1c7f5cba34d3e0e08154bce" +version = "0.2.0" +source = "git+https://github.com/linebender/vello#5d56854ff871ee98dc53cdf8a0e3f7ec628cb90f" dependencies = [ "bytemuck", "naga", @@ -7340,12 +7321,11 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "0.20.1" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e37c7b9921b75dfd26dd973fdcbce36f13dfa6e2dc82aece584e0ed48c355c" +checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433" dependencies = [ "arrayvec", - "cfg-if", "cfg_aliases 0.1.1", "document-features", "js-sys", @@ -7366,16 +7346,15 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.21.1" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" +checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a" dependencies = [ "arrayvec", "bit-vec", "bitflags 2.6.0", "bytemuck", "cfg_aliases 0.1.1", - "codespan-reporting", "document-features", "indexmap 2.2.6", "log", @@ -7387,7 +7366,6 @@ dependencies = [ "rustc-hash 1.1.0", "smallvec", "thiserror", - "web-sys", "wgpu-hal", "wgpu-types", ] @@ -7420,9 +7398,9 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "0.21.1" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" +checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" dependencies = [ "android_system_properties", "arrayvec", @@ -7442,7 +7420,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.5", + "libloading", "log", "metal", "naga", @@ -7465,9 +7443,9 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.20.0" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" +checksum = "bc9d91f0e2c4b51434dfa6db77846f2793149d8e73f800fa2e41f52b8eac3c5d" dependencies = [ "bitflags 2.6.0", "js-sys", @@ -8124,7 +8102,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading 0.8.5", + "libloading", "once_cell", "rustix", "x11rb-protocol", diff --git a/Cargo.toml b/Cargo.toml index 9d225b74..45a72550 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,8 +56,8 @@ chrono = "0.4" ron = "0.8" fastnoise-lite = "1.1" spirv-std = { git = "https://github.com/GraphiteEditor/rust-gpu.git" } -wgpu-types = "0.20" -wgpu = { version = "0.20", features = ["strict_asserts", "api_log_info"] } +wgpu-types = "22" +wgpu = { version = "22.1", features = ["strict_asserts"] } once_cell = "1.13" # Remove when `core::cell::LazyCell` () is stabilized in Rust 1.80 and we bump our MSRV wasm-bindgen = "=0.2.92" # NOTICE: ensure this stays in sync with the `wasm-bindgen-cli` version in `website/content/volunteer/guide/getting-started/_index.md`. We pin this version because wasm-bindgen upgrades may break various things. wasm-bindgen-futures = "0.4" @@ -66,7 +66,7 @@ web-sys = "=0.3.69" winit = "0.29" url = "2.5" tokio = { version = "1.29", features = ["fs", "io-std"] } -vello = "0.2" +vello = { git = "https://github.com/linebender/vello" } resvg = "0.42" usvg = "0.42" rand = { version = "0.8", default-features = false } @@ -92,6 +92,9 @@ syn = { version = "2.0", default-features = false, features = [ ] } kurbo = { version = "0.11.0", features = ["serde"] } +[patch.crates-io] +meval = { git = "https://github.com/Titaniumtown/meval-rs" } + [profile.dev] opt-level = 1 @@ -110,6 +113,7 @@ syn = { opt-level = 3 } [profile.release] lto = "thin" +debug = true [profile.profiling] inherits = "release" diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/document_node_types.rs index b33bac64..67d8298a 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_types.rs @@ -2099,7 +2099,7 @@ fn static_nodes() -> Vec { ..Default::default() }, DocumentNode { - manual_composition: Some(concrete!(())), + manual_composition: Some(concrete!(Footprint)), inputs: vec![NodeInput::node(NodeId(1), 0)], implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode<_, _, _>")), ..Default::default() diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 4bcf73ea..070bd317 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -385,11 +385,12 @@ pub async fn introspect_node(path: &[NodeId]) -> Option> None } -pub async fn run_node_graph() { - let Some(mut runtime) = NODE_RUNTIME.try_lock() else { return }; +pub async fn run_node_graph() -> bool { + let Some(mut runtime) = NODE_RUNTIME.try_lock() else { return false }; if let Some(ref mut runtime) = runtime.as_mut() { runtime.run().await; } + true } pub async fn replace_node_runtime(runtime: NodeRuntime) -> Option { diff --git a/frontend/wasm/Cargo.toml b/frontend/wasm/Cargo.toml index 9b4ea8f9..3003dfd3 100644 --- a/frontend/wasm/Cargo.toml +++ b/frontend/wasm/Cargo.toml @@ -52,8 +52,6 @@ web-sys = { workspace = true, features = [ # Optional workspace dependencies ron = { workspace = true, optional = true } -[profile.release] -debug = true [package.metadata.wasm-pack.profile.dev] wasm-opt = false diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 9e83369b..95b21b65 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -948,10 +948,10 @@ fn set_timeout(f: &Closure, delay: Duration) { } /// Provides access to the `Editor` by calling the given closure with it as an argument. -fn editor(callback: impl FnOnce(&mut editor::application::Editor) -> T) -> T { +fn editor(callback: impl FnOnce(&mut editor::application::Editor) -> T) -> T { EDITOR.with(|editor| { - let mut guard = editor.lock(); - let Ok(Some(ref mut editor)) = guard.as_deref_mut() else { panic!("Failed to borrow the editor") }; + let mut guard = editor.try_lock(); + let Ok(Some(ref mut editor)) = guard.as_deref_mut() else { return T::default() }; callback(editor) }) @@ -961,7 +961,7 @@ fn editor(callback: impl FnOnce(&mut editor::application::Editor) -> T) -> T pub(crate) fn editor_and_handle(mut callback: impl FnMut(&mut Editor, &mut EditorHandle)) { EDITOR_HANDLE.with(|editor_handle| { editor(|editor| { - let mut guard = editor_handle.lock(); + let mut guard = editor_handle.try_lock(); let Ok(Some(ref mut editor_handle)) = guard.as_deref_mut() else { log::error!("Failed to borrow editor handle"); return; @@ -979,7 +979,9 @@ async fn poll_node_graph_evaluation() { return; } - editor::node_graph_executor::run_node_graph().await; + if !editor::node_graph_executor::run_node_graph().await { + return; + }; editor_and_handle(|editor, handle| { let mut messages = VecDeque::new(); diff --git a/node-graph/gcore/Cargo.toml b/node-graph/gcore/Cargo.toml index 5b331142..abde8e32 100644 --- a/node-graph/gcore/Cargo.toml +++ b/node-graph/gcore/Cargo.toml @@ -14,7 +14,8 @@ nightly = [] alloc = ["dyn-any", "bezier-rs"] type_id_logging = [] wasm = ["web-sys"] -vello = ["dep:vello", "bezier-rs/kurbo"] +wgpu = ["dep:wgpu"] +vello = ["dep:vello", "bezier-rs/kurbo", "wgpu"] std = [ "dyn-any", "dyn-any/std", @@ -59,6 +60,7 @@ bezier-rs = { workspace = true, optional = true } kurbo = { workspace = true, optional = true } base64 = { workspace = true, optional = true } vello = { workspace = true, optional = true } +wgpu = { workspace = true, optional = true } specta = { workspace = true, optional = true } rustybuzz = { workspace = true, optional = true } wasm-bindgen = { workspace = true, optional = true } diff --git a/node-graph/gcore/src/application_io.rs b/node-graph/gcore/src/application_io.rs index fa71fbfd..91c84dee 100644 --- a/node-graph/gcore/src/application_io.rs +++ b/node-graph/gcore/src/application_io.rs @@ -63,6 +63,55 @@ impl Size for web_sys::HtmlCanvasElement { } } +#[derive(Debug, Clone)] +pub struct TextureFrame { + #[cfg(feature = "wgpu")] + pub texture: Arc, + #[cfg(not(feature = "wgpu"))] + pub texture: (), + pub transform: DAffine2, +} + +impl Hash for TextureFrame { + fn hash(&self, state: &mut H) { + self.transform.to_cols_array().iter().for_each(|x| x.to_bits().hash(state)); + #[cfg(feature = "wgpu")] + self.texture.global_id().hash(state); + } +} + +impl PartialEq for TextureFrame { + fn eq(&self, other: &Self) -> bool { + #[cfg(feature = "wgpu")] + return self.transform.eq(&other.transform) && self.texture.global_id() == other.texture.global_id(); + + #[cfg(not(feature = "wgpu"))] + self.transform.eq(&other.transform) + } +} + +impl Transform for TextureFrame { + fn transform(&self) -> DAffine2 { + self.transform + } +} +impl TransformMut for TextureFrame { + fn transform_mut(&mut self) -> &mut DAffine2 { + &mut self.transform + } +} + +unsafe impl StaticType for TextureFrame { + type Static = TextureFrame; +} + +#[cfg(feature = "wgpu")] +impl Size for TextureFrame { + fn size(&self) -> UVec2 { + UVec2::new(self.texture.width(), self.texture.height()) + } +} + impl From> for SurfaceFrame { fn from(x: SurfaceHandleFrame) -> Self { Self { diff --git a/node-graph/gcore/src/graphic_element.rs b/node-graph/gcore/src/graphic_element.rs index 1842b831..1a4aad31 100644 --- a/node-graph/gcore/src/graphic_element.rs +++ b/node-graph/gcore/src/graphic_element.rs @@ -1,15 +1,14 @@ -use crate::application_io::SurfaceHandleFrame; +use crate::application_io::TextureFrame; use crate::raster::{BlendMode, ImageFrame}; -use crate::transform::Footprint; +use crate::transform::{Footprint, Transform, TransformMut}; use crate::vector::VectorData; -use crate::{Color, Node, SurfaceFrame}; +use crate::{Color, Node}; use dyn_any::{DynAny, StaticType}; use node_macro::node_fn; use core::ops::{Deref, DerefMut}; -use glam::{DAffine2, IVec2, UVec2}; -use web_sys::HtmlCanvasElement; +use glam::{DAffine2, IVec2}; pub mod renderer; @@ -65,10 +64,7 @@ pub enum GraphicElement { GraphicGroup(GraphicGroup), /// A vector shape, equivalent to the SVG tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path VectorData(Box), - /// A bitmap image with a finite position and extent, equivalent to the SVG tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image - ImageFrame(ImageFrame), - /// A Canvas element - Surface(SurfaceFrame), + Raster(Raster), } // TODO: Can this be removed? It doesn't necessarily make that much sense to have a default when, instead, the entire GraphicElement just shouldn't exist if there's no specific content to assign it. @@ -78,6 +74,58 @@ impl Default for GraphicElement { } } +#[derive(Clone, Debug, Hash, PartialEq, DynAny)] +pub enum Raster { + /// A bitmap image with a finite position and extent, equivalent to the SVG tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image + ImageFrame(ImageFrame), + Texture(TextureFrame), +} + +impl<'de> serde::Deserialize<'de> for Raster { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let frame = ImageFrame::deserialize(deserializer)?; + Ok(Raster::ImageFrame(frame)) + } +} + +impl serde::Serialize for Raster { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Raster::ImageFrame(_) => self.serialize(serializer), + Raster::Texture(_) => todo!(), + } + } +} + +impl Transform for Raster { + fn transform(&self) -> DAffine2 { + match self { + Raster::ImageFrame(frame) => frame.transform(), + Raster::Texture(frame) => frame.transform(), + } + } + fn local_pivot(&self, pivot: glam::DVec2) -> glam::DVec2 { + match self { + Raster::ImageFrame(frame) => frame.local_pivot(pivot), + Raster::Texture(frame) => frame.local_pivot(pivot), + } + } +} +impl TransformMut for Raster { + fn transform_mut(&mut self) -> &mut DAffine2 { + match self { + Raster::ImageFrame(frame) => frame.transform_mut(), + Raster::Texture(frame) => frame.transform_mut(), + } + } +} + /// Some [`ArtboardData`] with some optional clipping bounds that can be exported. /// Similar to an Inkscape page: https://media.inkscape.org/media/doc/release_notes/1.2/Inkscape_1.2.html#Page_tool #[derive(Clone, Debug, Hash, PartialEq, DynAny)] @@ -202,7 +250,12 @@ async fn add_artboard + Send>(footprint: Footprint, artboar impl From> for GraphicElement { fn from(image_frame: ImageFrame) -> Self { - GraphicElement::ImageFrame(image_frame) + GraphicElement::Raster(Raster::ImageFrame(image_frame)) + } +} +impl From for GraphicElement { + fn from(texture: TextureFrame) -> Self { + GraphicElement::Raster(Raster::Texture(texture)) } } impl From for GraphicElement { @@ -215,39 +268,6 @@ impl From for GraphicElement { GraphicElement::GraphicGroup(graphic_group) } } -impl From for GraphicElement { - fn from(surface: SurfaceFrame) -> Self { - GraphicElement::Surface(surface) - } -} -impl From>> for GraphicElement { - fn from(surface: alloc::sync::Arc>) -> Self { - let surface_id = surface.surface_handle.window_id; - let transform = surface.transform; - GraphicElement::Surface(SurfaceFrame { - surface_id, - transform, - resolution: UVec2 { - x: surface.surface_handle.surface.width(), - y: surface.surface_handle.surface.height(), - }, - }) - } -} -impl From> for GraphicElement { - fn from(surface: SurfaceHandleFrame) -> Self { - let surface_id = surface.surface_handle.window_id; - let transform = surface.transform; - GraphicElement::Surface(SurfaceFrame { - surface_id, - transform, - resolution: UVec2 { - x: surface.surface_handle.surface.width(), - y: surface.surface_handle.surface.height(), - }, - }) - } -} impl Deref for GraphicGroup { type Target = Vec; diff --git a/node-graph/gcore/src/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index 2182e246..481be53a 100644 --- a/node-graph/gcore/src/graphic_element/renderer.rs +++ b/node-graph/gcore/src/graphic_element/renderer.rs @@ -3,13 +3,12 @@ mod rect; pub use quad::Quad; pub use rect::Rect; -use crate::raster::bbox::Bbox; use crate::raster::{BlendMode, Image, ImageFrame}; use crate::transform::Transform; use crate::uuid::generate_uuid; use crate::vector::style::{Fill, Stroke, ViewMode}; use crate::vector::PointId; -use crate::SurfaceFrame; +use crate::Raster; use crate::{vector::VectorData, Artboard, Color, GraphicElement, GraphicGroup}; use bezier_rs::Subpath; @@ -219,6 +218,12 @@ pub enum ImageRenderMode { Base64, } +#[derive(Clone, Debug, Default)] +pub struct RenderContext { + #[cfg(feature = "wgpu")] + pub ressource_overrides: std::collections::HashMap>, +} + /// Static state used whilst rendering #[derive(Default)] pub struct RenderParams { @@ -268,13 +273,13 @@ pub trait GraphicElementRendered { fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]>; fn add_click_targets(&self, click_targets: &mut Vec); #[cfg(feature = "vello")] - fn to_vello_scene(&self, transform: DAffine2) -> Scene { + fn to_vello_scene(&self, transform: DAffine2, context: &mut RenderContext) -> Scene { let mut scene = vello::Scene::new(); - self.render_to_vello(&mut scene, transform); + self.render_to_vello(&mut scene, transform, context); scene } #[cfg(feature = "vello")] - fn render_to_vello(&self, _scene: &mut Scene, _transform: DAffine2) {} + fn render_to_vello(&self, _scene: &mut Scene, _transform: DAffine2, _render_condext: &mut RenderContext) {} fn contains_artboard(&self) -> bool { false @@ -323,7 +328,7 @@ impl GraphicElementRendered for GraphicGroup { } #[cfg(feature = "vello")] - fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2) { + fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext) { let child_transform = transform * self.transform; let Some(bounds) = self.bounding_box(transform) else { return }; @@ -340,7 +345,7 @@ impl GraphicElementRendered for GraphicGroup { ); } for element in self.iter() { - element.render_to_vello(scene, child_transform); + element.render_to_vello(scene, child_transform, context); } if layer { scene.pop_layer(); @@ -406,7 +411,7 @@ impl GraphicElementRendered for VectorData { } #[cfg(feature = "vello")] - fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2) { + fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, _: &mut RenderContext) { use crate::vector::style::GradientType; use vello::peniko; @@ -593,7 +598,7 @@ impl GraphicElementRendered for Artboard { } #[cfg(feature = "vello")] - fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2) { + fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext) { use vello::peniko; // Render background @@ -608,7 +613,7 @@ impl GraphicElementRendered for Artboard { if self.clip { scene.push_layer(blend_mode, 1., kurbo::Affine::new(transform.to_cols_array()), &rect); } - self.graphic_group.render_to_vello(scene, transform); + self.graphic_group.render_to_vello(scene, transform, context); if self.clip { scene.pop_layer(); } @@ -643,9 +648,9 @@ impl GraphicElementRendered for crate::ArtboardGroup { } #[cfg(feature = "vello")] - fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2) { + fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext) { for artboard in &self.artboards { - artboard.render_to_vello(scene, transform) + artboard.render_to_vello(scene, transform, context) } } @@ -654,42 +659,6 @@ impl GraphicElementRendered for crate::ArtboardGroup { } } -impl GraphicElementRendered for SurfaceFrame { - fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { - let transform = self.transform; - - let (width, height) = (transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length()); - - let matrix = format_transform_matrix(transform * DAffine2::from_scale((width, height).into()).inverse()); - let transform = if matrix.is_empty() { String::new() } else { format!(r#" transform="{}""#, matrix) }; - - let canvas = format!( - r#"
"#, - width.abs(), - height.abs(), - self.surface_id - ); - render.svg.push(canvas.into()) - } - - #[cfg(feature = "vello")] - fn render_to_vello(&self, _scene: &mut Scene, _transform: DAffine2) { - todo!() - } - - fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> { - let bbox = Bbox::from_transform(transform); - let aabb = bbox.to_axis_aligned_bbox(); - Some([aabb.start, aabb.end]) - } - - fn add_click_targets(&self, _click_targets: &mut Vec) {} - - fn contains_artboard(&self) -> bool { - false - } -} - impl GraphicElementRendered for ImageFrame { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { let transform = self.transform * render.transform; @@ -737,7 +706,7 @@ impl GraphicElementRendered for ImageFrame { } #[cfg(feature = "vello")] - fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2) { + fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, _: &mut RenderContext) { use vello::peniko; let image = &self.image; @@ -756,51 +725,135 @@ impl GraphicElementRendered for ImageFrame { scene.draw_image(&image, vello::kurbo::Affine::new(transform.to_cols_array())); } } +impl GraphicElementRendered for Raster { + fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { + let transform = self.transform() * render.transform; + + match render_params.image_render_mode { + ImageRenderMode::Base64 => { + let image = match self { + Raster::ImageFrame(ref image) => image, + Raster::Texture(_) => return, + }; + let (image, blending) = (&image.image, image.alpha_blending); + if image.data.is_empty() { + return; + } + + let base64_string = image.base64_string.clone().unwrap_or_else(|| { + let output = image.to_png(); + let preamble = "data:image/png;base64,"; + let mut base64_string = String::with_capacity(preamble.len() + output.len() * 4); + base64_string.push_str(preamble); + base64::engine::general_purpose::STANDARD.encode_string(output, &mut base64_string); + base64_string + }); + render.leaf_tag("image", |attributes| { + attributes.push("width", 1.to_string()); + attributes.push("height", 1.to_string()); + attributes.push("preserveAspectRatio", "none"); + attributes.push("href", base64_string); + let matrix = format_transform_matrix(transform); + if !matrix.is_empty() { + attributes.push("transform", matrix); + } + if blending.blend_mode != BlendMode::default() { + attributes.push("style", blending.blend_mode.render()); + } + }); + } + } + } + + fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> { + let transform = self.transform() * transform; + (transform.matrix2 != glam::DMat2::ZERO).then(|| (transform * Quad::from_box([DVec2::ZERO, DVec2::ONE])).bounding_box()) + } + + fn add_click_targets(&self, click_targets: &mut Vec) { + let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE); + click_targets.push(ClickTarget::new(subpath, 0.)); + } + + #[cfg(feature = "vello")] + fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext) { + use vello::peniko; + + match self { + Raster::ImageFrame(image_frame) => { + let image = &image_frame.image; + if image.data.is_empty() { + return; + } + let image = vello::peniko::Image { + data: image.to_flat_u8().0.into(), + width: image.width, + height: image.height, + format: peniko::Format::Rgba8, + extend: peniko::Extend::Repeat, + }; + let transform = transform * self.transform() * DAffine2::from_scale(1. / DVec2::new(image.width as f64, image.height as f64)); + + scene.draw_image(&image, vello::kurbo::Affine::new(transform.to_cols_array())); + } + Raster::Texture(texture) => { + let image = vello::peniko::Image { + data: vec![].into(), + width: texture.texture.width(), + height: texture.texture.height(), + format: peniko::Format::Rgba8, + extend: peniko::Extend::Repeat, + }; + let id = image.data.id(); + context.ressource_overrides.insert(id, texture.texture.clone()); + + let transform = transform * self.transform() * DAffine2::from_scale(1. / DVec2::new(image.width as f64, image.height as f64)); + + scene.draw_image(&image, vello::kurbo::Affine::new(transform.to_cols_array())); + } + }; + } +} impl GraphicElementRendered for GraphicElement { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { match self { GraphicElement::VectorData(vector_data) => vector_data.render_svg(render, render_params), - GraphicElement::ImageFrame(image_frame) => image_frame.render_svg(render, render_params), + GraphicElement::Raster(raster) => raster.render_svg(render, render_params), GraphicElement::GraphicGroup(graphic_group) => graphic_group.render_svg(render, render_params), - GraphicElement::Surface(surface) => surface.render_svg(render, render_params), } } fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> { match self { GraphicElement::VectorData(vector_data) => GraphicElementRendered::bounding_box(&**vector_data, transform), - GraphicElement::ImageFrame(image_frame) => image_frame.bounding_box(transform), + GraphicElement::Raster(raster) => raster.bounding_box(transform), GraphicElement::GraphicGroup(graphic_group) => graphic_group.bounding_box(transform), - GraphicElement::Surface(surface) => surface.bounding_box(transform), } } fn add_click_targets(&self, click_targets: &mut Vec) { match self { GraphicElement::VectorData(vector_data) => vector_data.add_click_targets(click_targets), - GraphicElement::ImageFrame(image_frame) => image_frame.add_click_targets(click_targets), + GraphicElement::Raster(raster) => raster.add_click_targets(click_targets), GraphicElement::GraphicGroup(graphic_group) => graphic_group.add_click_targets(click_targets), - GraphicElement::Surface(surface) => surface.add_click_targets(click_targets), } } #[cfg(feature = "vello")] - fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2) { + fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext) { match self { - GraphicElement::VectorData(vector_data) => vector_data.render_to_vello(scene, transform), - GraphicElement::ImageFrame(image_frame) => image_frame.render_to_vello(scene, transform), - GraphicElement::GraphicGroup(graphic_group) => graphic_group.render_to_vello(scene, transform), - GraphicElement::Surface(surface) => surface.render_to_vello(scene, transform), + GraphicElement::VectorData(vector_data) => vector_data.render_to_vello(scene, transform, context), + GraphicElement::GraphicGroup(graphic_group) => graphic_group.render_to_vello(scene, transform, context), + GraphicElement::Raster(raster) => raster.render_to_vello(scene, transform, context), } } fn contains_artboard(&self) -> bool { match self { GraphicElement::VectorData(vector_data) => vector_data.contains_artboard(), - GraphicElement::ImageFrame(image_frame) => image_frame.contains_artboard(), GraphicElement::GraphicGroup(graphic_group) => graphic_group.contains_artboard(), - GraphicElement::Surface(surface) => surface.contains_artboard(), + GraphicElement::Raster(raster) => raster.contains_artboard(), } } } diff --git a/node-graph/gcore/src/transform.rs b/node-graph/gcore/src/transform.rs index 7d387362..05284d49 100644 --- a/node-graph/gcore/src/transform.rs +++ b/node-graph/gcore/src/transform.rs @@ -65,17 +65,15 @@ impl Transform for GraphicElement { fn transform(&self) -> DAffine2 { match self { GraphicElement::VectorData(vector_shape) => vector_shape.transform(), - GraphicElement::ImageFrame(image_frame) => image_frame.transform(), GraphicElement::GraphicGroup(graphic_group) => graphic_group.transform(), - GraphicElement::Surface(surface) => surface.transform(), + GraphicElement::Raster(raster) => raster.transform(), } } fn local_pivot(&self, pivot: DVec2) -> DVec2 { match self { GraphicElement::VectorData(vector_shape) => vector_shape.local_pivot(pivot), - GraphicElement::ImageFrame(image_frame) => image_frame.local_pivot(pivot), GraphicElement::GraphicGroup(graphic_group) => graphic_group.local_pivot(pivot), - GraphicElement::Surface(surface) => surface.local_pivot(pivot), + GraphicElement::Raster(raster) => raster.local_pivot(pivot), } } } @@ -83,9 +81,8 @@ impl TransformMut for GraphicElement { fn transform_mut(&mut self) -> &mut DAffine2 { match self { GraphicElement::VectorData(vector_shape) => vector_shape.transform_mut(), - GraphicElement::ImageFrame(image_frame) => image_frame.transform_mut(), GraphicElement::GraphicGroup(graphic_group) => graphic_group.transform_mut(), - GraphicElement::Surface(surface) => surface.transform_mut(), + GraphicElement::Raster(raster) => raster.transform_mut(), } } } diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 9e380b47..8e4c7ddc 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -199,7 +199,7 @@ pub struct MaskImageNode { fn mask_image< // _P is the color of the input image. It must have an alpha channel because that is going to // be modified by the mask - _P: Copy + Alpha, + _P: Alpha, // _S is the color of the stencil. It must have a luminance channel because that is used to // mask the input image _S: Luminance, diff --git a/node-graph/gstd/src/vector.rs b/node-graph/gstd/src/vector.rs index 339ab585..34d7376b 100644 --- a/node-graph/gstd/src/vector.rs +++ b/node-graph/gstd/src/vector.rs @@ -74,8 +74,7 @@ fn boolean_operation_node(graphic_group: GraphicGroup, boolean_operation: Boolea let vector_data = collect_vector_data(graphic_group); boolean_operation_on_vector_data(&vector_data, BooleanOperation::Union) } - GraphicElement::ImageFrame(image) => vector_from_image(image), - GraphicElement::Surface(image) => vector_from_image(image), + GraphicElement::Raster(image) => vector_from_image(image), } } diff --git a/node-graph/gstd/src/wasm_application_io.rs b/node-graph/gstd/src/wasm_application_io.rs index 54a8d34e..21d8ac43 100644 --- a/node-graph/gstd/src/wasm_application_io.rs +++ b/node-graph/gstd/src/wasm_application_io.rs @@ -120,12 +120,13 @@ async fn render_canvas(render_config: RenderConfig, data: impl GraphicElementRen let mut scene = Scene::new(); let mut child = Scene::new(); - data.render_to_vello(&mut child, glam::DAffine2::IDENTITY); + let mut context = wgpu_executor::RenderContext::default(); + data.render_to_vello(&mut child, glam::DAffine2::IDENTITY, &mut context); // TODO: Instead of applying the transform here, pass the transform during the translation to avoid the O(Nr cost scene.append(&child, Some(kurbo::Affine::new(footprint.transform.to_cols_array()))); - exec.render_vello_scene(&scene, &surface_handle, footprint.resolution.x, footprint.resolution.y) + exec.render_vello_scene(&scene, &surface_handle, footprint.resolution.x, footprint.resolution.y, &context) .await .expect("Failed to render Vello scene"); } else { diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 5a9a347d..eb14e2df 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -17,7 +17,7 @@ use graphene_core::{concrete, generic, Artboard, ArtboardGroup, GraphicGroup}; use graphene_core::{Cow, ProtoNodeIdentifier, Type}; use graphene_core::{Node, NodeIO, NodeIOTypes}; use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode}; -use graphene_std::application_io::RenderConfig; +use graphene_std::application_io::{RenderConfig, TextureFrame}; use graphene_std::raster::*; use graphene_std::wasm_application_io::*; use graphene_std::GraphicElement; @@ -389,7 +389,7 @@ fn node_registry() -> HashMap, input: ImageFrame, - output: ShaderInputFrame, + output: TextureFrame, params: [&WgpuExecutor] ), #[cfg(feature = "gpu")] @@ -608,6 +608,7 @@ fn node_registry() -> HashMap, input: (), output: QuantizationChannels, params: [QuantizationChannels]), async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Vec, params: [Vec]), async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Arc, params: [Arc]), + async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Arc, params: [Arc]), async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: WindowHandle, params: [WindowHandle]), #[cfg(feature = "gpu")] async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: ShaderInputFrame, params: [ShaderInputFrame]), @@ -639,6 +640,7 @@ fn node_registry() -> HashMap, input: Footprint, output: ShaderInputFrame, fn_params: [Footprint => ShaderInputFrame]), async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Footprint, output: WgpuSurface, fn_params: [Footprint => WgpuSurface]), async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Footprint, output: Option, fn_params: [Footprint => Option]), + async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Footprint, output: TextureFrame, fn_params: [Footprint => TextureFrame]), register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]), register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image, params: [DAffine2]), register_node!(graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>, input: (), params: [UVec2, u32, f64, NoiseType, DomainWarpType, f64, FractalType, u32, f64, f64, f64, f64, CellularDistanceFunction, CellularReturnType, f64]), @@ -665,6 +667,7 @@ fn node_registry() -> HashMap, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, () => DVec2, () => f64, () => DVec2, () => DVec2, () => DVec2]), // async_node!(graphene_core::transform::TransformNode<_, _, _, _, _, _>, input: Footprint, output: WasmSurfaceHandleFrame, fn_params: [Footprint => WasmSurfaceHandleFrame, () => DVec2, () => f64, () => DVec2, () => DVec2, () => DVec2]), async_node!(graphene_core::transform::TransformNode<_, _, _, _, _, _>, input: Footprint, output: ImageFrame, fn_params: [Footprint => ImageFrame, () => DVec2, () => f64, () => DVec2, () => DVec2, () => DVec2]), + async_node!(graphene_core::transform::TransformNode<_, _, _, _, _, _>, input: Footprint, output: TextureFrame, fn_params: [Footprint => TextureFrame, () => DVec2, () => f64, () => DVec2, () => DVec2, () => DVec2]), async_node!(graphene_core::transform::TransformNode<_, _, _, _, _, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => GraphicGroup, () => DVec2, () => f64, () => DVec2, () => DVec2, () => DVec2]), register_node!(graphene_core::transform::SetTransformNode<_>, input: VectorData, params: [VectorData]), register_node!(graphene_core::transform::SetTransformNode<_>, input: ImageFrame, params: [ImageFrame]), @@ -771,11 +774,7 @@ fn node_registry() -> HashMap, params: []), register_node!(graphene_core::ToGraphicElementNode, input: GraphicGroup, params: []), - register_node!(graphene_core::ToGraphicElementNode, input: graphene_std::SurfaceFrame, params: []), - #[cfg(target_arch = "wasm32")] - register_node!(graphene_core::ToGraphicElementNode, input: Arc>, params: []), - #[cfg(target_arch = "wasm32")] - register_node!(graphene_core::ToGraphicElementNode, input: graphene_std::application_io::SurfaceHandleFrame, params: []), + register_node!(graphene_core::ToGraphicElementNode, input: TextureFrame, params: []), register_node!(graphene_core::ToGraphicGroupNode, input: graphene_core::vector::VectorData, params: []), register_node!(graphene_core::ToGraphicGroupNode, input: ImageFrame, params: []), register_node!(graphene_core::ToGraphicGroupNode, input: GraphicGroup, params: []), diff --git a/node-graph/wgpu-executor/Cargo.toml b/node-graph/wgpu-executor/Cargo.toml index 4f29b5a7..8d716e5f 100644 --- a/node-graph/wgpu-executor/Cargo.toml +++ b/node-graph/wgpu-executor/Cargo.toml @@ -14,7 +14,7 @@ passthrough = [] gpu-executor = { path = "../gpu-executor" } # Workspace dependencies -graphene-core = { workspace = true, features = ["std", "alloc", "gpu"] } +graphene-core = { workspace = true, features = ["std", "alloc", "gpu", "wgpu"] } dyn-any = { workspace = true, features = ["log-bad-types", "rc", "glam"] } node-macro = { workspace = true } num-traits = { workspace = true } @@ -26,7 +26,6 @@ anyhow = { workspace = true } wgpu = { workspace = true, features = [ "spirv", "strict_asserts", - "api_log_info", ] } spirv = { workspace = true } futures = { workspace = true } diff --git a/node-graph/wgpu-executor/src/context.rs b/node-graph/wgpu-executor/src/context.rs index ddbb07a7..be9b8f16 100644 --- a/node-graph/wgpu-executor/src/context.rs +++ b/node-graph/wgpu-executor/src/context.rs @@ -38,6 +38,7 @@ impl Context { #[cfg(feature = "passthrough")] required_features: wgpu::Features::SPIRV_SHADER_PASSTHROUGH, required_limits, + memory_hints: Default::default(), }, None, ) diff --git a/node-graph/wgpu-executor/src/executor.rs b/node-graph/wgpu-executor/src/executor.rs index 4c23b024..970685d4 100644 --- a/node-graph/wgpu-executor/src/executor.rs +++ b/node-graph/wgpu-executor/src/executor.rs @@ -104,6 +104,7 @@ async fn execute_shader(device: Arc< module: &cs_module, entry_point: entry_point.as_str(), compilation_options: Default::default(), + cache: None, }); // Instantiates the bind group, once again specifying the binding of buffers. diff --git a/node-graph/wgpu-executor/src/lib.rs b/node-graph/wgpu-executor/src/lib.rs index e8fac332..4e1e12a3 100644 --- a/node-graph/wgpu-executor/src/lib.rs +++ b/node-graph/wgpu-executor/src/lib.rs @@ -6,9 +6,8 @@ pub use executor::GpuExecutor; use dyn_any::{DynAny, StaticType}; use gpu_executor::{ComputePassDimensions, GPUConstant, StorageBufferOptions, TextureBufferOptions, TextureBufferType, ToStorageBuffer, ToUniformBuffer}; -use graphene_core::application_io::{ApplicationIo, EditorApi, SurfaceHandle}; -use graphene_core::raster::color::RGBA16F; -use graphene_core::raster::{Image, ImageFrame}; +use graphene_core::application_io::{ApplicationIo, EditorApi, SurfaceHandle, TextureFrame}; +use graphene_core::raster::{Image, ImageFrame, SRGBA8}; use graphene_core::transform::{Footprint, Transform}; use graphene_core::Type; use graphene_core::{Color, Cow, Node, SurfaceFrame}; @@ -18,9 +17,9 @@ use futures::Future; use glam::{DAffine2, UVec2}; use std::pin::Pin; use std::sync::Arc; -use vello::{AaConfig, AaSupport, RenderParams, Renderer, RendererOptions, Scene}; +use vello::{AaConfig, AaSupport, DebugLayers, RenderParams, Renderer, RendererOptions, Scene}; use wgpu::util::DeviceExt; -use wgpu::{Buffer, BufferDescriptor, ShaderModule, SurfaceConfiguration, SurfaceError, Texture, TextureView}; +use wgpu::{Buffer, BufferDescriptor, Origin3d, ShaderModule, SurfaceConfiguration, SurfaceError, Texture, TextureAspect, TextureView}; #[cfg(target_arch = "wasm32")] use web_sys::HtmlCanvasElement; @@ -129,12 +128,14 @@ unsafe impl StaticType for Surface { type Static = Surface; } +pub use graphene_core::renderer::RenderContext; + // pub trait SpirVCompiler { // fn compile(&self, network: &[ProtoNetwork], io: &ShaderIO) -> Result; // } impl WgpuExecutor { - pub async fn render_vello_scene(&self, scene: &Scene, surface: &WgpuSurface, width: u32, height: u32) -> Result<()> { + pub async fn render_vello_scene(&self, scene: &Scene, surface: &WgpuSurface, width: u32, height: u32, context: &RenderContext) -> Result<()> { let surface = &surface.surface.inner; let surface_caps = surface.get_capabilities(&self.context.adapter); surface.configure( @@ -159,10 +160,23 @@ impl WgpuExecutor { width, height, antialiasing_method: AaConfig::Msaa8, + debug: DebugLayers::all(), }; { let mut renderer = self.vello_renderer.lock().unwrap(); + for (id, texture) in context.ressource_overrides.iter() { + let texture_view = wgpu::ImageCopyTextureBase { + texture: texture.clone(), + mip_level: 0, + origin: Origin3d::ZERO, + aspect: TextureAspect::All, + }; + renderer.override_image( + &vello::peniko::Image::new(vello::peniko::Blob::from_raw_parts(Arc::new(vec![]), *id), vello::peniko::Format::Rgba8, 0, 0), + Some(texture_view), + ); + } renderer .render_to_surface_async(&self.context.device, &self.context.queue, scene, &surface_texture, &render_params) .await @@ -229,14 +243,14 @@ impl WgpuExecutor { let bytes = data.to_bytes(); let usage = match options { TextureBufferOptions::Storage => wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC, - TextureBufferOptions::Texture => wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + TextureBufferOptions::Texture => wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC, TextureBufferOptions::Surface => wgpu::TextureUsages::RENDER_ATTACHMENT, }; let format = match T::format() { TextureBufferType::Rgba32Float => wgpu::TextureFormat::Rgba32Float, TextureBufferType::Rgba16Float => wgpu::TextureFormat::Rgba16Float, - TextureBufferType::Rgba8Srgb => wgpu::TextureFormat::Bgra8UnormSrgb, + TextureBufferType::Rgba8Srgb => wgpu::TextureFormat::Rgba8UnormSrgb, }; let buffer = self.context.device.create_texture_with_data( @@ -288,6 +302,7 @@ impl WgpuExecutor { module: &layout.shader.0, entry_point: layout.entry_point.as_str(), compilation_options: Default::default(), + cache: None, }); let bind_group_layout = compute_pipeline.get_bind_group_layout(0); @@ -625,6 +640,7 @@ impl WgpuExecutor { // If the pipeline will be used with a multiview render pass, this // indicates how many array layers the attachments will have. multiview: None, + cache: None, }); let vertex_buffer = context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { @@ -949,8 +965,9 @@ pub struct UploadTextureNode { } #[node_macro::node_fn(UploadTextureNode)] -async fn upload_texture<'a: 'input>(input: ImageFrame, executor: &'a WgpuExecutor) -> ShaderInputFrame { - let new_data: Vec = input.image.data.into_iter().map(|c| c.into()).collect(); +async fn upload_texture<'a: 'input>(input: ImageFrame, executor: &'a WgpuExecutor) -> TextureFrame { + // let new_data: Vec = input.image.data.into_iter().map(|c| c.into()).collect(); + let new_data = input.image.data.into_iter().map(|c| SRGBA8::from(c)).collect(); let new_image = Image { width: input.image.width, height: input.image.height, @@ -959,9 +976,14 @@ async fn upload_texture<'a: 'input>(input: ImageFrame, executor: &'a Wgpu }; let shader_input = executor.create_texture_buffer(new_image, TextureBufferOptions::Texture).unwrap(); + let texture = match shader_input { + ShaderInput::TextureBuffer(buffer, _) => buffer, + ShaderInput::StorageTextureBuffer(buffer, _) => buffer, + _ => unreachable!("Unsupported ShaderInput type"), + }; - ShaderInputFrame { - shader_input: Arc::new(shader_input), + TextureFrame { + texture: texture.into(), transform: input.transform, } }