Lay groundwork for directly rendering to the canvas without a cpu roundrip (#1291)

* Add Texture handle type

* Add Texture View to shader inputs

* Implement basic rendering pipeline

* Render first texture using render pipeline

* Fix output color space

* Precompute the rendering pipeline

* Move gpu context creation to editor api

* Port gpu-executor nodes to node registry

* Fix canvas nodes and make code compile for non wasm targets

* Pin wasm-bindgen version

* Disable miri temoporarily for better ci times

* Fix formatting

* Remove unsafe block

* Bump wasm-pack version

* Bump wasm-bindgen version

* Add gpu feature guard for push node

* Make Into node async
This commit is contained in:
Dennis Kobert 2023-06-07 17:13:21 +02:00 committed by Keavon Chambers
parent 0c93a62d55
commit 45b04f4eb9
33 changed files with 1574 additions and 339 deletions

View file

@ -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

476
Cargo.lock generated
View file

@ -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"

View file

@ -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]

View file

@ -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<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
},
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<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
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),

View file

@ -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<NodeRuntimeMessage>,
sender: Sender<GenerationResponse>,
wasm_io: WasmApplicationIo,
wasm_io: Option<WasmApplicationIo>,
pub(crate) thumbnails: HashMap<LayerId, HashMap<NodeId, SvgSegmentList>>,
canvas_cache: HashMap<Vec<LayerId>, 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<ImageFrame<Color>>) -> Result<TaggedValue, String> {
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<T: dyn_any::StaticType>(&mut self, _old_network: &NodeNetwork, _node_path: &[NodeId], _input_index: usize, _editor_api: Cow<EditorApi<'_>>) -> Result<u64, String> {
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::<T>(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<Color>, resize: Option<DVec2>, format: image::ImageOutputFormat) -> Result<(Vec<u8>, (u32, u32)), String> {
use image::{ImageBuffer, Rgba};

View file

@ -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": {

View file

@ -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",

View file

@ -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);
});
}

View file

@ -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"

View file

@ -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"]

View file

@ -45,7 +45,7 @@ impl<S> From<SurfaceHandleFrame<S>> for SurfaceFrame {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct SurfaceHandle<Surface> {
pub surface_id: SurfaceId,
pub surface: Surface,
@ -55,7 +55,7 @@ unsafe impl<T: 'static> StaticType for SurfaceHandle<T> {
type Static = SurfaceHandle<T>;
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct SurfaceHandleFrame<Surface> {
pub surface_handle: Arc<SurfaceHandle<Surface>>,
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<Self::Surface>;
fn destroy_surface(&self, surface_id: SurfaceId);
fn gpu_executor(&self) -> Option<&Self::Executor> {
None
}
}
impl<T: ApplicationIo> ApplicationIo for &T {
type Surface = T::Surface;
type Executor = T::Executor;
fn create_surface(&self) -> SurfaceHandle<T::Surface> {
(**self).create_surface()
}
@ -100,6 +106,10 @@ impl<T: ApplicationIo> 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;

View file

@ -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<web_sys::HtmlCanvasElement>;
#[cfg(feature = "wasm")]
pub type WasmSurfaceHandleFrame = application_io::SurfaceHandleFrame<web_sys::HtmlCanvasElement>;

View file

@ -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<T>> for LetNode<T> {
}
}
impl<T> std::marker::Unpin for LetNode<T> {}
impl<T> LetNode<T> {
pub fn new() -> LetNode<T> {
LetNode { cache: Default::default() }

View file

@ -211,7 +211,7 @@ pub struct IntoNode<I, O> {
_o: PhantomData<O>,
}
#[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>,
{

View file

@ -88,7 +88,7 @@ impl BrushCacheImpl {
impl Hash for BrushCacheImpl {
// Zero hash.
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {}
fn hash<H: core::hash::Hasher>(&self, _state: &mut H) {}
}
#[derive(Clone, Debug, Default)]

View file

@ -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<Text, FontName, Size> {
}
#[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))
}

View file

@ -108,6 +108,10 @@ pub enum Type {
Future(Box<Type>),
}
unsafe impl StaticType for Type {
type Static = Self;
}
impl Type {
pub fn is_generic(&self) -> bool {
matches!(self, Type::Generic(_))

View file

@ -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"

View file

@ -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<T: gpu_executor::GpuExecutor>(input: &ShaderInput<T>, 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,)
}

View file

@ -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",
] }

View file

@ -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<Box<dyn Future<Output = Result<Vec<u8>>>>>;
#[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<TextureView>(&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<Self::ShaderHandle>;
fn create_uniform_buffer<T: ToUniformBuffer>(&self, data: T) -> Result<ShaderInput<Self::BufferHandle>>;
fn create_storage_buffer<T: ToStorageBuffer>(&self, data: T, options: StorageBufferOptions) -> Result<ShaderInput<Self::BufferHandle>>;
fn create_output_buffer(&self, len: usize, ty: Type, cpu_readable: bool) -> Result<ShaderInput<Self::BufferHandle>>;
fn create_compute_pass(&self, layout: &PipelineLayout<Self>, read_back: Option<Arc<ShaderInput<Self::BufferHandle>>>, instances: ComputePassDimensions) -> Result<Self::CommandBuffer>;
fn create_uniform_buffer<T: ToUniformBuffer>(&self, data: T) -> Result<ShaderInput<Self>>;
fn create_storage_buffer<T: ToStorageBuffer>(&self, data: T, options: StorageBufferOptions) -> Result<ShaderInput<Self>>;
fn create_texture_buffer<T: ToTextureBuffer>(&self, data: T, options: TextureBufferOptions) -> Result<ShaderInput<Self>>;
fn create_texture_view(&self, texture: ShaderInput<Self>) -> Result<ShaderInput<Self>>;
fn create_output_buffer(&self, len: usize, ty: Type, cpu_readable: bool) -> Result<ShaderInput<Self>>;
fn create_compute_pass(&self, layout: &PipelineLayout<Self>, read_back: Option<Arc<ShaderInput<Self>>>, instances: ComputePassDimensions) -> Result<Self::CommandBuffer>;
fn create_render_pass(&self, texture: Arc<ShaderInput<Self>>, canvas: Arc<SurfaceHandle<Self::Surface>>) -> Result<()>;
fn execute_compute_pipeline(&self, encoder: Self::CommandBuffer) -> Result<()>;
fn read_output_buffer(&self, buffer: Arc<ShaderInput<Self::BufferHandle>>) -> ReadBackFuture;
fn read_output_buffer(&self, buffer: Arc<ShaderInput<Self>>) -> ReadBackFuture;
fn create_surface(&self, window: SurfaceHandle<Self::Window>) -> Result<SurfaceHandle<Self::Surface>>;
}
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<Self::ShaderHandle> {
todo!()
}
fn create_uniform_buffer<T: ToUniformBuffer>(&self, _data: T) -> Result<ShaderInput<Self>> {
todo!()
}
fn create_storage_buffer<T: ToStorageBuffer>(&self, _data: T, _options: StorageBufferOptions) -> Result<ShaderInput<Self>> {
todo!()
}
fn create_texture_buffer<T: ToTextureBuffer>(&self, _data: T, _options: TextureBufferOptions) -> Result<ShaderInput<Self>> {
todo!()
}
fn create_output_buffer(&self, _len: usize, _ty: Type, _cpu_readable: bool) -> Result<ShaderInput<Self>> {
todo!()
}
fn create_compute_pass(&self, _layout: &PipelineLayout<Self>, _read_back: Option<Arc<ShaderInput<Self>>>, _instances: ComputePassDimensions) -> Result<Self::CommandBuffer> {
todo!()
}
fn execute_compute_pipeline(&self, _encoder: Self::CommandBuffer) -> Result<()> {
todo!()
}
fn create_render_pass(&self, _texture: Arc<ShaderInput<Self>>, _canvas: Arc<SurfaceHandle<Self::Surface>>) -> Result<()> {
todo!()
}
fn read_output_buffer(&self, _buffer: Arc<ShaderInput<Self>>) -> ReadBackFuture {
todo!()
}
fn create_texture_view(&self, _texture: ShaderInput<Self>) -> Result<ShaderInput<Self>> {
todo!()
}
fn create_surface(&self, _window: SurfaceHandle<Self::Window>) -> Result<SurfaceHandle<Self::Surface>> {
todo!()
}
}
type AbstractShaderInput = ShaderInput<DummyExecutor>;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
/// All the possible inputs to a shader.
pub enum ShaderInput<BufferHandle> {
UniformBuffer(BufferHandle, Type),
StorageBuffer(BufferHandle, Type),
pub enum ShaderInput<E: GpuExecutor + ?Sized> {
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<E: 'static> StaticType for ShaderInput<E>
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<BufferHandle> ShaderInput<BufferHandle> {
pub fn buffer(&self) -> Option<&BufferHandle> {
impl<E: GpuExecutor> ShaderInput<E> {
pub fn binding(&self) -> Option<BindingType<E>> {
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<ShaderInput<()>>,
pub output: ShaderInput<()>,
pub inputs: Vec<AbstractShaderInput>,
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<T: Pod + Zeroable + StaticTypeSized> ToStorageBuffer for Vec<T> {
}
}
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<T: Pod + Zeroable + StaticTypeSized + Pixel + TextureFormat> ToTextureBuffer for Image<T>
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<E: GpuExecutor + ?Sized> {
pub buffers: Vec<Arc<ShaderInput<E::BufferHandle>>>,
pub buffers: Vec<Arc<ShaderInput<E>>>,
}
unsafe impl<E: GpuExecutor + ?Sized + StaticType> StaticType for Bindgroup<E>
where
E::Static: GpuExecutor,
{
type Static = Bindgroup<E::Static>;
}
/// A struct representing a compute pipeline.
@ -178,7 +360,14 @@ pub struct PipelineLayout<E: GpuExecutor + ?Sized> {
pub shader: E::ShaderHandle,
pub entry_point: String,
pub bind_group: Bindgroup<E>,
pub output_buffer: Arc<ShaderInput<E::BufferHandle>>,
pub output_buffer: Arc<ShaderInput<E>>,
}
unsafe impl<E: GpuExecutor + ?Sized + StaticType> StaticType for PipelineLayout<E>
where
E::Static: GpuExecutor,
{
type Static = PipelineLayout<E::Static>;
}
/// Extracts arguments from the function arguments and wraps them in a node.
@ -205,7 +394,7 @@ pub struct UniformNode<Executor> {
}
#[node_macro::node_fn(UniformNode)]
fn uniform_node<T: ToUniformBuffer, E: GpuExecutor>(data: T, executor: &'input E) -> ShaderInput<E::BufferHandle> {
async fn uniform_node<'a: 'input, T: ToUniformBuffer, E: GpuExecutor + 'a>(data: T, executor: &'a E) -> ShaderInput<E> {
executor.create_uniform_buffer(data).unwrap()
}
@ -214,7 +403,7 @@ pub struct StorageNode<Executor> {
}
#[node_macro::node_fn(StorageNode)]
fn storage_node<T: ToStorageBuffer, E: GpuExecutor>(data: T, executor: &'input E) -> ShaderInput<E::BufferHandle> {
async fn storage_node<'a: 'input, T: ToStorageBuffer, E: GpuExecutor + 'a>(data: T, executor: &'a E) -> ShaderInput<E> {
executor
.create_storage_buffer(
data,
@ -233,7 +422,7 @@ pub struct PushNode<Value> {
}
#[node_macro::node_fn(PushNode)]
fn push_node<T>(mut vec: Vec<T>, value: T) {
async fn push_node<T>(mut vec: Vec<T>, value: T) {
vec.push(value);
}
@ -243,8 +432,8 @@ pub struct CreateOutputBufferNode<Executor, Ty> {
}
#[node_macro::node_fn(CreateOutputBufferNode)]
fn create_output_buffer_node<E: GpuExecutor>(size: usize, executor: &'input E, ty: Type) -> ShaderInput<E::BufferHandle> {
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<ShaderInput<E>> {
Arc::new(executor.create_output_buffer(size, ty, true).unwrap())
}
pub struct CreateComputePassNode<Executor, Output, Instances> {
@ -254,12 +443,7 @@ pub struct CreateComputePassNode<Executor, Output, Instances> {
}
#[node_macro::node_fn(CreateComputePassNode)]
fn create_compute_pass_node<'any_input, E: 'any_input + GpuExecutor>(
layout: PipelineLayout<E>,
executor: &'any_input E,
output: ShaderInput<E::BufferHandle>,
instances: ComputePassDimensions,
) -> E::CommandBuffer {
async fn create_compute_pass_node<'a: 'input, E: 'a + GpuExecutor>(layout: PipelineLayout<E>, executor: &'a E, output: ShaderInput<E>, 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<ShaderInput<_E::BufferHandle>>) -> PipelineLayout<_E> {
async fn create_pipeline_layout_node<_E: GpuExecutor>(shader: _E::ShaderHandle, entry_point: String, bind_group: Bindgroup<_E>, output_buffer: Arc<ShaderInput<_E>>) -> PipelineLayout<_E> {
PipelineLayout {
shader,
entry_point,
@ -285,15 +469,68 @@ pub struct ExecuteComputePipelineNode<Executor> {
}
#[node_macro::node_fn(ExecuteComputePipelineNode)]
fn execute_compute_pipeline_node<E: GpuExecutor>(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: Executor,
// }
// #[node_macro::node_fn(ReadOutputBufferNode)]
// fn read_output_buffer_node<E: GpuExecutor>(buffer: E::BufferHandle, executor: &'input mut E) -> Vec<u8> {
// executor.read_output_buffer(buffer).await.unwrap()
// }
pub struct ReadOutputBufferNode<Executor, ComputePass> {
executor: Executor,
_compute_pass: ComputePass,
}
#[node_macro::node_fn(ReadOutputBufferNode)]
async fn read_output_buffer_node<'a: 'input, E: 'a + GpuExecutor>(buffer: Arc<ShaderInput<E>>, executor: &'a E, _compute_pass: ()) -> Vec<u8> {
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<Window = Io::Surface>, Io: ApplicationIo<Executor = E>>(editor_api: EditorApi<'a, Io>) -> SurfaceHandle<E::Surface> {
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, EditorApi> {
surface: Surface,
executor: EditorApi,
}
#[derive(Clone)]
pub struct ShaderInputFrame<E: GpuExecutor + ?Sized> {
shader_input: Arc<ShaderInput<E>>,
transform: DAffine2,
}
unsafe impl<E: GpuExecutor + ?Sized + StaticType> StaticType for ShaderInputFrame<E>
where
E::Static: GpuExecutor,
{
type Static = ShaderInputFrame<E::Static>;
}
#[node_macro::node_fn(RenderTextureNode)]
async fn render_texture_node<'a: 'input, E: 'a + GpuExecutor>(image: ShaderInputFrame<E>, surface: Arc<SurfaceHandle<E::Surface>>, executor: &'a E) -> SurfaceFrame {
let surface_id = surface.surface_id;
executor.create_render_pass(image.shader_input, surface).unwrap();
SurfaceFrame {
surface_id,
transform: image.transform,
}
}
pub struct UploadTextureNode<E> {
executor: E,
}
#[node_macro::node_fn(UploadTextureNode)]
async fn upload_texture<'a: 'input, E: 'a + GpuExecutor>(input: ImageFrame<Color>, executor: &'a E) -> ShaderInputFrame<E> {
let shader_input = executor.create_texture_buffer(input.image, TextureBufferOptions::Texture).unwrap();
ShaderInputFrame {
shader_input: Arc::new(shader_input),
transform: input.transform,
}
}

View file

@ -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()
},
),

View file

@ -299,8 +299,8 @@ impl<'a> TaggedValue {
x if x == TypeId::of::<graphene_core::Artboard>() => Ok(TaggedValue::Artboard(*downcast(input).unwrap())),
x if x == TypeId::of::<glam::IVec2>() => Ok(TaggedValue::IVec2(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::SurfaceFrame>() => Ok(TaggedValue::SurfaceFrame(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::wasm_application_io::WasmSurfaceHandleFrame>() => {
let frame = *downcast::<graphene_core::wasm_application_io::WasmSurfaceHandleFrame>(input).unwrap();
x if x == TypeId::of::<graphene_core::WasmSurfaceHandleFrame>() => {
let frame = *downcast::<graphene_core::WasmSurfaceHandleFrame>(input).unwrap();
Ok(TaggedValue::SurfaceFrame(frame.into()))
}
_ => Err(format!("Cannot convert {:?} to TaggedValue", DynAny::type_name(input.as_ref()))),

View file

@ -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",
]

View file

@ -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<TypingContext, ShaderIO> {
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<Node> {
pub struct MapGpuNode<Node, EditorApi> {
node: Node,
editor_api: EditorApi,
}
#[node_macro::node_fn(MapGpuNode)]
async fn map_gpu(image: ImageFrame<Color>, node: DocumentNode) -> ImageFrame<Color> {
async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, editor_api: graphene_core::application_io::EditorApi<'a, WasmApplicationIo>) -> ImageFrame<Color> {
log::debug!("Executing gpu node");
let compiler = graph_craft::graphene_compiler::Compiler {};
let inner_network = NodeNetwork::value_network(node);
@ -121,7 +124,24 @@ async fn map_gpu(image: ImageFrame<Color>, node: DocumentNode) -> ImageFrame<Col
//return ImageFrame::empty();
let len: usize = image.image.data.len();
let executor = NewExecutor::new().await.unwrap();
let executor = &editor_api.application_io.gpu_executor.as_ref().unwrap();
/*
let canvas = editor_api.application_io.create_surface();
let surface = unsafe { executor.create_surface(canvas) }.unwrap();
//log::debug!("id: {:?}", surface);
let surface_id = surface.surface_id;
let texture = executor.create_texture_buffer(image.image.clone(), TextureBufferOptions::Texture).unwrap();
//executor.create_render_pass(texture, surface).unwrap();
let frame = SurfaceFrame {
surface_id,
transform: image.transform,
};
return frame;*/
log::debug!("creating buffer");
let width_uniform = executor.create_uniform_buffer(image.image.width).unwrap();
let storage_buffer = executor
@ -152,7 +172,6 @@ async fn map_gpu(image: ImageFrame<Color>, node: DocumentNode) -> ImageFrame<Col
io: shader.io,
};
log::debug!("loading shader");
log::debug!("shader: {:?}", shader.source);
let shader = executor.load_shader(shader).unwrap();
log::debug!("loaded shader");
let pipeline = PipelineLayout {
@ -333,7 +352,7 @@ async fn blend_gpu_image(foreground: ImageFrame<Color>, background: ImageFrame<C
.unwrap();
let len = background.image.data.len();
let executor = NewExecutor::new()
let executor = WgpuExecutor::new()
.await
.expect("Failed to create wgpu executor. Please make sure that webgpu is enabled for your browser.");
log::debug!("creating buffer");

View file

@ -12,7 +12,7 @@ pub mod http;
pub mod any;
#[cfg(feature = "gpu")]
pub mod executor;
pub mod gpu_nodes;
#[cfg(feature = "quantization")]
pub mod quantization;
@ -22,3 +22,6 @@ pub use graphene_core::*;
pub mod image_segmentation;
pub mod brush;
#[cfg(feature = "wasm")]
pub mod wasm_application_io;

View file

@ -1,27 +1,34 @@
use std::{cell::RefCell, collections::HashMap};
use std::cell::RefCell;
use super::{ApplicationIo, SurfaceHandle, SurfaceHandleFrame, SurfaceId};
use crate::{
use dyn_any::StaticType;
use graphene_core::application_io::{ApplicationIo, SurfaceHandle, SurfaceHandleFrame, SurfaceId};
use graphene_core::{
raster::{color::SRGBA8, ImageFrame},
Node,
};
use alloc::sync::Arc;
use dyn_any::StaticType;
use js_sys::{Object, Reflect};
use std::sync::Arc;
use wasm_bindgen::{Clamped, JsCast, JsValue};
use web_sys::{window, CanvasRenderingContext2d, HtmlCanvasElement};
#[cfg(feature = "wgpu")]
use wgpu_executor::WgpuExecutor;
pub struct Canvas(CanvasRenderingContext2d);
#[derive(Debug, Default)]
pub struct WasmApplicationIo {
ids: RefCell<u64>,
canvases: RefCell<HashMap<SurfaceId, CanvasRenderingContext2d>>,
#[cfg(feature = "wgpu")]
pub(crate) gpu_executor: Option<WgpuExecutor>,
}
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<WasmEditorApi<'a>> 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<Self::Surface> {
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::<HtmlCanvasElement>()?;
// 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::<CanvasRenderingContext2d>().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<CanvasRenderingContext2d>;
pub type WasmSurfaceHandleFrame = SurfaceHandleFrame<CanvasRenderingContext2d>;
pub type WasmSurfaceHandle = SurfaceHandle<HtmlCanvasElement>;
pub type WasmSurfaceHandleFrame = SurfaceHandleFrame<HtmlCanvasElement>;
pub struct CreateSurfaceNode {}
#[node_macro::node_fn(CreateSurfaceNode)]
fn create_surface_node<'a: 'input>(editor: WasmEditorApi<'a>) -> Arc<SurfaceHandle<CanvasRenderingContext2d>> {
async fn create_surface_node<'a: 'input>(editor: WasmEditorApi<'a>) -> Arc<SurfaceHandle<HtmlCanvasElement>> {
editor.application_io.create_surface().into()
}
@ -110,15 +132,17 @@ pub struct DrawImageFrameNode<Surface> {
}
#[node_macro::node_fn(DrawImageFrameNode)]
async fn draw_image_frame_node<'a: 'input>(image: ImageFrame<SRGBA8>, surface_handle: Arc<SurfaceHandle<CanvasRenderingContext2d>>) -> SurfaceHandleFrame<CanvasRenderingContext2d> {
async fn draw_image_frame_node<'a: 'input>(image: ImageFrame<SRGBA8>, surface_handle: Arc<SurfaceHandle<HtmlCanvasElement>>) -> SurfaceHandleFrame<HtmlCanvasElement> {
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::<CanvasRenderingContext2d>().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,

View file

@ -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",

View file

@ -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<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
register_node!(graphene_core::ops::AddNode, input: (u32, u32), params: []),
register_node!(graphene_core::ops::AddNode, input: (u32, &u32), params: []),
register_node!(graphene_core::ops::CloneNode<_>, input: &ImageFrame<Color>, 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<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
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::AddParameterNode<_>, input: &f64, params: [&f64]),
register_node!(graphene_core::ops::SomeNode, input: graphene_core::EditorApi, params: []),
register_node!(graphene_core::ops::IntoNode<_, ImageFrame<SRGBA8>>, input: ImageFrame<Color>, params: []),
register_node!(graphene_core::ops::IntoNode<_, ImageFrame<Color>>, input: ImageFrame<SRGBA8>, params: []),
register_node!(graphene_core::ops::SomeNode, input: WasmEditorApi, params: []),
async_node!(graphene_core::ops::IntoNode<_, ImageFrame<SRGBA8>>, input: ImageFrame<Color>, output: ImageFrame<SRGBA8>, params: []),
async_node!(graphene_core::ops::IntoNode<_, ImageFrame<Color>>, input: ImageFrame<SRGBA8>, output: ImageFrame<Color>, params: []),
#[cfg(feature = "gpu")]
async_node!(graphene_core::ops::IntoNode<_, &WgpuExecutor>, input: WasmEditorApi, output: &WgpuExecutor, params: []),
register_node!(graphene_std::raster::DownresNode<_>, input: ImageFrame<Color>, params: []),
register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame<Color>, params: [ImageFrame<Luma>]),
@ -244,26 +250,68 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
register_node!(graphene_std::raster::EmptyImageNode<_, _>, input: DAffine2, params: [Color]),
register_node!(graphene_core::memo::MonitorNode<_>, input: ImageFrame<Color>, params: []),
register_node!(graphene_core::memo::MonitorNode<_>, input: 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<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>, params: []),
async_node!(
graphene_core::wasm_application_io::DrawImageFrameNode<_>,
graphene_std::wasm_application_io::DrawImageFrameNode<_>,
input: ImageFrame<SRGBA8>,
output: WasmSurfaceHandleFrame,
params: [Arc<WasmSurfaceHandle>]
),
#[cfg(feature = "gpu")]
async_node!(gpu_executor::UniformNode<_>, input: f32, output: ShaderInput<WgpuExecutor>, params: [&WgpuExecutor]),
#[cfg(feature = "gpu")]
async_node!(gpu_executor::StorageNode<_>, input: Vec<u8>, output: ShaderInput<WgpuExecutor>, params: [&WgpuExecutor]),
#[cfg(feature = "gpu")]
async_node!(
gpu_executor::PushNode<_>,
input: Vec<ShaderInput<WgpuExecutor>>,
output: Vec<ShaderInput<WgpuExecutor>>,
params: [ShaderInput<WgpuExecutor>]
),
#[cfg(feature = "gpu")]
async_node!(gpu_executor::CreateOutputBufferNode<_, _>, input: usize, output: gpu_executor::ShaderInput<WgpuExecutor>, params: [&WgpuExecutor, Type]),
#[cfg(feature = "gpu")]
async_node!(gpu_executor::CreateComputePassNode<_, _, _>, input: gpu_executor::PipelineLayout<WgpuExecutor>, output: <WgpuExecutor as GpuExecutor>::CommandBuffer, params: [&WgpuExecutor, ShaderInput<WgpuExecutor>, gpu_executor::ComputePassDimensions]),
#[cfg(feature = "gpu")]
async_node!(gpu_executor::CreatePipelineLayoutNode<_, _, _, _>, input: <WgpuExecutor as GpuExecutor>::ShaderHandle, output: gpu_executor::PipelineLayout<WgpuExecutor>, params: [String, gpu_executor::Bindgroup<WgpuExecutor>, Arc<ShaderInput<WgpuExecutor>>]),
#[cfg(feature = "gpu")]
async_node!(
gpu_executor::ExecuteComputePipelineNode<_>,
input: <WgpuExecutor as GpuExecutor>::CommandBuffer,
output: (),
params: [&WgpuExecutor]
),
#[cfg(feature = "gpu")]
async_node!(gpu_executor::ReadOutputBufferNode<_, _>, input: Arc<ShaderInput<WgpuExecutor>>, output: Vec<u8>, params: [&WgpuExecutor, ()]),
#[cfg(all(feature = "gpu", target_arch = "wasm32"))]
async_node!(gpu_executor::CreateGpuSurfaceNode, input: WasmEditorApi, output: Arc<SurfaceHandle<<WgpuExecutor as GpuExecutor>::Surface>>, params: []),
#[cfg(feature = "gpu")]
async_node!(gpu_executor::RenderTextureNode<_, _>, input: ShaderInputFrame<WgpuExecutor>, output: SurfaceFrame, params: [Arc<SurfaceHandle<<WgpuExecutor as GpuExecutor>::Surface>>, &WgpuExecutor]),
#[cfg(feature = "gpu")]
async_node!(
gpu_executor::UploadTextureNode<_>,
input: ImageFrame<Color>,
output: ShaderInputFrame<WgpuExecutor>,
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<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
})
},
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![fn_type!(graph_craft::document::DocumentNode)]),
NodeIOTypes::new(
concrete!(ImageFrame<Color>),
concrete!(SurfaceFrame),
vec![fn_type!(graph_craft::document::DocumentNode), fn_type!(WasmEditorApi)],
),
)],
#[cfg(feature = "gpu")]
vec![(
@ -273,7 +321,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
let background: DowncastBothNode<(), ImageFrame<Color>> = 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<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
@ -374,47 +422,39 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
register_node!(graphene_core::memo::LetNode<_>, input: Option<ImageFrame<Color>>, params: []),
register_node!(graphene_core::memo::LetNode<_>, input: Option<graphene_core::EditorApi>, params: []),
register_node!(graphene_core::memo::LetNode<_>, input: Option<WasmEditorApi>, params: []),
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: ImageFrame<Color>, params: [ImageFrame<Color>]),
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: VectorData, params: [VectorData]),
async_node!(
graphene_core::memo::EndLetNode<_>,
input: graphene_core::EditorApi,
output: ImageFrame<Color>,
params: [ImageFrame<Color>]
),
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<Option<graphene_core::EditorApi>, graphene_core::EditorApi> = graphene_std::any::DowncastBothNode::new(args[0].clone());
let node: DowncastBothNode<Option<WasmEditorApi>, WasmEditorApi> = graphene_std::any::DowncastBothNode::new(args[0].clone());
let node = <graphene_core::memo::RefNode<_, _>>::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>, graphene_core::EditorApi)],
),
NodeIOTypes::new(concrete!(()), concrete!(WasmEditorApi), vec![fn_type!(Option<WasmEditorApi>, WasmEditorApi)]),
)],
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Image<Color>, params: [Image<Color>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: ImageFrame<Color>, params: [ImageFrame<Color>]),
@ -443,9 +483,9 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
input: Vec<graphene_core::vector::bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>,
params: [Vec<graphene_core::uuid::ManipulatorGroupId>]
),
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<Color>, 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]),

View file

@ -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"] }
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"
@ -27,3 +36,5 @@ 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"

View file

@ -6,6 +6,7 @@ pub struct Context {
pub device: Arc<Device>,
pub queue: Arc<Queue>,
pub instance: Arc<Instance>,
pub adapter: Arc<wgpu::Adapter>,
}
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),
})
}

View file

@ -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<Executor = WgpuExecutor>> From<EditorApi<'a, T>> 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::<Vertex>() 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<WgpuExecutor>;
#[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<Self::ShaderHandle> {
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<T: ToUniformBuffer>(&self, data: T) -> Result<ShaderInput<Self::BufferHandle>> {
fn create_uniform_buffer<T: ToUniformBuffer>(&self, data: T) -> Result<WgpuShaderInput> {
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::<T>()))
}
fn create_storage_buffer<T: ToStorageBuffer>(&self, data: T, options: StorageBufferOptions) -> Result<ShaderInput<Self::BufferHandle>> {
fn create_storage_buffer<T: ToStorageBuffer>(&self, data: T, options: StorageBufferOptions) -> Result<WgpuShaderInput> {
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<T: gpu_executor::ToTextureBuffer>(&self, data: T, options: TextureBufferOptions) -> Result<WgpuShaderInput> {
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<ShaderInput<Self::BufferHandle>> {
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<WgpuShaderInput> {
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<Self>, read_back: Option<Arc<ShaderInput<Self::BufferHandle>>>, instances: ComputePassDimensions) -> Result<CommandBuffer> {
fn create_compute_pass(&self, layout: &gpu_executor::PipelineLayout<Self>, read_back: Option<Arc<WgpuShaderInput>>, instances: ComputePassDimensions) -> Result<Self::CommandBuffer> {
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::<Vec<_>>();
@ -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<ShaderInput<Self>>, canvas: Arc<SurfaceHandle<wgpu::Surface>>) -> 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<ShaderInput<Self::BufferHandle>>) -> Pin<Box<dyn Future<Output = Result<Vec<u8>>>>> {
fn read_output_buffer(&self, buffer: Arc<ShaderInput<Self>>) -> Pin<Box<dyn Future<Output = Result<Vec<u8>>>>> {
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<Self>) -> Result<ShaderInput<Self>> {
//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<SurfaceHandle<wgpu::Surface>> {
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<winit::window::Window>) -> Result<SurfaceHandle<wgpu::Surface>> {
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<Self> {
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,
}

View file

@ -0,0 +1,43 @@
// Vertex shader
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) tex_coords: vec2<f32>,
}
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) tex_coords: vec2<f32>,
}
@vertex
fn vs_main(
model: VertexInput,
) -> VertexOutput {
var out: VertexOutput;
out.tex_coords = model.tex_coords;
out.clip_position = vec4<f32>(model.position, 1.0);
return out;
}
// Fragment shader
@group(0) @binding(0)
var t_diffuse: texture_2d<f32>;
@group(0)@binding(1)
var s_diffuse: sampler;
fn linearToSRGB(color: vec3<f32>) -> vec3<f32> {
let a = 0.055;
return select(pow(color, vec3<f32>(1.0 / 2.2)) * (1.0 + a) - a,
color / 12.92,
color <= vec3<f32>(0.0031308));
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
var color = textureSample(t_diffuse, s_diffuse, in.tex_coords);
var linearColor = color.rgb;
var srgbColor = linearToSRGB(linearColor);
return vec4<f32>(srgbColor, color.a);
}