diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49f5d9048..fe3a7a89c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,15 +107,15 @@ jobs: run: | mold -run cargo nextest run - miri: - runs-on: self-hosted - - steps: - - uses: actions/checkout@v2 - - - name: 🧪 Run Rust miri - run: | - mold -run cargo +nightly miri nextest run -j32 + #miri: + # runs-on: self-hosted + # + # steps: + # - uses: actions/checkout@v2 + # + # - name: 🧪 Run Rust miri + # run: | + # mold -run cargo +nightly miri nextest run -j32 cargo-deny: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 6e0cbb89d..7b030e83f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,22 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +[[package]] +name = "ab_glyph" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + [[package]] name = "addr2line" version = "0.19.0" @@ -79,6 +95,30 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "android-activity" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c77a0045eda8b888c76ea473c2b0515ba6f471d318f8927c5c72240937035a6" +dependencies = [ + "android-properties", + "bitflags 1.3.2", + "cc", + "jni-sys", + "libc", + "log", + "ndk 0.7.0", + "ndk-context", + "ndk-sys 0.4.1+23.1.7779620", + "num_enum 0.5.11", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -142,6 +182,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.2" @@ -390,6 +436,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-sys" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +dependencies = [ + "block-sys", + "objc2-encode", +] + [[package]] name = "brotli" version = "3.3.4" @@ -483,6 +548,19 @@ dependencies = [ "system-deps 6.0.5", ] +[[package]] +name = "calloop" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192" +dependencies = [ + "log", + "nix 0.25.1", + "slotmap", + "thiserror", + "vec_map", +] + [[package]] name = "cargo_toml" version = "0.13.3" @@ -498,6 +576,9 @@ name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] [[package]] name = "cesu8" @@ -540,6 +621,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" version = "0.4.24" @@ -979,6 +1066,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading 0.7.4", +] + [[package]] name = "document-features" version = "0.2.7" @@ -988,6 +1084,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + [[package]] name = "dtoa" version = "0.4.8" @@ -1117,7 +1219,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" dependencies = [ - "memoffset", + "memoffset 0.8.0", "rustc_version", ] @@ -1640,6 +1742,7 @@ dependencies = [ "num-traits", "serde", "spirv", + "web-sys", ] [[package]] @@ -1705,6 +1808,7 @@ dependencies = [ "graph-craft", "graphene-core", "image", + "js-sys", "kurbo", "log", "node-macro", @@ -1713,7 +1817,11 @@ dependencies = [ "serde_json", "tempfile", "vulkan-executor", + "wasm-bindgen", + "web-sys", + "wgpu", "wgpu-executor", + "wgpu-types", "xxhash-rust", ] @@ -2190,6 +2298,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2200,6 +2311,7 @@ dependencies = [ "dyn-clone", "futures", "glam", + "gpu-executor", "graph-craft", "graphene-core", "graphene-std", @@ -2208,6 +2320,7 @@ dependencies = [ "once_cell", "serde", "typed-arena", + "wgpu-executor", ] [[package]] @@ -2282,6 +2395,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.61" @@ -2535,6 +2657,24 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -2564,6 +2704,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.6.2" @@ -2686,11 +2832,25 @@ checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" dependencies = [ "bitflags 1.3.2", "jni-sys", - "ndk-sys", + "ndk-sys 0.3.0", "num_enum 0.5.11", "thiserror", ] +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys 0.4.1+23.1.7779620", + "num_enum 0.5.11", + "raw-window-handle", + "thiserror", +] + [[package]] name = "ndk-context" version = "0.1.1" @@ -2706,12 +2866,46 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + [[package]] name = "node-macro" version = "0.0.0" @@ -2727,6 +2921,16 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "notify-rust" version = "4.8.0" @@ -2920,6 +3124,32 @@ dependencies = [ "objc_id", ] +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3.patch-leaks.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +dependencies = [ + "block2", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -3007,6 +3237,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "orbclient" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221d488cd70617f1bd599ed8ceb659df2147d9393717954d82a0f5e8032a6ab1" +dependencies = [ + "redox_syscall 0.3.5", +] + [[package]] name = "os_info" version = "3.7.0" @@ -3034,6 +3273,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owned_ttf_parser" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" +dependencies = [ + "ttf-parser 0.19.0", +] + [[package]] name = "pango" version = "0.15.10" @@ -3719,7 +3967,7 @@ dependencies = [ "bitflags 1.3.2", "bytemuck", "smallvec", - "ttf-parser", + "ttf-parser 0.17.1", "unicode-bidi-mirroring", "unicode-ccc", "unicode-general-category", @@ -3793,6 +4041,19 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sctk-adwaita" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "security-framework" version = "2.8.2" @@ -4113,6 +4374,25 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "smithay-client-toolkit" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" +dependencies = [ + "bitflags 1.3.2", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2", + "nix 0.24.3", + "pkg-config", + "wayland-client", + "wayland-cursor", + "wayland-protocols", +] + [[package]] name = "socket2" version = "0.4.9" @@ -4254,6 +4534,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + [[package]] name = "string_cache" version = "0.8.7" @@ -4395,9 +4681,9 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk", + "ndk 0.6.0", "ndk-context", - "ndk-sys", + "ndk-sys 0.3.0", "objc", "once_cell", "parking_lot", @@ -4760,6 +5046,31 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -5012,6 +5323,12 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" +[[package]] +name = "ttf-parser" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746" + [[package]] name = "typed-arena" version = "2.0.2" @@ -5150,6 +5467,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version-compare" version = "0.0.11" @@ -5260,9 +5583,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -5270,16 +5593,16 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-shared", ] @@ -5297,9 +5620,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5307,22 +5630,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "wasm-bindgen-test" @@ -5361,6 +5684,79 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix 0.24.3", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix 0.24.3", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix 0.24.3", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.61" @@ -5541,7 +5937,9 @@ dependencies = [ "num-traits", "serde", "spirv", + "web-sys", "wgpu", + "winit", ] [[package]] @@ -5928,6 +6326,41 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winit" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "866db3f712fffba75d31bf0cdecf357c8aeafd158c5b7ab51dba2a2b2d47f196" +dependencies = [ + "android-activity", + "bitflags 1.3.2", + "cfg_aliases", + "core-foundation", + "core-graphics", + "dispatch", + "instant", + "libc", + "log", + "mio", + "ndk 0.7.0", + "objc2", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle", + "redox_syscall 0.3.5", + "sctk-adwaita", + "smithay-client-toolkit", + "wasm-bindgen", + "wayland-client", + "wayland-commons", + "wayland-protocols", + "wayland-scanner", + "web-sys", + "windows-sys 0.45.0", + "x11-dl", +] + [[package]] name = "winnow" version = "0.4.1" @@ -6023,6 +6456,15 @@ dependencies = [ "libc", ] +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + [[package]] name = "xml-rs" version = "0.8.4" diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 29c26157b..18835e3bd 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -12,7 +12,10 @@ license = "Apache-2.0" [features] gpu = ["interpreted-executor/gpu", "graphene-std/gpu", "graphene-core/gpu"] -quantization = ["graphene-std/quantization", "interpreted-executor/quantization"] +quantization = [ + "graphene-std/quantization", + "interpreted-executor/quantization", +] [dependencies] log = "0.4" @@ -40,7 +43,7 @@ graph-craft = { path = "../node-graph/graph-craft" } interpreted-executor = { path = "../node-graph/interpreted-executor" } dyn-any = { path = "../libraries/dyn-any" } graphene-core = { path = "../node-graph/gcore" } -graphene-std = { path = "../node-graph/gstd" } +graphene-std = { path = "../node-graph/gstd", features = ["wasm"] } num_enum = "0.6.1" [dependencies.document-legacy] diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index f2d88e249..fa0cb04e0 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -14,6 +14,7 @@ use graphene_core::text::Font; use graphene_core::vector::VectorData; use graphene_core::*; +use graphene_std::wasm_application_io::WasmEditorApi; use once_cell::sync::Lazy; use std::collections::VecDeque; @@ -239,7 +240,7 @@ fn static_nodes() -> Vec { inputs: vec![DocumentInputType { name: "In", data_type: FrontendGraphDataType::General, - default: NodeInput::Network(concrete!(EditorApi)), + default: NodeInput::Network(concrete!(WasmEditorApi)), }], outputs: vec![DocumentOutputType { name: "Image Frame", @@ -256,8 +257,8 @@ fn static_nodes() -> Vec { nodes: [ DocumentNode { name: "Create Canvas".to_string(), - inputs: vec![NodeInput::Network(concrete!(EditorApi))], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::wasm_application_io::CreateSurfaceNode")), + inputs: vec![NodeInput::Network(concrete!(WasmEditorApi))], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")), ..Default::default() }, DocumentNode { @@ -276,7 +277,7 @@ fn static_nodes() -> Vec { inputs: vec![DocumentInputType { name: "In", data_type: FrontendGraphDataType::General, - default: NodeInput::Network(concrete!(EditorApi)), + default: NodeInput::Network(concrete!(WasmEditorApi)), }], outputs: vec![DocumentOutputType { name: "Canvas", @@ -299,8 +300,8 @@ fn static_nodes() -> Vec { }, DocumentNode { name: "Create Canvas".to_string(), - inputs: vec![NodeInput::Network(concrete!(EditorApi))], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::wasm_application_io::CreateSurfaceNode")), + inputs: vec![NodeInput::Network(concrete!(WasmEditorApi))], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")), ..Default::default() }, DocumentNode { @@ -312,7 +313,7 @@ fn static_nodes() -> Vec { DocumentNode { name: "Draw Canvas".to_string(), inputs: vec![NodeInput::node(0, 0), NodeInput::node(2, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::wasm_application_io::DrawImageFrameNode<_>")), + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::DrawImageFrameNode<_>")), ..Default::default() }, ] @@ -331,7 +332,7 @@ fn static_nodes() -> Vec { DocumentInputType { name: "In", data_type: FrontendGraphDataType::General, - default: NodeInput::Network(concrete!(EditorApi)), + default: NodeInput::Network(concrete!(WasmEditorApi)), }, ], outputs: vec![DocumentOutputType { @@ -349,7 +350,7 @@ fn static_nodes() -> Vec { nodes: [ DocumentNode { name: "SetNode".to_string(), - inputs: vec![NodeInput::ShortCircut(concrete!(EditorApi))], + inputs: vec![NodeInput::ShortCircut(concrete!(WasmEditorApi))], implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::SomeNode")), ..Default::default() }, @@ -376,7 +377,7 @@ fn static_nodes() -> Vec { inputs: vec![DocumentInputType { name: "In", data_type: FrontendGraphDataType::Raster, - default: NodeInput::Network(concrete!(EditorApi)), + default: NodeInput::Network(concrete!(WasmEditorApi)), }], outputs: vec![ DocumentOutputType { @@ -727,6 +728,57 @@ fn static_nodes() -> Vec { properties: node_properties::no_properties, }, #[cfg(feature = "gpu")] + DocumentNodeType { + name: "Uniform", + category: "Gpu", + identifier: NodeImplementation::DocumentNode(NodeNetwork { + inputs: vec![1, 0], + outputs: vec![NodeOutput::new(2, 0)], + nodes: [ + DocumentNode { + name: "Extract Executor".to_string(), + inputs: vec![NodeInput::Network(concrete!(WasmEditorApi))], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IntoNode<_, &WgpuExecutor>")), + ..Default::default() + }, + DocumentNode { + name: "Create Uniform".to_string(), + inputs: vec![NodeInput::Network(concrete!(f32))], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("gpu-executor::UniformNode<_>")), + ..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() + .enumerate() + .map(|(id, node)| (id as NodeId, node)) + .collect(), + ..Default::default() + }), + inputs: vec![ + DocumentInputType { + name: "In", + data_type: FrontendGraphDataType::General, + default: NodeInput::value(TaggedValue::F32(0.), true), + }, + DocumentInputType { + name: "In", + data_type: FrontendGraphDataType::General, + default: NodeInput::Network(concrete!(WasmEditorApi)), + }, + ], + outputs: vec![DocumentOutputType { + name: "Uniform", + data_type: FrontendGraphDataType::General, + }], + properties: node_properties::input_properties, + }, + #[cfg(feature = "gpu")] DocumentNodeType { name: "GpuImage", category: "Image Adjustments", @@ -738,6 +790,11 @@ fn static_nodes() -> Vec { data_type: FrontendGraphDataType::General, default: NodeInput::value(TaggedValue::DocumentNode(DocumentNode::default()), true), }, + DocumentInputType { + name: "In", + data_type: FrontendGraphDataType::General, + default: NodeInput::Network(concrete!(WasmEditorApi)), + }, ], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], properties: node_properties::no_properties, @@ -1374,7 +1431,7 @@ pub fn new_text_network(text: String, font: Font, size: f64) -> NodeNetwork { network.push_node( text_generator.to_document_node( [ - NodeInput::Network(concrete!(graphene_core::EditorApi)), + NodeInput::Network(concrete!(WasmEditorApi)), NodeInput::value(TaggedValue::String(text), false), NodeInput::value(TaggedValue::Font(font), false), NodeInput::value(TaggedValue::F64(size), false), diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 83f8243f9..c0a0febcb 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -17,8 +17,9 @@ use graphene_core::renderer::{SvgSegment, SvgSegmentList}; use graphene_core::text::FontCache; use graphene_core::vector::style::ViewMode; -use graphene_core::wasm_application_io::WasmApplicationIo; -use graphene_core::{Color, EditorApi, SurfaceFrame, SurfaceId}; +use graphene_core::{Color, SurfaceFrame, SurfaceId}; +use graphene_std::wasm_application_io::WasmApplicationIo; +use graphene_std::wasm_application_io::WasmEditorApi; use interpreted_executor::dynamic_executor::DynamicExecutor; use glam::{DAffine2, DVec2}; @@ -33,7 +34,7 @@ pub struct NodeRuntime { font_cache: FontCache, receiver: Receiver, sender: Sender, - wasm_io: WasmApplicationIo, + wasm_io: Option, pub(crate) thumbnails: HashMap>, canvas_cache: HashMap, SurfaceId>, } @@ -69,7 +70,7 @@ impl NodeRuntime { sender, font_cache: FontCache::default(), thumbnails: Default::default(), - wasm_io: WasmApplicationIo::default(), + wasm_io: None, canvas_cache: Default::default(), } } @@ -125,10 +126,14 @@ impl NodeRuntime { } async fn execute_network<'a>(&'a mut self, path: &[LayerId], scoped_network: NodeNetwork, image_frame: Option>) -> Result { - let editor_api = EditorApi { + if self.wasm_io.is_none() { + self.wasm_io = Some(WasmApplicationIo::new().await); + } + + let editor_api = WasmEditorApi { font_cache: &self.font_cache, image_frame, - application_io: &self.wasm_io, + application_io: &self.wasm_io.as_ref().unwrap(), }; // We assume only one output @@ -145,7 +150,7 @@ impl NodeRuntime { use graph_craft::graphene_compiler::Executor; let result = match self.executor.input_type() { - Some(t) if t == concrete!(EditorApi) => (&self.executor).execute(editor_api).await.map_err(|e| e.to_string()), + Some(t) if t == concrete!(WasmEditorApi) => (&self.executor).execute(editor_api).await.map_err(|e| e.to_string()), Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()), _ => Err("Invalid input type".to_string()), }?; @@ -154,7 +159,7 @@ impl NodeRuntime { let old_id = self.canvas_cache.insert(path.to_vec(), surface_id); if let Some(old_id) = old_id { if old_id != surface_id { - self.wasm_io.destroy_surface(old_id); + self.wasm_io.as_ref().map(|io| io.destroy_surface(old_id)); } } } @@ -293,49 +298,6 @@ impl NodeGraphExecutor { self.last_output_type.get(path).cloned().flatten() } - /// Computes an input for a node in the graph - pub fn compute_input(&mut self, _old_network: &NodeNetwork, _node_path: &[NodeId], _input_index: usize, _editor_api: Cow>) -> Result { - todo!() - /* - let mut network = old_network.clone(); - // Adjust the output of the graph so we find the relevant output - 'outer: for end in (0..node_path.len()).rev() { - let mut inner_network = &mut network; - for &node_id in &node_path[..end] { - inner_network.outputs[0] = NodeOutput::new(node_id, 0); - - let Some(new_inner) = inner_network.nodes.get_mut(&node_id).and_then(|node| node.implementation.get_network_mut()) else { - return Err("Failed to find network".to_string()); - }; - inner_network = new_inner; - } - match &inner_network.nodes.get(&node_path[end]).unwrap().inputs[input_index] { - // If the input is from a parent network then adjust the input index and continue iteration - NodeInput::Network(_) => { - input_index = inner_network - .inputs - .iter() - .enumerate() - .filter(|&(_index, &id)| id == node_path[end]) - .nth(input_index) - .ok_or_else(|| "Invalid network input".to_string())? - .0; - } - // If the input is just a value, return that value - NodeInput::Value { tagged_value, .. } => return Some(dyn_any::downcast::(tagged_value.clone().to_any()).map(|v| *v)), - // If the input is from a node, set the node to be the output (so that is what is evaluated) - NodeInput::Node { node_id, output_index, .. } => { - inner_network.outputs[0] = NodeOutput::new(*node_id, *output_index); - break 'outer; - } - NodeInput::ShortCircut(_) => (), - } - } - - self.queue_execution(network, editor_api.into_owned())? - */ - } - /// Encodes an image into a format using the image crate fn encode_img(image: Image, resize: Option, format: image::ImageOutputFormat) -> Result<(Vec, (u32, u32)), String> { use image::{ImageBuffer, Rgba}; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3a8d2f885..0f77efbff 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -35,7 +35,7 @@ "webpack-cli": "^5.0.2" }, "optionalDependencies": { - "wasm-pack": "0.10.3" + "wasm-pack": "0.11.1" } }, "node_modules/@babel/code-frame": { @@ -2139,12 +2139,12 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", "optional": true, "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.8" } }, "node_modules/balanced-match": { @@ -2192,14 +2192,14 @@ } }, "node_modules/binary-install": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-0.1.1.tgz", - "integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-1.1.0.tgz", + "integrity": "sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==", "optional": true, "dependencies": { - "axios": "^0.21.1", + "axios": "^0.26.1", "rimraf": "^3.0.2", - "tar": "^6.1.0" + "tar": "^6.1.11" }, "engines": { "node": ">=10" @@ -3946,9 +3946,9 @@ } }, "node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "optional": true, "engines": { "node": ">=8" @@ -5006,14 +5006,14 @@ } }, "node_modules/tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", + "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", "optional": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -5266,13 +5266,13 @@ "dev": true }, "node_modules/wasm-pack": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.10.3.tgz", - "integrity": "sha512-dg1PPyp+QwWrhfHsgG12K/y5xzwfaAoK1yuVC/DUAuQsDy5JywWDuA7Y/ionGwQz+JBZVw8jknaKBnaxaJfwTA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.11.1.tgz", + "integrity": "sha512-0BKEioKJY/SMqahDEoaUUR8jrRkHO0cdYhRqqHKQMY3Bac6Eep3ZRsTlpFSSwS4LYPxd+Tb5KFFNhUikCkq8Yg==", "hasInstallScript": true, "optional": true, "dependencies": { - "binary-install": "^0.1.0" + "binary-install": "^1.0.1" }, "bin": { "wasm-pack": "run.js" @@ -7027,12 +7027,12 @@ "dev": true }, "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", "optional": true, "requires": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.8" } }, "balanced-match": { @@ -7063,14 +7063,14 @@ "dev": true }, "binary-install": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-0.1.1.tgz", - "integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-1.1.0.tgz", + "integrity": "sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==", "optional": true, "requires": { - "axios": "^0.21.1", + "axios": "^0.26.1", "rimraf": "^3.0.2", - "tar": "^6.1.0" + "tar": "^6.1.11" }, "dependencies": { "rimraf": { @@ -8259,9 +8259,9 @@ "dev": true }, "minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "optional": true }, "minizlib": { @@ -9005,14 +9005,14 @@ "dev": true }, "tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", + "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", "optional": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -9163,12 +9163,12 @@ "dev": true }, "wasm-pack": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.10.3.tgz", - "integrity": "sha512-dg1PPyp+QwWrhfHsgG12K/y5xzwfaAoK1yuVC/DUAuQsDy5JywWDuA7Y/ionGwQz+JBZVw8jknaKBnaxaJfwTA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.11.1.tgz", + "integrity": "sha512-0BKEioKJY/SMqahDEoaUUR8jrRkHO0cdYhRqqHKQMY3Bac6Eep3ZRsTlpFSSwS4LYPxd+Tb5KFFNhUikCkq8Yg==", "optional": true, "requires": { - "binary-install": "^0.1.0" + "binary-install": "^1.0.1" } }, "watchpack": { diff --git a/frontend/package.json b/frontend/package.json index b31eaea5b..a9dc24d86 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -56,7 +56,7 @@ "webpack": "^5.81.0" }, "optionalDependencies": { - "wasm-pack": "0.10.3" + "wasm-pack": "0.11.1" }, "//": "Notes about dependency issues and incompatibilities should be added here when needed.", "homepage": "https://graphite.rs", diff --git a/frontend/src/components/panels/Document.svelte b/frontend/src/components/panels/Document.svelte index a75fc4d65..8615bf212 100644 --- a/frontend/src/components/panels/Document.svelte +++ b/frontend/src/components/panels/Document.svelte @@ -132,8 +132,8 @@ placeholders.forEach((placeholder) => { const canvasName = placeholder.getAttribute("data-canvas-placeholder"); // Get the canvas element from the global storage - const context = (window as any).imageCanvases[canvasName]; - placeholder.replaceWith(context.canvas); + const canvas = (window as any).imageCanvases[canvasName]; + placeholder.replaceWith(canvas); }); } diff --git a/frontend/wasm/Cargo.toml b/frontend/wasm/Cargo.toml index 4ac4419b6..fea6fb5e3 100644 --- a/frontend/wasm/Cargo.toml +++ b/frontend/wasm/Cargo.toml @@ -29,7 +29,7 @@ graphene-core = { path = "../../node-graph/gcore", features = [ "alloc", ] } serde = { version = "1.0", features = ["derive"] } -wasm-bindgen = { version = "0.2.84" } +wasm-bindgen = { version = "=0.2.86" } serde-wasm-bindgen = "0.4.1" js-sys = "0.3.55" wasm-bindgen-futures = "0.4.33" diff --git a/node-graph/gcore/Cargo.toml b/node-graph/gcore/Cargo.toml index 0df9208ec..ae51bcdb1 100644 --- a/node-graph/gcore/Cargo.toml +++ b/node-graph/gcore/Cargo.toml @@ -32,7 +32,7 @@ async = ["async-trait", "alloc"] nightly = [] alloc = ["dyn-any", "bezier-rs", "once_cell"] type_id_logging = [] -wasm = ["wasm-bindgen", "web-sys", "js-sys", "std"] +wasm = ["web-sys"] [dependencies] dyn-any = { path = "../../libraries/dyn-any", features = [ @@ -77,10 +77,4 @@ js-sys = { version = "0.3.55", optional = true } [dependencies.web-sys] version = "0.3.4" optional = true -features = [ - "Window", - "CanvasRenderingContext2d", - "ImageData", - "Document", - "HtmlCanvasElement", -] +features = ["HtmlCanvasElement"] diff --git a/node-graph/gcore/src/application_io.rs b/node-graph/gcore/src/application_io.rs index 857fc3ade..a83c4b8b8 100644 --- a/node-graph/gcore/src/application_io.rs +++ b/node-graph/gcore/src/application_io.rs @@ -45,7 +45,7 @@ impl From> for SurfaceFrame { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct SurfaceHandle { pub surface_id: SurfaceId, pub surface: Surface, @@ -55,7 +55,7 @@ unsafe impl StaticType for SurfaceHandle { type Static = SurfaceHandle; } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct SurfaceHandleFrame { pub surface_handle: Arc>, pub transform: DAffine2, @@ -87,12 +87,18 @@ impl<'a, Surface> Drop for SurfaceHandle<'a, Surface> { pub trait ApplicationIo { type Surface; + type Executor; fn create_surface(&self) -> SurfaceHandle; fn destroy_surface(&self, surface_id: SurfaceId); + fn gpu_executor(&self) -> Option<&Self::Executor> { + None + } } impl ApplicationIo for &T { type Surface = T::Surface; + type Executor = T::Executor; + fn create_surface(&self) -> SurfaceHandle { (**self).create_surface() } @@ -100,6 +106,10 @@ impl ApplicationIo for &T { fn destroy_surface(&self, surface_id: SurfaceId) { (**self).destroy_surface(surface_id) } + + fn gpu_executor(&self) -> Option<&T::Executor> { + (**self).gpu_executor() + } } pub struct EditorApi<'a, Io> { @@ -162,6 +172,3 @@ impl ExtractImageFrame { Self } } - -#[cfg(feature = "wasm")] -pub mod wasm_application_io; diff --git a/node-graph/gcore/src/lib.rs b/node-graph/gcore/src/lib.rs index d0ae7148e..018588c4a 100644 --- a/node-graph/gcore/src/lib.rs +++ b/node-graph/gcore/src/lib.rs @@ -20,6 +20,7 @@ pub mod value; #[cfg(feature = "gpu")] pub mod gpu; +#[cfg(feature = "alloc")] pub mod memo; pub mod storage; @@ -34,6 +35,7 @@ pub use graphic_element::*; #[cfg(feature = "alloc")] pub mod vector; +#[cfg(feature = "alloc")] pub mod application_io; pub mod quantization; @@ -146,6 +148,9 @@ impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<&'i (dyn NodeIO<'i, I, Output = O> + } } +#[cfg(feature = "alloc")] pub use crate::application_io::{ExtractImageFrame, SurfaceFrame, SurfaceId}; #[cfg(feature = "wasm")] -pub use application_io::{wasm_application_io, wasm_application_io::WasmEditorApi as EditorApi}; +pub type WasmSurfaceHandle = application_io::SurfaceHandle; +#[cfg(feature = "wasm")] +pub type WasmSurfaceHandleFrame = application_io::SurfaceHandleFrame; diff --git a/node-graph/gcore/src/memo.rs b/node-graph/gcore/src/memo.rs index 851301222..8ba30abc2 100644 --- a/node-graph/gcore/src/memo.rs +++ b/node-graph/gcore/src/memo.rs @@ -4,8 +4,8 @@ use core::future::Future; #[cfg(feature = "alloc")] use alloc::sync::Arc; use core::cell::Cell; +use core::marker::PhantomData; use core::pin::Pin; -use std::marker::PhantomData; // Caches the output of a given Node and acts as a proxy #[derive(Default)] @@ -103,8 +103,6 @@ impl<'i, T: 'i + Clone> Node<'i, Option> for LetNode { } } -impl std::marker::Unpin for LetNode {} - impl LetNode { pub fn new() -> LetNode { LetNode { cache: Default::default() } diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index c951a5687..dd1523352 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -211,7 +211,7 @@ pub struct IntoNode { _o: PhantomData, } #[node_macro::node_fn(IntoNode<_I, _O>)] -fn into<_I, _O>(input: _I) -> _O +async fn into<_I, _O>(input: _I) -> _O where _I: Into<_O>, { diff --git a/node-graph/gcore/src/raster/brush_cache.rs b/node-graph/gcore/src/raster/brush_cache.rs index 88884c4b7..f6002aca9 100644 --- a/node-graph/gcore/src/raster/brush_cache.rs +++ b/node-graph/gcore/src/raster/brush_cache.rs @@ -88,7 +88,7 @@ impl BrushCacheImpl { impl Hash for BrushCacheImpl { // Zero hash. - fn hash(&self, state: &mut H) {} + fn hash(&self, _state: &mut H) {} } #[derive(Clone, Debug, Default)] diff --git a/node-graph/gcore/src/text.rs b/node-graph/gcore/src/text.rs index da1da20bf..2bab0dd4a 100644 --- a/node-graph/gcore/src/text.rs +++ b/node-graph/gcore/src/text.rs @@ -1,7 +1,7 @@ mod font_cache; mod to_path; -use crate::EditorApi; +use crate::application_io::EditorApi; pub use font_cache::*; use node_macro::node_fn; pub use to_path::*; @@ -15,7 +15,7 @@ pub struct TextGenerator { } #[node_fn(TextGenerator)] -fn generate_text<'a: 'input>(editor: EditorApi<'a>, 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: f64) -> crate::vector::VectorData { 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)) } diff --git a/node-graph/gcore/src/types.rs b/node-graph/gcore/src/types.rs index 931ff323a..cff34f7b2 100644 --- a/node-graph/gcore/src/types.rs +++ b/node-graph/gcore/src/types.rs @@ -108,6 +108,10 @@ pub enum Type { Future(Box), } +unsafe impl StaticType for Type { + type Static = Self; +} + impl Type { pub fn is_generic(&self) -> bool { matches!(self, Type::Generic(_)) diff --git a/node-graph/gpu-compiler/Cargo.lock b/node-graph/gpu-compiler/Cargo.lock index fdc405a73..bcd970331 100644 --- a/node-graph/gpu-compiler/Cargo.lock +++ b/node-graph/gpu-compiler/Cargo.lock @@ -63,7 +63,7 @@ checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -134,7 +134,7 @@ checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -260,7 +260,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 1.0.107", ] [[package]] @@ -277,7 +277,7 @@ checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -320,7 +320,7 @@ version = "0.3.0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -427,7 +427,7 @@ checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -565,6 +565,7 @@ dependencies = [ "num-traits", "serde", "spirv", + "web-sys", ] [[package]] @@ -596,6 +597,7 @@ dependencies = [ "bytemuck", "dyn-any", "glam", + "js-sys", "kurbo", "log", "node-macro", @@ -608,6 +610,8 @@ dependencies = [ "specta", "spin", "spirv-std", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -724,9 +728,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] @@ -810,7 +814,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -821,7 +825,7 @@ checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -933,7 +937,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1006,18 +1010,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -1141,7 +1145,7 @@ dependencies = [ "smallvec", "spirt", "spirv-tools", - "syn", + "syn 1.0.107", ] [[package]] @@ -1224,7 +1228,7 @@ checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1304,7 +1308,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "termcolor", ] @@ -1383,7 +1387,7 @@ dependencies = [ "proc-macro2", "quote", "spirv-std-types", - "syn", + "syn 1.0.107", ] [[package]] @@ -1429,6 +1433,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tempfile" version = "3.3.0" @@ -1491,7 +1506,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1641,9 +1656,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1651,24 +1666,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1676,22 +1691,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "winapi" diff --git a/node-graph/gpu-compiler/src/lib.rs b/node-graph/gpu-compiler/src/lib.rs index bf6617a8d..5056f37a5 100644 --- a/node-graph/gpu-compiler/src/lib.rs +++ b/node-graph/gpu-compiler/src/lib.rs @@ -67,7 +67,7 @@ fn constant_attribute(constant: &GPUConstant) -> &'static str { } } -pub fn construct_argument(input: &ShaderInput<()>, position: u32, binding_offset: u32) -> String { +pub fn construct_argument(input: &ShaderInput, position: u32, binding_offset: u32) -> String { let line = match input { ShaderInput::Constant(constant) => format!("#[spirv({})] i{}: {}", constant_attribute(constant), position, constant.ty()), ShaderInput::UniformBuffer(_, ty) => { @@ -76,6 +76,19 @@ pub fn construct_argument(input: &ShaderInput<()>, position: u32, binding_offset ShaderInput::StorageBuffer(_, ty) | ShaderInput::ReadBackBuffer(_, ty) => { format!("#[spirv(storage_buffer, descriptor_set = 0, binding = {})] i{}: &[{}]", position + binding_offset, position, ty,) } + ShaderInput::StorageTextureBuffer(_, ty) => { + format!("#[spirv(storage_buffer, descriptor_set = 0, binding = {})] i{}: &mut [{}]]", position + binding_offset, position, ty,) + } + ShaderInput::TextureView(_, _) => { + format!( + "#[spirv(texture, descriptor_set = 0, binding = {})] i{}: spirv_std::image::Image2d", + position + binding_offset, + position, + ) + } + ShaderInput::TextureBuffer(_, _) => { + panic!("Texture Buffers cannot be used as inputs use TextureView instead") + } ShaderInput::OutputBuffer(_, ty) => { format!("#[spirv(storage_buffer, descriptor_set = 0, binding = {})] o{}: &mut[{}]", position + binding_offset, position, ty,) } diff --git a/node-graph/gpu-executor/Cargo.toml b/node-graph/gpu-executor/Cargo.toml index 41af2995a..49c5b50bf 100644 --- a/node-graph/gpu-executor/Cargo.toml +++ b/node-graph/gpu-executor/Cargo.toml @@ -34,3 +34,7 @@ anyhow = "1.0.66" spirv = "0.2.0" futures-intrusive = "0.5.0" futures = "0.3.25" +web-sys = { version = "0.3.4", features = [ + "HtmlCanvasElement", + "ImageBitmapRenderingContext", +] } diff --git a/node-graph/gpu-executor/src/lib.rs b/node-graph/gpu-executor/src/lib.rs index 9d40673cd..edf22b9c5 100644 --- a/node-graph/gpu-executor/src/lib.rs +++ b/node-graph/gpu-executor/src/lib.rs @@ -5,7 +5,9 @@ use graphene_core::*; use anyhow::Result; use dyn_any::{StaticType, StaticTypeSized}; use futures::Future; -use glam::UVec3; +use glam::{DAffine2, UVec3}; +use graphene_core::application_io::{ApplicationIo, EditorApi, SurfaceHandle}; +use graphene_core::raster::{Image, ImageFrame, Pixel, SRGBA8}; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::pin::Pin; @@ -13,6 +15,7 @@ use std::sync::Arc; type ReadBackFuture = Pin>>>>; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, dyn_any::DynAny)] pub enum ComputePassDimensions { X(u32), XY(u32, u32), @@ -29,18 +32,33 @@ impl ComputePassDimensions { } } +pub trait Texture { + fn width(&self) -> u32; + fn height(&self) -> u32; + fn format(&self) -> TextureBufferType; + fn view(&self) -> TextureView; +} + pub trait GpuExecutor { type ShaderHandle; type BufferHandle; + type TextureHandle; + type TextureView; + type Surface; + type Window; type CommandBuffer; fn load_shader(&self, shader: Shader) -> Result; - fn create_uniform_buffer(&self, data: T) -> Result>; - fn create_storage_buffer(&self, data: T, options: StorageBufferOptions) -> Result>; - fn create_output_buffer(&self, len: usize, ty: Type, cpu_readable: bool) -> Result>; - fn create_compute_pass(&self, layout: &PipelineLayout, read_back: Option>>, instances: ComputePassDimensions) -> Result; + fn create_uniform_buffer(&self, data: T) -> Result>; + fn create_storage_buffer(&self, data: T, options: StorageBufferOptions) -> Result>; + fn create_texture_buffer(&self, data: T, options: TextureBufferOptions) -> Result>; + fn create_texture_view(&self, texture: ShaderInput) -> Result>; + fn create_output_buffer(&self, len: usize, ty: Type, cpu_readable: bool) -> Result>; + fn create_compute_pass(&self, layout: &PipelineLayout, read_back: Option>>, instances: ComputePassDimensions) -> Result; + fn create_render_pass(&self, texture: Arc>, canvas: Arc>) -> Result<()>; fn execute_compute_pipeline(&self, encoder: Self::CommandBuffer) -> Result<()>; - fn read_output_buffer(&self, buffer: Arc>) -> ReadBackFuture; + fn read_output_buffer(&self, buffer: Arc>) -> ReadBackFuture; + fn create_surface(&self, window: SurfaceHandle) -> Result>; } pub trait SpirVCompiler { @@ -84,37 +102,143 @@ impl GPUConstant { } } } +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DummyExecutor; + +impl GpuExecutor for DummyExecutor { + type ShaderHandle = (); + type BufferHandle = (); + type TextureHandle = (); + type TextureView = (); + type Surface = (); + type Window = (); + type CommandBuffer = (); + + fn load_shader(&self, _shader: Shader) -> Result { + todo!() + } + + fn create_uniform_buffer(&self, _data: T) -> Result> { + todo!() + } + + fn create_storage_buffer(&self, _data: T, _options: StorageBufferOptions) -> Result> { + todo!() + } + + fn create_texture_buffer(&self, _data: T, _options: TextureBufferOptions) -> Result> { + todo!() + } + + fn create_output_buffer(&self, _len: usize, _ty: Type, _cpu_readable: bool) -> Result> { + todo!() + } + + fn create_compute_pass(&self, _layout: &PipelineLayout, _read_back: Option>>, _instances: ComputePassDimensions) -> Result { + todo!() + } + + fn execute_compute_pipeline(&self, _encoder: Self::CommandBuffer) -> Result<()> { + todo!() + } + + fn create_render_pass(&self, _texture: Arc>, _canvas: Arc>) -> Result<()> { + todo!() + } + + fn read_output_buffer(&self, _buffer: Arc>) -> ReadBackFuture { + todo!() + } + + fn create_texture_view(&self, _texture: ShaderInput) -> Result> { + todo!() + } + + fn create_surface(&self, _window: SurfaceHandle) -> Result> { + todo!() + } +} + +type AbstractShaderInput = ShaderInput; #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] /// All the possible inputs to a shader. -pub enum ShaderInput { - UniformBuffer(BufferHandle, Type), - StorageBuffer(BufferHandle, Type), +pub enum ShaderInput { + UniformBuffer(E::BufferHandle, Type), + StorageBuffer(E::BufferHandle, Type), + TextureBuffer(E::TextureHandle, Type), + StorageTextureBuffer(E::TextureHandle, Type), + TextureView(E::TextureView, Type), /// A struct representing a work group memory buffer. This cannot be accessed by the CPU. WorkGroupMemory(usize, Type), Constant(GPUConstant), - OutputBuffer(BufferHandle, Type), - ReadBackBuffer(BufferHandle, Type), + OutputBuffer(E::BufferHandle, Type), + ReadBackBuffer(E::BufferHandle, Type), +} + +unsafe impl StaticType for ShaderInput +where + E: GpuExecutor, +{ + type Static = Self; +} + +pub enum BindingType<'a, E: GpuExecutor> { + UniformBuffer(&'a E::BufferHandle), + StorageBuffer(&'a E::BufferHandle), + TextureView(&'a E::TextureView), } /// Extract the buffer handle from a shader input. -impl ShaderInput { - pub fn buffer(&self) -> Option<&BufferHandle> { +impl ShaderInput { + pub fn binding(&self) -> Option> { + match self { + ShaderInput::UniformBuffer(buffer, _) => Some(BindingType::UniformBuffer(buffer)), + ShaderInput::StorageBuffer(buffer, _) => Some(BindingType::StorageBuffer(buffer)), + ShaderInput::WorkGroupMemory(_, _) => None, + ShaderInput::Constant(_) => None, + ShaderInput::TextureBuffer(_, _) => None, + ShaderInput::StorageTextureBuffer(_, _) => None, + ShaderInput::TextureView(tex, _) => Some(BindingType::TextureView(tex)), + ShaderInput::OutputBuffer(buffer, _) => Some(BindingType::StorageBuffer(buffer)), + ShaderInput::ReadBackBuffer(buffer, _) => Some(BindingType::StorageBuffer(buffer)), + } + } + pub fn buffer(&self) -> Option<&E::BufferHandle> { match self { ShaderInput::UniformBuffer(buffer, _) => Some(buffer), ShaderInput::StorageBuffer(buffer, _) => Some(buffer), ShaderInput::WorkGroupMemory(_, _) => None, ShaderInput::Constant(_) => None, + ShaderInput::TextureBuffer(_, _) => None, + ShaderInput::StorageTextureBuffer(_, _) => None, + ShaderInput::TextureView(_tex, _) => None, ShaderInput::OutputBuffer(buffer, _) => Some(buffer), ShaderInput::ReadBackBuffer(buffer, _) => Some(buffer), } } + pub fn texture(&self) -> Option<&E::TextureHandle> { + match self { + ShaderInput::UniformBuffer(_, _) => None, + ShaderInput::StorageBuffer(_, _) => None, + ShaderInput::WorkGroupMemory(_, _) => None, + ShaderInput::Constant(_) => None, + ShaderInput::TextureBuffer(tex, _) => Some(tex), + ShaderInput::StorageTextureBuffer(tex, _) => Some(tex), + ShaderInput::TextureView(_, _) => None, + ShaderInput::OutputBuffer(_, _) => None, + ShaderInput::ReadBackBuffer(_, _) => None, + } + } pub fn ty(&self) -> Type { match self { ShaderInput::UniformBuffer(_, ty) => ty.clone(), ShaderInput::StorageBuffer(_, ty) => ty.clone(), ShaderInput::WorkGroupMemory(_, ty) => ty.clone(), ShaderInput::Constant(c) => c.ty(), + ShaderInput::TextureBuffer(_, ty) => ty.clone(), + ShaderInput::StorageTextureBuffer(_, ty) => ty.clone(), + ShaderInput::TextureView(_, ty) => ty.clone(), ShaderInput::OutputBuffer(_, ty) => ty.clone(), ShaderInput::ReadBackBuffer(_, ty) => ty.clone(), } @@ -133,8 +257,8 @@ pub struct Shader<'a> { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ShaderIO { - pub inputs: Vec>, - pub output: ShaderInput<()>, + pub inputs: Vec, + pub output: AbstractShaderInput, } pub struct StorageBufferOptions { @@ -144,6 +268,12 @@ pub struct StorageBufferOptions { pub storage: bool, } +pub enum TextureBufferOptions { + Storage, + Texture, + Surface, +} + pub trait ToUniformBuffer: StaticType { fn to_bytes(&self) -> Cow<[u8]>; } @@ -168,9 +298,61 @@ impl ToStorageBuffer for Vec { } } +pub trait TextureFormat { + fn format() -> TextureBufferType; +} + +impl TextureFormat for Color { + fn format() -> TextureBufferType { + TextureBufferType::Rgba32Float + } +} +impl TextureFormat for SRGBA8 { + fn format() -> TextureBufferType { + TextureBufferType::Rgba8Srgb + } +} + +pub enum TextureBufferType { + Rgba32Float, + Rgba8Srgb, +} + +pub trait ToTextureBuffer: StaticType { + fn to_bytes(&self) -> Cow<[u8]>; + fn ty() -> Type; + fn format() -> TextureBufferType; + fn size(&self) -> (u32, u32); +} + +impl ToTextureBuffer for Image +where + T::Static: Pixel, +{ + fn to_bytes(&self) -> Cow<[u8]> { + Cow::Borrowed(bytemuck::cast_slice(self.data.as_slice())) + } + fn ty() -> Type { + concrete!(T) + } + fn format() -> TextureBufferType { + T::format() + } + fn size(&self) -> (u32, u32) { + (self.width, self.height) + } +} + /// Collection of all arguments that are passed to the shader. pub struct Bindgroup { - pub buffers: Vec>>, + pub buffers: Vec>>, +} + +unsafe impl StaticType for Bindgroup +where + E::Static: GpuExecutor, +{ + type Static = Bindgroup; } /// A struct representing a compute pipeline. @@ -178,7 +360,14 @@ pub struct PipelineLayout { pub shader: E::ShaderHandle, pub entry_point: String, pub bind_group: Bindgroup, - pub output_buffer: Arc>, + pub output_buffer: Arc>, +} + +unsafe impl StaticType for PipelineLayout +where + E::Static: GpuExecutor, +{ + type Static = PipelineLayout; } /// Extracts arguments from the function arguments and wraps them in a node. @@ -205,7 +394,7 @@ pub struct UniformNode { } #[node_macro::node_fn(UniformNode)] -fn uniform_node(data: T, executor: &'input E) -> ShaderInput { +async fn uniform_node<'a: 'input, T: ToUniformBuffer, E: GpuExecutor + 'a>(data: T, executor: &'a E) -> ShaderInput { executor.create_uniform_buffer(data).unwrap() } @@ -214,7 +403,7 @@ pub struct StorageNode { } #[node_macro::node_fn(StorageNode)] -fn storage_node(data: T, executor: &'input E) -> ShaderInput { +async fn storage_node<'a: 'input, T: ToStorageBuffer, E: GpuExecutor + 'a>(data: T, executor: &'a E) -> ShaderInput { executor .create_storage_buffer( data, @@ -233,7 +422,7 @@ pub struct PushNode { } #[node_macro::node_fn(PushNode)] -fn push_node(mut vec: Vec, value: T) { +async fn push_node(mut vec: Vec, value: T) { vec.push(value); } @@ -243,8 +432,8 @@ pub struct CreateOutputBufferNode { } #[node_macro::node_fn(CreateOutputBufferNode)] -fn create_output_buffer_node(size: usize, executor: &'input E, ty: Type) -> ShaderInput { - executor.create_output_buffer(size, ty, true).unwrap() +async fn create_output_buffer_node<'a: 'input, E: GpuExecutor + 'a>(size: usize, executor: &'a E, ty: Type) -> Arc> { + Arc::new(executor.create_output_buffer(size, ty, true).unwrap()) } pub struct CreateComputePassNode { @@ -254,12 +443,7 @@ pub struct CreateComputePassNode { } #[node_macro::node_fn(CreateComputePassNode)] -fn create_compute_pass_node<'any_input, E: 'any_input + GpuExecutor>( - layout: PipelineLayout, - executor: &'any_input E, - output: ShaderInput, - instances: ComputePassDimensions, -) -> E::CommandBuffer { +async fn create_compute_pass_node<'a: 'input, E: 'a + GpuExecutor>(layout: PipelineLayout, executor: &'a E, output: ShaderInput, instances: ComputePassDimensions) -> E::CommandBuffer { executor.create_compute_pass(&layout, Some(output.into()), instances).unwrap() } @@ -271,7 +455,7 @@ pub struct CreatePipelineLayoutNode<_E, EntryPoint, Bindgroup, OutputBuffer> { } #[node_macro::node_fn(CreatePipelineLayoutNode<_E>)] -fn create_pipeline_layout_node<_E: GpuExecutor>(shader: _E::ShaderHandle, entry_point: String, bind_group: Bindgroup<_E>, output_buffer: Arc>) -> PipelineLayout<_E> { +async fn create_pipeline_layout_node<_E: GpuExecutor>(shader: _E::ShaderHandle, entry_point: String, bind_group: Bindgroup<_E>, output_buffer: Arc>) -> PipelineLayout<_E> { PipelineLayout { shader, entry_point, @@ -285,15 +469,68 @@ pub struct ExecuteComputePipelineNode { } #[node_macro::node_fn(ExecuteComputePipelineNode)] -fn execute_compute_pipeline_node(encoder: E::CommandBuffer, executor: &'input mut E) { +async fn execute_compute_pipeline_node<'a: 'input, E: 'a + GpuExecutor>(encoder: E::CommandBuffer, executor: &'a E) { executor.execute_compute_pipeline(encoder).unwrap(); } -// TODO -// pub struct ReadOutputBufferNode { -// executor: Executor, -// } -// #[node_macro::node_fn(ReadOutputBufferNode)] -// fn read_output_buffer_node(buffer: E::BufferHandle, executor: &'input mut E) -> Vec { -// executor.read_output_buffer(buffer).await.unwrap() -// } +pub struct ReadOutputBufferNode { + executor: Executor, + _compute_pass: ComputePass, +} +#[node_macro::node_fn(ReadOutputBufferNode)] +async fn read_output_buffer_node<'a: 'input, E: 'a + GpuExecutor>(buffer: Arc>, executor: &'a E, _compute_pass: ()) -> Vec { + executor.read_output_buffer(buffer).await.unwrap() +} + +pub struct CreateGpuSurfaceNode {} + +#[node_macro::node_fn(CreateGpuSurfaceNode)] +async fn create_gpu_surface<'a: 'input, E: 'a + GpuExecutor, Io: ApplicationIo>(editor_api: EditorApi<'a, Io>) -> SurfaceHandle { + let canvas = editor_api.application_io.create_surface(); + let executor = editor_api.application_io.gpu_executor().unwrap(); + executor.create_surface(canvas).unwrap() +} + +pub struct RenderTextureNode { + surface: Surface, + executor: EditorApi, +} + +#[derive(Clone)] +pub struct ShaderInputFrame { + shader_input: Arc>, + transform: DAffine2, +} + +unsafe impl StaticType for ShaderInputFrame +where + E::Static: GpuExecutor, +{ + type Static = ShaderInputFrame; +} + +#[node_macro::node_fn(RenderTextureNode)] +async fn render_texture_node<'a: 'input, E: 'a + GpuExecutor>(image: ShaderInputFrame, surface: Arc>, executor: &'a E) -> SurfaceFrame { + let surface_id = surface.surface_id; + + executor.create_render_pass(image.shader_input, surface).unwrap(); + + SurfaceFrame { + surface_id, + transform: image.transform, + } +} + +pub struct UploadTextureNode { + executor: E, +} + +#[node_macro::node_fn(UploadTextureNode)] +async fn upload_texture<'a: 'input, E: 'a + GpuExecutor>(input: ImageFrame, executor: &'a E) -> ShaderInputFrame { + let shader_input = executor.create_texture_buffer(input.image, TextureBufferOptions::Texture).unwrap(); + + ShaderInputFrame { + shader_input: Arc::new(shader_input), + transform: input.transform, + } +} diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 9279d9749..7f5cab88c 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -394,9 +394,9 @@ impl NodeNetwork { ( 0, DocumentNode { - name: "CacheNode".to_string(), + name: "MemoNode".to_string(), inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::Network(ty)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")), + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode")), ..Default::default() }, ), diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index b6f48bf75..5832ebac6 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -299,8 +299,8 @@ impl<'a> TaggedValue { x if x == TypeId::of::() => Ok(TaggedValue::Artboard(*downcast(input).unwrap())), x if x == TypeId::of::() => Ok(TaggedValue::IVec2(*downcast(input).unwrap())), x if x == TypeId::of::() => Ok(TaggedValue::SurfaceFrame(*downcast(input).unwrap())), - x if x == TypeId::of::() => { - let frame = *downcast::(input).unwrap(); + x if x == TypeId::of::() => { + let frame = *downcast::(input).unwrap(); Ok(TaggedValue::SurfaceFrame(frame.into())) } _ => Err(format!("Cannot convert {:?} to TaggedValue", DynAny::type_name(input.as_ref()))), diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 80f8fec1c..b9690e4db 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -9,16 +9,12 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["wgpu"] -gpu = [ - "graphene-core/gpu", - "gpu-compiler-bin-wrapper", - "compilation-client", - "gpu-executor", -] +default = ["wasm"] +gpu = ["graphene-core/gpu", "gpu-compiler-bin-wrapper", "compilation-client", "gpu-executor"] vulkan = ["gpu", "vulkan-executor"] wgpu = ["gpu", "wgpu-executor"] quantization = ["autoquant"] +wasm = ["wasm-bindgen", "web-sys", "js-sys"] [dependencies] @@ -29,6 +25,7 @@ graphene-core = { path = "../gcore", features = [ "async", "std", "serde", + "alloc", ], default-features = false } dyn-any = { path = "../../libraries/dyn-any", features = ["derive"] } graph-craft = { path = "../graph-craft" } @@ -53,8 +50,25 @@ xxhash-rust = { workspace = true } serde_json = "1.0.96" reqwest = { version = "0.11.17", features = ["rustls", "rustls-tls"] } futures = "0.3.28" +wasm-bindgen = { version = "0.2.84", optional = true } +js-sys = { version = "0.3.55", optional = true } +wgpu-types = "0.16.0" +wgpu = "0.16.1" [dependencies.serde] version = "1.0" optional = true features = ["derive"] + + +[dependencies.web-sys] +version = "0.3.4" +optional = true +features = [ + "Window", + "CanvasRenderingContext2d", + "ImageData", + "Document", + "HtmlCanvasElement", + "ImageBitmapRenderingContext", +] diff --git a/node-graph/gstd/src/executor.rs b/node-graph/gstd/src/gpu_nodes.rs similarity index 94% rename from node-graph/gstd/src/executor.rs rename to node-graph/gstd/src/gpu_nodes.rs index 764faba75..3b18a4daf 100644 --- a/node-graph/gstd/src/executor.rs +++ b/node-graph/gstd/src/gpu_nodes.rs @@ -6,10 +6,12 @@ use graph_craft::document::*; use graph_craft::proto::*; use graphene_core::raster::*; use graphene_core::*; -use wgpu_executor::NewExecutor; +use wgpu_executor::WgpuExecutor; use std::sync::Arc; +use crate::wasm_application_io::WasmApplicationIo; + pub struct GpuCompiler { typing_context: TypingContext, io: ShaderIO, @@ -37,12 +39,13 @@ async fn compile_gpu(node: &'input DocumentNode, mut typing_context: TypingConte compilation_client::compile(proto_networks, input_types, output_types, io).await.unwrap() } -pub struct MapGpuNode { +pub struct MapGpuNode { node: Node, + editor_api: EditorApi, } #[node_macro::node_fn(MapGpuNode)] -async fn map_gpu(image: ImageFrame, node: DocumentNode) -> ImageFrame { +async fn map_gpu<'a: 'input>(image: ImageFrame, node: DocumentNode, editor_api: graphene_core::application_io::EditorApi<'a, WasmApplicationIo>) -> ImageFrame { log::debug!("Executing gpu node"); let compiler = graph_craft::graphene_compiler::Compiler {}; let inner_network = NodeNetwork::value_network(node); @@ -121,7 +124,24 @@ async fn map_gpu(image: ImageFrame, node: DocumentNode) -> ImageFrame, node: DocumentNode) -> ImageFrame, background: ImageFrame, - canvases: RefCell>, + #[cfg(feature = "wgpu")] + pub(crate) gpu_executor: Option, } impl WasmApplicationIo { - pub fn new() -> Self { - Self::default() + pub async fn new() -> Self { + Self { + ids: RefCell::new(0), + #[cfg(feature = "wgpu")] + gpu_executor: WgpuExecutor::new().await, + } } } @@ -29,24 +36,36 @@ unsafe impl StaticType for WasmApplicationIo { type Static = WasmApplicationIo; } -pub type WasmEditorApi<'a> = super::EditorApi<'a, WasmApplicationIo>; +impl<'a> From> for &'a WasmApplicationIo { + fn from(editor_api: WasmEditorApi<'a>) -> Self { + editor_api.application_io + } +} +#[cfg(feature = "wgpu")] +impl<'a> From<&'a WasmApplicationIo> for &'a WgpuExecutor { + fn from(app_io: &'a WasmApplicationIo) -> Self { + app_io.gpu_executor.as_ref().unwrap() + } +} + +pub type WasmEditorApi<'a> = graphene_core::application_io::EditorApi<'a, WasmApplicationIo>; impl ApplicationIo for WasmApplicationIo { - type Surface = CanvasRenderingContext2d; + type Surface = HtmlCanvasElement; + #[cfg(feature = "wgpu")] + type Executor = WgpuExecutor; + #[cfg(not(feature = "wgpu"))] + type Executor = (); fn create_surface(&self) -> SurfaceHandle { let wrapper = || { let document = window().expect("should have a window in this context").document().expect("window should have a document"); let canvas: HtmlCanvasElement = document.create_element("canvas")?.dyn_into::()?; - // TODO: replace "2d" with "bitmaprenderer" once we switch to ImageBitmap (lives on gpu) from ImageData (lives on cpu) - let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::().unwrap(); let mut guard = self.ids.borrow_mut(); let id = SurfaceId(*guard); *guard += 1; - self.canvases.borrow_mut().insert(id, context.clone()); // store the canvas in the global scope so it doesn't get garbage collected - let window = window().expect("should have a window in this context"); let window = Object::from(window); @@ -60,21 +79,19 @@ impl ApplicationIo for WasmApplicationIo { // Convert key and value to JsValue let js_key = JsValue::from_str(format!("canvas{}", id.0).as_str()); - let js_value = JsValue::from(context.clone()); + let js_value = JsValue::from(canvas.clone()); let canvases = Object::from(canvases.unwrap()); // Use Reflect API to set property Reflect::set(&canvases, &js_key, &js_value)?; - Ok::<_, JsValue>(SurfaceHandle { surface_id: id, surface: context }) + Ok::<_, JsValue>(SurfaceHandle { surface_id: id, surface: canvas }) }; wrapper().expect("should be able to set canvas in global scope") } fn destroy_surface(&self, surface_id: SurfaceId) { - self.canvases.borrow_mut().remove(&surface_id); - let window = window().expect("should have a window in this context"); let window = Object::from(window); @@ -93,15 +110,20 @@ impl ApplicationIo for WasmApplicationIo { wrapper().expect("should be able to set canvas in global scope") } + + #[cfg(feature = "wgpu")] + fn gpu_executor(&self) -> Option<&Self::Executor> { + self.gpu_executor.as_ref() + } } -pub type WasmSurfaceHandle = SurfaceHandle; -pub type WasmSurfaceHandleFrame = SurfaceHandleFrame; +pub type WasmSurfaceHandle = SurfaceHandle; +pub type WasmSurfaceHandleFrame = SurfaceHandleFrame; pub struct CreateSurfaceNode {} #[node_macro::node_fn(CreateSurfaceNode)] -fn create_surface_node<'a: 'input>(editor: WasmEditorApi<'a>) -> Arc> { +async fn create_surface_node<'a: 'input>(editor: WasmEditorApi<'a>) -> Arc> { editor.application_io.create_surface().into() } @@ -110,15 +132,17 @@ pub struct DrawImageFrameNode { } #[node_macro::node_fn(DrawImageFrameNode)] -async fn draw_image_frame_node<'a: 'input>(image: ImageFrame, surface_handle: Arc>) -> SurfaceHandleFrame { +async fn draw_image_frame_node<'a: 'input>(image: ImageFrame, surface_handle: Arc>) -> SurfaceHandleFrame { let image_data = image.image.data; let array: Clamped<&[u8]> = Clamped(bytemuck::cast_slice(image_data.as_slice())); if image.image.width > 0 && image.image.height > 0 { - let canvas = surface_handle.surface.canvas().expect("Failed to get canvas"); + let canvas = &surface_handle.surface; canvas.set_width(image.image.width); canvas.set_height(image.image.height); - let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(array, image.image.width, image.image.height).expect("Failed to construct ImageData"); - surface_handle.surface.put_image_data(&image_data, 0.0, 0.0).unwrap(); + // TODO: replace "2d" with "bitmaprenderer" once we switch to ImageBitmap (lives on gpu) from ImageData (lives on cpu) + let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::().unwrap(); + let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(array, image.image.width as u32, image.image.height as u32).expect("Failed to construct ImageData"); + context.put_image_data(&image_data, 0.0, 0.0).unwrap(); } SurfaceHandleFrame { surface_handle, diff --git a/node-graph/interpreted-executor/Cargo.toml b/node-graph/interpreted-executor/Cargo.toml index e7493d1ec..2c386feb4 100644 --- a/node-graph/interpreted-executor/Cargo.toml +++ b/node-graph/interpreted-executor/Cargo.toml @@ -16,6 +16,8 @@ quantization = ["graphene-std/quantization"] graphene-core = { path = "../gcore", features = ["async", "std"] } graphene-std = { path = "../gstd" } graph-craft = { path = "../graph-craft" } +gpu-executor = { path = "../gpu-executor" } +wgpu-executor = { path = "../wgpu-executor" } dyn-any = { path = "../../libraries/dyn-any", features = [ "log-bad-types", "glam", diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 79cb681ed..1b30df5d2 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -8,15 +8,19 @@ use graphene_core::structural::Then; use graphene_core::value::{ClonedNode, CopiedNode, ValueNode}; use graphene_core::vector::brush_stroke::BrushStroke; use graphene_core::vector::VectorData; -use graphene_core::wasm_application_io::WasmSurfaceHandle; -use graphene_core::wasm_application_io::*; +use graphene_core::{application_io::SurfaceHandle, SurfaceFrame, WasmSurfaceHandleFrame}; use graphene_core::{concrete, generic}; use graphene_core::{fn_type, raster::*}; use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor}; use graphene_core::{Node, NodeIO, NodeIOTypes}; use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode}; +use graphene_std::wasm_application_io::*; +#[cfg(feature = "gpu")] +use gpu_executor::{GpuExecutor, ShaderInput, ShaderInputFrame}; use graphene_std::raster::*; +use graphene_std::wasm_application_io::WasmEditorApi; +use wgpu_executor::WgpuExecutor; use dyn_any::StaticType; use glam::{DAffine2, DVec2}; @@ -79,7 +83,7 @@ macro_rules! async_node { args.reverse(); let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node"))),*); let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); - any.into_type_erased() + Box::new(any) as TypeErasedBox }) }, { @@ -181,7 +185,7 @@ fn node_registry() -> HashMap, input: &ImageFrame, params: []), - register_node!(graphene_core::ops::CloneNode<_>, input: &graphene_core::EditorApi, params: []), + register_node!(graphene_core::ops::CloneNode<_>, input: &WasmEditorApi, params: []), 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]), @@ -190,9 +194,11 @@ fn node_registry() -> HashMap, input: &f64, params: [f64]), register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]), register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]), - register_node!(graphene_core::ops::SomeNode, input: graphene_core::EditorApi, params: []), - register_node!(graphene_core::ops::IntoNode<_, ImageFrame>, input: ImageFrame, params: []), - register_node!(graphene_core::ops::IntoNode<_, ImageFrame>, input: ImageFrame, params: []), + register_node!(graphene_core::ops::SomeNode, input: WasmEditorApi, params: []), + async_node!(graphene_core::ops::IntoNode<_, ImageFrame>, input: ImageFrame, output: ImageFrame, params: []), + async_node!(graphene_core::ops::IntoNode<_, ImageFrame>, input: ImageFrame, output: ImageFrame, params: []), + #[cfg(feature = "gpu")] + async_node!(graphene_core::ops::IntoNode<_, &WgpuExecutor>, input: WasmEditorApi, output: &WgpuExecutor, params: []), register_node!(graphene_std::raster::DownresNode<_>, input: ImageFrame, params: []), register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame, params: [ImageFrame]), register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame, params: [ImageFrame]), @@ -244,26 +250,68 @@ fn node_registry() -> HashMap, input: DAffine2, params: [Color]), register_node!(graphene_core::memo::MonitorNode<_>, input: ImageFrame, params: []), register_node!(graphene_core::memo::MonitorNode<_>, input: graphene_core::GraphicGroup, params: []), - register_node!(graphene_core::wasm_application_io::CreateSurfaceNode, input: graphene_core::EditorApi, params: []), + async_node!(graphene_std::wasm_application_io::CreateSurfaceNode, input: WasmEditorApi, output: Arc::Surface>>, params: []), async_node!( - graphene_core::wasm_application_io::DrawImageFrameNode<_>, + graphene_std::wasm_application_io::DrawImageFrameNode<_>, input: ImageFrame, output: WasmSurfaceHandleFrame, params: [Arc] ), #[cfg(feature = "gpu")] + async_node!(gpu_executor::UniformNode<_>, input: f32, output: ShaderInput, params: [&WgpuExecutor]), + #[cfg(feature = "gpu")] + async_node!(gpu_executor::StorageNode<_>, input: Vec, output: ShaderInput, params: [&WgpuExecutor]), + #[cfg(feature = "gpu")] + async_node!( + gpu_executor::PushNode<_>, + input: Vec>, + output: Vec>, + params: [ShaderInput] + ), + #[cfg(feature = "gpu")] + async_node!(gpu_executor::CreateOutputBufferNode<_, _>, input: usize, output: gpu_executor::ShaderInput, params: [&WgpuExecutor, Type]), + #[cfg(feature = "gpu")] + async_node!(gpu_executor::CreateComputePassNode<_, _, _>, input: gpu_executor::PipelineLayout, output: ::CommandBuffer, params: [&WgpuExecutor, ShaderInput, gpu_executor::ComputePassDimensions]), + #[cfg(feature = "gpu")] + async_node!(gpu_executor::CreatePipelineLayoutNode<_, _, _, _>, input: ::ShaderHandle, output: gpu_executor::PipelineLayout, params: [String, gpu_executor::Bindgroup, Arc>]), + #[cfg(feature = "gpu")] + async_node!( + gpu_executor::ExecuteComputePipelineNode<_>, + input: ::CommandBuffer, + output: (), + params: [&WgpuExecutor] + ), + #[cfg(feature = "gpu")] + async_node!(gpu_executor::ReadOutputBufferNode<_, _>, input: Arc>, output: Vec, params: [&WgpuExecutor, ()]), + #[cfg(all(feature = "gpu", target_arch = "wasm32"))] + async_node!(gpu_executor::CreateGpuSurfaceNode, input: WasmEditorApi, output: Arc::Surface>>, params: []), + #[cfg(feature = "gpu")] + async_node!(gpu_executor::RenderTextureNode<_, _>, input: ShaderInputFrame, output: SurfaceFrame, params: [Arc::Surface>>, &WgpuExecutor]), + #[cfg(feature = "gpu")] + async_node!( + gpu_executor::UploadTextureNode<_>, + input: ImageFrame, + output: ShaderInputFrame, + params: [&WgpuExecutor] + ), + #[cfg(feature = "gpu")] vec![( NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode<_>"), |args| { Box::pin(async move { let document_node: DowncastBothNode<(), graph_craft::document::DocumentNode> = DowncastBothNode::new(args[0].clone()); + let editor_api: DowncastBothNode<(), WasmEditorApi> = DowncastBothNode::new(args[1].clone()); //let document_node = ClonedNode::new(document_node.eval(())); - let node = graphene_std::executor::MapGpuNode::new(document_node); + let node = graphene_std::gpu_nodes::MapGpuNode::new(document_node, editor_api); let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); any.into_type_erased() }) }, - NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![fn_type!(graph_craft::document::DocumentNode)]), + NodeIOTypes::new( + concrete!(ImageFrame), + concrete!(SurfaceFrame), + vec![fn_type!(graph_craft::document::DocumentNode), fn_type!(WasmEditorApi)], + ), )], #[cfg(feature = "gpu")] vec![( @@ -273,7 +321,7 @@ fn node_registry() -> HashMap> = DowncastBothNode::new(args[0].clone()); let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1].clone()); let opacity: DowncastBothNode<(), f32> = DowncastBothNode::new(args[2].clone()); - let node = graphene_std::executor::BlendGpuImageNode::new(background, blend_mode, opacity); + let node = graphene_std::gpu_nodes::BlendGpuImageNode::new(background, blend_mode, opacity); let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); any.into_type_erased() @@ -374,47 +422,39 @@ fn node_registry() -> HashMap, params: [f64]), raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]), register_node!(graphene_core::memo::LetNode<_>, input: Option>, params: []), - register_node!(graphene_core::memo::LetNode<_>, input: Option, params: []), + register_node!(graphene_core::memo::LetNode<_>, input: Option, params: []), + async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: ImageFrame, params: [ImageFrame]), + async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: VectorData, params: [VectorData]), async_node!( graphene_core::memo::EndLetNode<_>, - input: graphene_core::EditorApi, - output: ImageFrame, - params: [ImageFrame] - ), - async_node!(graphene_core::memo::EndLetNode<_>, input: graphene_core::EditorApi, output: VectorData, params: [VectorData]), - async_node!( - graphene_core::memo::EndLetNode<_>, - input: graphene_core::EditorApi, + input: WasmEditorApi, output: graphene_core::GraphicGroup, params: [graphene_core::GraphicGroup] ), async_node!( graphene_core::memo::EndLetNode<_>, - input: graphene_core::EditorApi, + input: WasmEditorApi, output: graphene_core::Artboard, params: [graphene_core::Artboard] ), async_node!( graphene_core::memo::EndLetNode<_>, - input: graphene_core::EditorApi, + input: WasmEditorApi, output: WasmSurfaceHandleFrame, params: [WasmSurfaceHandleFrame] ), + async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: SurfaceFrame, params: [SurfaceFrame]), vec![( NodeIdentifier::new("graphene_core::memo::RefNode<_, _>"), |args| { Box::pin(async move { - let node: DowncastBothNode, graphene_core::EditorApi> = graphene_std::any::DowncastBothNode::new(args[0].clone()); + let node: DowncastBothNode, WasmEditorApi> = graphene_std::any::DowncastBothNode::new(args[0].clone()); let node = >::new(node); let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); any.into_type_erased() }) }, - NodeIOTypes::new( - concrete!(()), - concrete!(graphene_core::EditorApi), - vec![fn_type!(Option, graphene_core::EditorApi)], - ), + NodeIOTypes::new(concrete!(()), concrete!(WasmEditorApi), vec![fn_type!(Option, WasmEditorApi)]), )], async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Image, params: [Image]), async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: ImageFrame, params: [ImageFrame]), @@ -443,9 +483,9 @@ fn node_registry() -> HashMap>, params: [Vec] ), - register_node!(graphene_core::text::TextGenerator<_, _, _>, input: graphene_core::EditorApi, params: [String, graphene_core::text::Font, f64]), + register_node!(graphene_core::text::TextGenerator<_, _, _>, input: WasmEditorApi, params: [String, graphene_core::text::Font, f64]), register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []), - register_node!(graphene_core::ExtractImageFrame, input: graphene_core::EditorApi, 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: ImageFrame, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::GraphicGroup, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), diff --git a/node-graph/wgpu-executor/Cargo.toml b/node-graph/wgpu-executor/Cargo.toml index 974a26e22..5d99ea723 100644 --- a/node-graph/wgpu-executor/Cargo.toml +++ b/node-graph/wgpu-executor/Cargo.toml @@ -10,10 +10,19 @@ default = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -graphene-core = { path = "../gcore", features = ["async", "std", "alloc", "gpu"] } -graph-craft = {path = "../graph-craft" } +graphene-core = { path = "../gcore", features = [ + "async", + "std", + "alloc", + "gpu", +] } +graph-craft = { path = "../graph-craft" } gpu-executor = { path = "../gpu-executor" } -dyn-any = { path = "../../libraries/dyn-any", features = ["log-bad-types", "rc", "glam"] } +dyn-any = { path = "../../libraries/dyn-any", features = [ + "log-bad-types", + "rc", + "glam", +] } future-executor = { path = "../future-executor" } num-traits = "0.2" log = "0.4" @@ -21,9 +30,11 @@ serde = { version = "1", features = ["derive", "rc"], optional = true } glam = { version = "0.22" } base64 = "0.13" -bytemuck = {version = "1.8" } +bytemuck = { version = "1.8" } anyhow = "1.0.66" wgpu = { version = "0.16", features = ["spirv"] } spirv = "0.2.0" futures-intrusive = "0.5.0" futures = "0.3.25" +web-sys = { version = "0.3.4", features = ["HtmlCanvasElement"] } +winit = "0.28.6" diff --git a/node-graph/wgpu-executor/src/context.rs b/node-graph/wgpu-executor/src/context.rs index bbdfb1fac..2285ad3a9 100644 --- a/node-graph/wgpu-executor/src/context.rs +++ b/node-graph/wgpu-executor/src/context.rs @@ -6,6 +6,7 @@ pub struct Context { pub device: Arc, pub queue: Arc, pub instance: Arc, + pub adapter: Arc, } impl Context { @@ -16,9 +17,9 @@ impl Context { // `request_adapter` instantiates the general connection to the GPU let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await?; - let limits = adapter.limits(); + //let limits = adapter.limits(); - log::trace!("Adapter limits: {:?}", limits); + //log::trace!("Adapter limits: {:?}", limits); // `request_device` instantiates the feature specific connection to the GPU, defining some parameters, // `features` being the available features. @@ -27,7 +28,7 @@ impl Context { &wgpu::DeviceDescriptor { label: None, features: wgpu::Features::empty(), - limits, + limits: Default::default(), }, None, ) @@ -42,6 +43,7 @@ impl Context { Some(Self { device: Arc::new(device), queue: Arc::new(queue), + adapter: Arc::new(adapter), instance: Arc::new(instance), }) } diff --git a/node-graph/wgpu-executor/src/lib.rs b/node-graph/wgpu-executor/src/lib.rs index 020f5888e..8b07cb785 100644 --- a/node-graph/wgpu-executor/src/lib.rs +++ b/node-graph/wgpu-executor/src/lib.rs @@ -2,36 +2,117 @@ mod context; mod executor; pub use context::Context; +use dyn_any::{DynAny, StaticType}; pub use executor::GpuExecutor; -use gpu_executor::{ComputePassDimensions, Shader, ShaderInput, StorageBufferOptions, ToStorageBuffer, ToUniformBuffer}; +use gpu_executor::{ComputePassDimensions, Shader, ShaderInput, StorageBufferOptions, TextureBufferOptions, TextureBufferType, ToStorageBuffer, ToUniformBuffer}; use graph_craft::Type; use anyhow::{bail, Result}; use futures::Future; +use graphene_core::application_io::{ApplicationIo, EditorApi, SurfaceHandle}; + use std::pin::Pin; use std::sync::Arc; -use wgpu::util::DeviceExt; -use wgpu::{Buffer, BufferDescriptor, CommandBuffer, ShaderModule}; -#[derive(Debug, Clone)] -pub struct NewExecutor { +use wgpu::util::DeviceExt; +use wgpu::{Buffer, BufferDescriptor, CommandBuffer, ShaderModule, Texture, TextureView}; + +#[cfg(target_arch = "wasm32")] +use web_sys::HtmlCanvasElement; + +#[derive(Debug, dyn_any::DynAny)] +pub struct WgpuExecutor { context: Context, + render_configuration: RenderConfiguration, } -impl gpu_executor::GpuExecutor for NewExecutor { - type ShaderHandle = ShaderModule; +impl<'a, T: ApplicationIo> From> for &'a WgpuExecutor { + fn from(editor_api: EditorApi<'a, T>) -> Self { + editor_api.application_io.gpu_executor().unwrap() + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +struct Vertex { + position: [f32; 3], + tex_coords: [f32; 2], +} + +impl Vertex { + fn desc() -> wgpu::VertexBufferLayout<'static> { + use std::mem; + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float32x2, + }, + ], + } + } +} + +const VERTICES: &[Vertex] = &[ + Vertex { + position: [-1., 1., 0.0], + tex_coords: [0., 0.], + }, // A + Vertex { + position: [-1., -1., 0.0], + tex_coords: [0., 1.], + }, // B + Vertex { + position: [1., 1., 0.0], + tex_coords: [1., 0.], + }, // C + Vertex { + position: [1., -1., 0.0], + tex_coords: [1., 1.], + }, // D +]; + +const INDICES: &[u16] = &[0, 1, 2, 2, 1, 3]; + +type WgpuShaderInput = ShaderInput; + +#[derive(Debug, DynAny)] +#[repr(transparent)] +pub struct CommandBufferWrapper(CommandBuffer); + +#[derive(Debug, DynAny)] +#[repr(transparent)] +pub struct ShaderModuleWrapper(ShaderModule); + +impl gpu_executor::GpuExecutor for WgpuExecutor { + type ShaderHandle = ShaderModuleWrapper; type BufferHandle = Buffer; - type CommandBuffer = CommandBuffer; + type TextureHandle = Texture; + type TextureView = TextureView; + type CommandBuffer = CommandBufferWrapper; + type Surface = wgpu::Surface; + #[cfg(target_arch = "wasm32")] + type Window = HtmlCanvasElement; + #[cfg(not(target_arch = "wasm32"))] + type Window = winit::window::Window; fn load_shader(&self, shader: Shader) -> Result { let shader_module = self.context.device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some(shader.name), source: wgpu::ShaderSource::SpirV(shader.source), }); - Ok(shader_module) + Ok(ShaderModuleWrapper(shader_module)) } - fn create_uniform_buffer(&self, data: T) -> Result> { + fn create_uniform_buffer(&self, data: T) -> Result { let bytes = data.to_bytes(); let buffer = self.context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, @@ -41,7 +122,7 @@ impl gpu_executor::GpuExecutor for NewExecutor { Ok(ShaderInput::UniformBuffer(buffer, Type::new::())) } - fn create_storage_buffer(&self, data: T, options: StorageBufferOptions) -> Result> { + fn create_storage_buffer(&self, data: T, options: StorageBufferOptions) -> Result { let bytes = data.to_bytes(); let mut usage = wgpu::BufferUsages::empty(); @@ -66,8 +147,44 @@ impl gpu_executor::GpuExecutor for NewExecutor { }); Ok(ShaderInput::StorageBuffer(buffer, data.ty())) } + fn create_texture_buffer(&self, data: T, options: TextureBufferOptions) -> Result { + 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::Surface => wgpu::TextureUsages::RENDER_ATTACHMENT, + }; + let format = match T::format() { + TextureBufferType::Rgba32Float => wgpu::TextureFormat::Rgba32Float, + TextureBufferType::Rgba8Srgb => wgpu::TextureFormat::Rgba8UnormSrgb, + }; - fn create_output_buffer(&self, len: usize, ty: Type, cpu_readable: bool) -> Result> { + let buffer = self.context.device.create_texture_with_data( + self.context.queue.as_ref(), + &wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: data.size().0, + height: data.size().1, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage, + view_formats: &[format], + }, + bytes.as_ref(), + ); + match options { + TextureBufferOptions::Storage => Ok(ShaderInput::StorageTextureBuffer(buffer, T::ty())), + TextureBufferOptions::Texture => Ok(ShaderInput::TextureBuffer(buffer, T::ty())), + TextureBufferOptions::Surface => Ok(ShaderInput::TextureBuffer(buffer, T::ty())), + } + } + + fn create_output_buffer(&self, len: usize, ty: Type, cpu_readable: bool) -> Result { log::debug!("Creating output buffer with len: {}", len); let create_buffer = |usage| { Ok::<_, anyhow::Error>(self.context.device.create_buffer(&BufferDescriptor { @@ -83,11 +200,11 @@ impl gpu_executor::GpuExecutor for NewExecutor { }; Ok(buffer) } - fn create_compute_pass(&self, layout: &gpu_executor::PipelineLayout, read_back: Option>>, instances: ComputePassDimensions) -> Result { + fn create_compute_pass(&self, layout: &gpu_executor::PipelineLayout, read_back: Option>, instances: ComputePassDimensions) -> Result { let compute_pipeline = self.context.device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { label: None, layout: None, - module: &layout.shader, + module: &layout.shader.0, entry_point: layout.entry_point.as_str(), }); let bind_group_layout = compute_pipeline.get_bind_group_layout(0); @@ -97,11 +214,15 @@ impl gpu_executor::GpuExecutor for NewExecutor { .buffers .iter() .chain(std::iter::once(&layout.output_buffer)) - .flat_map(|input| input.buffer()) + .flat_map(|input| input.binding()) .enumerate() .map(|(i, buffer)| wgpu::BindGroupEntry { binding: i as u32, - resource: buffer.as_entire_binding(), + resource: match buffer { + gpu_executor::BindingType::UniformBuffer(buf) => buf.as_entire_binding(), + gpu_executor::BindingType::StorageBuffer(buf) => buf.as_entire_binding(), + gpu_executor::BindingType::TextureView(buf) => wgpu::BindingResource::TextureView(buf), + }, }) .collect::>(); @@ -123,27 +244,77 @@ impl gpu_executor::GpuExecutor for NewExecutor { // Sets adds copy operation to command encoder. // Will copy data from storage buffer on GPU to staging buffer on CPU. if let Some(buffer) = read_back { - let ShaderInput::ReadBackBuffer(output, ty) = buffer.as_ref() else { + let ShaderInput::ReadBackBuffer(output, _ty) = buffer.as_ref() else { bail!("Tried to read back from a non read back buffer"); }; let size = output.size(); - assert_eq!(size, layout.output_buffer.buffer().unwrap().size()); + let ShaderInput::OutputBuffer(output_buffer, ty) = layout.output_buffer.as_ref() else { + bail!("Tried to read back from a non output buffer"); + }; + assert_eq!(size, output_buffer.size()); assert_eq!(ty, &layout.output_buffer.ty()); - encoder.copy_buffer_to_buffer( - layout.output_buffer.buffer().ok_or_else(|| anyhow::anyhow!("Tried to use an non buffer as the shader output"))?, - 0, - output, - 0, - size, - ); + encoder.copy_buffer_to_buffer(output_buffer, 0, output, 0, size); } // Submits command encoder for processing - Ok(encoder.finish()) + Ok(CommandBufferWrapper(encoder.finish())) + } + + fn create_render_pass(&self, texture: Arc>, canvas: Arc>) -> Result<()> { + let texture = texture.texture().expect("Expected texture input"); + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let output = canvas.as_ref().surface.get_current_texture()?; + let view = output.texture.create_view(&wgpu::TextureViewDescriptor { + format: Some(wgpu::TextureFormat::Rgba8Unorm), + ..Default::default() + }); + let output_texture_bind_group = self.context.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &self.render_configuration.texture_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&texture_view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&self.render_configuration.sampler), + }, + ], + label: Some("output_texture_bind_group"), + }); + + let mut encoder = self.context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Render Encoder") }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.render_configuration.render_pipeline); + render_pass.set_bind_group(0, &output_texture_bind_group, &[]); + 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.draw_indexed(0..self.render_configuration.num_indices, 0, 0..1); + } + + let encoder = encoder.finish(); + self.context.queue.submit(Some(encoder)); + output.present(); + + Ok(()) } fn execute_compute_pipeline(&self, encoder: Self::CommandBuffer) -> Result<()> { - self.context.queue.submit(Some(encoder)); + 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 @@ -152,7 +323,7 @@ impl gpu_executor::GpuExecutor for NewExecutor { Ok(()) } - fn read_output_buffer(&self, buffer: Arc>) -> Pin>>>> { + fn read_output_buffer(&self, buffer: Arc>) -> Pin>>>> { Box::pin(async move { if let ShaderInput::ReadBackBuffer(buffer, _) = buffer.as_ref() { let buffer_slice = buffer.slice(..); @@ -187,13 +358,168 @@ impl gpu_executor::GpuExecutor for NewExecutor { } else { bail!("Tried to read a non readback buffer") } - }) as _ + }) + } + + fn create_texture_view(&self, texture: ShaderInput) -> Result> { + //Ok(ShaderInput::TextureView(texture.create_view(&wgpu::TextureViewDescriptor::default()), ) ) + let ShaderInput::TextureBuffer(texture, ty) = &texture else { + bail!("Tried to create a texture view from a non texture"); + }; + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + Ok(ShaderInput::TextureView(view, ty.clone())) + } + + #[cfg(target_arch = "wasm32")] + fn create_surface(&self, canvas: graphene_core::WasmSurfaceHandle) -> Result> { + let surface = self.context.instance.create_surface_from_canvas(canvas.surface)?; + + let surface_caps = surface.get_capabilities(&self.context.adapter); + let surface_format = wgpu::TextureFormat::Rgba8Unorm; + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: surface_format, + width: 1920, + height: 1080, + present_mode: surface_caps.present_modes[0], + alpha_mode: wgpu::CompositeAlphaMode::PreMultiplied, + view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb], + }; + surface.configure(&self.context.device, &config); + Ok(SurfaceHandle { + surface_id: canvas.surface_id, + surface, + }) + } + #[cfg(not(target_arch = "wasm32"))] + fn create_surface(&self, window: SurfaceHandle) -> Result> { + let surface = unsafe { self.context.instance.create_surface(&window.surface) }?; + let surface_id = window.surface_id; + Ok(SurfaceHandle { surface_id, surface }) } } -impl NewExecutor { +impl WgpuExecutor { pub async fn new() -> Option { let context = Context::new().await?; - Some(Self { context }) + + let texture_bind_group_layout = context.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering), + count: None, + }, + ], + label: Some("texture_bind_group_layout"), + }); + + let sampler = context.device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Nearest, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + + let shader = context.device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }); + + let render_pipeline_layout = context.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[&texture_bind_group_layout], + push_constant_ranges: &[], + }); + + let render_pipeline = context.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: wgpu::TextureFormat::Rgba8Unorm, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent::REPLACE, + alpha: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + // Setting this to anything other than Fill requires Features::POLYGON_MODE_LINE + // or Features::POLYGON_MODE_POINT + polygon_mode: wgpu::PolygonMode::Fill, + // Requires Features::DEPTH_CLIP_CONTROL + unclipped_depth: false, + // Requires Features::CONSERVATIVE_RASTERIZATION + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + // If the pipeline will be used with a multiview render pass, this + // indicates how many array layers the attachments will have. + multiview: None, + }); + + let vertex_buffer = context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(VERTICES), + usage: wgpu::BufferUsages::VERTEX, + }); + let index_buffer = context.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(INDICES), + usage: wgpu::BufferUsages::INDEX, + }); + let num_indices = INDICES.len() as u32; + let render_configuration = RenderConfiguration { + vertex_buffer, + index_buffer, + num_indices, + render_pipeline, + texture_bind_group_layout, + sampler, + }; + + Some(Self { context, render_configuration }) } } + +#[derive(Debug)] +struct RenderConfiguration { + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + num_indices: u32, + render_pipeline: wgpu::RenderPipeline, + texture_bind_group_layout: wgpu::BindGroupLayout, + sampler: wgpu::Sampler, +} diff --git a/node-graph/wgpu-executor/src/shader.wgsl b/node-graph/wgpu-executor/src/shader.wgsl new file mode 100644 index 000000000..6598baa00 --- /dev/null +++ b/node-graph/wgpu-executor/src/shader.wgsl @@ -0,0 +1,43 @@ +// Vertex shader + +struct VertexInput { + @location(0) position: vec3, + @location(1) tex_coords: vec2, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) tex_coords: vec2, +} + +@vertex +fn vs_main( + model: VertexInput, +) -> VertexOutput { + var out: VertexOutput; + out.tex_coords = model.tex_coords; + out.clip_position = vec4(model.position, 1.0); + return out; +} + +// Fragment shader + +@group(0) @binding(0) +var t_diffuse: texture_2d; +@group(0)@binding(1) +var s_diffuse: sampler; + +fn linearToSRGB(color: vec3) -> vec3 { + let a = 0.055; + return select(pow(color, vec3(1.0 / 2.2)) * (1.0 + a) - a, + color / 12.92, + color <= vec3(0.0031308)); +} + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + var color = textureSample(t_diffuse, s_diffuse, in.tex_coords); + var linearColor = color.rgb; + var srgbColor = linearToSRGB(linearColor); + return vec4(srgbColor, color.a); +}