From 88e66d59d793205dfb8b60a0bf753f64b00711b9 Mon Sep 17 00:00:00 2001 From: marc0246 <40955683+marc0246@users.noreply.github.com> Date: Sat, 5 Oct 2024 19:55:15 +0200 Subject: [PATCH 1/2] Update dependencies --- Cargo.lock | 406 +++++--- Cargo.toml | 10 +- examples/async-update/main.rs | 416 +++++---- examples/bloom/bloom.rs | 27 +- examples/bloom/main.rs | 449 +++++---- examples/bloom/scene.rs | 117 +-- examples/bloom/tonemap.rs | 97 +- examples/buffer-allocator/main.rs | 306 ++++--- examples/clear-attachments/main.rs | 209 +++-- examples/deferred/main.rs | 213 +++-- examples/dynamic-buffers/main.rs | 1 + examples/gl-interop/Cargo.toml | 1 - examples/gl-interop/main.rs | 865 +++++++++--------- examples/image-self-copy-blit/main.rs | 376 ++++---- examples/image/main.rs | 376 ++++---- examples/immutable-sampler/main.rs | 368 ++++---- examples/indirect/main.rs | 419 +++++---- examples/instancing/main.rs | 342 ++++--- examples/interactive-fractal/app.rs | 370 -------- .../fractal_compute_pipeline.rs | 11 +- examples/interactive-fractal/input.rs | 124 +++ examples/interactive-fractal/main.rs | 425 ++++++--- examples/mesh-shader/main.rs | 342 ++++--- examples/msaa-renderpass/main.rs | 1 + examples/multi-window-game-of-life/app.rs | 126 --- .../multi-window-game-of-life/game_of_life.rs | 13 +- examples/multi-window-game-of-life/main.rs | 334 ++++--- .../multi-window-game-of-life/pixels_draw.rs | 2 +- .../multi-window-game-of-life/render_pass.rs | 7 +- examples/multi-window/main.rs | 393 ++++---- examples/occlusion-query/main.rs | 412 +++++---- examples/push-descriptors/main.rs | 349 ++++--- examples/runtime-array/main.rs | 403 ++++---- examples/runtime-shader/main.rs | 319 ++++--- examples/simple-particles/main.rs | 628 +++++++------ examples/specialization-constants/main.rs | 6 +- examples/teapot/main.rs | 349 ++++--- examples/tessellation/main.rs | 503 +++++----- examples/texture-array/main.rs | 377 ++++---- examples/triangle-util/main.rs | 222 +++-- examples/triangle-v1_3/main.rs | 323 ++++--- examples/triangle/main.rs | 333 ++++--- vulkano-taskgraph/src/command_buffer/mod.rs | 18 +- vulkano-util/src/context.rs | 8 +- vulkano-util/src/window.rs | 61 +- 45 files changed, 6317 insertions(+), 5140 deletions(-) delete mode 100644 examples/interactive-fractal/app.rs create mode 100644 examples/interactive-fractal/input.rs delete mode 100644 examples/multi-window-game-of-life/app.rs diff --git a/Cargo.lock b/Cargo.lock index d0c736084a..31d5d4c3b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "android-activity" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", "bitflags 2.5.0", @@ -60,9 +60,9 @@ dependencies = [ "jni-sys", "libc", "log", - "ndk 0.8.0", + "ndk 0.9.0", "ndk-context", - "ndk-sys 0.5.0+25.2.9519653", + "ndk-sys 0.6.0+11769913", "num_enum 0.7.2", "thiserror", ] @@ -115,7 +115,7 @@ dependencies = [ "vulkano", "vulkano-shaders", "vulkano-taskgraph", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -180,32 +180,13 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" -dependencies = [ - "objc-sys", -] - -[[package]] -name = "block2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" -dependencies = [ - "block-sys", - "objc2 0.4.1", -] - [[package]] name = "block2" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2 0.5.2", + "objc2", ] [[package]] @@ -215,7 +196,7 @@ dependencies = [ "vulkano", "vulkano-shaders", "vulkano-taskgraph", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -224,7 +205,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -324,9 +305,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "cgl" @@ -342,7 +323,7 @@ name = "clear-attachments" version = "0.0.0" dependencies = [ "vulkano", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -421,8 +402,9 @@ dependencies = [ [[package]] name = "concurrent-slotmap" -version = "0.1.0" -source = "git+https://github.com/vulkano-rs/concurrent-slotmap?rev=fa906d916d8d126d3cc3a2b4ab9a29fa27bee62d#fa906d916d8d126d3cc3a2b4ab9a29fa27bee62d" +version = "0.1.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "288086ecfcd80978f795a7ba8ffd9777d6dfcb6e5c6c74bba10e3d9a1eb52169" dependencies = [ "virtual-buffer", ] @@ -606,7 +588,7 @@ dependencies = [ "glam", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -630,11 +612,17 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + [[package]] name = "dwrote" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +checksum = "2da3498378ed373237bdef1eddcc64e7be2d3ba4841f4c22a998e81cadeea83c" dependencies = [ "lazy_static", "libc", @@ -811,7 +799,7 @@ dependencies = [ "vulkano", "vulkano-shaders", "winit 0.27.5", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -827,9 +815,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.25.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" +checksum = "c28091a37a5d09b555cb6628fd954da299b536433834f5b8e59eba78e0cbbf8a" [[package]] name = "glium" @@ -925,9 +913,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "heck" @@ -941,17 +929,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "icrate" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" -dependencies = [ - "block2 0.3.0", - "dispatch", - "objc2 0.4.1", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -965,7 +942,7 @@ dependencies = [ "png", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -975,7 +952,7 @@ dependencies = [ "png", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -985,14 +962,14 @@ dependencies = [ "png", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -1004,7 +981,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1013,7 +990,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1037,7 +1014,7 @@ dependencies = [ "vulkano", "vulkano-shaders", "vulkano-util", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1094,9 +1071,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -1205,7 +1182,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1251,7 +1228,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1263,7 +1240,7 @@ dependencies = [ "vulkano", "vulkano-shaders", "vulkano-util", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1291,14 +1268,14 @@ dependencies = [ [[package]] name = "ndk" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ "bitflags 2.5.0", "jni-sys", "log", - "ndk-sys 0.5.0+25.2.9519653", + "ndk-sys 0.6.0+11769913", "num_enum 0.7.2", "raw-window-handle 0.6.2", "thiserror", @@ -1350,9 +1327,9 @@ dependencies = [ [[package]] name = "ndk-sys" -version = "0.5.0+25.2.9519653" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ "jni-sys", ] @@ -1428,7 +1405,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 2.0.2", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 2.0.66", @@ -1451,29 +1428,89 @@ checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" [[package]] name = "objc2" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ "objc-sys", - "objc2-encode 3.0.0", + "objc2-encode", ] [[package]] -name = "objc2" -version = "0.5.2" +name = "objc2-app-kit" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "objc-sys", - "objc2-encode 4.0.3", + "bitflags 2.5.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", ] [[package]] -name = "objc2-encode" -version = "3.0.0" +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.5.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.5.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] [[package]] name = "objc2-encode" @@ -1488,9 +1525,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.5.0", - "block2 0.5.1", + "block2", + "dispatch", "libc", - "objc2 0.5.2", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", ] [[package]] @@ -1500,8 +1550,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.5.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -1512,12 +1562,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.5.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.5.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.5.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + [[package]] name = "object" version = "0.35.0" @@ -1533,7 +1638,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1543,7 +1648,7 @@ dependencies = [ "png", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1608,6 +1713,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1674,12 +1799,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.22.22", ] [[package]] @@ -1706,7 +1830,7 @@ dependencies = [ "png", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1784,20 +1908,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2000e45d7daa9b6d946e88dfa1d7ae330424a81918a6545741821c989eb80a9" dependencies = [ - "objc2 0.5.2", + "objc2", "objc2-foundation", "objc2-quartz-core", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1844,7 +1959,7 @@ dependencies = [ "png", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1852,7 +1967,7 @@ name = "runtime-shader" version = "0.0.0" dependencies = [ "vulkano", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -1924,9 +2039,9 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" +checksum = "7555fcb4f753d095d734fdefebb0ad8c98478a21db500492d87c55913d3b0086" dependencies = [ "ab_glyph", "log", @@ -2011,7 +2126,7 @@ dependencies = [ "serde", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2065,7 +2180,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2206,7 +2321,7 @@ dependencies = [ "glam", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2215,7 +2330,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2225,7 +2340,7 @@ dependencies = [ "png", "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2310,9 +2425,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" @@ -2322,18 +2437,18 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -2358,7 +2473,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2368,7 +2483,7 @@ dependencies = [ "vulkano", "vulkano-shaders", "vulkano-util", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2377,7 +2492,7 @@ version = "0.0.0" dependencies = [ "vulkano", "vulkano-shaders", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2422,9 +2537,9 @@ dependencies = [ [[package]] name = "vk-parse" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81086c28be67a8759cd80cbb3c8f7b520e0874605fc5eb74d5a1c9c2d1878e79" +checksum = "3859da4d7b98bec73e68fb65815d47a263819c415c90eed42b80440a02cbce8c" dependencies = [ "xml-rs", ] @@ -2464,7 +2579,7 @@ dependencies = [ name = "vulkano-macros" version = "0.34.0" dependencies = [ - "proc-macro-crate 2.0.2", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 2.0.66", @@ -2502,7 +2617,7 @@ version = "0.34.0" dependencies = [ "ahash", "vulkano", - "winit 0.29.15", + "winit 0.30.3", ] [[package]] @@ -2791,9 +2906,9 @@ dependencies = [ [[package]] name = "web-time" -version = "0.2.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -3113,37 +3228,41 @@ dependencies = [ [[package]] name = "winit" -version = "0.29.15" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +checksum = "49f45a7b7e2de6af35448d7718dab6d95acec466eb3bb7a56f4d31d1af754004" dependencies = [ "ahash", "android-activity", "atomic-waker", "bitflags 2.5.0", + "block2", "bytemuck", "calloop 0.12.4", "cfg_aliases", + "concurrent-queue", "core-foundation", "core-graphics 0.23.2", "cursor-icon", - "icrate", + "dpi", "js-sys", "libc", - "log", "memmap2 0.9.4", - "ndk 0.8.0", - "ndk-sys 0.5.0+25.2.9519653", - "objc2 0.4.1", - "once_cell", + "ndk 0.9.0", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", "orbclient", "percent-encoding", + "pin-project", "raw-window-handle 0.6.2", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", - "sctk-adwaita 0.8.1", + "sctk-adwaita 0.9.1", "smithay-client-toolkit 0.18.1", "smol_str", + "tracing", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", @@ -3153,7 +3272,7 @@ dependencies = [ "wayland-protocols-plasma", "web-sys", "web-time", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", "x11rb", "xkbcommon-dl", @@ -3168,6 +3287,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + [[package]] name = "wio" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 15fb51942e..e456f5450e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ ahash = "0.8" # https://github.com/KhronosGroup/Vulkan-Headers/commits/main/registry/vk.xml ash = "0.38.0" bytemuck = "1.9" -concurrent-slotmap = { git = "https://github.com/vulkano-rs/concurrent-slotmap", rev = "fa906d916d8d126d3cc3a2b4ab9a29fa27bee62d" } +concurrent-slotmap = "0.1.0-alpha.1" crossbeam-queue = "0.3" half = "2.0" heck = "0.4" @@ -56,7 +56,7 @@ nom = "7.1" once_cell = "1.17" parking_lot = "0.12" proc-macro2 = "1.0" -proc-macro-crate = "2.0" +proc-macro-crate = "3.0" quote = "1.0" rangemap = "1.5" raw-window-handle = "0.6" @@ -68,13 +68,13 @@ slabbin = "1.0" smallvec = "1.8" syn = "2.0" thread_local = "1.1" -vk-parse = "0.12" -winit = { version = "0.29", default-features = false } +vk-parse = "0.15" +winit = { version = "0.30", default-features = false } x11-dl = "2.0" x11rb = "0.13" # Only used in examples -glam = "0.25" +glam = "0.29" png = "0.17" rand = "0.8" ron = "0.8" diff --git a/examples/async-update/main.rs b/examples/async-update/main.rs index 0406ab548f..4b86896934 100644 --- a/examples/async-update/main.rs +++ b/examples/async-update/main.rs @@ -81,15 +81,16 @@ use vulkano_taskgraph::{ command_buffer::{ BufferImageCopy, ClearColorImageInfo, CopyBufferToImageInfo, RecordingCommandBuffer, }, - graph::{CompileInfo, ExecuteError, TaskGraph}, + graph::{CompileInfo, ExecutableTaskGraph, ExecuteError, TaskGraph}, resource::{AccessType, Flight, HostAccessType, ImageLayoutType, Resources}, resource_map, Id, QueueFamilyType, Task, TaskContext, TaskResult, }; use winit::{ - event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + application::ApplicationHandler, + event::{ElementState, KeyEvent, WindowEvent}, + event_loop::{ActiveEventLoop, EventLoop}, keyboard::{Key, NamedKey}, - window::WindowBuilder, + window::{Window, WindowId}, }; const TRANSFER_GRANULARITY: u32 = 4096; @@ -97,9 +98,46 @@ const MAX_FRAMES_IN_FLIGHT: u32 = 2; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + println!("\nPress space to update part of the texture"); + + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + graphics_family_index: u32, + transfer_family_index: u32, + graphics_queue: Arc, + resources: Arc, + graphics_flight_id: Id, + vertex_buffer_id: Id, + uniform_buffer_ids: [Id; MAX_FRAMES_IN_FLIGHT as usize], + texture_ids: [Id; 2], + current_texture_index: Arc, + channel: mpsc::Sender<()>, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain_id: Id, + render_pass: Arc, + framebuffers: Vec>, + viewport: Viewport, + recreate_swapchain: bool, + task_graph: ExecutableTaskGraph, + virtual_swapchain_id: Id, + virtual_texture_id: Id, + virtual_uniform_buffer_id: Id, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -124,7 +162,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -197,7 +235,7 @@ fn main() -> Result<(), impl Error> { // Even if we can't get an async transfer queue family, it's still better to use // different queues on the same queue family. This way, at least the threads on the - // host don't have lock the same queue when submitting. + // host don't have to lock the same queue when submitting. if queue_family_properties.queue_count > 1 { queue_create_infos[0].queues.push(0.5); } @@ -229,40 +267,6 @@ fn main() -> Result<(), impl Error> { let graphics_flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); let transfer_flight_id = resources.create_flight(1).unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let swapchain_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - let mut swapchain_id = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - - resources - .create_swapchain( - graphics_flight_id, - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(3), - image_format: swapchain_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - let vertices = [ MyVertex { position: [-0.5, -0.5], @@ -311,28 +315,29 @@ fn main() -> Result<(), impl Error> { .unwrap() }); - let texture_create_info = ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::R8G8B8A8_UNORM, - extent: [TRANSFER_GRANULARITY * 2, TRANSFER_GRANULARITY * 2, 1], - usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, - sharing: if graphics_family_index != transfer_family_index { - Sharing::Concurrent( - [graphics_family_index, transfer_family_index] - .into_iter() - .collect(), - ) - } else { - Sharing::Exclusive - }, - ..Default::default() - }; - - // Create two textures, where at any point in time one is used exclusively for reading and one - // is used exclusively for writing, swapping the two after each update. + // Create two textures, where at any point in time one is used exclusively for reading and + // one is used exclusively for writing, swapping the two after each update. let texture_ids = [(); 2].map(|_| { resources - .create_image(texture_create_info.clone(), AllocationCreateInfo::default()) + .create_image( + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_UNORM, + extent: [TRANSFER_GRANULARITY * 2, TRANSFER_GRANULARITY * 2, 1], + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + sharing: if graphics_family_index != transfer_family_index { + Sharing::Concurrent( + [graphics_family_index, transfer_family_index] + .into_iter() + .collect(), + ) + } else { + Sharing::Exclusive + }, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) .unwrap() }); @@ -381,17 +386,79 @@ fn main() -> Result<(), impl Error> { let (channel, receiver) = mpsc::channel(); run_worker( receiver, - transfer_queue, + graphics_family_index, + transfer_family_index, + transfer_queue.clone(), resources.clone(), graphics_flight_id, transfer_flight_id, - &texture_create_info, texture_ids, current_texture_index.clone(), ); + App { + instance, + device, + graphics_family_index, + transfer_family_index, + graphics_queue, + resources, + graphics_flight_id, + vertex_buffer_id, + uniform_buffer_ids, + texture_ids, + current_texture_index, + channel, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let swapchain_format; + let swapchain_id = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + (swapchain_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + self.resources + .create_swapchain( + self.graphics_flight_id, + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(3), + image_format: swapchain_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + let render_pass = vulkano::single_pass_renderpass!( - device.clone(), + self.device.clone(), attachments: { color: { format: swapchain_format, @@ -407,12 +474,14 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let framebuffers = window_size_dependent_setup(&self.resources, swapchain_id, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); @@ -422,16 +491,16 @@ fn main() -> Result<(), impl Error> { PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -455,23 +524,21 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let framebuffers = - window_size_dependent_setup(&resources, swapchain_id, &render_pass, &mut viewport); let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), + self.device.clone(), Default::default(), )); // A byproduct of always using the same set of uniform buffers is that we can also create one // descriptor set for each, reusing them in the same way as the buffers. - let uniform_buffer_sets = uniform_buffer_ids.map(|buffer_id| { - let buffer_state = resources.buffer(buffer_id).unwrap(); + let uniform_buffer_sets = self.uniform_buffer_ids.map(|buffer_id| { + let buffer_state = self.resources.buffer(buffer_id).unwrap(); let buffer = buffer_state.buffer(); DescriptorSet::new( @@ -484,9 +551,13 @@ fn main() -> Result<(), impl Error> { }); // Create the descriptor sets for sampling the textures. - let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap(); - let sampler_sets = texture_ids.map(|texture_id| { - let texture_state = resources.image(texture_id).unwrap(); + let sampler = Sampler::new( + self.device.clone(), + SamplerCreateInfo::simple_repeat_linear(), + ) + .unwrap(); + let sampler_sets = self.texture_ids.map(|texture_id| { + let texture_state = self.resources.image(texture_id).unwrap(); let texture = texture_state.image(); DescriptorSet::new( @@ -501,15 +572,21 @@ fn main() -> Result<(), impl Error> { .unwrap() }); - let mut rcx = RenderContext { - viewport, - framebuffers, - }; - - let mut task_graph = TaskGraph::new(&resources, 1, 4); + let mut task_graph = TaskGraph::new(&self.resources, 1, 4); let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default()); - let virtual_texture_id = task_graph.add_image(&texture_create_info); + let virtual_texture_id = task_graph.add_image(&ImageCreateInfo { + sharing: if self.graphics_family_index != self.transfer_family_index { + Sharing::Concurrent( + [self.graphics_family_index, self.transfer_family_index] + .into_iter() + .collect(), + ) + } else { + Sharing::Exclusive + }, + ..Default::default() + }); let virtual_uniform_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default()); task_graph.add_host_buffer_access(virtual_uniform_buffer_id, HostAccessType::Write); @@ -520,12 +597,12 @@ fn main() -> Result<(), impl Error> { QueueFamilyType::Graphics, RenderTask { swapchain_id: virtual_swapchain_id, - vertex_buffer_id, - current_texture_index: current_texture_index.clone(), - pipeline: pipeline.clone(), + vertex_buffer_id: self.vertex_buffer_id, + current_texture_index: self.current_texture_index.clone(), + pipeline, uniform_buffer_id: virtual_uniform_buffer_id, - uniform_buffer_sets: uniform_buffer_sets.clone(), - sampler_sets: sampler_sets.clone(), + uniform_buffer_sets, + sampler_sets, }, ) .image_access( @@ -533,7 +610,7 @@ fn main() -> Result<(), impl Error> { AccessType::ColorAttachmentWrite, ImageLayoutType::Optimal, ) - .buffer_access(vertex_buffer_id, AccessType::VertexAttributeRead) + .buffer_access(self.vertex_buffer_id, AccessType::VertexAttributeRead) .image_access( virtual_texture_id, AccessType::FragmentShaderSampledRead, @@ -546,121 +623,117 @@ fn main() -> Result<(), impl Error> { let task_graph = unsafe { task_graph.compile(&CompileInfo { - queues: &[&graphics_queue], - present_queue: Some(&graphics_queue), - flight_id: graphics_flight_id, + queues: &[&self.graphics_queue], + present_queue: Some(&self.graphics_queue), + flight_id: self.graphics_flight_id, ..Default::default() }) } .unwrap(); - let mut recreate_swapchain = false; - - println!("\nPress space to update part of the texture"); + self.rcx = Some(RenderContext { + window, + swapchain_id, + render_pass, + framebuffers, + viewport, + recreate_swapchain: false, + task_graph, + virtual_swapchain_id, + virtual_texture_id, + virtual_uniform_buffer_id, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { + WindowEvent::KeyboardInput { event: - WindowEvent::KeyboardInput { - event: - KeyEvent { - logical_key: Key::Named(NamedKey::Space), - state: ElementState::Released, - .. - }, + KeyEvent { + logical_key: Key::Named(NamedKey::Space), + state: ElementState::Released, .. }, .. } => { - channel.send(()).unwrap(); + self.channel.send(()).unwrap(); } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - let flight = resources.flight(graphics_flight_id).unwrap(); + let flight = self.resources.flight(self.graphics_flight_id).unwrap(); - if recreate_swapchain { - swapchain_id = resources - .recreate_swapchain(swapchain_id, |create_info| SwapchainCreateInfo { - image_extent, + if rcx.recreate_swapchain { + rcx.swapchain_id = self + .resources + .recreate_swapchain(rcx.swapchain_id, |create_info| SwapchainCreateInfo { + image_extent: window_size.into(), ..create_info }) .expect("failed to recreate swapchain"); - - flight.destroy_objects(rcx.framebuffers.drain(..)); - rcx.framebuffers = window_size_dependent_setup( - &resources, - swapchain_id, - &render_pass, - &mut rcx.viewport, + &self.resources, + rcx.swapchain_id, + &rcx.render_pass, ); - - recreate_swapchain = false; + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let frame_index = flight.current_frame_index(); - let texture_index = current_texture_index.load(Ordering::Relaxed); + let texture_index = self.current_texture_index.load(Ordering::Relaxed); let resource_map = resource_map!( - &task_graph, - virtual_swapchain_id => swapchain_id, - virtual_texture_id => texture_ids[texture_index as usize], - virtual_uniform_buffer_id => uniform_buffer_ids[frame_index as usize], + &rcx.task_graph, + rcx.virtual_swapchain_id => rcx.swapchain_id, + rcx.virtual_texture_id => self.texture_ids[texture_index as usize], + rcx.virtual_uniform_buffer_id => self.uniform_buffer_ids[frame_index as usize], ) .unwrap(); flight.wait(None).unwrap(); match unsafe { - task_graph.execute(resource_map, &rcx, || window.pre_present_notify()) + rcx.task_graph + .execute(resource_map, rcx, || rcx.window.pre_present_notify()) } { Ok(()) => {} Err(ExecuteError::Swapchain { error: Validated::Error(VulkanError::OutOfDate), .. }) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } Err(e) => { panic!("failed to execute next frame: {e:?}"); } } } - Event::AboutToWait => { - window.request_redraw(); - } - Event::LoopExiting => { - let flight = resources.flight(graphics_flight_id).unwrap(); - flight.destroy_objects(rcx.framebuffers.drain(..)); - flight.destroy_objects(uniform_buffer_sets.clone()); - flight.destroy_objects(sampler_sets.clone()); - } - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } } #[derive(Clone, Copy, BufferContents, Vertex)] @@ -710,11 +783,6 @@ mod fs { } } -struct RenderContext { - viewport: Viewport, - framebuffers: Vec>, -} - struct RenderTask { swapchain_id: Id, vertex_buffer_id: Id, @@ -785,6 +853,10 @@ impl Task for RenderTask { cbf.as_raw().end_render_pass(&Default::default())?; + cbf.destroy_objects(rcx.framebuffers.iter().cloned()); + cbf.destroy_objects(self.uniform_buffer_sets.iter().cloned()); + cbf.destroy_objects(self.sampler_sets.iter().cloned()); + Ok(()) } } @@ -792,11 +864,12 @@ impl Task for RenderTask { #[allow(clippy::too_many_arguments)] fn run_worker( channel: mpsc::Receiver<()>, + graphics_family_index: u32, + transfer_family_index: u32, transfer_queue: Arc, resources: Arc, graphics_flight_id: Id, transfer_flight_id: Id, - texture_create_info: &ImageCreateInfo, texture_ids: [Id; 2], current_texture_index: Arc, ) { @@ -834,7 +907,18 @@ fn run_worker( let virtual_front_staging_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default()); let virtual_back_staging_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default()); - let virtual_texture_id = task_graph.add_image(texture_create_info); + let virtual_texture_id = task_graph.add_image(&ImageCreateInfo { + sharing: if graphics_family_index != transfer_family_index { + Sharing::Concurrent( + [graphics_family_index, transfer_family_index] + .into_iter() + .collect(), + ) + } else { + Sharing::Exclusive + }, + ..Default::default() + }); task_graph.add_host_buffer_access(virtual_front_staging_buffer_id, HostAccessType::Write); @@ -1009,17 +1093,15 @@ fn window_size_dependent_setup( resources: &Resources, swapchain_id: Id, render_pass: &Arc, - viewport: &mut Viewport, ) -> Vec> { let swapchain_state = resources.swapchain(swapchain_id).unwrap(); let images = swapchain_state.images(); - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/bloom/bloom.rs b/examples/bloom/bloom.rs index 1c0bd9ba09..c339e5c2fd 100644 --- a/examples/bloom/bloom.rs +++ b/examples/bloom/bloom.rs @@ -1,9 +1,9 @@ -use crate::RenderContext; +use crate::{App, RenderContext}; use std::{slice, sync::Arc}; use vulkano::{ image::{mip_level_extent, Image}, pipeline::{ - compute::ComputePipelineCreateInfo, ComputePipeline, PipelineBindPoint, + compute::ComputePipelineCreateInfo, ComputePipeline, PipelineBindPoint, PipelineLayout, PipelineShaderStageCreateInfo, }, sync::{AccessFlags, PipelineStages}, @@ -24,33 +24,37 @@ pub struct BloomTask { } impl BloomTask { - pub fn new(rcx: &RenderContext, bloom_image_id: Id) -> Self { + pub fn new( + app: &App, + pipeline_layout: &Arc, + virtual_bloom_image_id: Id, + ) -> Self { let downsample_pipeline = { - let cs = downsample::load(rcx.device.clone()) + let cs = downsample::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); let stage = PipelineShaderStageCreateInfo::new(cs); ComputePipeline::new( - rcx.device.clone(), + app.device.clone(), None, - ComputePipelineCreateInfo::stage_layout(stage, rcx.pipeline_layout.clone()), + ComputePipelineCreateInfo::stage_layout(stage, pipeline_layout.clone()), ) .unwrap() }; let upsample_pipeline = { - let cs = upsample::load(rcx.device.clone()) + let cs = upsample::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); let stage = PipelineShaderStageCreateInfo::new(cs); ComputePipeline::new( - rcx.device.clone(), + app.device.clone(), None, - ComputePipelineCreateInfo::stage_layout(stage, rcx.pipeline_layout.clone()), + ComputePipelineCreateInfo::stage_layout(stage, pipeline_layout.clone()), ) .unwrap() }; @@ -58,7 +62,7 @@ impl BloomTask { BloomTask { downsample_pipeline, upsample_pipeline, - bloom_image_id, + bloom_image_id: virtual_bloom_image_id, } } } @@ -136,6 +140,9 @@ impl Task for BloomTask { } } + cbf.destroy_object(bloom_image.clone()); + cbf.destroy_object(rcx.descriptor_set.as_ref().0.clone()); + Ok(()) } } diff --git a/examples/bloom/main.rs b/examples/bloom/main.rs index cba71d6b3e..a301ee5cd3 100644 --- a/examples/bloom/main.rs +++ b/examples/bloom/main.rs @@ -37,14 +37,15 @@ use vulkano::{ Validated, Version, VulkanError, VulkanLibrary, }; use vulkano_taskgraph::{ - graph::{CompileInfo, ExecuteError, TaskGraph}, + graph::{CompileInfo, ExecutableTaskGraph, ExecuteError, NodeId, TaskGraph}, resource::{AccessType, Flight, ImageLayoutType, Resources}, resource_map, Id, QueueFamilyType, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::{Window, WindowBuilder}, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; mod bloom; @@ -56,201 +57,51 @@ const MAX_BLOOM_MIP_LEVELS: u32 = 6; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let mut rcx = RenderContext::new(&event_loop, &instance); - - let mut task_graph = TaskGraph::new(&rcx.resources, 3, 2); - - let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default()); - let virtual_bloom_image_id = task_graph.add_image(&ImageCreateInfo::default()); - - let scene_node_id = task_graph - .create_task_node("Scene", QueueFamilyType::Graphics, SceneTask::new(&rcx)) - .image_access( - virtual_bloom_image_id, - AccessType::ColorAttachmentWrite, - ImageLayoutType::Optimal, - ) - .build(); - let bloom_node_id = task_graph - .create_task_node( - "Bloom", - QueueFamilyType::Compute, - BloomTask::new(&rcx, virtual_bloom_image_id), - ) - .image_access( - virtual_bloom_image_id, - AccessType::ComputeShaderSampledRead, - ImageLayoutType::General, - ) - .image_access( - virtual_bloom_image_id, - AccessType::ComputeShaderStorageWrite, - ImageLayoutType::General, - ) - .build(); - let tonemap_node_id = task_graph - .create_task_node( - "Tonemap", - QueueFamilyType::Graphics, - TonemapTask::new(&rcx, virtual_swapchain_id), - ) - .image_access( - virtual_swapchain_id.current_image_id(), - AccessType::ColorAttachmentWrite, - ImageLayoutType::Optimal, - ) - .image_access( - virtual_bloom_image_id, - AccessType::FragmentShaderSampledRead, - ImageLayoutType::General, - ) - .build(); - - task_graph.add_edge(scene_node_id, bloom_node_id).unwrap(); - task_graph.add_edge(bloom_node_id, tonemap_node_id).unwrap(); - - let mut task_graph = unsafe { - task_graph.compile(&CompileInfo { - queues: &[&rcx.queue], - present_queue: Some(&rcx.queue), - flight_id: rcx.flight_id, - ..Default::default() - }) - } - .unwrap(); - - let mut recreate_swapchain = false; - - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); - } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; - } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = rcx.window.inner_size().into(); - - if image_extent.contains(&0) { - return; - } - - if recreate_swapchain { - rcx.handle_resize(); - - task_graph - .task_node_mut(scene_node_id) - .unwrap() - .task_mut() - .downcast_mut::() - .unwrap() - .handle_resize(&rcx); - task_graph - .task_node_mut(tonemap_node_id) - .unwrap() - .task_mut() - .downcast_mut::() - .unwrap() - .handle_resize(&rcx); - - recreate_swapchain = false; - } - - let flight = rcx.resources.flight(rcx.flight_id).unwrap(); - - flight.wait(None).unwrap(); - - let resource_map = resource_map!( - &task_graph, - virtual_swapchain_id => rcx.swapchain_id, - virtual_bloom_image_id => rcx.bloom_image_id, - ) - .unwrap(); - - match unsafe { - task_graph.execute(resource_map, &rcx, || rcx.window.pre_present_notify()) - } { - Ok(()) => {} - Err(ExecuteError::Swapchain { - error: Validated::Error(VulkanError::OutOfDate), - .. - }) => { - recreate_swapchain = true; - } - Err(e) => { - panic!("failed to execute next frame: {e:?}"); - } - } - } - Event::AboutToWait => { - rcx.window.request_redraw(); - } - Event::LoopExiting => { - rcx.cleanup(); - - task_graph - .task_node_mut(scene_node_id) - .unwrap() - .task_mut() - .downcast_mut::() - .unwrap() - .cleanup(&rcx); - task_graph - .task_node_mut(tonemap_node_id) - .unwrap() - .task_mut() - .downcast_mut::() - .unwrap() - .cleanup(&rcx); - } - _ => (), - } - }) + event_loop.run_app(&mut app) } -pub struct RenderContext { +struct App { + instance: Arc, device: Arc, queue: Arc, resources: Arc, flight_id: Id, + rcx: Option, +} + +pub struct RenderContext { window: Arc, swapchain_id: Id, - swapchain_format: Format, bloom_image_id: Id, viewport: Viewport, pipeline_layout: Arc, + recreate_swapchain: bool, descriptor_set_allocator: Arc, sampler: Arc, descriptor_set: DescriptorSetWithOffsets, + task_graph: ExecutableTaskGraph, + scene_node_id: NodeId, + tonemap_node_id: NodeId, + virtual_swapchain_id: Id, + virtual_bloom_image_id: Id, } -impl RenderContext { - fn new(event_loop: &EventLoop<()>, instance: &Arc) -> Self { +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) + .unwrap(); + let mut device_extensions = DeviceExtensions { khr_swapchain: true, ..DeviceExtensions::empty() @@ -318,16 +169,32 @@ impl RenderContext { let flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); - let window = Arc::new(WindowBuilder::new().build(event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + App { + instance, + device, + queue, + resources, + flight_id, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); let swapchain_format; let swapchain_id = { - let surface_capabilities = device + let surface_capabilities = self + .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - (swapchain_format, _) = device + (swapchain_format, _) = self + .device .physical_device() .surface_formats(&surface, Default::default()) .unwrap() @@ -338,9 +205,9 @@ impl RenderContext { }) .unwrap(); - resources + self.resources .create_swapchain( - flight_id, + self.flight_id, surface, SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count.max(3), @@ -360,15 +227,15 @@ impl RenderContext { let viewport = Viewport { offset: [0.0, 0.0], - extent: window.inner_size().into(), + extent: window_size.into(), depth_range: 0.0..=1.0, }; let pipeline_layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineLayoutCreateInfo { set_layouts: vec![DescriptorSetLayout::new( - device.clone(), + self.device.clone(), DescriptorSetLayoutCreateInfo { bindings: [ ( @@ -417,12 +284,12 @@ impl RenderContext { .unwrap(); let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), + self.device.clone(), Default::default(), )); let sampler = Sampler::new( - device.clone(), + self.device.clone(), SamplerCreateInfo { mag_filter: Filter::Linear, min_filter: Filter::Linear, @@ -434,61 +301,191 @@ impl RenderContext { .unwrap(); let (bloom_image_id, descriptor_set) = window_size_dependent_setup( - &resources, + &self.resources, swapchain_id, &pipeline_layout, &sampler, &descriptor_set_allocator, ); - RenderContext { - device, - queue, + let mut task_graph = TaskGraph::new(&self.resources, 3, 2); + + let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default()); + let virtual_bloom_image_id = task_graph.add_image(&ImageCreateInfo::default()); + + let scene_node_id = task_graph + .create_task_node( + "Scene", + QueueFamilyType::Graphics, + SceneTask::new(self, &pipeline_layout, bloom_image_id), + ) + .image_access( + virtual_bloom_image_id, + AccessType::ColorAttachmentWrite, + ImageLayoutType::Optimal, + ) + .build(); + let bloom_node_id = task_graph + .create_task_node( + "Bloom", + QueueFamilyType::Compute, + BloomTask::new(self, &pipeline_layout, virtual_bloom_image_id), + ) + .image_access( + virtual_bloom_image_id, + AccessType::ComputeShaderSampledRead, + ImageLayoutType::General, + ) + .image_access( + virtual_bloom_image_id, + AccessType::ComputeShaderStorageWrite, + ImageLayoutType::General, + ) + .build(); + let tonemap_node_id = task_graph + .create_task_node( + "Tonemap", + QueueFamilyType::Graphics, + TonemapTask::new(self, &pipeline_layout, swapchain_id, virtual_swapchain_id), + ) + .image_access( + virtual_swapchain_id.current_image_id(), + AccessType::ColorAttachmentWrite, + ImageLayoutType::Optimal, + ) + .image_access( + virtual_bloom_image_id, + AccessType::FragmentShaderSampledRead, + ImageLayoutType::General, + ) + .build(); + + task_graph.add_edge(scene_node_id, bloom_node_id).unwrap(); + task_graph.add_edge(bloom_node_id, tonemap_node_id).unwrap(); + + let task_graph = unsafe { + task_graph.compile(&CompileInfo { + queues: &[&self.queue], + present_queue: Some(&self.queue), + flight_id: self.flight_id, + ..Default::default() + }) + } + .unwrap(); + + self.rcx = Some(RenderContext { window, - resources, - flight_id, swapchain_id, - swapchain_format, bloom_image_id, viewport, pipeline_layout, + recreate_swapchain: false, sampler, descriptor_set_allocator, descriptor_set, - } + task_graph, + scene_node_id, + tonemap_node_id, + virtual_swapchain_id, + virtual_bloom_image_id, + }); } - fn handle_resize(&mut self) { - let window_size = self.window.inner_size(); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); - self.swapchain_id = self - .resources - .recreate_swapchain(self.swapchain_id, |create_info| SwapchainCreateInfo { - image_extent: window_size.into(), - ..create_info - }) - .expect("failed to recreate swapchain"); + match event { + WindowEvent::CloseRequested => { + event_loop.exit(); + } + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; + } + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); + + if window_size.width == 0 || window_size.height == 0 { + return; + } - let flight = self.resources.flight(self.flight_id).unwrap(); - let bloom_image_state = - unsafe { self.resources.remove_image(self.bloom_image_id) }.unwrap(); - flight.destroy_object(bloom_image_state.image().clone()); - flight.destroy_object(self.descriptor_set.as_ref().0.clone()); + let flight = self.resources.flight(self.flight_id).unwrap(); - (self.bloom_image_id, self.descriptor_set) = window_size_dependent_setup( - &self.resources, - self.swapchain_id, - &self.pipeline_layout, - &self.sampler, - &self.descriptor_set_allocator, - ); + if rcx.recreate_swapchain { + rcx.swapchain_id = self + .resources + .recreate_swapchain(rcx.swapchain_id, |create_info| SwapchainCreateInfo { + image_extent: window_size.into(), + ..create_info + }) + .expect("failed to recreate swapchain"); + + rcx.viewport.extent = window_size.into(); + + unsafe { self.resources.remove_image(rcx.bloom_image_id) }.unwrap(); + + (rcx.bloom_image_id, rcx.descriptor_set) = window_size_dependent_setup( + &self.resources, + rcx.swapchain_id, + &rcx.pipeline_layout, + &rcx.sampler, + &rcx.descriptor_set_allocator, + ); + + rcx.task_graph + .task_node_mut(rcx.scene_node_id) + .unwrap() + .task_mut() + .downcast_mut::() + .unwrap() + .handle_resize(&self.resources, rcx.bloom_image_id); + rcx.task_graph + .task_node_mut(rcx.tonemap_node_id) + .unwrap() + .task_mut() + .downcast_mut::() + .unwrap() + .handle_resize(&self.resources, rcx.swapchain_id); + + rcx.recreate_swapchain = false; + } + + flight.wait(None).unwrap(); + + let resource_map = resource_map!( + &rcx.task_graph, + rcx.virtual_swapchain_id => rcx.swapchain_id, + rcx.virtual_bloom_image_id => rcx.bloom_image_id, + ) + .unwrap(); - self.viewport.extent = window_size.into(); + match unsafe { + rcx.task_graph + .execute(resource_map, rcx, || rcx.window.pre_present_notify()) + } { + Ok(()) => {} + Err(ExecuteError::Swapchain { + error: Validated::Error(VulkanError::OutOfDate), + .. + }) => { + rcx.recreate_swapchain = true; + } + Err(e) => { + panic!("failed to execute next frame: {e:?}"); + } + } + } + _ => {} + } } - fn cleanup(&mut self) { - let flight = self.resources.flight(self.flight_id).unwrap(); - flight.destroy_object(self.descriptor_set.as_ref().0.clone()); + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); } } diff --git a/examples/bloom/scene.rs b/examples/bloom/scene.rs index 014fee8fbf..d9b1381563 100644 --- a/examples/bloom/scene.rs +++ b/examples/bloom/scene.rs @@ -1,12 +1,12 @@ -use crate::RenderContext; -use std::{alloc::Layout, mem, slice, sync::Arc}; +use crate::{App, RenderContext}; +use std::{alloc::Layout, slice, sync::Arc}; use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::RenderPassBeginInfo, format::Format, image::{ view::{ImageView, ImageViewCreateInfo}, - ImageAspects, ImageSubresourceRange, ImageUsage, + Image, ImageAspects, ImageSubresourceRange, ImageUsage, }, memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter}, pipeline::{ @@ -19,13 +19,14 @@ use vulkano::{ viewport::ViewportState, GraphicsPipelineCreateInfo, }, - DynamicState, GraphicsPipeline, PipelineShaderStageCreateInfo, + DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, }; use vulkano_taskgraph::{ - command_buffer::RecordingCommandBuffer, resource::HostAccessType, Id, Task, TaskContext, - TaskResult, + command_buffer::RecordingCommandBuffer, + resource::{HostAccessType, Resources}, + Id, Task, TaskContext, TaskResult, }; pub struct SceneTask { @@ -36,9 +37,13 @@ pub struct SceneTask { } impl SceneTask { - pub fn new(rcx: &RenderContext) -> Self { + pub fn new( + app: &App, + pipeline_layout: &Arc, + bloom_image_id: Id, + ) -> Self { let render_pass = vulkano::single_pass_renderpass!( - rcx.device.clone(), + app.device.clone(), attachments: { color: { format: Format::R32_UINT, @@ -55,11 +60,11 @@ impl SceneTask { .unwrap(); let pipeline = { - let vs = vs::load(rcx.device.clone()) + let vs = vs::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(rcx.device.clone()) + let fs = fs::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); @@ -71,7 +76,7 @@ impl SceneTask { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - rcx.device.clone(), + app.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -86,13 +91,13 @@ impl SceneTask { )), dynamic_state: [DynamicState::Viewport].into_iter().collect(), subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(rcx.pipeline_layout.clone()) + ..GraphicsPipelineCreateInfo::layout(pipeline_layout.clone()) }, ) .unwrap() }; - let framebuffer = window_size_dependent_setup(rcx, &render_pass); + let framebuffer = window_size_dependent_setup(&app.resources, bloom_image_id, &render_pass); let vertices = [ MyVertex { @@ -105,7 +110,7 @@ impl SceneTask { position: [0.0, -0.5], }, ]; - let vertex_buffer_id = rcx + let vertex_buffer_id = app .resources .create_buffer( BufferCreateInfo { @@ -123,9 +128,9 @@ impl SceneTask { unsafe { vulkano_taskgraph::execute( - &rcx.queue, - &rcx.resources, - rcx.flight_id, + &app.queue, + &app.resources, + app.flight_id, |_cbf, tcx| { tcx.write_buffer::<[MyVertex]>(vertex_buffer_id, ..)? .copy_from_slice(&vertices); @@ -147,16 +152,9 @@ impl SceneTask { } } - pub fn handle_resize(&mut self, rcx: &RenderContext) { - let framebuffer = window_size_dependent_setup(rcx, &self.render_pass); - - let flight = rcx.resources.flight(rcx.flight_id).unwrap(); - flight.destroy_object(mem::replace(&mut self.framebuffer, framebuffer)); - } - - pub fn cleanup(&mut self, rcx: &RenderContext) { - let flight = rcx.resources.flight(rcx.flight_id).unwrap(); - flight.destroy_object(self.framebuffer.clone()); + pub fn handle_resize(&mut self, resources: &Resources, bloom_image_id: Id) { + self.framebuffer = + window_size_dependent_setup(resources, bloom_image_id, &self.render_pass); } } @@ -184,6 +182,8 @@ impl Task for SceneTask { cbf.as_raw().end_render_pass(&Default::default())?; + cbf.destroy_object(self.framebuffer.clone()); + Ok(()) } } @@ -195,6 +195,38 @@ struct MyVertex { position: [f32; 2], } +fn window_size_dependent_setup( + resources: &Resources, + bloom_image_id: Id, + render_pass: &Arc, +) -> Arc { + let image_state = resources.image(bloom_image_id).unwrap(); + let image = image_state.image(); + let view = ImageView::new( + image.clone(), + ImageViewCreateInfo { + format: Format::R32_UINT, + subresource_range: ImageSubresourceRange { + aspects: ImageAspects::COLOR, + mip_levels: 0..1, + array_layers: 0..1, + }, + usage: ImageUsage::COLOR_ATTACHMENT, + ..Default::default() + }, + ) + .unwrap(); + + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() +} + mod vs { vulkano_shaders::shader! { ty: "vertex", @@ -226,34 +258,3 @@ mod fs { include: ["."], } } - -fn window_size_dependent_setup( - rcx: &RenderContext, - render_pass: &Arc, -) -> Arc { - let image_state = rcx.resources.image(rcx.bloom_image_id).unwrap(); - let image = image_state.image(); - let view = ImageView::new( - image.clone(), - ImageViewCreateInfo { - format: Format::R32_UINT, - subresource_range: ImageSubresourceRange { - aspects: ImageAspects::COLOR, - mip_levels: 0..1, - array_layers: 0..1, - }, - usage: ImageUsage::COLOR_ATTACHMENT, - ..Default::default() - }, - ) - .unwrap(); - - Framebuffer::new( - render_pass.clone(), - FramebufferCreateInfo { - attachments: vec![view], - ..Default::default() - }, - ) - .unwrap() -} diff --git a/examples/bloom/tonemap.rs b/examples/bloom/tonemap.rs index 3900f40253..23f49e8616 100644 --- a/examples/bloom/tonemap.rs +++ b/examples/bloom/tonemap.rs @@ -1,5 +1,5 @@ -use crate::RenderContext; -use std::{mem, slice, sync::Arc}; +use crate::{App, RenderContext}; +use std::{slice, sync::Arc}; use vulkano::{ command_buffer::RenderPassBeginInfo, image::view::ImageView, @@ -13,13 +13,14 @@ use vulkano::{ viewport::ViewportState, GraphicsPipelineCreateInfo, }, - DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineShaderStageCreateInfo, + DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineLayout, + PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, swapchain::Swapchain, }; use vulkano_taskgraph::{ - command_buffer::RecordingCommandBuffer, Id, Task, TaskContext, TaskResult, + command_buffer::RecordingCommandBuffer, resource::Resources, Id, Task, TaskContext, TaskResult, }; const EXPOSURE: f32 = 1.0; @@ -32,12 +33,20 @@ pub struct TonemapTask { } impl TonemapTask { - pub fn new(rcx: &RenderContext, swapchain_id: Id) -> Self { + pub fn new( + app: &App, + pipeline_layout: &Arc, + swapchain_id: Id, + virtual_swapchain_id: Id, + ) -> Self { + let swapchain_state = app.resources.swapchain(swapchain_id).unwrap(); + let swapchain_format = swapchain_state.swapchain().image_format(); + let render_pass = vulkano::single_pass_renderpass!( - rcx.device.clone(), + app.device.clone(), attachments: { color: { - format: rcx.swapchain_format, + format: swapchain_format, samples: 1, load_op: DontCare, store_op: Store, @@ -51,11 +60,11 @@ impl TonemapTask { .unwrap(); let pipeline = { - let vs = vs::load(rcx.device.clone()) + let vs = vs::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(rcx.device.clone()) + let fs = fs::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); @@ -66,7 +75,7 @@ impl TonemapTask { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - rcx.device.clone(), + app.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -81,32 +90,24 @@ impl TonemapTask { )), dynamic_state: [DynamicState::Viewport].into_iter().collect(), subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(rcx.pipeline_layout.clone()) + ..GraphicsPipelineCreateInfo::layout(pipeline_layout.clone()) }, ) .unwrap() }; - let framebuffers = window_size_dependent_setup(rcx, &render_pass); + let framebuffers = window_size_dependent_setup(&app.resources, swapchain_id, &render_pass); TonemapTask { render_pass, pipeline, framebuffers, - swapchain_id, + swapchain_id: virtual_swapchain_id, } } - pub fn handle_resize(&mut self, rcx: &RenderContext) { - let framebuffers = window_size_dependent_setup(rcx, &self.render_pass); - - let flight = rcx.resources.flight(rcx.flight_id).unwrap(); - flight.destroy_objects(mem::replace(&mut self.framebuffers, framebuffers)); - } - - pub fn cleanup(&mut self, rcx: &RenderContext) { - let flight = rcx.resources.flight(rcx.flight_id).unwrap(); - flight.destroy_objects(self.framebuffers.drain(..)); + pub fn handle_resize(&mut self, resources: &Resources, swapchain_id: Id) { + self.framebuffers = window_size_dependent_setup(resources, swapchain_id, &self.render_pass); } } @@ -148,10 +149,37 @@ impl Task for TonemapTask { cbf.as_raw().end_render_pass(&Default::default())?; + cbf.destroy_objects(self.framebuffers.iter().cloned()); + Ok(()) } } +fn window_size_dependent_setup( + resources: &Resources, + swapchain_id: Id, + render_pass: &Arc, +) -> Vec> { + let swapchain_state = resources.swapchain(swapchain_id).unwrap(); + let images = swapchain_state.images(); + + images + .iter() + .map(|image| { + let view = ImageView::new_default(image.clone()).unwrap(); + + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() + }) + .collect::>() +} + mod vs { vulkano_shaders::shader! { ty: "vertex", @@ -187,26 +215,3 @@ mod fs { include: ["."], } } - -fn window_size_dependent_setup( - rcx: &RenderContext, - render_pass: &Arc, -) -> Vec> { - let swapchain_state = rcx.resources.swapchain(rcx.swapchain_id).unwrap(); - let images = swapchain_state.images(); - - images - .iter() - .map(|image| { - let view = ImageView::new_default(image.clone()).unwrap(); - Framebuffer::new( - render_pass.clone(), - FramebufferCreateInfo { - attachments: vec![view], - ..Default::default() - }, - ) - .unwrap() - }) - .collect::>() -} diff --git a/examples/buffer-allocator/main.rs b/examples/buffer-allocator/main.rs index 6801cbff48..9f2eb75997 100644 --- a/examples/buffer-allocator/main.rs +++ b/examples/buffer-allocator/main.rs @@ -15,8 +15,8 @@ use vulkano::{ CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -42,16 +42,43 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + buffer_allocator: SubbufferAllocator, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -76,7 +103,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -111,27 +138,65 @@ fn main() -> Result<(), impl Error> { let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // Using a buffer allocator allows multiple buffers to be "in-flight" simultaneously and is + // suited to highly dynamic data like vertex, index and uniform buffers. + let buffer_allocator = SubbufferAllocator::new( + memory_allocator, + SubbufferAllocatorCreateInfo { + // We want to use the allocated subbuffers as vertex buffers. + buffer_usage: BufferUsage::VERTEX_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); + + App { + instance, + device, + queue, + command_buffer_allocator, + buffer_allocator, + rcx: None, + } +} +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); - let (mut swapchain, images) = { - let surface_capabilities = device + let (swapchain, images) = { + let surface_capabilities = self + .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let image_format = device + let (image_format, _) = self + .device .physical_device() .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; + .unwrap()[0]; Swapchain::new( - device.clone(), + self.device.clone(), surface, SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count.max(2), image_format, - image_extent: window.inner_size().into(), + image_extent: window_size.into(), image_usage: ImageUsage::COLOR_ATTACHMENT, composite_alpha: surface_capabilities .supported_composite_alpha @@ -144,27 +209,24 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(Clone, Copy, BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } - - // Using a buffer allocator allows multiple buffers to be "in-flight" simultaneously and is - // suited to highly dynamic data like vertex, index and uniform buffers. - let buffer_allocator = SubbufferAllocator::new( - memory_allocator, - SubbufferAllocatorCreateInfo { - // We want to use the allocated subbuffers as vertex buffers. - buffer_usage: BufferUsage::VERTEX_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ); + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); mod vs { vulkano_shaders::shader! { @@ -196,48 +258,31 @@ fn main() -> Result<(), impl Error> { } } - let render_pass = vulkano::single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -258,77 +303,77 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } // Rotate once (PI*2) every 5 seconds @@ -345,16 +390,16 @@ fn main() -> Result<(), impl Error> { const ANGLE_OFFSET: f32 = (std::f32::consts::PI * 2.0) / 3.0; // Calculate vertices let data = [ - Vertex { + MyVertex { position: [angle.cos() * RADIUS, angle.sin() * RADIUS], }, - Vertex { + MyVertex { position: [ (angle + ANGLE_OFFSET).cos() * RADIUS, (angle + ANGLE_OFFSET).sin() * RADIUS, ], }, - Vertex { + MyVertex { position: [ (angle - ANGLE_OFFSET).cos() * RADIUS, (angle - ANGLE_OFFSET).sin() * RADIUS, @@ -364,12 +409,12 @@ fn main() -> Result<(), impl Error> { let num_vertices = data.len() as u32; // Allocate a new subbuffer using the buffer allocator. - let buffer = buffer_allocator.allocate_slice(data.len() as _).unwrap(); + let buffer = self.buffer_allocator.allocate_slice(data.len() as _).unwrap(); buffer.write().unwrap().copy_from_slice(&data); let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -383,16 +428,16 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() // Draw our buffer - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_vertex_buffers(0, buffer) .unwrap(); @@ -404,51 +449,60 @@ fn main() -> Result<(), impl Error> { builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(Box::new(future) as Box<_>); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<_>); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<_>); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(Clone, Copy, BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/clear-attachments/main.rs b/examples/clear-attachments/main.rs index 7260c2a329..6facb4f3f4 100644 --- a/examples/clear-attachments/main.rs +++ b/examples/clear-attachments/main.rs @@ -6,8 +6,8 @@ use vulkano::{ RenderPassBeginInfo, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -19,9 +19,10 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { @@ -29,9 +30,34 @@ fn main() -> Result<(), impl Error> { // example if you haven't done so yet. let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + width: u32, + height: u32, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -56,7 +82,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -88,29 +114,53 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); + let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + App { + instance, + device, + queue, + command_buffer_allocator, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); - let (mut swapchain, images) = { - let surface_capabilities = device + let (swapchain, images) = { + let surface_capabilities = self + .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let image_format = device + let (image_format, _) = self + .device .physical_device() .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; + .unwrap()[0]; Swapchain::new( - device.clone(), + self.device.clone(), surface, SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count.max(2), image_format, - image_extent: window.inner_size().into(), + image_extent: window_size.into(), image_usage: ImageUsage::COLOR_ATTACHMENT, composite_alpha: surface_capabilities .supported_composite_alpha @@ -123,7 +173,8 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let render_pass = vulkano::single_pass_renderpass!(device.clone(), + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), attachments: { color: { format: swapchain.image_format(), @@ -139,78 +190,80 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let framebuffers = window_size_dependent_setup(&images, &render_pass); + + let [width, height] = window_size.into(); - let mut width = swapchain.image_extent()[0]; - let mut height = swapchain.image_extent()[1]; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone()); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + width, + height, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - width = swapchain.image_extent()[0]; - height = swapchain.image_extent()[1]; - framebuffers = window_size_dependent_setup(&new_images, render_pass.clone()); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + [rcx.width, rcx.height] = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -223,7 +276,7 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), @@ -250,13 +303,13 @@ fn main() -> Result<(), impl Error> { // Fixed offset, relative extent. ClearRect { offset: [100, 150], - extent: [width / 4, height / 4], + extent: [rcx.width / 4, rcx.height / 4], array_layers: 0..1, }, // Relative offset and extent. ClearRect { - offset: [width / 2, height / 2], - extent: [width / 3, height / 5], + offset: [rcx.width / 2, rcx.height / 2], + extent: [rcx.width / 3, rcx.height / 5], array_layers: 0..1, }, ] @@ -268,47 +321,53 @@ fn main() -> Result<(), impl Error> { .unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, + render_pass: &Arc, ) -> Vec> { images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/deferred/main.rs b/examples/deferred/main.rs index 552e99a50f..8b4c0d9f3c 100644 --- a/examples/deferred/main.rs +++ b/examples/deferred/main.rs @@ -27,8 +27,8 @@ use vulkano::{ StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -40,9 +40,10 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; mod frame; @@ -52,9 +53,34 @@ fn main() -> Result<(), impl Error> { // Basic initialization. See the triangle example if you want more details about this. let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + memory_allocator: Arc, + command_buffer_allocator: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + images: Vec>, + frame_system: FrameSystem, + triangle_draw_system: TriangleDrawSystem, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -79,7 +105,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -111,29 +137,54 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); + let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + StandardCommandBufferAllocatorCreateInfo { + secondary_buffer_count: 32, + ..Default::default() + }, + )); - let (mut swapchain, mut images) = { - let surface_capabilities = device + App { + instance, + device, + queue, + memory_allocator, + command_buffer_allocator, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let image_format = device + let (image_format, _) = self + .device .physical_device() .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; + .unwrap()[0]; let (swapchain, images) = Swapchain::new( - device.clone(), + self.device.clone(), surface, SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count.max(2), image_format, - image_extent: window.inner_size().into(), + image_extent: window_size.into(), image_usage: ImageUsage::COLOR_ATTACHMENT, composite_alpha: surface_capabilities .supported_composite_alpha @@ -144,72 +195,72 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); - let images = images - .into_iter() - .map(|image| ImageView::new_default(image).unwrap()) - .collect::>(); + (swapchain, images) }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, - ..Default::default() - }, - )); + let images = images + .into_iter() + .map(|image| ImageView::new_default(image).unwrap()) + .collect::>(); // Here is the basic initialization for the deferred system. - let mut frame_system = FrameSystem::new( - queue.clone(), + let frame_system = FrameSystem::new( + self.queue.clone(), swapchain.image_format(), - memory_allocator.clone(), - command_buffer_allocator.clone(), + self.memory_allocator.clone(), + self.command_buffer_allocator.clone(), ); let triangle_draw_system = TriangleDrawSystem::new( - queue.clone(), + self.queue.clone(), frame_system.deferred_subpass(), - memory_allocator.clone(), - command_buffer_allocator, + self.memory_allocator.clone(), + self.command_buffer_allocator.clone(), ); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + images, + frame_system, + triangle_draw_system, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); let new_images = new_images @@ -217,36 +268,36 @@ fn main() -> Result<(), impl Error> { .map(|image| ImageView::new_default(image).unwrap()) .collect::>(); - swapchain = new_swapchain; - images = new_images; - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.images = new_images; + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } - let future = previous_frame_end.take().unwrap().join(acquire_future); - let mut frame = frame_system.frame( + let future = rcx.previous_frame_end.take().unwrap().join(acquire_future); + let mut frame = rcx.frame_system.frame( future, - images[image_index as usize].clone(), + rcx.images[image_index as usize].clone(), Mat4::IDENTITY, ); let mut after_future = None; while let Some(pass) = frame.next_pass() { match pass { Pass::Deferred(mut draw_pass) => { - let cb = triangle_draw_system.draw(draw_pass.viewport_dimensions()); + let cb = rcx.triangle_draw_system.draw(draw_pass.viewport_dimensions()); draw_pass.execute(cb); } Pass::Lighting(mut lighting) => { @@ -265,27 +316,31 @@ fn main() -> Result<(), impl Error> { let future = after_future .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } } diff --git a/examples/dynamic-buffers/main.rs b/examples/dynamic-buffers/main.rs index 8424171ac2..422d87b85e 100644 --- a/examples/dynamic-buffers/main.rs +++ b/examples/dynamic-buffers/main.rs @@ -83,6 +83,7 @@ fn main() { }, ) .unwrap(); + let queue = queues.next().unwrap(); mod cs { diff --git a/examples/gl-interop/Cargo.toml b/examples/gl-interop/Cargo.toml index 9a2751b98b..7a235c92e0 100644 --- a/examples/gl-interop/Cargo.toml +++ b/examples/gl-interop/Cargo.toml @@ -16,5 +16,4 @@ glium = "0.32.1" vulkano = { workspace = true, default-features = true } vulkano-shaders = { workspace = true } winit = { workspace = true, default-features = true } -# Glium has still not been updated to the latest winit version winit_glium = { package = "winit", version = "0.27.1" } diff --git a/examples/gl-interop/main.rs b/examples/gl-interop/main.rs index d8f951d29d..cc3ae3cd1c 100644 --- a/examples/gl-interop/main.rs +++ b/examples/gl-interop/main.rs @@ -70,7 +70,7 @@ mod linux { acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, }, sync::{ - now, + self, semaphore::{ ExternalSemaphoreHandleType, ExternalSemaphoreHandleTypes, Semaphore, SemaphoreCreateInfo, @@ -80,12 +80,50 @@ mod linux { Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::{Window, WindowBuilder}, + application::ApplicationHandler, + error::EventLoopError, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; - pub fn main() -> Result<(), winit::error::EventLoopError> { + pub fn main() -> Result<(), EventLoopError> { + let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + + event_loop.run_app(&mut app) + } + + struct App { + instance: Arc, + device: Arc, + queue: Arc, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + image_view: Arc, + sampler: Arc, + barrier: Arc, + barrier_2: Arc, + acquire_sem: Arc, + release_sem: Arc, + rcx: Option, + } + + struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + descriptor_set: Arc, + recreate_swapchain: bool, + previous_frame_end: Option>, + } + + impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let event_loop_gl = winit_glium::event_loop::EventLoop::new(); // For some reason, this must be created before the vulkan window let hrb = glutin::ContextBuilder::new() @@ -107,21 +145,144 @@ mod linux { ) .unwrap(); - let event_loop = EventLoop::new().unwrap(); - let ( - device, - _instance, - mut swapchain, - window, - mut viewport, - queue, - render_pass, - mut framebuffers, - sampler, - pipeline, - memory_allocator, - vertex_buffer, - ) = vk_setup(display, &event_loop); + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: InstanceExtensions { + khr_get_physical_device_properties2: true, + khr_external_memory_capabilities: true, + khr_external_semaphore_capabilities: true, + khr_external_fence_capabilities: true, + ext_debug_utils: true, + ..required_extensions + }, + ..Default::default() + }, + ) + .unwrap(); + + let _debug_callback = unsafe { + DebugUtilsMessenger::new( + instance.clone(), + DebugUtilsMessengerCreateInfo::user_callback(DebugUtilsMessengerCallback::new( + |message_severity, message_type, callback_data| { + println!( + "{} {:?} {:?}: {}", + callback_data.message_id_name.unwrap_or("unknown"), + message_type, + message_severity, + callback_data.message, + ); + }, + )), + ) + .unwrap() + }; + + let device_extensions = DeviceExtensions { + khr_external_semaphore: true, + khr_external_semaphore_fd: true, + khr_external_memory: true, + khr_external_memory_fd: true, + khr_external_fence: true, + khr_external_fence_fd: true, + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .filter(|(p, _)| p.properties().driver_uuid.unwrap() == display.driver_uuid().unwrap()) + .filter(|(p, _)| { + display + .device_uuids() + .unwrap() + .contains(&p.properties().device_uuid.unwrap()) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-0.5, -0.5], + }, + MyVertex { + position: [-0.5, 0.5], + }, + MyVertex { + position: [0.5, -0.5], + }, + MyVertex { + position: [0.5, 0.5], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); let raw_image = RawImage::new( device.clone(), @@ -171,6 +332,17 @@ mod linux { let image_view = ImageView::new_default(image).unwrap(); + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + ) + .unwrap(); + let barrier = Arc::new(Barrier::new(2)); let barrier_2 = Arc::new(Barrier::new(2)); @@ -271,129 +443,256 @@ mod linux { } }); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let layout = &pipeline.layout().set_layouts()[0]; - - let set = DescriptorSet::new( + App { + instance, + device, + queue, descriptor_set_allocator, - layout.clone(), - [ - WriteDescriptorSet::sampler(0, sampler), - WriteDescriptorSet::image_view(1, image_view), - ], - [], - ) - .unwrap(); - - let mut recreate_swapchain = false; - let mut previous_frame_end: Option> = - Some(Box::new(now(device.clone()))); - - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); - } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; - } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - queue - .with(|mut q| unsafe { - q.submit( - &[SubmitInfo { - signal_semaphores: vec![SemaphoreSubmitInfo::new( - acquire_sem.clone(), - )], - ..Default::default() - }], - None, - ) - }) - .unwrap(); + command_buffer_allocator, + vertex_buffer, + sampler, + image_view, + barrier, + barrier_2, + acquire_sem, + release_sem, + rcx: None, + } + } + } - barrier.wait(); - barrier_2.wait(); + impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); - queue - .with(|mut q| unsafe { - q.submit( - &[SubmitInfo { - wait_semaphores: vec![SemaphoreSubmitInfo::new( - release_sem.clone(), - )], - ..Default::default() - }], - None, - ) - }) - .unwrap(); + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; - let image_extent: [u32; 2] = window.inner_size().into(); + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - if image_extent.contains(&0) { - return; - } + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); - previous_frame_end.as_mut().unwrap().cleanup_finished(); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain - .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let layout = &pipeline.layout().set_layouts()[0]; + + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), + layout.clone(), + [ + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view(1, self.image_view.clone()), + ], + [], + ) + .unwrap(); + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); + + match event { + WindowEvent::CloseRequested => { + event_loop.exit(); + } + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; + } + WindowEvent::RedrawRequested => { + self.queue + .with(|mut q| unsafe { + q.submit( + &[SubmitInfo { + signal_semaphores: vec![SemaphoreSubmitInfo::new( + self.acquire_sem.clone(), + )], + ..Default::default() + }], + None, + ) + }) + .unwrap(); + + self.barrier.wait(); + self.barrier_2.wait(); + + self.queue + .with(|mut q| unsafe { + q.submit( + &[SubmitInfo { + wait_semaphores: vec![SemaphoreSubmitInfo::new( + self.release_sem.clone(), + )], + ..Default::default() + }], + None, + ) + }) + .unwrap(); + + let window_size = rcx.window.inner_size(); + + if window_size.width == 0 || window_size.height == 0 { + return; + } + + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain + .recreate(SwapchainCreateInfo { + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = match acquire_next_image( - swapchain.clone(), + rcx.swapchain.clone(), None, ) .map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -407,42 +706,45 @@ mod linux { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, - set.clone(), + rcx.descriptor_set.clone(), ) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end.take().unwrap().join(acquire_future); - let future = future - .then_execute(queue.clone(), command_buffer) + let future = rcx + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), + self.queue.clone(), SwapchainPresentInfo::swapchain_image_index( - swapchain.clone(), + rcx.swapchain.clone(), image_index, ), ) @@ -451,22 +753,26 @@ mod linux { match future.map_err(Validated::unwrap) { Ok(future) => { future.wait(None).unwrap(); - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } }; } - Event::AboutToWait => window.request_redraw(), - _ => (), - }; - }) + _ => {} + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } } #[derive(BufferContents, Vertex)] @@ -476,289 +782,6 @@ mod linux { position: [f32; 2], } - #[allow(clippy::type_complexity)] - fn vk_setup( - display: glium::HeadlessRenderer, - event_loop: &EventLoop<()>, - ) -> ( - Arc, - Arc, - Arc, - Arc, - Viewport, - Arc, - Arc, - Vec>, - Arc, - Arc, - Arc, - Subbuffer<[MyVertex]>, - ) { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: InstanceExtensions { - khr_get_physical_device_properties2: true, - khr_external_memory_capabilities: true, - khr_external_semaphore_capabilities: true, - khr_external_fence_capabilities: true, - ext_debug_utils: true, - ..required_extensions - }, - ..Default::default() - }, - ) - .unwrap(); - - let _debug_callback = unsafe { - DebugUtilsMessenger::new( - instance.clone(), - DebugUtilsMessengerCreateInfo::user_callback(DebugUtilsMessengerCallback::new( - |message_severity, message_type, callback_data| { - println!( - "{} {:?} {:?}: {}", - callback_data.message_id_name.unwrap_or("unknown"), - message_type, - message_severity, - callback_data.message, - ); - }, - )), - ) - .unwrap() - }; - - let device_extensions = DeviceExtensions { - khr_external_semaphore: true, - khr_external_semaphore_fd: true, - khr_external_memory: true, - khr_external_memory_fd: true, - khr_external_fence: true, - khr_external_fence_fd: true, - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .filter(|(p, _)| p.properties().driver_uuid.unwrap() == display.driver_uuid().unwrap()) - .filter(|(p, _)| { - display - .device_uuids() - .unwrap() - .contains(&p.properties().device_uuid.unwrap()) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .unwrap(); - - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, - ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let window = Arc::new(WindowBuilder::new().build(event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - let vertices = [ - MyVertex { - position: [-0.5, -0.5], - }, - MyVertex { - position: [-0.5, 0.5], - }, - MyVertex { - position: [0.5, -0.5], - }, - MyVertex { - position: [0.5, 0.5], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let render_pass = vulkano::single_pass_renderpass!(device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let sampler = Sampler::new( - device.clone(), - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - ..Default::default() - }, - ) - .unwrap(); - - let pipeline = { - let vs = vs::load(device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - - GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let mut viewport = Viewport { - offset: [0.0, 0.0], - extent: [0.0, 0.0], - depth_range: 0.0..=1.0, - }; - let framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - - ( - device, - instance, - swapchain, - window, - viewport, - queue, - render_pass, - framebuffers, - sampler, - pipeline, - memory_allocator, - vertex_buffer, - ) - } - fn build_display(ctx: glutin::Context, f: F) where F: FnOnce(Box), @@ -779,12 +802,8 @@ mod linux { fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { diff --git a/examples/image-self-copy-blit/main.rs b/examples/image-self-copy-blit/main.rs index 8a6ff7c4e8..1cce399a4c 100644 --- a/examples/image-self-copy-blit/main.rs +++ b/examples/image-self-copy-blit/main.rs @@ -1,6 +1,6 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, BlitImageInfo, BufferImageCopy, ClearColorImageInfo, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, @@ -11,8 +11,8 @@ use vulkano::{ allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, format::Format, image::{ @@ -44,9 +44,10 @@ use vulkano::{ DeviceSize, Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { @@ -54,9 +55,39 @@ fn main() -> Result<(), impl Error> { // example if you haven't done so yet. let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + texture: Arc, + sampler: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + descriptor_set: Arc, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -81,7 +112,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -113,61 +144,30 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); - let queue = queues.next().unwrap(); - - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; + let queue = queues.next().unwrap(); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertices = [ - Vertex { + MyVertex { position: [-0.5, -0.5], }, - Vertex { + MyVertex { position: [-0.5, 0.5], }, - Vertex { + MyVertex { position: [0.5, -0.5], }, - Vertex { + MyVertex { position: [0.5, 0.5], }, ]; @@ -186,31 +186,6 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let render_pass = vulkano::single_pass_renderpass!(device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - let mut uploads = RecordingCommandBuffer::new( command_buffer_allocator.clone(), queue.queue_family_index(), @@ -334,31 +309,103 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -385,97 +432,95 @@ fn main() -> Result<(), impl Error> { .unwrap() }; + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + let layout = &pipeline.layout().set_layouts()[0]; - let set = DescriptorSet::new( - descriptor_set_allocator, + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), layout.clone(), [ - WriteDescriptorSet::sampler(0, sampler), - WriteDescriptorSet::image_view(1, texture), + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view(1, self.texture.clone()), ], [], ) .unwrap(); - let mut viewport = Viewport { - offset: [0.0, 0.0], - extent: [0.0, 0.0], - depth_range: 0.0..=1.0, - }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - - let mut recreate_swapchain = false; - let mut previous_frame_end = Some( - uploads - .end() - .unwrap() - .execute(queue.clone()) - .unwrap() - .boxed(), - ); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -489,78 +534,87 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, - set.clone(), + rcx.descriptor_set.clone(), ) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/image/main.rs b/examples/image/main.rs index caf682b0ae..98d5a3944b 100644 --- a/examples/image/main.rs +++ b/examples/image/main.rs @@ -1,6 +1,6 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, @@ -9,8 +9,8 @@ use vulkano::{ allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, format::Format, image::{ @@ -42,9 +42,10 @@ use vulkano::{ DeviceSize, Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { @@ -52,9 +53,39 @@ fn main() -> Result<(), impl Error> { // example if you haven't done so yet. let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + texture: Arc, + sampler: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + descriptor_set: Arc, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -79,7 +110,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -113,59 +144,27 @@ fn main() -> Result<(), impl Error> { .unwrap(); let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertices = [ - Vertex { + MyVertex { position: [-0.5, -0.5], }, - Vertex { + MyVertex { position: [-0.5, 0.5], }, - Vertex { + MyVertex { position: [0.5, -0.5], }, - Vertex { + MyVertex { position: [0.5, 0.5], }, ]; @@ -184,32 +183,6 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let render_pass = vulkano::single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - let mut uploads = RecordingCommandBuffer::new( command_buffer_allocator.clone(), queue.queue_family_index(), @@ -281,31 +254,103 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window.inner_size().into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -332,97 +377,95 @@ fn main() -> Result<(), impl Error> { .unwrap() }; + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + let layout = &pipeline.layout().set_layouts()[0]; - let set = DescriptorSet::new( - descriptor_set_allocator, + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), layout.clone(), [ - WriteDescriptorSet::sampler(0, sampler), - WriteDescriptorSet::image_view(1, texture), + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view(1, self.texture.clone()), ], [], ) .unwrap(); - let mut viewport = Viewport { - offset: [0.0, 0.0], - extent: [0.0, 0.0], - depth_range: 0.0..=1.0, - }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - - let mut recreate_swapchain = false; - let mut previous_frame_end = Some( - uploads - .end() - .unwrap() - .execute(queue.clone()) - .unwrap() - .boxed(), - ); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -436,78 +479,87 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, - set.clone(), + rcx.descriptor_set.clone(), ) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/immutable-sampler/main.rs b/examples/immutable-sampler/main.rs index 982a390608..0a4ea13be6 100644 --- a/examples/immutable-sampler/main.rs +++ b/examples/immutable-sampler/main.rs @@ -9,7 +9,7 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, @@ -18,8 +18,8 @@ use vulkano::{ allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags }, format::Format, image::{ @@ -51,16 +51,47 @@ use vulkano::{ DeviceSize, Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + texture: Arc, + sampler: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + descriptor_set: Arc, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -85,7 +116,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -119,59 +150,27 @@ fn main() -> Result<(), impl Error> { .unwrap(); let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertices = [ - Vertex { + MyVertex { position: [-0.5, -0.5], }, - Vertex { + MyVertex { position: [-0.5, 0.5], }, - Vertex { + MyVertex { position: [0.5, -0.5], }, - Vertex { + MyVertex { position: [0.5, 0.5], }, ]; @@ -190,32 +189,6 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let render_pass = vulkano::single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - let mut uploads = RecordingCommandBuffer::new( command_buffer_allocator.clone(), queue.queue_family_index(), @@ -287,16 +260,88 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), @@ -310,12 +355,12 @@ fn main() -> Result<(), impl Error> { .bindings .get_mut(&0) .unwrap() - .immutable_samplers = vec![sampler]; + .immutable_samplers = vec![self.sampler.clone()]; PipelineLayout::new( - device.clone(), + self.device.clone(), layout_create_info - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap() @@ -323,7 +368,7 @@ fn main() -> Result<(), impl Error> { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -354,93 +399,91 @@ fn main() -> Result<(), impl Error> { // Use `image_view` instead of `image_view_sampler`, since the sampler is already in the // layout. - let set = DescriptorSet::new( - descriptor_set_allocator, + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), layout.clone(), - [WriteDescriptorSet::image_view(1, texture)], + [WriteDescriptorSet::image_view(1, self.texture.clone())], [], ) .unwrap(); - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some( - uploads - .end() - .unwrap() - .execute(queue.clone()) - .unwrap() - .boxed(), - ); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -454,78 +497,87 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, - set.clone(), + rcx.descriptor_set.clone(), ) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/indirect/main.rs b/examples/indirect/main.rs index 921fa2e48e..794208fdaf 100644 --- a/examples/indirect/main.rs +++ b/examples/indirect/main.rs @@ -28,8 +28,8 @@ use vulkano::{ allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -58,16 +58,46 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + indirect_buffer_allocator: SubbufferAllocator, + vertex_buffer_allocator: SubbufferAllocator, + compute_pipeline: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -93,7 +123,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -128,69 +158,36 @@ fn main() -> Result<(), impl Error> { let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - // The triangle vertex positions. - layout(location = 0) in vec2 position; - - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", - } - } - - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r#" - #version 450 - - layout(location = 0) out vec4 f_color; + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - "#, - } - } + // Each frame we generate a new set of vertices and each frame we need a new + // `DrawIndirectCommand` struct to set the number of vertices to draw. + let indirect_buffer_allocator = SubbufferAllocator::new( + memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::INDIRECT_BUFFER | BufferUsage::STORAGE_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); + let vertex_buffer_allocator = SubbufferAllocator::new( + memory_allocator, + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::STORAGE_BUFFER | BufferUsage::VERTEX_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); // A simple compute shader that generates vertices. It has two buffers bound: the first is // where we output the vertices, the second is the `IndirectDrawArgs` struct we passed the @@ -235,29 +232,6 @@ fn main() -> Result<(), impl Error> { } } - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - // Each frame we generate a new set of vertices and each frame we need a new - // DrawIndirectCommand struct to set the number of vertices to draw. - let indirect_args_pool = SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::INDIRECT_BUFFER | BufferUsage::STORAGE_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - let vertex_pool = SubbufferAllocator::new( - memory_allocator, - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::STORAGE_BUFFER | BufferUsage::VERTEX_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - let compute_pipeline = { let cs = cs::load(device.clone()) .unwrap() @@ -279,8 +253,59 @@ fn main() -> Result<(), impl Error> { .unwrap() }; + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + indirect_buffer_allocator, + vertex_buffer_allocator, + compute_pipeline, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + let render_pass = single_pass_renderpass!( - device.clone(), + self.device.clone(), attachments: { color: { format: swapchain.image_format(), @@ -296,40 +321,64 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - // `Vertex` is the vertex type that will be output from the compute shader and be input to the - // vertex shader. - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], + let framebuffers = window_size_dependent_setup(&images, &render_pass); + + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + // The triangle vertex positions. + layout(location = 0) in vec2 position; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } + } + + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + + layout(location = 0) out vec4 f_color; + + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } } - let render_pipeline = { - let vs = vs::load(device.clone()) + let pipeline = { + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -350,81 +399,77 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } // Allocate a buffer to hold the arguments for this frame's draw call. The compute @@ -436,7 +481,8 @@ fn main() -> Result<(), impl Error> { first_vertex: 0, first_instance: 0, }]; - let indirect_buffer = indirect_args_pool + let indirect_buffer = self + .indirect_buffer_allocator .allocate_slice(indirect_commands.len() as _) .unwrap(); indirect_buffer @@ -446,16 +492,16 @@ fn main() -> Result<(), impl Error> { // Allocate a buffer to hold this frame's vertices. This needs to be large enough // to hold the worst case number of vertices generated by the compute shader. - let iter = (0..(6 * 16)).map(|_| Vertex { position: [0.0; 2] }); - let vertices = vertex_pool.allocate_slice(iter.len() as _).unwrap(); + let iter = (0..(6 * 16)).map(|_| MyVertex { position: [0.0; 2] }); + let vertices = self.vertex_buffer_allocator.allocate_slice(iter.len() as _).unwrap(); for (o, i) in vertices.write().unwrap().iter_mut().zip(iter) { *o = i; } // Pass the two buffers to the compute shader. - let layout = &compute_pipeline.layout().set_layouts()[0]; + let layout = &self.compute_pipeline.layout().set_layouts()[0]; let cs_descriptor_set = DescriptorSet::new( - descriptor_set_allocator.clone(), + self.descriptor_set_allocator.clone(), layout.clone(), [ WriteDescriptorSet::buffer(0, vertices.clone()), @@ -466,8 +512,8 @@ fn main() -> Result<(), impl Error> { .unwrap(); let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -479,11 +525,11 @@ fn main() -> Result<(), impl Error> { // First in the command buffer we dispatch the compute shader to generate the // vertices and fill out the draw call arguments. builder - .bind_pipeline_compute(compute_pipeline.clone()) + .bind_pipeline_compute(self.compute_pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, - compute_pipeline.layout().clone(), + self.compute_pipeline.layout().clone(), 0, cs_descriptor_set, ) @@ -498,15 +544,15 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(render_pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_vertex_buffers(0, vertices) .unwrap(); @@ -521,51 +567,62 @@ fn main() -> Result<(), impl Error> { let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +// `MyVertex` is the vertex type that will be output from the compute shader and be input to the +// vertex shader. +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/instancing/main.rs b/examples/instancing/main.rs index 21bfcf146d..a7f18968ca 100644 --- a/examples/instancing/main.rs +++ b/examples/instancing/main.rs @@ -5,14 +5,14 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -39,34 +39,44 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; -/// The vertex type that we will be used to describe the triangle's geometry. -#[derive(BufferContents, Vertex)] -#[repr(C)] -struct TriangleVertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], +fn main() -> Result<(), impl Error> { + let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + + event_loop.run_app(&mut app) } -/// The vertex type that describes the unique data per instance. -#[derive(BufferContents, Vertex)] -#[repr(C)] -struct InstanceData { - #[format(R32G32_SFLOAT)] - position_offset: [f32; 2], - #[format(R32_SFLOAT)] - scale: f32, +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[TriangleVertex]>, + instance_buffer: Subbuffer<[InstanceData]>, + rcx: Option, } -fn main() -> Result<(), impl Error> { - let event_loop = EventLoop::new().unwrap(); +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, +} +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -91,7 +101,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -126,40 +136,11 @@ fn main() -> Result<(), impl Error> { let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); // We now create a buffer that will store the shape of our triangle. This triangle is identical // to the one in the `triangle.rs` example. @@ -227,6 +208,74 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + instance_buffer, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + mod vs { vulkano_shaders::shader! { ty: "vertex", @@ -263,29 +312,12 @@ fn main() -> Result<(), impl Error> { } } - let render_pass = single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); @@ -297,16 +329,16 @@ fn main() -> Result<(), impl Error> { PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -329,82 +361,82 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -418,25 +450,25 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() // We pass both our lists of vertices here. - .bind_vertex_buffers(0, (vertex_buffer.clone(), instance_buffer.clone())) + .bind_vertex_buffers(0, (self.vertex_buffer.clone(), self.instance_buffer.clone())) .unwrap(); unsafe { builder .draw( - vertex_buffer.len() as u32, - instance_buffer.len() as u32, + self.vertex_buffer.len() as u32, + self.instance_buffer.len() as u32, 0, 0, ) @@ -446,51 +478,71 @@ fn main() -> Result<(), impl Error> { builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +/// The vertex type that we will be used to describe the triangle's geometry. +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct TriangleVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], +} + +/// The vertex type that describes the unique data per instance. +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct InstanceData { + #[format(R32G32_SFLOAT)] + position_offset: [f32; 2], + #[format(R32_SFLOAT)] + scale: f32, } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/interactive-fractal/app.rs b/examples/interactive-fractal/app.rs deleted file mode 100644 index 2af8102832..0000000000 --- a/examples/interactive-fractal/app.rs +++ /dev/null @@ -1,370 +0,0 @@ -use crate::{ - fractal_compute_pipeline::FractalComputePipeline, place_over_frame::RenderPassPlaceOverFrame, -}; -use glam::f32::Vec2; -use std::{sync::Arc, time::Instant}; -use vulkano::{ - command_buffer::allocator::{ - StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo, - }, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::Queue, - image::view::ImageView, - memory::allocator::StandardMemoryAllocator, - sync::GpuFuture, -}; -use vulkano_util::{renderer::VulkanoWindowRenderer, window::WindowDescriptor}; -use winit::{ - dpi::PhysicalPosition, - event::{ElementState, Event, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent}, - keyboard::{Key, NamedKey}, - window::Fullscreen, -}; - -const MAX_ITERS_INIT: u32 = 200; -const MOVE_SPEED: f32 = 0.5; - -/// App for exploring Julia and Mandelbrot fractals. -pub struct FractalApp { - /// Pipeline that computes Mandelbrot & Julia fractals and writes them to an image. - fractal_pipeline: FractalComputePipeline, - /// Our render pipeline (pass). - pub place_over_frame: RenderPassPlaceOverFrame, - /// Toggle that flips between Julia and Mandelbrot. - pub is_julia: bool, - /// Toggle that stops the movement on Julia. - is_c_paused: bool, - /// C is a constant input to Julia escape time algorithm (mouse position). - c: Vec2, - /// Our zoom level. - scale: Vec2, - /// Our translation on the complex plane. - translation: Vec2, - /// How long the escape time algorithm should run (higher = less performance, more accurate - /// image). - pub max_iters: u32, - /// Time tracking, useful for frame independent movement. - time: Instant, - dt: f32, - dt_sum: f32, - frame_count: f32, - avg_fps: f32, - /// Input state to handle mouse positions, continuous movement etc. - input_state: InputState, -} - -impl FractalApp { - pub fn new( - gfx_queue: Arc, - image_format: vulkano::format::Format, - swapchain_image_views: &[Arc], - ) -> FractalApp { - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default( - gfx_queue.device().clone(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - gfx_queue.device().clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, - ..Default::default() - }, - )); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - gfx_queue.device().clone(), - Default::default(), - )); - - FractalApp { - fractal_pipeline: FractalComputePipeline::new( - gfx_queue.clone(), - memory_allocator.clone(), - command_buffer_allocator.clone(), - descriptor_set_allocator.clone(), - ), - place_over_frame: RenderPassPlaceOverFrame::new( - gfx_queue, - command_buffer_allocator, - descriptor_set_allocator, - image_format, - swapchain_image_views, - ), - is_julia: false, - is_c_paused: false, - c: Vec2::new(0.0, 0.0), - scale: Vec2::new(4.0, 4.0), - translation: Vec2::new(0.0, 0.0), - max_iters: MAX_ITERS_INIT, - time: Instant::now(), - dt: 0.0, - dt_sum: 0.0, - frame_count: 0.0, - avg_fps: 0.0, - input_state: InputState::new(), - } - } - - pub fn print_guide(&self) { - println!( - "\ -Usage: - WASD: Pan view - Scroll: Zoom in/out - Space: Toggle between Mandelbrot and Julia - Enter: Randomize color palette - Equals/Minus: Increase/Decrease max iterations - F: Toggle full-screen - Right mouse: Stop movement in Julia (mouse position determines c) - Esc: Quit\ - ", - ); - } - - /// Runs our compute pipeline and return a future of when the compute is finished. - pub fn compute(&self, image_target: Arc) -> Box { - self.fractal_pipeline.compute( - image_target, - self.c, - self.scale, - self.translation, - self.max_iters, - self.is_julia, - ) - } - - /// Returns whether the app should quit. (Happens on when pressing ESC.) - pub fn is_running(&self) -> bool { - !self.input_state.should_quit - } - - /// Returns the average FPS. - pub fn avg_fps(&self) -> f32 { - self.avg_fps - } - - /// Returns the delta time in milliseconds. - pub fn dt(&self) -> f32 { - self.dt * 1000.0 - } - - /// Updates times and dt at the end of each frame. - pub fn update_time(&mut self) { - // Each second, update average fps & reset frame count & dt sum. - if self.dt_sum > 1.0 { - self.avg_fps = self.frame_count / self.dt_sum; - self.frame_count = 0.0; - self.dt_sum = 0.0; - } - self.dt = self.time.elapsed().as_secs_f32(); - self.dt_sum += self.dt; - self.frame_count += 1.0; - self.time = Instant::now(); - } - - /// Updates app state based on input state. - pub fn update_state_after_inputs(&mut self, renderer: &mut VulkanoWindowRenderer) { - // Zoom in or out. - if self.input_state.scroll_delta > 0. { - self.scale /= 1.05; - } else if self.input_state.scroll_delta < 0. { - self.scale *= 1.05; - } - - // Move speed scaled by zoom level. - let move_speed = MOVE_SPEED * self.dt * self.scale.x; - - // Panning. - if self.input_state.pan_up { - self.translation += Vec2::new(0.0, move_speed); - } - if self.input_state.pan_down { - self.translation += Vec2::new(0.0, -move_speed); - } - if self.input_state.pan_right { - self.translation += Vec2::new(move_speed, 0.0); - } - if self.input_state.pan_left { - self.translation += Vec2::new(-move_speed, 0.0); - } - - // Toggle between Julia and Mandelbrot. - if self.input_state.toggle_julia { - self.is_julia = !self.is_julia; - } - - // Toggle c. - if self.input_state.toggle_c { - self.is_c_paused = !self.is_c_paused; - } - - // Update c. - if !self.is_c_paused { - // Scale normalized mouse pos between -1.0 and 1.0. - let mouse_pos = self.input_state.normalized_mouse_pos() * 2.0 - Vec2::new(1.0, 1.0); - // Scale by our zoom (scale) level so when zooming in the movement on Julia is not so - // drastic. - self.c = mouse_pos * self.scale.x; - } - - // Update how many iterations we have. - if self.input_state.increase_iterations { - self.max_iters += 1; - } - if self.input_state.decrease_iterations { - if self.max_iters as i32 - 1 <= 0 { - self.max_iters = 0; - } else { - self.max_iters -= 1; - } - } - - // Randomize our palette. - if self.input_state.randomize_palette { - self.fractal_pipeline.randomize_palette(); - } - - // Toggle full-screen. - if self.input_state.toggle_full_screen { - let is_full_screen = renderer.window().fullscreen().is_some(); - renderer.window().set_fullscreen(if !is_full_screen { - Some(Fullscreen::Borderless(renderer.window().current_monitor())) - } else { - None - }); - } - } - - /// Update input state. - pub fn handle_input(&mut self, window_size: [f32; 2], event: &Event<()>) { - self.input_state.handle_input(window_size, event); - } - - /// Reset input state at the end of the frame. - pub fn reset_input_state(&mut self) { - self.input_state.reset() - } -} - -fn state_is_pressed(state: ElementState) -> bool { - match state { - ElementState::Pressed => true, - ElementState::Released => false, - } -} - -/// Just a very simple input state (mappings). Winit only has `Pressed` and `Released` events, thus -/// continuous movement needs toggles. Panning is one of those things where continuous movement -/// feels better. -struct InputState { - pub window_size: [f32; 2], - pub pan_up: bool, - pub pan_down: bool, - pub pan_right: bool, - pub pan_left: bool, - pub increase_iterations: bool, - pub decrease_iterations: bool, - pub randomize_palette: bool, - pub toggle_full_screen: bool, - pub toggle_julia: bool, - pub toggle_c: bool, - pub should_quit: bool, - pub scroll_delta: f32, - pub mouse_pos: Vec2, -} - -impl InputState { - fn new() -> InputState { - InputState { - window_size: [ - WindowDescriptor::default().width, - WindowDescriptor::default().height, - ], - pan_up: false, - pan_down: false, - pan_right: false, - pan_left: false, - increase_iterations: false, - decrease_iterations: false, - randomize_palette: false, - toggle_full_screen: false, - toggle_julia: false, - toggle_c: false, - should_quit: false, - scroll_delta: 0.0, - mouse_pos: Vec2::new(0.0, 0.0), - } - } - - fn normalized_mouse_pos(&self) -> Vec2 { - Vec2::new( - (self.mouse_pos.x / self.window_size[0]).clamp(0.0, 1.0), - (self.mouse_pos.y / self.window_size[1]).clamp(0.0, 1.0), - ) - } - - /// Resets values that should be reset. All incremental mappings and toggles should be reset. - fn reset(&mut self) { - *self = InputState { - scroll_delta: 0.0, - toggle_full_screen: false, - toggle_julia: false, - toggle_c: false, - randomize_palette: false, - increase_iterations: false, - decrease_iterations: false, - ..*self - } - } - - fn handle_input(&mut self, window_size: [f32; 2], event: &Event<()>) { - self.window_size = window_size; - if let Event::WindowEvent { event, .. } = event { - match event { - WindowEvent::KeyboardInput { event, .. } => self.on_keyboard_event(event), - WindowEvent::MouseInput { state, button, .. } => { - self.on_mouse_click_event(*state, *button) - } - WindowEvent::CursorMoved { position, .. } => self.on_cursor_moved_event(position), - WindowEvent::MouseWheel { delta, .. } => self.on_mouse_wheel_event(delta), - _ => {} - } - } - } - - /// Matches keyboard events to our defined inputs. - fn on_keyboard_event(&mut self, event: &KeyEvent) { - match event.logical_key.as_ref() { - Key::Named(NamedKey::Escape) => self.should_quit = state_is_pressed(event.state), - Key::Character("w") => self.pan_up = state_is_pressed(event.state), - Key::Character("a") => self.pan_left = state_is_pressed(event.state), - Key::Character("s") => self.pan_down = state_is_pressed(event.state), - Key::Character("d") => self.pan_right = state_is_pressed(event.state), - Key::Character("f") => self.toggle_full_screen = state_is_pressed(event.state), - Key::Named(NamedKey::Enter) => self.randomize_palette = state_is_pressed(event.state), - Key::Character("=") => self.increase_iterations = state_is_pressed(event.state), - Key::Character("-") => self.decrease_iterations = state_is_pressed(event.state), - Key::Named(NamedKey::Space) => self.toggle_julia = state_is_pressed(event.state), - _ => (), - } - } - - /// Updates mouse scroll delta. - fn on_mouse_wheel_event(&mut self, delta: &MouseScrollDelta) { - let change = match delta { - MouseScrollDelta::LineDelta(_x, y) => *y, - MouseScrollDelta::PixelDelta(pos) => pos.y as f32, - }; - self.scroll_delta += change; - } - - /// Update mouse position - fn on_cursor_moved_event(&mut self, pos: &PhysicalPosition) { - self.mouse_pos = Vec2::new(pos.x as f32, pos.y as f32); - } - - /// Update toggle julia state (if right mouse is clicked) - fn on_mouse_click_event(&mut self, state: ElementState, mouse_btn: MouseButton) { - if mouse_btn == MouseButton::Right { - self.toggle_c = state_is_pressed(state) - } - } -} diff --git a/examples/interactive-fractal/fractal_compute_pipeline.rs b/examples/interactive-fractal/fractal_compute_pipeline.rs index 6f6451b96c..efe4d5adec 100644 --- a/examples/interactive-fractal/fractal_compute_pipeline.rs +++ b/examples/interactive-fractal/fractal_compute_pipeline.rs @@ -136,11 +136,10 @@ impl FractalComputePipeline { ) -> Box { // Resize image if needed. let image_extent = image_view.image().extent(); - let pipeline_layout = self.pipeline.layout(); - let desc_layout = &pipeline_layout.set_layouts()[0]; - let set = DescriptorSet::new( + let layout = &self.pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new( self.descriptor_set_allocator.clone(), - desc_layout.clone(), + layout.clone(), [ WriteDescriptorSet::image_view(0, image_view), WriteDescriptorSet::buffer(1, self.palette.clone()), @@ -172,9 +171,9 @@ impl FractalComputePipeline { builder .bind_pipeline_compute(self.pipeline.clone()) .unwrap() - .bind_descriptor_sets(PipelineBindPoint::Compute, pipeline_layout.clone(), 0, set) + .bind_descriptor_sets(PipelineBindPoint::Compute, self.pipeline.layout().clone(), 0, descriptor_set) .unwrap() - .push_constants(pipeline_layout.clone(), 0, push_constants) + .push_constants(self.pipeline.layout().clone(), 0, push_constants) .unwrap(); unsafe { diff --git a/examples/interactive-fractal/input.rs b/examples/interactive-fractal/input.rs new file mode 100644 index 0000000000..5413f4a1b8 --- /dev/null +++ b/examples/interactive-fractal/input.rs @@ -0,0 +1,124 @@ +use glam::f32::Vec2; +use vulkano_util::window::WindowDescriptor; +use winit::{ + dpi::{PhysicalPosition, PhysicalSize}, + event::{ElementState, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent}, + keyboard::{Key, NamedKey}, +}; + +/// Just a very simple input state (mappings). Winit only has `Pressed` and `Released` events, thus +/// continuous movement needs toggles. Panning is one of those things where continuous movement +/// feels better. +pub struct InputState { + pub window_size: [f32; 2], + pub pan_up: bool, + pub pan_down: bool, + pub pan_right: bool, + pub pan_left: bool, + pub increase_iterations: bool, + pub decrease_iterations: bool, + pub randomize_palette: bool, + pub toggle_full_screen: bool, + pub toggle_julia: bool, + pub toggle_c: bool, + pub should_quit: bool, + pub scroll_delta: f32, + pub mouse_pos: Vec2, +} + +impl InputState { + pub fn new() -> InputState { + InputState { + window_size: [ + WindowDescriptor::default().width, + WindowDescriptor::default().height, + ], + pan_up: false, + pan_down: false, + pan_right: false, + pan_left: false, + increase_iterations: false, + decrease_iterations: false, + randomize_palette: false, + toggle_full_screen: false, + toggle_julia: false, + toggle_c: false, + should_quit: false, + scroll_delta: 0.0, + mouse_pos: Vec2::new(0.0, 0.0), + } + } + + pub fn normalized_mouse_pos(&self) -> Vec2 { + Vec2::new( + (self.mouse_pos.x / self.window_size[0]).clamp(0.0, 1.0), + (self.mouse_pos.y / self.window_size[1]).clamp(0.0, 1.0), + ) + } + + /// Resets values that should be reset. All incremental mappings and toggles should be reset. + pub fn reset(&mut self) { + *self = InputState { + scroll_delta: 0.0, + toggle_full_screen: false, + toggle_julia: false, + toggle_c: false, + randomize_palette: false, + increase_iterations: false, + decrease_iterations: false, + ..*self + } + } + + pub fn handle_input(&mut self, window_size: PhysicalSize, event: &WindowEvent) { + self.window_size = window_size.into(); + + match event { + WindowEvent::KeyboardInput { event, .. } => self.on_keyboard_event(event), + WindowEvent::MouseInput { state, button, .. } => { + self.on_mouse_click_event(*state, *button) + } + WindowEvent::CursorMoved { position, .. } => self.on_cursor_moved_event(position), + WindowEvent::MouseWheel { delta, .. } => self.on_mouse_wheel_event(delta), + _ => {} + } + } + + /// Matches keyboard events to our defined inputs. + fn on_keyboard_event(&mut self, event: &KeyEvent) { + match event.logical_key.as_ref() { + Key::Named(NamedKey::Escape) => self.should_quit = event.state.is_pressed(), + Key::Character("w") => self.pan_up = event.state.is_pressed(), + Key::Character("a") => self.pan_left = event.state.is_pressed(), + Key::Character("s") => self.pan_down = event.state.is_pressed(), + Key::Character("d") => self.pan_right = event.state.is_pressed(), + Key::Character("f") => self.toggle_full_screen = event.state.is_pressed(), + Key::Named(NamedKey::Enter) => self.randomize_palette = event.state.is_pressed(), + Key::Character("=") => self.increase_iterations = event.state.is_pressed(), + Key::Character("-") => self.decrease_iterations = event.state.is_pressed(), + Key::Named(NamedKey::Space) => self.toggle_julia = event.state.is_pressed(), + _ => {} + } + } + + /// Updates mouse scroll delta. + fn on_mouse_wheel_event(&mut self, delta: &MouseScrollDelta) { + let change = match delta { + MouseScrollDelta::LineDelta(_x, y) => *y, + MouseScrollDelta::PixelDelta(pos) => pos.y as f32, + }; + self.scroll_delta += change; + } + + /// Update mouse position + fn on_cursor_moved_event(&mut self, pos: &PhysicalPosition) { + self.mouse_pos = Vec2::new(pos.x as f32, pos.y as f32); + } + + /// Update toggle julia state (if right mouse is clicked) + fn on_mouse_click_event(&mut self, state: ElementState, mouse_btn: MouseButton) { + if mouse_btn == MouseButton::Right { + self.toggle_c = state.is_pressed(); + } + } +} diff --git a/examples/interactive-fractal/main.rs b/examples/interactive-fractal/main.rs index cafb746f76..60cf7c788c 100644 --- a/examples/interactive-fractal/main.rs +++ b/examples/interactive-fractal/main.rs @@ -10,32 +10,132 @@ // - A simple `FractalApp` to handle runtime state. // - A simple `InputState` to interact with the application. -use crate::app::FractalApp; -use std::{error::Error, time::Duration}; -use vulkano::{image::ImageUsage, swapchain::PresentMode, sync::GpuFuture}; +use std::{error::Error, sync::Arc, time::{Duration, Instant}}; +use input::InputState; +use fractal_compute_pipeline::FractalComputePipeline; +use glam::Vec2; +use place_over_frame::RenderPassPlaceOverFrame; +use vulkano::{ + command_buffer::allocator::{ + StandardCommandBufferAllocator, + StandardCommandBufferAllocatorCreateInfo, + }, + descriptor_set::allocator::StandardDescriptorSetAllocator, + image::ImageUsage, + swapchain::PresentMode, + sync::GpuFuture, +}; use vulkano_util::{ context::{VulkanoConfig, VulkanoContext}, renderer::{VulkanoWindowRenderer, DEFAULT_IMAGE_FORMAT}, window::{VulkanoWindows, WindowDescriptor}, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Fullscreen, WindowId}, }; -mod app; mod fractal_compute_pipeline; +mod input; mod pixels_draw_pipeline; mod place_over_frame; +const MAX_ITERS_INIT: u32 = 200; +const MOVE_SPEED: f32 = 0.5; + fn main() -> Result<(), impl Error> { // Create the event loop. let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + + println!( + "\ +Usage: + WASD: Pan view + Scroll: Zoom in/out + Space: Toggle between Mandelbrot and Julia + Enter: Randomize color palette + Equals/Minus: Increase/Decrease max iterations + F: Toggle full-screen + Right mouse: Stop movement in Julia (mouse position determines c) + Esc: Quit\ + ", + ); + + event_loop.run_app(&mut app) +} + +struct App { + context: VulkanoContext, + windows: VulkanoWindows, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + rcx: Option, +} + +struct RenderContext { + /// Pipeline that computes Mandelbrot & Julia fractals and writes them to an image. + fractal_pipeline: FractalComputePipeline, + /// Our render pipeline (pass). + place_over_frame: RenderPassPlaceOverFrame, + /// Toggle that flips between Julia and Mandelbrot. + is_julia: bool, + /// Toggle that stops the movement on Julia. + is_c_paused: bool, + /// C is a constant input to Julia escape time algorithm (mouse position). + c: Vec2, + /// Our zoom level. + scale: Vec2, + /// Our translation on the complex plane. + translation: Vec2, + /// How long the escape time algorithm should run (higher = less performance, more accurate + /// image). + max_iters: u32, + /// Time tracking, useful for frame independent movement. + time: Instant, + dt: f32, + dt_sum: f32, + frame_count: f32, + avg_fps: f32, + /// Input state to handle mouse positions, continuous movement etc. + input_state: InputState, + render_target_id: usize, +} + +impl App { + fn new(_event_loop: &EventLoop<()>) -> Self { let context = VulkanoContext::new(VulkanoConfig::default()); - let mut windows = VulkanoWindows::default(); - let _id = windows.create_window( - &event_loop, - &context, + let windows = VulkanoWindows::default(); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + context.device().clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + context.device().clone(), + StandardCommandBufferAllocatorCreateInfo { + secondary_buffer_count: 32, + ..Default::default() + }, + )); + + App { + context, + windows, + descriptor_set_allocator, + command_buffer_allocator, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let _id = self.windows.create_window( + event_loop, + &self.context, &WindowDescriptor { title: "Fractal".to_string(), present_mode: PresentMode::Fifo, @@ -46,132 +146,241 @@ fn main() -> Result<(), impl Error> { // Add our render target image onto which we'll be rendering our fractals. let render_target_id = 0; - let primary_window_renderer = windows.get_primary_renderer_mut().unwrap(); + let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); // Make sure the image usage is correct (based on your pipeline). - primary_window_renderer.add_additional_image_view( + window_renderer.add_additional_image_view( render_target_id, DEFAULT_IMAGE_FORMAT, ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST, ); - // Create app to hold the logic of our fractal explorer. - let gfx_queue = context.graphics_queue(); + let gfx_queue = self.context.graphics_queue(); - // We intend to eventually render on our swapchain, thus we use that format when creating the - // app here. - let mut app = FractalApp::new( - gfx_queue.clone(), - primary_window_renderer.swapchain_format(), - primary_window_renderer.swapchain_image_views(), - ); - app.print_guide(); - - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + self.rcx = Some(RenderContext { + render_target_id, + fractal_pipeline: FractalComputePipeline::new( + gfx_queue.clone(), + self.context.memory_allocator().clone(), + self.command_buffer_allocator.clone(), + self.descriptor_set_allocator.clone(), + ), + place_over_frame: RenderPassPlaceOverFrame::new( + gfx_queue.clone(), + self.command_buffer_allocator.clone(), + self.descriptor_set_allocator.clone(), + window_renderer.swapchain_format(), + window_renderer.swapchain_image_views(), + ), + is_julia: false, + is_c_paused: false, + c: Vec2::new(0.0, 0.0), + scale: Vec2::new(4.0, 4.0), + translation: Vec2::new(0.0, 0.0), + max_iters: MAX_ITERS_INIT, + time: Instant::now(), + dt: 0.0, + dt_sum: 0.0, + frame_count: 0.0, + avg_fps: 0.0, + input_state: InputState::new(), + }); + } - let renderer = windows.get_primary_renderer_mut().unwrap(); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let renderer = self.windows.get_primary_renderer_mut().unwrap(); + let rcx = self.rcx.as_mut().unwrap(); + let window_size = renderer.window().inner_size(); - if process_event(renderer, &event, &mut app, render_target_id) { - elwt.exit(); - return; + match event { + WindowEvent::CloseRequested => { + event_loop.exit(); } - - // Pass event for the app to handle our inputs. - app.handle_input(renderer.window_size(), &event); - }) -} - -/// Processes a single event for an event loop. -/// Returns true only if the window is to be closed. -pub fn process_event( - renderer: &mut VulkanoWindowRenderer, - event: &Event<()>, - app: &mut FractalApp, - render_target_id: usize, -) -> bool { - match &event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - return true; - } - Event::WindowEvent { - event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. }, - .. - } => renderer.resize(), - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => 'redraw: { + WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. } => { + renderer.resize(); + } + WindowEvent::RedrawRequested => { // Tasks for redrawing: // 1. Update state based on events // 2. Compute & Render // 3. Reset input state // 4. Update time & title - // The rendering part goes here: - match renderer.window_size() { - [w, h] => { - // Skip this frame when minimized. - if w == 0.0 || h == 0.0 { - break 'redraw; - } - } + // Skip this frame when minimized. + if window_size.width == 0 || window_size.height == 0 { + return; } - app.update_state_after_inputs(renderer); - compute_then_render(renderer, app, render_target_id); - app.reset_input_state(); - app.update_time(); + + rcx.update_state_after_inputs(renderer); + + // Start the frame. + let before_pipeline_future = + match renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| { + rcx.place_over_frame + .recreate_framebuffers(swapchain_image_views) + }) { + Err(e) => { + println!("{e}"); + return; + } + Ok(future) => future, + }; + + // Retrieve the target image. + let image = renderer.get_additional_image_view(rcx.render_target_id); + + // Compute our fractal (writes to target image). Join future with `before_pipeline_future`. + let after_compute = rcx + .fractal_pipeline + .compute( + image.clone(), + rcx.c, + rcx.scale, + rcx.translation, + rcx.max_iters, + rcx.is_julia, + ) + .join(before_pipeline_future); + + // Render the image over the swapchain image, inputting the previous future. + let after_renderpass_future = rcx.place_over_frame.render( + after_compute, + image, + renderer.swapchain_image_view(), + renderer.image_index(), + ); + + // Finish the frame (which presents the view), inputting the last future. Wait for the future + // so resources are not in use when we render. + renderer.present(after_renderpass_future, true); + + rcx.input_state.reset(); + rcx.update_time(); renderer.window().set_title(&format!( "{} fps: {:.2} dt: {:.2}, Max Iterations: {}", - if app.is_julia { "Julia" } else { "Mandelbrot" }, - app.avg_fps(), - app.dt(), - app.max_iters + if rcx.is_julia { "Julia" } else { "Mandelbrot" }, + rcx.avg_fps(), + rcx.dt(), + rcx.max_iters )); } - Event::AboutToWait => renderer.window().request_redraw(), - _ => (), + _ => { + // Pass event for the app to handle our inputs. + rcx.input_state.handle_input(window_size, &event); + } + } + + if rcx.input_state.should_quit { + event_loop.exit(); + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); + window_renderer.window().request_redraw(); } - !app.is_running() } -/// Orchestrates rendering. -fn compute_then_render( - renderer: &mut VulkanoWindowRenderer, - app: &mut FractalApp, - target_image_id: usize, -) { - // Start the frame. - let before_pipeline_future = - match renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| { - app.place_over_frame - .recreate_framebuffers(swapchain_image_views) - }) { - Err(e) => { - println!("{e}"); - return; +impl RenderContext { + /// Updates app state based on input state. + fn update_state_after_inputs(&mut self, renderer: &mut VulkanoWindowRenderer) { + // Zoom in or out. + if self.input_state.scroll_delta > 0. { + self.scale /= 1.05; + } else if self.input_state.scroll_delta < 0. { + self.scale *= 1.05; + } + + // Move speed scaled by zoom level. + let move_speed = MOVE_SPEED * self.dt * self.scale.x; + + // Panning. + if self.input_state.pan_up { + self.translation += Vec2::new(0.0, move_speed); + } + if self.input_state.pan_down { + self.translation += Vec2::new(0.0, -move_speed); + } + if self.input_state.pan_right { + self.translation += Vec2::new(move_speed, 0.0); + } + if self.input_state.pan_left { + self.translation += Vec2::new(-move_speed, 0.0); + } + + // Toggle between Julia and Mandelbrot. + if self.input_state.toggle_julia { + self.is_julia = !self.is_julia; + } + + // Toggle c. + if self.input_state.toggle_c { + self.is_c_paused = !self.is_c_paused; + } + + // Update c. + if !self.is_c_paused { + // Scale normalized mouse pos between -1.0 and 1.0. + let mouse_pos = self.input_state.normalized_mouse_pos() * 2.0 - Vec2::new(1.0, 1.0); + // Scale by our zoom (scale) level so when zooming in the movement on Julia is not so + // drastic. + self.c = mouse_pos * self.scale.x; + } + + // Update how many iterations we have. + if self.input_state.increase_iterations { + self.max_iters += 1; + } + if self.input_state.decrease_iterations { + if self.max_iters as i32 - 1 <= 0 { + self.max_iters = 0; + } else { + self.max_iters -= 1; } - Ok(future) => future, - }; + } - // Retrieve the target image. - let image = renderer.get_additional_image_view(target_image_id); + // Randomize our palette. + if self.input_state.randomize_palette { + self.fractal_pipeline.randomize_palette(); + } - // Compute our fractal (writes to target image). Join future with `before_pipeline_future`. - let after_compute = app.compute(image.clone()).join(before_pipeline_future); + // Toggle full-screen. + if self.input_state.toggle_full_screen { + let is_full_screen = renderer.window().fullscreen().is_some(); + renderer.window().set_fullscreen(if !is_full_screen { + Some(Fullscreen::Borderless(renderer.window().current_monitor())) + } else { + None + }); + } + } - // Render the image over the swapchain image, inputting the previous future. - let after_renderpass_future = app.place_over_frame.render( - after_compute, - image, - renderer.swapchain_image_view(), - renderer.image_index(), - ); + /// Returns the average FPS. + fn avg_fps(&self) -> f32 { + self.avg_fps + } - // Finish the frame (which presents the view), inputting the last future. Wait for the future - // so resources are not in use when we render. - renderer.present(after_renderpass_future, true); + /// Returns the delta time in milliseconds. + fn dt(&self) -> f32 { + self.dt * 1000.0 + } + + /// Updates times and dt at the end of each frame. + fn update_time(&mut self) { + // Each second, update average fps & reset frame count & dt sum. + if self.dt_sum > 1.0 { + self.avg_fps = self.frame_count / self.dt_sum; + self.frame_count = 0.0; + self.dt_sum = 0.0; + } + self.dt = self.time.elapsed().as_secs_f32(); + self.dt_sum += self.dt; + self.frame_count += 1.0; + self.time = Instant::now(); + } } diff --git a/examples/mesh-shader/main.rs b/examples/mesh-shader/main.rs index f0d86afdbe..9644470bc6 100644 --- a/examples/mesh-shader/main.rs +++ b/examples/mesh-shader/main.rs @@ -15,7 +15,7 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, @@ -25,7 +25,7 @@ use vulkano::{ }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, - QueueCreateInfo, QueueFlags, + Queue, QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -52,41 +52,48 @@ use vulkano::{ DeviceSize, Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; -/// The vertex type that we will be used to describe the triangle's geometry. -#[derive(BufferContents)] -#[repr(C)] -struct TriangleVertex { - position: [f32; 2], -} - -/// The vertex type that describes the unique data per instance. -type InstanceData = mesh::Instance; +fn main() -> Result<(), impl Error> { + let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); -mod mesh { - vulkano_shaders::shader! { - ty: "mesh", - path: "mesh.glsl", - vulkan_version: "1.2", - } + event_loop.run_app(&mut app) } -mod fs { - vulkano_shaders::shader! { - ty: "fragment", - path: "frag.glsl", - } +struct App { + instance: Arc, + device: Arc, + queue: Arc, + vertex_buffer: Subbuffer<[TriangleVertex]>, + instance_buffer: Subbuffer, + rows: u32, + cols: u32, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + rcx: Option, } -fn main() -> Result<(), impl Error> { - let event_loop = EventLoop::new().unwrap(); +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + descriptor_set: Arc, + recreate_swapchain: bool, + previous_frame_end: Option>, +} +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -112,7 +119,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -151,44 +158,15 @@ fn main() -> Result<(), impl Error> { let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( device.clone(), Default::default(), )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); // We now create a buffer that will store the shape of our triangle. This triangle is identical // to the one in the `triangle.rs` example. @@ -262,8 +240,60 @@ fn main() -> Result<(), impl Error> { } } + App { + instance, + device, + queue, + vertex_buffer, + instance_buffer, + rows, + cols, + descriptor_set_allocator, + command_buffer_allocator, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + let render_pass = single_pass_renderpass!( - device.clone(), + self.device.clone(), attachments: { color: { format: swapchain.image_format(), @@ -279,12 +309,14 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let mesh = mesh::load(device.clone()) + let mesh = mesh::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); @@ -293,16 +325,16 @@ fn main() -> Result<(), impl Error> { PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -321,93 +353,94 @@ fn main() -> Result<(), impl Error> { .unwrap() }; + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + let descriptor_set = DescriptorSet::new( - descriptor_set_allocator, + self.descriptor_set_allocator.clone(), pipeline.layout().set_layouts()[0].clone(), [ - WriteDescriptorSet::buffer(0, vertex_buffer.clone()), - WriteDescriptorSet::buffer(1, instance_buffer.clone()), + WriteDescriptorSet::buffer(0, self.vertex_buffer.clone()), + WriteDescriptorSet::buffer(1, self.instance_buffer.clone()), ], [], ) .unwrap(); - let mut viewport = Viewport { - offset: [0.0, 0.0], - extent: [0.0, 0.0], - depth_range: 0.0..=1.0, - }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); - - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -421,77 +454,89 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() // Instead of binding vertex attributes, bind buffers as descriptor sets .bind_descriptor_sets( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, - descriptor_set.clone(), + rcx.descriptor_set.clone(), ) .unwrap(); unsafe { - builder.draw_mesh_tasks([cols, rows, 1]).unwrap(); + builder.draw_mesh_tasks([self.cols, self.rows, 1]).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +/// The vertex type that we will be used to describe the triangle's geometry. +#[derive(BufferContents)] +#[repr(C)] +struct TriangleVertex { + position: [f32; 2], } +/// The vertex type that describes the unique data per instance. +type InstanceData = mesh::Instance; + /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { @@ -503,3 +548,18 @@ fn window_size_dependent_setup( }) .collect::>() } + +mod mesh { + vulkano_shaders::shader! { + ty: "mesh", + path: "mesh.glsl", + vulkan_version: "1.2", + } +} + +mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: "frag.glsl", + } +} diff --git a/examples/msaa-renderpass/main.rs b/examples/msaa-renderpass/main.rs index cb46608451..5fbf581d18 100644 --- a/examples/msaa-renderpass/main.rs +++ b/examples/msaa-renderpass/main.rs @@ -139,6 +139,7 @@ fn main() { }, ) .unwrap(); + let queue = queues.next().unwrap(); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); diff --git a/examples/multi-window-game-of-life/app.rs b/examples/multi-window-game-of-life/app.rs deleted file mode 100644 index dea2121300..0000000000 --- a/examples/multi-window-game-of-life/app.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::{ - game_of_life::GameOfLifeComputePipeline, render_pass::RenderPassPlaceOverFrame, SCALING, - WINDOW2_HEIGHT, WINDOW2_WIDTH, WINDOW_HEIGHT, WINDOW_WIDTH, -}; -use std::{collections::HashMap, sync::Arc}; -use vulkano::{ - command_buffer::allocator::{ - StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo, - }, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::Queue, -}; -use vulkano_util::{ - context::{VulkanoConfig, VulkanoContext}, - renderer::VulkanoWindowRenderer, - window::{VulkanoWindows, WindowDescriptor}, -}; -use winit::{event_loop::EventLoop, window::WindowId}; - -pub struct RenderPipeline { - pub compute: GameOfLifeComputePipeline, - pub place_over_frame: RenderPassPlaceOverFrame, -} - -impl RenderPipeline { - pub fn new( - app: &App, - compute_queue: Arc, - gfx_queue: Arc, - size: [u32; 2], - window_renderer: &VulkanoWindowRenderer, - ) -> RenderPipeline { - RenderPipeline { - compute: GameOfLifeComputePipeline::new(app, compute_queue, size), - place_over_frame: RenderPassPlaceOverFrame::new(app, gfx_queue, window_renderer), - } - } -} - -pub struct App { - pub context: VulkanoContext, - pub windows: VulkanoWindows, - pub command_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, - pub pipelines: HashMap, -} - -impl App { - pub fn open(&mut self, event_loop: &EventLoop<()>) { - // Create windows & pipelines. - let id1 = self.windows.create_window( - event_loop, - &self.context, - &WindowDescriptor { - width: WINDOW_WIDTH, - height: WINDOW_HEIGHT, - title: "Game of Life Primary".to_string(), - ..Default::default() - }, - |_| {}, - ); - let id2 = self.windows.create_window( - event_loop, - &self.context, - &WindowDescriptor { - width: WINDOW2_WIDTH, - height: WINDOW2_HEIGHT, - title: "Game of Life Secondary".to_string(), - ..Default::default() - }, - |_| {}, - ); - self.pipelines.insert( - id1, - RenderPipeline::new( - self, - // Use same queue.. for synchronization. - self.context.graphics_queue().clone(), - self.context.graphics_queue().clone(), - [ - (WINDOW_WIDTH / SCALING) as u32, - (WINDOW_HEIGHT / SCALING) as u32, - ], - self.windows.get_primary_renderer().unwrap(), - ), - ); - self.pipelines.insert( - id2, - RenderPipeline::new( - self, - self.context.graphics_queue().clone(), - self.context.graphics_queue().clone(), - [ - (WINDOW2_WIDTH / SCALING) as u32, - (WINDOW2_HEIGHT / SCALING) as u32, - ], - self.windows.get_renderer(id2).unwrap(), - ), - ); - } -} - -impl Default for App { - fn default() -> Self { - let context = VulkanoContext::new(VulkanoConfig::default()); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - context.device().clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, - ..Default::default() - }, - )); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - context.device().clone(), - Default::default(), - )); - - App { - context, - windows: VulkanoWindows::default(), - command_buffer_allocator, - descriptor_set_allocator, - pipelines: HashMap::new(), - } - } -} diff --git a/examples/multi-window-game-of-life/game_of_life.rs b/examples/multi-window-game-of-life/game_of_life.rs index d95c6e4b25..fe0bb742ae 100644 --- a/examples/multi-window-game-of-life/game_of_life.rs +++ b/examples/multi-window-game-of-life/game_of_life.rs @@ -1,4 +1,4 @@ -use crate::app::App; +use crate::App; use glam::IVec2; use rand::Rng; use std::sync::Arc; @@ -175,11 +175,10 @@ impl GameOfLifeComputePipeline { ) { // Resize image if needed. let image_extent = self.image.image().extent(); - let pipeline_layout = self.compute_life_pipeline.layout(); - let desc_layout = &pipeline_layout.set_layouts()[0]; - let set = DescriptorSet::new( + let layout = &self.compute_life_pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new( self.descriptor_set_allocator.clone(), - desc_layout.clone(), + layout.clone(), [ WriteDescriptorSet::image_view(0, self.image.clone()), WriteDescriptorSet::buffer(1, self.life_in.clone()), @@ -197,9 +196,9 @@ impl GameOfLifeComputePipeline { builder .bind_pipeline_compute(self.compute_life_pipeline.clone()) .unwrap() - .bind_descriptor_sets(PipelineBindPoint::Compute, pipeline_layout.clone(), 0, set) + .bind_descriptor_sets(PipelineBindPoint::Compute, self.compute_life_pipeline.layout().clone(), 0, descriptor_set) .unwrap() - .push_constants(pipeline_layout.clone(), 0, push_constants) + .push_constants(self.compute_life_pipeline.layout().clone(), 0, push_constants) .unwrap(); unsafe { diff --git a/examples/multi-window-game-of-life/main.rs b/examples/multi-window-game-of-life/main.rs index d51badf1a8..4ef4d1d368 100644 --- a/examples/multi-window-game-of-life/main.rs +++ b/examples/multi-window-game-of-life/main.rs @@ -7,151 +7,230 @@ // // The possibilities are limitless. ;) -mod app; -mod game_of_life; -mod pixels_draw; -mod render_pass; - -use crate::app::{App, RenderPipeline}; +use game_of_life::GameOfLifeComputePipeline; use glam::{f32::Vec2, IVec2}; +use render_pass::RenderPassPlaceOverFrame; +use vulkano::{ + command_buffer::allocator::{ + StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo, + }, + descriptor_set::allocator::StandardDescriptorSetAllocator, +}; use std::{ - error::Error, - time::{Duration, Instant}, + collections::HashMap, error::Error, sync::Arc, time::{Duration, Instant} +}; +use vulkano_util::{ + context::{VulkanoConfig, VulkanoContext}, + renderer::VulkanoWindowRenderer, + window::{VulkanoWindows, WindowDescriptor}, }; -use vulkano_util::renderer::VulkanoWindowRenderer; use winit::{ - event::{ElementState, Event, MouseButton, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + application::ApplicationHandler, + event::{MouseButton, WindowEvent}, + event_loop::{ActiveEventLoop, EventLoop}, + window::WindowId, }; -pub const WINDOW_WIDTH: f32 = 1024.0; -pub const WINDOW_HEIGHT: f32 = 1024.0; -pub const WINDOW2_WIDTH: f32 = 512.0; -pub const WINDOW2_HEIGHT: f32 = 512.0; -pub const SCALING: f32 = 2.0; +mod game_of_life; +mod pixels_draw; +mod render_pass; + +const WINDOW_WIDTH: f32 = 1024.0; +const WINDOW_HEIGHT: f32 = 1024.0; +const WINDOW2_WIDTH: f32 = 512.0; +const WINDOW2_HEIGHT: f32 = 512.0; +const SCALING: f32 = 2.0; fn main() -> Result<(), impl Error> { + let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + println!("Welcome to Vulkano Game of Life\nUse the mouse to draw life on the grid(s)\n"); - // Create event loop. - let event_loop = EventLoop::new().unwrap(); + event_loop.run_app(&mut app) +} - // Create app with vulkano context. - let mut app = App::default(); - app.open(&event_loop); - - // Time & inputs... - let mut time = Instant::now(); - let mut cursor_pos = Vec2::ZERO; - - // An extremely crude way to handle input state... but works for this example. - let mut mouse_is_pressed_w1 = false; - let mut mouse_is_pressed_w2 = false; - - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); - - if process_event( - &event, - &mut app, - &mut cursor_pos, - &mut mouse_is_pressed_w1, - &mut mouse_is_pressed_w2, - ) { - elwt.exit(); - return; - } else if event == Event::AboutToWait { - for (_, renderer) in app.windows.iter() { - renderer.window().request_redraw(); - } - } +struct App { + context: VulkanoContext, + windows: VulkanoWindows, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + rcxs: HashMap, + time: Instant, + cursor_pos: Vec2, +} - // Draw life on windows if mouse is down. - draw_life( - &mut app, - cursor_pos, - mouse_is_pressed_w1, - mouse_is_pressed_w2, - ); +struct RenderContext { + compute_pipeline: GameOfLifeComputePipeline, + place_over_frame: RenderPassPlaceOverFrame, + life_color: [f32; 4], + dead_color: [f32; 4], + mouse_is_pressed: bool, +} - // Compute life & render 60fps. - if (Instant::now() - time).as_secs_f64() > 1.0 / 60.0 { - compute_then_render_per_window(&mut app); - time = Instant::now(); +impl App { + fn new(_event_loop: &EventLoop<()>) -> Self { + let context = VulkanoContext::new(VulkanoConfig::default()); + let windows = VulkanoWindows::default(); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + context.device().clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + context.device().clone(), + StandardCommandBufferAllocatorCreateInfo { + secondary_buffer_count: 32, + ..Default::default() + }, + )); + + // Time & inputs... + let time = Instant::now(); + let cursor_pos = Vec2::ZERO; + + App { + context, + windows, + descriptor_set_allocator, + command_buffer_allocator, + rcxs: HashMap::new(), + time, + cursor_pos, } - }) + } } -/// Processes a single event for an event loop. -/// Returns true only if the window is to be closed. -pub fn process_event( - event: &Event<()>, - app: &mut App, - cursor_pos: &mut Vec2, - mouse_pressed_w1: &mut bool, - mouse_pressed_w2: &mut bool, -) -> bool { - if let Event::WindowEvent { - event, window_id, .. - } = &event - { +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + // Create windows & pipelines. + let id1 = self.windows.create_window( + event_loop, + &self.context, + &WindowDescriptor { + width: WINDOW_WIDTH, + height: WINDOW_HEIGHT, + title: "Game of Life Primary".to_string(), + ..Default::default() + }, + |_| {}, + ); + let id2 = self.windows.create_window( + event_loop, + &self.context, + &WindowDescriptor { + width: WINDOW2_WIDTH, + height: WINDOW2_HEIGHT, + title: "Game of Life Secondary".to_string(), + ..Default::default() + }, + |_| {}, + ); + let gfx_queue = self.context.graphics_queue(); + self.rcxs.insert( + id1, + RenderContext { + compute_pipeline: GameOfLifeComputePipeline::new( + self, + gfx_queue.clone(), + [ + (WINDOW_WIDTH / SCALING) as u32, + (WINDOW_HEIGHT / SCALING) as u32, + ], + ), + place_over_frame: RenderPassPlaceOverFrame::new(self, gfx_queue.clone(), id1), + life_color: [1.0, 0.0, 0.0, 1.0], + dead_color: [0.0; 4], + mouse_is_pressed: false, + } + ); + self.rcxs.insert( + id2, + RenderContext { + compute_pipeline: GameOfLifeComputePipeline::new( + self, + gfx_queue.clone(), + [ + (WINDOW2_WIDTH / SCALING) as u32, + (WINDOW2_HEIGHT / SCALING) as u32, + ], + ), + place_over_frame: RenderPassPlaceOverFrame::new(self, gfx_queue.clone(), id2), + life_color: [0.0, 0.0, 0.0, 1.0], + dead_color: [1.0; 4], + mouse_is_pressed: false, + } + ); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: WindowEvent, + ) { match event { WindowEvent::CloseRequested => { - if *window_id == app.windows.primary_window_id().unwrap() { - return true; + if window_id == self.windows.primary_window_id().unwrap() { + event_loop.exit(); } else { // Destroy window by removing its renderer. - app.windows.remove_renderer(*window_id); - app.pipelines.remove(window_id); + self.windows.remove_renderer(window_id); + self.rcxs.remove(&window_id); } } // Resize window and its images. WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. } => { - let vulkano_window = app.windows.get_renderer_mut(*window_id).unwrap(); - vulkano_window.resize(); + let window_renderer = self.windows.get_renderer_mut(window_id).unwrap(); + window_renderer.resize(); } // Handle mouse position events. WindowEvent::CursorMoved { position, .. } => { - *cursor_pos = Vec2::new(position.x as f32, position.y as f32) + self.cursor_pos = Vec2::from_array(position.into()); } // Handle mouse button events. WindowEvent::MouseInput { state, button, .. } => { - let mut mouse_pressed = false; - if button == &MouseButton::Left && state == &ElementState::Pressed { - mouse_pressed = true; - } - if button == &MouseButton::Left && state == &ElementState::Released { - mouse_pressed = false; + let rcx = self.rcxs.get_mut(&window_id).unwrap(); + + if button == MouseButton::Left { + rcx.mouse_is_pressed = state.is_pressed(); } - if window_id == &app.windows.primary_window_id().unwrap() { - *mouse_pressed_w1 = mouse_pressed; - } else { - *mouse_pressed_w2 = mouse_pressed; + } + WindowEvent::RedrawRequested => { + let Some(window_renderer) = self.windows.get_renderer_mut(window_id) else { + return; + }; + let rcx = self.rcxs.get_mut(&window_id).unwrap(); + let window_size = window_renderer.window().inner_size(); + + if window_size.width == 0 || window_size.height == 0 { + return; } + + // Draw life on windows if mouse is down. + draw_life(window_renderer, rcx, self.cursor_pos); + + // Compute life & render 60fps. + compute_then_render(window_renderer, rcx); + self.time = Instant::now(); } - _ => (), + _ => {} + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + for (_, renderer) in self.windows.iter() { + renderer.window().request_redraw(); } } - false } fn draw_life( - app: &mut App, + window_renderer: &mut VulkanoWindowRenderer, + rcx: &mut RenderContext, cursor_pos: Vec2, - mouse_is_pressed_w1: bool, - mouse_is_pressed_w2: bool, ) { - let primary_window_id = app.windows.primary_window_id().unwrap(); - for (id, window) in app.windows.iter_mut() { - if id == &primary_window_id && !mouse_is_pressed_w1 { - continue; - } - if id != &primary_window_id && !mouse_is_pressed_w2 { - continue; - } - - let window_size = window.window_size(); - let compute_pipeline = &mut app.pipelines.get_mut(id).unwrap().compute; + if rcx.mouse_is_pressed { + let window_size = window_renderer.window_size(); let mut normalized_pos = Vec2::new( (cursor_pos.x / window_size[0]).clamp(0.0, 1.0), (cursor_pos.y / window_size[1]).clamp(0.0, 1.0), @@ -159,48 +238,23 @@ fn draw_life( // Flip y. normalized_pos.y = 1.0 - normalized_pos.y; - let image_extent = compute_pipeline.color_image().image().extent(); - compute_pipeline.draw_life(IVec2::new( + let image_extent = rcx.compute_pipeline.color_image().image().extent(); + rcx.compute_pipeline.draw_life(IVec2::new( (image_extent[0] as f32 * normalized_pos.x) as i32, (image_extent[1] as f32 * normalized_pos.y) as i32, - )) - } -} - -/// Compute and render per window. -fn compute_then_render_per_window(app: &mut App) { - let primary_window_id = app.windows.primary_window_id().unwrap(); - for (window_id, window_renderer) in app.windows.iter_mut() { - let pipeline = app.pipelines.get_mut(window_id).unwrap(); - if *window_id == primary_window_id { - compute_then_render(window_renderer, pipeline, [1.0, 0.0, 0.0, 1.0], [0.0; 4]); - } else { - compute_then_render(window_renderer, pipeline, [0.0, 0.0, 0.0, 1.0], [1.0; 4]); - } + )); } } /// Compute game of life, then display result on target image. fn compute_then_render( window_renderer: &mut VulkanoWindowRenderer, - pipeline: &mut RenderPipeline, - life_color: [f32; 4], - dead_color: [f32; 4], + rcx: &mut RenderContext, ) { - // Skip this window when minimized. - match window_renderer.window_size() { - [w, h] => { - if w == 0.0 || h == 0.0 { - return; - } - } - } - // Start the frame. let before_pipeline_future = match window_renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| { - pipeline - .place_over_frame + rcx.place_over_frame .recreate_framebuffers(swapchain_image_views) }) { Err(e) => { @@ -211,15 +265,15 @@ fn compute_then_render( }; // Compute. - let after_compute = pipeline - .compute - .compute(before_pipeline_future, life_color, dead_color); + let after_compute = rcx + .compute_pipeline + .compute(before_pipeline_future, rcx.life_color, rcx.dead_color); // Render. - let color_image = pipeline.compute.color_image(); + let color_image = rcx.compute_pipeline.color_image(); let target_image = window_renderer.swapchain_image_view(); - let after_render = pipeline.place_over_frame.render( + let after_render = rcx.place_over_frame.render( after_compute, color_image, target_image, diff --git a/examples/multi-window-game-of-life/pixels_draw.rs b/examples/multi-window-game-of-life/pixels_draw.rs index 7186d17c17..6d495f15f1 100644 --- a/examples/multi-window-game-of-life/pixels_draw.rs +++ b/examples/multi-window-game-of-life/pixels_draw.rs @@ -1,4 +1,4 @@ -use crate::app::App; +use crate::App; use std::sync::Arc; use vulkano::{ command_buffer::{ diff --git a/examples/multi-window-game-of-life/render_pass.rs b/examples/multi-window-game-of-life/render_pass.rs index af2dd9e0e3..28032bf4b6 100644 --- a/examples/multi-window-game-of-life/render_pass.rs +++ b/examples/multi-window-game-of-life/render_pass.rs @@ -1,4 +1,4 @@ -use crate::{app::App, pixels_draw::PixelsDrawPipeline}; +use crate::{App, pixels_draw::PixelsDrawPipeline}; use std::sync::Arc; use vulkano::{ command_buffer::{ @@ -11,7 +11,7 @@ use vulkano::{ render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sync::GpuFuture, }; -use vulkano_util::renderer::VulkanoWindowRenderer; +use winit::window::WindowId; /// A render pass which places an incoming image over the frame, filling it. pub struct RenderPassPlaceOverFrame { @@ -26,8 +26,9 @@ impl RenderPassPlaceOverFrame { pub fn new( app: &App, gfx_queue: Arc, - window_renderer: &VulkanoWindowRenderer, + window_id: WindowId, ) -> RenderPassPlaceOverFrame { + let window_renderer = app.windows.get_renderer(window_id).unwrap(); let render_pass = vulkano::single_pass_renderpass!( gfx_queue.device().clone(), attachments: { diff --git a/examples/multi-window/main.rs b/examples/multi-window/main.rs index a735442d92..f77d314509 100644 --- a/examples/multi-window/main.rs +++ b/examples/multi-window/main.rs @@ -9,14 +9,14 @@ use std::{collections::HashMap, error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -42,25 +42,43 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::{Window, WindowBuilder}, + application::ApplicationHandler, + event::{ElementState, KeyEvent, WindowEvent}, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; -/// A struct to contain resources related to a window. -struct WindowSurface { +fn main() -> Result<(), impl Error> { + let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + rcxs: HashMap, +} + +struct RenderContext { window: Arc, swapchain: Arc, + render_pass: Arc, framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, recreate_swapchain: bool, previous_frame_end: Option>, } -fn main() -> Result<(), impl Error> { - let event_loop = EventLoop::new().unwrap(); - +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -85,7 +103,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -117,36 +135,79 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); + let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + }, + MyVertex { + position: [0.0, 0.5], + }, + MyVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); // A hashmap that contains all of our created windows and their resources. - let mut window_surfaces = HashMap::new(); + let rcxs = HashMap::new(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcxs, + } + } - // Use the window's id as a means to access it from the hashmap. - let window_id = window.id(); + fn create_rcx(&self, window: Window) -> RenderContext { + let window = Arc::new(window); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); // The swapchain and framebuffer images for this particular window. let (swapchain, images) = { - let surface_capabilities = device + let surface_capabilities = self + .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let image_format = device + let (image_format, _) = self + .device .physical_device() .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; + .unwrap()[0]; Swapchain::new( - device.clone(), + self.device.clone(), surface.clone(), SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count.max(2), image_format, - image_extent: window.inner_size().into(), + image_extent: window_size.into(), image_usage: ImageUsage::COLOR_ATTACHMENT, composite_alpha: surface_capabilities .supported_composite_alpha @@ -159,41 +220,6 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } - - let vertices = [ - Vertex { - position: [-0.5, -0.25], - }, - Vertex { - position: [0.0, 0.5], - }, - Vertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - mod vs { vulkano_shaders::shader! { ty: "vertex", @@ -225,7 +251,7 @@ fn main() -> Result<(), impl Error> { } let render_pass = vulkano::single_pass_renderpass!( - device.clone(), + self.device.clone(), attachments: { color: { format: swapchain.image_format(), @@ -241,31 +267,33 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -286,162 +314,109 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + } + } +} - window_surfaces.insert( - window_id, - WindowSurface { - window, - swapchain, - recreate_swapchain: false, - framebuffers: window_size_dependent_setup(&images, render_pass.clone(), &mut viewport), - previous_frame_end: Some(sync::now(device.clone()).boxed()), - }, - ); +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = event_loop.create_window(Window::default_attributes()).unwrap(); + + // Use the window's id as a means to access it from the hashmap. + let window_id = window.id(); - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + let rcx = self.create_rcx(window); + self.rcxs.insert(window_id, rcx); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: WindowEvent, + ) { match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - window_id, - event: WindowEvent::Resized(_), - .. - } => { - window_surfaces - .get_mut(&window_id) - .unwrap() - .recreate_swapchain = true; + WindowEvent::Resized(_) => { + self.rcxs.get_mut(&window_id).unwrap().recreate_swapchain = true; } - Event::WindowEvent { + WindowEvent::KeyboardInput { event: - WindowEvent::KeyboardInput { - event: - KeyEvent { - state: ElementState::Pressed, - .. - }, + KeyEvent { + state: ElementState::Pressed, .. }, .. } => { - let window = Arc::new(WindowBuilder::new().build(elwt).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + let window = event_loop.create_window(Window::default_attributes()).unwrap(); let window_id = window.id(); - let (swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - - window_surfaces.insert( - window_id, - WindowSurface { - window, - swapchain, - recreate_swapchain: false, - framebuffers: window_size_dependent_setup( - &images, - render_pass.clone(), - &mut viewport, - ), - previous_frame_end: Some(sync::now(device.clone()).boxed()), - }, - ); + let rcx = self.create_rcx(window); + + self.rcxs.insert(window_id, rcx); } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - window_id, - } => { - let WindowSurface { - window, - swapchain, - recreate_swapchain, - framebuffers, - previous_frame_end, - } = window_surfaces.get_mut(&window_id).unwrap(); + WindowEvent::RedrawRequested => { + let rcx = self.rcxs.get_mut(&window_id).unwrap(); - let image_extent: [u32; 2] = window.inner_size().into(); + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if *recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - *swapchain = new_swapchain; - *framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - *recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - *recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - *recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -455,74 +430,80 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - *previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - *recreate_swapchain = true; - *previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - *previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => { - window_surfaces - .values() - .for_each(|s| s.window.request_redraw()); - } - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + self.rcxs + .values() + .for_each(|s| s.window.request_redraw()); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/occlusion-query/main.rs b/examples/occlusion-query/main.rs index bb88fd29fc..8c034dc36b 100644 --- a/examples/occlusion-query/main.rs +++ b/examples/occlusion-query/main.rs @@ -4,14 +4,14 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, format::Format, image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage}, @@ -40,16 +40,48 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + memory_allocator: Arc, + command_buffer_allocator: Arc, + triangle1: Subbuffer<[MyVertex]>, + triangle2: Subbuffer<[MyVertex]>, + triangle3: Subbuffer<[MyVertex]>, + query_pool: Arc, + query_results: [u32; 3], + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -74,7 +106,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -108,90 +140,52 @@ fn main() -> Result<(), impl Error> { .unwrap(); let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32B32_SFLOAT)] - position: [f32; 3], - #[format(R32G32B32_SFLOAT)] - color: [f32; 3], - } + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertices = [ // The first triangle (red) is the same one as in the triangle example. - Vertex { + MyVertex { position: [-0.5, -0.25, 0.5], color: [1.0, 0.0, 0.0], }, - Vertex { + MyVertex { position: [0.0, 0.5, 0.5], color: [1.0, 0.0, 0.0], }, - Vertex { + MyVertex { position: [0.25, -0.1, 0.5], color: [1.0, 0.0, 0.0], }, // The second triangle (cyan) is the same shape and position as the first, but smaller, and // moved behind a bit. It should be completely occluded by the first triangle. (You can // lower its z value to put it in front.) - Vertex { + MyVertex { position: [-0.25, -0.125, 0.6], color: [0.0, 1.0, 1.0], }, - Vertex { + MyVertex { position: [0.0, 0.25, 0.6], color: [0.0, 1.0, 1.0], }, - Vertex { + MyVertex { position: [0.125, -0.05, 0.6], color: [0.0, 1.0, 1.0], }, // The third triangle (green) is the same shape and size as the first, but moved to the // left and behind the second. It is partially occluded by the first two. - Vertex { + MyVertex { position: [-0.25, -0.25, 0.7], color: [0.0, 1.0, 0.0], }, - Vertex { + MyVertex { position: [0.25, 0.5, 0.7], color: [0.0, 1.0, 0.0], }, - Vertex { + MyVertex { position: [0.5, -0.1, 0.7], color: [0.0, 1.0, 0.0], }, @@ -231,7 +225,85 @@ fn main() -> Result<(), impl Error> { // element per query. You can ask for the number of elements needed at runtime by calling // `QueryType::result_len`. If you retrieve query results with `with_availability` enabled, // then this array needs to be 6 elements long instead of 3. - let mut query_results = [0u32; 3]; + let query_results = [0u32; 3]; + + App { + instance, + device, + queue, + memory_allocator, + command_buffer_allocator, + triangle1, + triangle2, + triangle3, + query_pool, + query_results, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + depth_stencil: { + format: Format::D16_UNORM, + samples: 1, + load_op: Clear, + store_op: DontCare, + }, + }, + pass: { + color: [color], + depth_stencil: {depth_stencil}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass, &self.memory_allocator); mod vs { vulkano_shaders::shader! { @@ -268,54 +340,31 @@ fn main() -> Result<(), impl Error> { } } - let render_pass = vulkano::single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - depth_stencil: { - format: Format::D16_UNORM, - samples: 1, - load_op: Clear, - store_op: DontCare, - }, - }, - pass: { - color: [color], - depth_stencil: {depth_stencil}, - }, - ) - .unwrap(); - let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -343,90 +392,86 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let mut framebuffers = window_size_dependent_setup( - &images, - render_pass.clone(), - &mut viewport, - memory_allocator.clone(), - ); - - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup( &new_images, - render_pass.clone(), - &mut viewport, - memory_allocator.clone(), + &rcx.render_pass, + &self.memory_allocator, ); - recreate_swapchain = false; + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -440,11 +485,11 @@ fn main() -> Result<(), impl Error> { builder // A query must be reset before each use, including the first use. This // must be done outside a render pass. - .reset_query_pool(query_pool.clone(), 0..3) + .reset_query_pool(self.query_pool.clone(), 0..3) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .begin_render_pass( RenderPassBeginInfo { @@ -453,7 +498,7 @@ fn main() -> Result<(), impl Error> { Some(1.0.into()), ], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), @@ -463,36 +508,36 @@ fn main() -> Result<(), impl Error> { // `QueryControlFlags::PRECISE` flag would give exact numeric results. This // needs the `occlusion_query_precise` feature to be enabled on the device. .begin_query( - query_pool.clone(), + self.query_pool.clone(), 0, QueryControlFlags::empty(), // QueryControlFlags::PRECISE, ) .unwrap() - .bind_vertex_buffers(0, triangle1.clone()) + .bind_vertex_buffers(0, self.triangle1.clone()) .unwrap() - .draw(triangle1.len() as u32, 1, 0, 0) + .draw(self.triangle1.len() as u32, 1, 0, 0) .unwrap() // End query 0. - .end_query(query_pool.clone(), 0) + .end_query(self.query_pool.clone(), 0) .unwrap() // Begin query 1 for the cyan triangle. - .begin_query(query_pool.clone(), 1, QueryControlFlags::empty()) + .begin_query(self.query_pool.clone(), 1, QueryControlFlags::empty()) .unwrap() - .bind_vertex_buffers(0, triangle2.clone()) + .bind_vertex_buffers(0, self.triangle2.clone()) .unwrap() - .draw(triangle2.len() as u32, 1, 0, 0) + .draw(self.triangle2.len() as u32, 1, 0, 0) .unwrap() - .end_query(query_pool.clone(), 1) + .end_query(self.query_pool.clone(), 1) .unwrap() // Finally, query 2 for the green triangle. - .begin_query(query_pool.clone(), 2, QueryControlFlags::empty()) + .begin_query(self.query_pool.clone(), 2, QueryControlFlags::empty()) .unwrap() - .bind_vertex_buffers(0, triangle3.clone()) + .bind_vertex_buffers(0, self.triangle3.clone()) .unwrap() - .draw(triangle3.len() as u32, 1, 0, 0) + .draw(self.triangle3.len() as u32, 1, 0, 0) .unwrap() - .end_query(query_pool.clone(), 2) + .end_query(self.query_pool.clone(), 2) .unwrap() .end_render_pass(Default::default()) .unwrap(); @@ -500,29 +545,30 @@ fn main() -> Result<(), impl Error> { let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } @@ -531,9 +577,9 @@ fn main() -> Result<(), impl Error> { // write results to a Vulkano buffer. This could then be used to influence draw // operations further down the line, either in the same frame or a future frame. #[rustfmt::skip] - query_pool.get_results( + self.query_pool.get_results( 0..3, - &mut query_results, + &mut self.query_results, // Block the function call until the results are available. // NOTE: If not all the queries have actually been executed, then this will // wait forever for something that never happens! @@ -560,33 +606,42 @@ fn main() -> Result<(), impl Error> { // Query 0 (red triangle) will always succeed, because the depth buffer starts // empty and will never occlude anything. - assert_ne!(query_results[0], 0); + assert_ne!(self.query_results[0], 0); // Query 1 (cyan triangle) will fail, because it's drawn completely behind the // first. - assert_eq!(query_results[1], 0); + assert_eq!(self.query_results[1], 0); // Query 2 (green triangle) will succeed, because it's only partially occluded. - assert_ne!(query_results[2], 0); + assert_ne!(self.query_results[2], 0); } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32B32_SFLOAT)] + position: [f32; 3], + #[format(R32G32B32_SFLOAT)] + color: [f32; 3], } fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, - memory_allocator: Arc, + render_pass: &Arc, + memory_allocator: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - let depth_attachment = ImageView::new_default( Image::new( - memory_allocator, + memory_allocator.clone(), ImageCreateInfo { image_type: ImageType::Dim2d, format: Format::D16_UNORM, @@ -604,6 +659,7 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/push-descriptors/main.rs b/examples/push-descriptors/main.rs index a5cd07fcb4..ef9560198f 100644 --- a/examples/push-descriptors/main.rs +++ b/examples/push-descriptors/main.rs @@ -1,14 +1,14 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, }, descriptor_set::{layout::DescriptorSetLayoutCreateFlags, WriteDescriptorSet}, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, format::Format, image::{ @@ -40,16 +40,45 @@ use vulkano::{ DeviceSize, Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + texture: Arc, + sampler: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -75,7 +104,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -109,59 +138,23 @@ fn main() -> Result<(), impl Error> { .unwrap(); let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertices = [ - Vertex { + MyVertex { position: [-0.5, -0.5], }, - Vertex { + MyVertex { position: [-0.5, 0.5], }, - Vertex { + MyVertex { position: [0.5, -0.5], }, - Vertex { + MyVertex { position: [0.5, 0.5], }, ]; @@ -180,27 +173,6 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let render_pass = vulkano::single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); let mut uploads = RecordingCommandBuffer::new( command_buffer_allocator.clone(), queue.queue_family_index(), @@ -272,16 +244,87 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), @@ -291,12 +334,12 @@ fn main() -> Result<(), impl Error> { PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); let set_layout = &mut layout_create_info.set_layouts[0]; set_layout.flags |= DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR; - set_layout.bindings.get_mut(&0).unwrap().immutable_samplers = vec![sampler]; + set_layout.bindings.get_mut(&0).unwrap().immutable_samplers = vec![self.sampler.clone()]; PipelineLayout::new( - device.clone(), + self.device.clone(), layout_create_info - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap() @@ -304,7 +347,7 @@ fn main() -> Result<(), impl Error> { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -331,85 +374,82 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some( - uploads - .end() - .unwrap() - .execute(queue.clone()) - .unwrap() - .boxed(), - ); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -423,85 +463,94 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .push_descriptor_set( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, [ // If the binding is an immutable sampler, using push descriptors // you must write a dummy value to the binding. WriteDescriptorSet::none(0), - WriteDescriptorSet::image_view(1, texture.clone()), + WriteDescriptorSet::image_view(1, self.texture.clone()), ] .into_iter() .collect(), ) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/runtime-array/main.rs b/examples/runtime-array/main.rs index 3d0a40e553..4974a4907f 100644 --- a/examples/runtime-array/main.rs +++ b/examples/runtime-array/main.rs @@ -1,6 +1,6 @@ use std::{error::Error, io::Cursor, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, @@ -11,7 +11,7 @@ use vulkano::{ }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, - QueueCreateInfo, QueueFlags, + Queue, QueueCreateInfo, QueueFlags, }, format::Format, image::{ @@ -43,9 +43,10 @@ use vulkano::{ DeviceSize, Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { @@ -53,9 +54,40 @@ fn main() -> Result<(), impl Error> { // example if you haven't done so yet. let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + vulkano_texture: Arc, + mascot_texture: Arc, + sampler: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + descriptor_set: Arc, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -80,7 +112,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -110,7 +142,7 @@ fn main() -> Result<(), impl Error> { enabled_extensions: device_extensions, enabled_features: DeviceFeatures { descriptor_indexing: true, - shader_uniform_buffer_array_non_uniform_indexing: true, + shader_sampled_image_array_non_uniform_indexing: true, runtime_descriptor_array: true, descriptor_binding_variable_descriptor_count: true, ..DeviceFeatures::empty() @@ -119,111 +151,76 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); - let queue = queues.next().unwrap(); - - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; + let queue = queues.next().unwrap(); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - #[format(R32_UINT)] - tex_i: u32, - #[format(R32G32_SFLOAT)] - coords: [f32; 2], - } + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertices = [ - Vertex { + MyVertex { position: [-0.1, -0.9], tex_i: 0, coords: [1.0, 0.0], }, - Vertex { + MyVertex { position: [-0.9, -0.9], tex_i: 0, coords: [0.0, 0.0], }, - Vertex { + MyVertex { position: [-0.9, -0.1], tex_i: 0, coords: [0.0, 1.0], }, - Vertex { + MyVertex { position: [-0.1, -0.9], tex_i: 0, coords: [1.0, 0.0], }, - Vertex { + MyVertex { position: [-0.9, -0.1], tex_i: 0, coords: [0.0, 1.0], }, - Vertex { + MyVertex { position: [-0.1, -0.1], tex_i: 0, coords: [1.0, 1.0], }, - Vertex { + MyVertex { position: [0.9, -0.9], tex_i: 1, coords: [1.0, 0.0], }, - Vertex { + MyVertex { position: [0.1, -0.9], tex_i: 1, coords: [0.0, 0.0], }, - Vertex { + MyVertex { position: [0.1, -0.1], tex_i: 1, coords: [0.0, 1.0], }, - Vertex { + MyVertex { position: [0.9, -0.9], tex_i: 1, coords: [1.0, 0.0], }, - Vertex { + MyVertex { position: [0.1, -0.1], tex_i: 1, coords: [0.0, 1.0], }, - Vertex { + MyVertex { position: [0.9, -0.1], tex_i: 1, coords: [1.0, 1.0], @@ -244,32 +241,6 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let render_pass = vulkano::single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - let mut uploads = RecordingCommandBuffer::new( command_buffer_allocator.clone(), queue.queue_family_index(), @@ -391,16 +362,89 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + vulkano_texture, + mascot_texture, + sampler, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), @@ -418,9 +462,9 @@ fn main() -> Result<(), impl Error> { binding.descriptor_count = 2; PipelineLayout::new( - device.clone(), + self.device.clone(), layout_create_info - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap() @@ -428,7 +472,7 @@ fn main() -> Result<(), impl Error> { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -452,98 +496,96 @@ fn main() -> Result<(), impl Error> { .unwrap() }; + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + let layout = &pipeline.layout().set_layouts()[0]; - let set = DescriptorSet::new_variable( - descriptor_set_allocator, + let descriptor_set = DescriptorSet::new_variable( + self.descriptor_set_allocator.clone(), layout.clone(), 2, [ - WriteDescriptorSet::sampler(0, sampler), - WriteDescriptorSet::image_view_array(1, 0, [mascot_texture as _, vulkano_texture as _]), + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view_array(1, 0, [self.mascot_texture.clone() as _, self.vulkano_texture.clone() as _]), ], [], ) .unwrap(); - let mut viewport = Viewport { - offset: [0.0, 0.0], - extent: [0.0, 0.0], - depth_range: 0.0..=1.0, - }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - - let mut recreate_swapchain = false; - let mut previous_frame_end = Some( - uploads - .end() - .unwrap() - .execute(queue.clone()) - .unwrap() - .boxed(), - ); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -557,78 +599,91 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, - set.clone(), + rcx.descriptor_set.clone(), ) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], + #[format(R32_UINT)] + tex_i: u32, + #[format(R32G32_SFLOAT)] + coords: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/runtime-shader/main.rs b/examples/runtime-shader/main.rs index 77196224fb..1857263a2d 100644 --- a/examples/runtime-shader/main.rs +++ b/examples/runtime-shader/main.rs @@ -14,14 +14,14 @@ use std::{error::Error, fs::File, io::Read, path::Path, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -48,16 +48,43 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -82,7 +109,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -114,29 +141,80 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); + let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-1.0, 1.0], + color: [1.0, 0.0, 0.0], + }, + MyVertex { + position: [0.0, -1.0], + color: [0.0, 1.0, 0.0], + }, + MyVertex { + position: [1.0, 1.0], + color: [0.0, 0.0, 1.0], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); - let (mut swapchain, images) = { - let surface_capabilities = device + let (swapchain, images) = { + let surface_capabilities = self + .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let image_format = device + let (image_format, _) = self + .device .physical_device() .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; + .unwrap()[0]; Swapchain::new( - device.clone(), + self.device.clone(), surface, SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count.max(2), image_format, - image_extent: window.inner_size().into(), + image_extent: window_size.into(), image_usage: ImageUsage::COLOR_ATTACHMENT, composite_alpha: surface_capabilities .supported_composite_alpha @@ -150,7 +228,7 @@ fn main() -> Result<(), impl Error> { }; let render_pass = vulkano::single_pass_renderpass!( - device.clone(), + self.device.clone(), attachments: { color: { format: swapchain.image_format(), @@ -166,14 +244,16 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let graphics_pipeline = { + let framebuffers = window_size_dependent_setup(&images, &render_pass); + + let pipeline = { let vs = { let code = read_spirv_words_from_file("vert.spv"); // Create a ShaderModule on a device the same Shader::load does it. // NOTE: You will have to verify correctness of the data by yourself! let module = unsafe { - ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&code)).unwrap() + ShaderModule::new(self.device.clone(), ShaderModuleCreateInfo::new(&code)).unwrap() }; module.entry_point("main").unwrap() }; @@ -182,27 +262,27 @@ fn main() -> Result<(), impl Error> { let code = read_spirv_words_from_file("frag.spv"); let module = unsafe { - ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&code)).unwrap() + ShaderModule::new(self.device.clone(), ShaderModuleCreateInfo::new(&code)).unwrap() }; module.entry_point("main").unwrap() }; - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -227,128 +307,82 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut recreate_swapchain = false; - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - pub struct Vertex { - #[format(R32G32_SFLOAT)] - pub position: [f32; 2], - #[format(R32G32B32_SFLOAT)] - pub color: [f32; 3], - } - - let vertices = [ - Vertex { - position: [-1.0, 1.0], - color: [1.0, 0.0, 0.0], - }, - Vertex { - position: [0.0, -1.0], - color: [0.0, 1.0, 0.0], - }, - Vertex { - position: [1.0, 1.0], - color: [0.0, 0.0, 1.0], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - // NOTE: We don't create any descriptor sets in this example, but you should - // note that passing wrong types, providing sets at wrong indexes will cause - // descriptor set builder to return Err! - // TODO: Outdated ^ - - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::MultipleSubmit, @@ -362,71 +396,82 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(graphics_pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], + #[format(R32G32B32_SFLOAT)] + color: [f32; 3], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/simple-particles/main.rs b/examples/simple-particles/main.rs index 631240764d..2799f965bc 100644 --- a/examples/simple-particles/main.rs +++ b/examples/simple-particles/main.rs @@ -5,7 +5,7 @@ use std::{error::Error, sync::Arc, time::SystemTime}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, CopyBufferInfo, RecordingCommandBuffer, RenderPassBeginInfo, @@ -14,8 +14,8 @@ use vulkano::{ allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -32,7 +32,7 @@ use vulkano::{ GraphicsPipelineCreateInfo, }, layout::PipelineDescriptorSetLayoutCreateInfo, - ComputePipeline, GraphicsPipeline, PipelineBindPoint, PipelineLayout, + ComputePipeline, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, Subpass}, @@ -40,13 +40,15 @@ use vulkano::{ acquire_next_image, PresentMode, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, }, - sync::{self, future::FenceSignalFuture, GpuFuture}, - Validated, VulkanLibrary, + sync::{self, GpuFuture}, + DeviceSize, Validated, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + dpi::PhysicalSize, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; const WINDOW_WIDTH: u32 = 800; @@ -55,12 +57,40 @@ const WINDOW_HEIGHT: u32 = 600; const PARTICLE_COUNT: usize = 100_000; fn main() -> Result<(), impl Error> { - // The usual Vulkan initialization. Largely the same as example `triangle.rs` until further + // The usual Vulkan initialization. Largely the same as the triangle example until further // commentation is provided. + let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + compute_pipeline: Arc, + descriptor_set: Arc, + rcx: Option, +} +struct RenderContext { + window: Arc, + swapchain: Arc, + framebuffers: Vec>, + pipeline: Arc, + previous_frame_end: Option>, + start_time: SystemTime, + last_frame_time: SystemTime, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -85,7 +115,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -117,205 +147,8 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); - let queue = queues.next().unwrap(); - - let window = Arc::new( - WindowBuilder::new() - // For simplicity, we are going to assert that the window size is static. - .with_resizable(false) - .with_title("simple particles") - .with_inner_size(winit::dpi::PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)) - .build(&event_loop) - .unwrap(), - ); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: [WINDOW_WIDTH, WINDOW_HEIGHT], - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - present_mode: PresentMode::Fifo, - ..Default::default() - }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let framebuffers: Vec> = images - .into_iter() - .map(|img| { - let view = ImageView::new_default(img).unwrap(); - Framebuffer::new( - render_pass.clone(), - FramebufferCreateInfo { - attachments: vec![view], - ..Default::default() - }, - ) - .unwrap() - }) - .collect(); - - // Compute shader for updating the position and velocity of each particle every frame. - mod cs { - vulkano_shaders::shader! { - ty: "compute", - src: r" - #version 450 - - layout(local_size_x = 128, local_size_y = 1, local_size_z = 1) in; - - struct VertexData { - vec2 pos; - vec2 vel; - }; - - // Storage buffer binding, which we optimize by using a DeviceLocalBuffer. - layout (binding = 0) buffer VertexBuffer { - VertexData vertices[]; - }; - - // Allow push constants to define a parameters of compute. - layout (push_constant) uniform PushConstants { - vec2 attractor; - float attractor_strength; - float delta_time; - } push; - - // Keep this value in sync with the `maxSpeed` const in the vertex shader. - const float maxSpeed = 10.0; - - const float minLength = 0.02; - const float friction = -2.0; - - void main() { - const uint index = gl_GlobalInvocationID.x; - - vec2 vel = vertices[index].vel; - - // Update particle position according to velocity. - vec2 pos = vertices[index].pos + push.delta_time * vel; - - // Bounce particle off screen-border. - if (abs(pos.x) > 1.0) { - vel.x = sign(pos.x) * (-0.95 * abs(vel.x) - 0.0001); - if (abs(pos.x) >= 1.05) { - pos.x = sign(pos.x); - } - } - if (abs(pos.y) > 1.0) { - vel.y = sign(pos.y) * (-0.95 * abs(vel.y) - 0.0001); - if (abs(pos.y) >= 1.05) { - pos.y = sign(pos.y); - } - } - - // Simple inverse-square force. - vec2 t = push.attractor - pos; - float r = max(length(t), minLength); - vec2 force = push.attractor_strength * (t / r) / (r * r); - - // Update velocity, enforcing a maximum speed. - vel += push.delta_time * force; - if (length(vel) > maxSpeed) { - vel = maxSpeed*normalize(vel); - } - - // Set new values back into buffer. - vertices[index].pos = pos; - vertices[index].vel = vel * exp(friction * push.delta_time); - } - ", - } - } - - // The vertex shader determines color and is run once per particle. The vertices will be - // updated by the compute shader each frame. - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 pos; - layout(location = 1) in vec2 vel; - - layout(location = 0) out vec4 outColor; - - // Keep this value in sync with the `maxSpeed` const in the compute shader. - const float maxSpeed = 10.0; - - void main() { - gl_Position = vec4(pos, 0.0, 1.0); - gl_PointSize = 1.0; - - // Mix colors based on position and velocity. - outColor = mix( - 0.2 * vec4(pos, abs(vel.x) + abs(vel.y), 1.0), - vec4(1.0, 0.5, 0.8, 1.0), - sqrt(length(vel) / maxSpeed) - ); - } - ", - } - } - - // The fragment shader will only need to apply the color forwarded by the vertex shader, - // because the color of a particle should be identical over all pixels. - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 - - layout(location = 0) in vec4 outColor; - - layout(location = 0) out vec4 fragColor; - void main() { - fragColor = outColor; - } - ", - } - } + let queue = queues.next().unwrap(); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( @@ -327,21 +160,12 @@ fn main() -> Result<(), impl Error> { Default::default(), )); - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - pos: [f32; 2], - #[format(R32G32_SFLOAT)] - vel: [f32; 2], - } - // Apply scoped logic to create `DeviceLocalBuffer` initialized with vertex data. let vertex_buffer = { // Initialize vertex data as an iterator. let vertices = (0..PARTICLE_COUNT).map(|i| { let f = i as f32 / (PARTICLE_COUNT / 10) as f32; - Vertex { + MyVertex { pos: [2. * f.fract() - 1., 0.2 * f.floor() - 1.], vel: [0.; 2], } @@ -367,7 +191,7 @@ fn main() -> Result<(), impl Error> { // Create a buffer in device-local memory with enough space for `PARTICLE_COUNT` number of // `Vertex`. - let device_local_buffer = Buffer::new_slice::( + let device_local_buffer = Buffer::new_slice::( memory_allocator, BufferCreateInfo { // Specify use as a storage buffer, vertex buffer, and transfer destination. @@ -381,7 +205,7 @@ fn main() -> Result<(), impl Error> { memory_type_filter: MemoryTypeFilter::PREFER_DEVICE, ..Default::default() }, - PARTICLE_COUNT as vulkano::DeviceSize, + PARTICLE_COUNT as DeviceSize, ) .unwrap(); @@ -425,19 +249,19 @@ fn main() -> Result<(), impl Error> { device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage]) .into_pipeline_layout_create_info(device.clone()) - .expect("failed to create descriptor set layouts"), + .unwrap(), ) - .expect("failed to create pipeline layout"); + .unwrap(); + ComputePipeline::new( device.clone(), None, ComputePipelineCreateInfo::stage_layout(stage, layout), ) - .expect("failed to create compute shader") + .unwrap() }; // Create a new descriptor set for binding vertices as a storage buffer. - use vulkano::pipeline::Pipeline; // Required to access the `layout` method of pipeline. let descriptor_set = DescriptorSet::new( descriptor_set_allocator.clone(), // 0 is the index of the descriptor set. @@ -450,31 +274,173 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + compute_pipeline, + descriptor_set, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new( + event_loop.create_window( + Window::default_attributes() + // For simplicity, we are going to assert that the window size is static. + .with_resizable(false) + .with_title("simple particles") + .with_inner_size(PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)), + ) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: [WINDOW_WIDTH, WINDOW_HEIGHT], + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + present_mode: PresentMode::Fifo, + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = images + .into_iter() + .map(|img| { + let view = ImageView::new_default(img).unwrap(); + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() + }) + .collect(); + + // The vertex shader determines color and is run once per particle. The vertices will be + // updated by the compute shader each frame. + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 pos; + layout(location = 1) in vec2 vel; + + layout(location = 0) out vec4 outColor; + + // Keep this value in sync with the `maxSpeed` const in the compute shader. + const float maxSpeed = 10.0; + + void main() { + gl_Position = vec4(pos, 0.0, 1.0); + gl_PointSize = 1.0; + + // Mix colors based on position and velocity. + outColor = mix( + 0.2 * vec4(pos, abs(vel.x) + abs(vel.y), 1.0), + vec4(1.0, 0.5, 0.8, 1.0), + sqrt(length(vel) / maxSpeed) + ); + } + ", + } + } + + // The fragment shader will only need to apply the color forwarded by the vertex shader, + // because the color of a particle should be identical over all pixels. + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + + layout(location = 0) in vec4 outColor; + + layout(location = 0) out vec4 fragColor; + + void main() { + fragColor = outColor; + } + ", + } + } + // Create a basic graphics pipeline for rendering particles. - let graphics_pipeline = { - let vs = vs::load(device.clone()) + let pipeline = { + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass, 0).unwrap(); + GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -507,37 +473,47 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut fences: Vec>> = - (0..framebuffers.len()).map(|_| None).collect(); - let mut previous_fence_index = 0u32; + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); let start_time = SystemTime::now(); - let mut last_frame_time = start_time; - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + + self.rcx = Some(RenderContext { + window, + swapchain, + framebuffers, + pipeline, + previous_frame_end, + start_time, + last_frame_time: start_time, + }); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); + // Update per-frame variables. let now = SystemTime::now(); - let time = now.duration_since(start_time).unwrap().as_secs_f32(); - let delta_time = now.duration_since(last_frame_time).unwrap().as_secs_f32(); - last_frame_time = now; + let time = now.duration_since(rcx.start_time).unwrap().as_secs_f32(); + let delta_time = now.duration_since(rcx.last_frame_time).unwrap().as_secs_f32(); + rcx.last_frame_time = now; // Create push constants to be passed to compute shader. let push_constants = cs::PushConstants { @@ -548,7 +524,7 @@ fn main() -> Result<(), impl Error> { // Acquire information on the next swapchain target. let (image_index, suboptimal, acquire_future) = match acquire_next_image( - swapchain.clone(), + rcx.swapchain.clone(), None, // timeout ) { Ok(tuple) => tuple, @@ -562,25 +538,9 @@ fn main() -> Result<(), impl Error> { "not handling sub-optimal swapchains in this sample code", ); - // If this image buffer already has a future then attempt to cleanup fence - // resources. Usually the future for this index will have completed by the time we - // are rendering it again. - if let Some(image_fence) = &mut fences[image_index as usize] { - image_fence.cleanup_finished() - } - - // If the previous image has a fence then use it for synchronization, else create - // a new one. - let previous_future = match fences[previous_fence_index as usize].take() { - // Ensure current frame is synchronized with previous. - Some(fence) => fence.boxed(), - // Create new future to guarantee synchronization with (fake) previous frame. - None => sync::now(device.clone()).boxed(), - }; - let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -591,16 +551,16 @@ fn main() -> Result<(), impl Error> { builder // Push constants for compute shader. - .push_constants(compute_pipeline.layout().clone(), 0, push_constants) + .push_constants(self.compute_pipeline.layout().clone(), 0, push_constants) .unwrap() // Perform compute operation to update particle positions. - .bind_pipeline_compute(compute_pipeline.clone()) + .bind_pipeline_compute(self.compute_pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, - compute_pipeline.layout().clone(), + self.compute_pipeline.layout().clone(), 0, // Bind this descriptor set to index 0. - descriptor_set.clone(), + self.descriptor_set.clone(), ) .unwrap(); @@ -616,15 +576,15 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0., 0., 0., 1.].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .bind_pipeline_graphics(graphics_pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { @@ -634,28 +594,114 @@ fn main() -> Result<(), impl Error> { builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_future + let future = rcx + .previous_frame_end + .take() + .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); - // Update this frame's future with current fence. - fences[image_index as usize] = match future.map_err(Validated::unwrap) { + rcx.previous_frame_end = match future.map_err(Validated::unwrap) { // Success, store result into vector. - Ok(future) => Some(future), - + Ok(future) => Some(future.boxed()), // Unknown failure. Err(e) => panic!("failed to flush future: {e}"), }; - previous_fence_index = image_index; } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + pos: [f32; 2], + #[format(R32G32_SFLOAT)] + vel: [f32; 2], +} + +// Compute shader for updating the position and velocity of each particle every frame. +mod cs { + vulkano_shaders::shader! { + ty: "compute", + src: r" + #version 450 + + layout(local_size_x = 128, local_size_y = 1, local_size_z = 1) in; + + struct VertexData { + vec2 pos; + vec2 vel; + }; + + // Storage buffer binding, which we optimize by using a DeviceLocalBuffer. + layout (binding = 0) buffer VertexBuffer { + VertexData vertices[]; + }; + + // Allow push constants to define a parameters of compute. + layout (push_constant) uniform PushConstants { + vec2 attractor; + float attractor_strength; + float delta_time; + } push; + + // Keep this value in sync with the `maxSpeed` const in the vertex shader. + const float maxSpeed = 10.0; + + const float minLength = 0.02; + const float friction = -2.0; + + void main() { + const uint index = gl_GlobalInvocationID.x; + + vec2 vel = vertices[index].vel; + + // Update particle position according to velocity. + vec2 pos = vertices[index].pos + push.delta_time * vel; + + // Bounce particle off screen-border. + if (abs(pos.x) > 1.0) { + vel.x = sign(pos.x) * (-0.95 * abs(vel.x) - 0.0001); + if (abs(pos.x) >= 1.05) { + pos.x = sign(pos.x); + } + } + if (abs(pos.y) > 1.0) { + vel.y = sign(pos.y) * (-0.95 * abs(vel.y) - 0.0001); + if (abs(pos.y) >= 1.05) { + pos.y = sign(pos.y); + } + } + + // Simple inverse-square force. + vec2 t = push.attractor - pos; + float r = max(length(t), minLength); + vec2 force = push.attractor_strength * (t / r) / (r * r); + + // Update velocity, enforcing a maximum speed. + vel += push.delta_time * force; + if (length(vel) > maxSpeed) { + vel = maxSpeed*normalize(vel); + } + + // Set new values back into buffer. + vertices[index].pos = pos; + vertices[index].vel = vel * exp(friction * push.delta_time); + } + ", + } } diff --git a/examples/specialization-constants/main.rs b/examples/specialization-constants/main.rs index 59b20b8c22..8093eadb89 100644 --- a/examples/specialization-constants/main.rs +++ b/examples/specialization-constants/main.rs @@ -78,6 +78,7 @@ fn main() { }, ) .unwrap(); + let queue = queues.next().unwrap(); mod cs { @@ -126,6 +127,7 @@ fn main() { .unwrap(), ) .unwrap(); + ComputePipeline::new( device.clone(), None, @@ -160,7 +162,7 @@ fn main() { .unwrap(); let layout = &pipeline.layout().set_layouts()[0]; - let set = DescriptorSet::new( + let descriptor_set = DescriptorSet::new( descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, data_buffer.clone())], @@ -186,7 +188,7 @@ fn main() { PipelineBindPoint::Compute, pipeline.layout().clone(), 0, - set, + descriptor_set, ) .unwrap(); diff --git a/examples/teapot/main.rs b/examples/teapot/main.rs index 1e27728e0b..a93a2aa009 100644 --- a/examples/teapot/main.rs +++ b/examples/teapot/main.rs @@ -7,7 +7,7 @@ use std::{error::Error, sync::Arc, time::Instant}; use vulkano::{ buffer::{ allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, - Buffer, BufferCreateInfo, BufferUsage, + Buffer, BufferCreateInfo, BufferUsage, Subbuffer, }, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, @@ -18,7 +18,7 @@ use vulkano::{ }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceOwned, - QueueCreateInfo, QueueFlags, + Queue, QueueCreateInfo, QueueFlags, }, format::Format, image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage}, @@ -48,9 +48,11 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::EventLoop, - window::WindowBuilder, + application::ApplicationHandler, + dpi::PhysicalSize, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; mod model; @@ -60,9 +62,42 @@ fn main() -> Result<(), impl Error> { // example if you haven't done so yet. let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + memory_allocator: Arc, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[Position]>, + normals_buffer: Subbuffer<[Normal]>, + index_buffer: Subbuffer<[u16]>, + uniform_buffer_allocator: SubbufferAllocator, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + vs: EntryPoint, + fs: EntryPoint, + pipeline: Arc, + recreate_swapchain: bool, + previous_frame_end: Option>, + rotation_start: Instant, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -87,7 +122,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -122,40 +157,15 @@ fn main() -> Result<(), impl Error> { let queue = queues.next().unwrap(); - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertex_buffer = Buffer::from_iter( memory_allocator.clone(), @@ -200,7 +210,7 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let uniform_buffer = SubbufferAllocator::new( + let uniform_buffer_allocator = SubbufferAllocator::new( memory_allocator.clone(), SubbufferAllocatorCreateInfo { buffer_usage: BufferUsage::UNIFORM_BUFFER, @@ -210,8 +220,61 @@ fn main() -> Result<(), impl Error> { }, ); + App { + instance, + device, + queue, + memory_allocator, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + normals_buffer, + index_buffer, + uniform_buffer_allocator, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + let render_pass = vulkano::single_pass_renderpass!( - device.clone(), + self.device.clone(), attachments: { color: { format: swapchain.image_format(), @@ -233,85 +296,89 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let (mut pipeline, mut framebuffers) = window_size_dependent_setup( - memory_allocator.clone(), - vs.clone(), - fs.clone(), + let (framebuffers, pipeline) = window_size_dependent_setup( + window_size, &images, - render_pass.clone(), + &render_pass, + &self.memory_allocator, + &vs, + &fs, ); - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + let rotation_start = Instant::now(); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + vs, + fs, + pipeline, + recreate_swapchain: false, + previous_frame_end, + rotation_start, + }); + } - event_loop.run(move |event, elwt| { + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); + match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - let (new_pipeline, new_framebuffers) = window_size_dependent_setup( - memory_allocator.clone(), - vs.clone(), - fs.clone(), + rcx.swapchain = new_swapchain; + (rcx.framebuffers, rcx.pipeline) = window_size_dependent_setup( + window_size, &new_images, - render_pass.clone(), + &rcx.render_pass, + &self.memory_allocator, + &rcx.vs, + &rcx.fs, ); - pipeline = new_pipeline; - framebuffers = new_framebuffers; - recreate_swapchain = false; + rcx.recreate_swapchain = false; } - let uniform_buffer_subbuffer = { - let elapsed = rotation_start.elapsed(); + let uniform_buffer = { + let elapsed = rcx.rotation_start.elapsed(); let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; let rotation = Mat3::from_rotation_y(rotation as f32); @@ -319,7 +386,7 @@ fn main() -> Result<(), impl Error> { // NOTE: This teapot was meant for OpenGL where the origin is at the lower left // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. let aspect_ratio = - swapchain.image_extent()[0] as f32 / swapchain.image_extent()[1] as f32; + rcx.swapchain.image_extent()[0] as f32 / rcx.swapchain.image_extent()[1] as f32; let proj = Mat4::perspective_rh_gl( std::f32::consts::FRAC_PI_2, @@ -340,38 +407,38 @@ fn main() -> Result<(), impl Error> { proj: proj.to_cols_array_2d(), }; - let subbuffer = uniform_buffer.allocate_sized().unwrap(); - *subbuffer.write().unwrap() = uniform_data; + let buffer = self.uniform_buffer_allocator.allocate_sized().unwrap(); + *buffer.write().unwrap() = uniform_data; - subbuffer + buffer }; - let layout = &pipeline.layout().set_layouts()[0]; - let set = DescriptorSet::new( - descriptor_set_allocator.clone(), + let layout = &rcx.pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), layout.clone(), - [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)], + [WriteDescriptorSet::buffer(0, uniform_buffer)], [], ) .unwrap(); let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -388,81 +455,86 @@ fn main() -> Result<(), impl Error> { Some(1f32.into()), ], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, - set, + descriptor_set, ) .unwrap() - .bind_vertex_buffers(0, (vertex_buffer.clone(), normals_buffer.clone())) + .bind_vertex_buffers(0, (self.vertex_buffer.clone(), self.normals_buffer.clone())) .unwrap() - .bind_index_buffer(index_buffer.clone()) + .bind_index_buffer(self.index_buffer.clone()) .unwrap(); unsafe { builder - .draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0) + .draw_indexed(self.index_buffer.len() as u32, 1, 0, 0, 0) .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( - memory_allocator: Arc, - vs: EntryPoint, - fs: EntryPoint, + window_size: PhysicalSize, images: &[Arc], - render_pass: Arc, -) -> (Arc, Vec>) { - let device = memory_allocator.device().clone(); - let extent = images[0].extent(); + render_pass: &Arc, + memory_allocator: &Arc, + vs: &EntryPoint, + fs: &EntryPoint, +) -> (Vec>, Arc) { + let device = memory_allocator.device(); let depth_buffer = ImageView::new_default( Image::new( - memory_allocator, + memory_allocator.clone(), ImageCreateInfo { image_type: ImageType::Dim2d, format: Format::D16_UNORM, @@ -480,6 +552,7 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { @@ -497,11 +570,11 @@ fn window_size_dependent_setup( // https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport let pipeline = { let vertex_input_state = [Position::per_vertex(), Normal::per_vertex()] - .definition(&vs) + .definition(vs) .unwrap(); let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), + PipelineShaderStageCreateInfo::new(vs.clone()), + PipelineShaderStageCreateInfo::new(fs.clone()), ]; let layout = PipelineLayout::new( device.clone(), @@ -510,10 +583,10 @@ fn window_size_dependent_setup( .unwrap(), ) .unwrap(); - let subpass = Subpass::from(render_pass, 0).unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device, + device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -522,7 +595,7 @@ fn window_size_dependent_setup( viewport_state: Some(ViewportState { viewports: [Viewport { offset: [0.0, 0.0], - extent: [extent[0] as f32, extent[1] as f32], + extent: window_size.into(), depth_range: 0.0..=1.0, }] .into_iter() @@ -546,7 +619,7 @@ fn window_size_dependent_setup( .unwrap() }; - (pipeline, framebuffers) + (framebuffers, pipeline) } mod vs { diff --git a/examples/tessellation/main.rs b/examples/tessellation/main.rs index db7296b054..1c0e3fe110 100644 --- a/examples/tessellation/main.rs +++ b/examples/tessellation/main.rs @@ -14,14 +14,14 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, - QueueCreateInfo, QueueFlags, + Queue, QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -48,113 +48,43 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; -mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 position; +fn main() -> Result<(), impl Error> { + let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", - } + event_loop.run_app(&mut app) } -mod tcs { - vulkano_shaders::shader! { - ty: "tess_ctrl", - src: r" - #version 450 - - // A value of 3 means a patch consists of a single triangle. - layout(vertices = 3) out; - - void main(void) { - // Save the position of the patch, so the TES can access it. We could define our - // own output variables for this, but `gl_out` is handily provided. - gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; - - // Many triangles are generated in the center. - gl_TessLevelInner[0] = 10; - // No triangles are generated for this edge. - gl_TessLevelOuter[0] = 1; - // Many triangles are generated for this edge. - gl_TessLevelOuter[1] = 10; - // Many triangles are generated for this edge. - gl_TessLevelOuter[2] = 10; - - // These are only used when TES uses `layout(quads)`. - // gl_TessLevelInner[1] = ...; - // gl_TessLevelOuter[3] = ...; - } - ", - } +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + rcx: Option, } -// There is a stage in between TCS and TES called Primitive Generation (PG). Shaders cannot be -// defined for it. It takes `gl_TessLevelInner` and `gl_TessLevelOuter` and uses them to generate -// positions within the patch and pass them to TES via `gl_TessCoord`. -// -// When TES uses `layout(triangles)` then `gl_TessCoord` is in Barycentric coordinates. If -// `layout(quads)` is used then `gl_TessCoord` is in Cartesian coordinates. Barycentric coordinates -// are of the form (x, y, z) where x + y + z = 1 and the values x, y and z represent the distance -// from a vertex of the triangle. -// https://mathworld.wolfram.com/BarycentricCoordinates.html - -mod tes { - vulkano_shaders::shader! { - ty: "tess_eval", - src: r" - #version 450 - - layout(triangles, equal_spacing, cw) in; - - void main(void) { - // Retrieve the vertex positions set by the TCS. - vec4 vert_x = gl_in[0].gl_Position; - vec4 vert_y = gl_in[1].gl_Position; - vec4 vert_z = gl_in[2].gl_Position; - - // Convert `gl_TessCoord` from Barycentric coordinates to Cartesian coordinates. - gl_Position = vec4( - gl_TessCoord.x * vert_x.x + gl_TessCoord.y * vert_y.x + gl_TessCoord.z * vert_z.x, - gl_TessCoord.x * vert_x.y + gl_TessCoord.y * vert_y.y + gl_TessCoord.z * vert_z.y, - gl_TessCoord.x * vert_x.z + gl_TessCoord.y * vert_y.z + gl_TessCoord.z * vert_z.z, - 1.0 - ); - } - ", - } -} - -mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 - - layout(location = 0) out vec4 f_color; - - void main() { - f_color = vec4(1.0, 1.0, 1.0, 1.0); - } - ", - } +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, } -fn main() -> Result<(), impl Error> { - let event_loop = EventLoop::new().unwrap(); - +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -169,7 +99,7 @@ fn main() -> Result<(), impl Error> { khr_swapchain: true, ..DeviceExtensions::empty() }; - let features = DeviceFeatures { + let device_features = DeviceFeatures { tessellation_shader: true, fill_mode_non_solid: true, ..DeviceFeatures::empty() @@ -178,14 +108,14 @@ fn main() -> Result<(), impl Error> { .enumerate_physical_devices() .unwrap() .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter(|p| p.supported_features().contains(&features)) + .filter(|p| p.supported_features().contains(&device_features)) .filter_map(|p| { p.queue_family_properties() .iter() .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -213,81 +143,46 @@ fn main() -> Result<(), impl Error> { ..Default::default() }], enabled_extensions: device_extensions, - enabled_features: features, + enabled_features: device_features, ..Default::default() }, ) .unwrap(); - let queue = queues.next().unwrap(); - - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; + let queue = queues.next().unwrap(); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertices = [ - Vertex { + MyVertex { position: [-0.5, -0.25], }, - Vertex { + MyVertex { position: [0.0, 0.5], }, - Vertex { + MyVertex { position: [0.25, -0.1], }, - Vertex { + MyVertex { position: [0.9, 0.9], }, - Vertex { + MyVertex { position: [0.9, 0.8], }, - Vertex { + MyVertex { position: [0.8, 0.8], }, - Vertex { + MyVertex { position: [-0.9, 0.9], }, - Vertex { + MyVertex { position: [-0.7, 0.6], }, - Vertex { + MyVertex { position: [-0.5, 0.9], }, ]; @@ -306,8 +201,56 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window.inner_size().into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + let render_pass = vulkano::single_pass_renderpass!( - device.clone(), + self.device.clone(), attachments: { color: { format: swapchain.image_format(), @@ -323,24 +266,26 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let tcs = tcs::load(device.clone()) + let tcs = tcs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let tes = tes::load(device.clone()) + let tes = tes::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(tcs), @@ -348,16 +293,16 @@ fn main() -> Result<(), impl Error> { PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -391,82 +336,82 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let mut recreate_swapchain = false; - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -480,71 +425,80 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { @@ -556,3 +510,100 @@ fn window_size_dependent_setup( }) .collect::>() } + +mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 position; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } +} + +mod tcs { + vulkano_shaders::shader! { + ty: "tess_ctrl", + src: r" + #version 450 + + // A value of 3 means a patch consists of a single triangle. + layout(vertices = 3) out; + + void main(void) { + // Save the position of the patch, so the TES can access it. We could define our + // own output variables for this, but `gl_out` is handily provided. + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + + // Many triangles are generated in the center. + gl_TessLevelInner[0] = 10; + // No triangles are generated for this edge. + gl_TessLevelOuter[0] = 1; + // Many triangles are generated for this edge. + gl_TessLevelOuter[1] = 10; + // Many triangles are generated for this edge. + gl_TessLevelOuter[2] = 10; + + // These are only used when TES uses `layout(quads)`. + // gl_TessLevelInner[1] = ...; + // gl_TessLevelOuter[3] = ...; + } + ", + } +} + +// There is a stage in between TCS and TES called Primitive Generation (PG). Shaders cannot be +// defined for it. It takes `gl_TessLevelInner` and `gl_TessLevelOuter` and uses them to generate +// positions within the patch and pass them to TES via `gl_TessCoord`. +// +// When TES uses `layout(triangles)` then `gl_TessCoord` is in Barycentric coordinates. If +// `layout(quads)` is used then `gl_TessCoord` is in Cartesian coordinates. Barycentric coordinates +// are of the form (x, y, z) where x + y + z = 1 and the values x, y and z represent the distance +// from a vertex of the triangle. +// https://mathworld.wolfram.com/BarycentricCoordinates.html + +mod tes { + vulkano_shaders::shader! { + ty: "tess_eval", + src: r" + #version 450 + + layout(triangles, equal_spacing, cw) in; + + void main(void) { + // Retrieve the vertex positions set by the TCS. + vec4 v1 = gl_in[0].gl_Position; + vec4 v2 = gl_in[1].gl_Position; + vec4 v3 = gl_in[2].gl_Position; + + // Convert `gl_TessCoord` from Barycentric coordinates to Cartesian coordinates. + gl_Position = vec4( + gl_TessCoord.x * v1.x + gl_TessCoord.y * v2.x + gl_TessCoord.z * v3.x, + gl_TessCoord.x * v1.y + gl_TessCoord.y * v2.y + gl_TessCoord.z * v3.y, + gl_TessCoord.x * v1.z + gl_TessCoord.y * v2.z + gl_TessCoord.z * v3.z, + 1.0 + ); + } + ", + } +} + +mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + + layout(location = 0) out vec4 f_color; + + void main() { + f_color = vec4(1.0, 1.0, 1.0, 1.0); + } + ", + } +} diff --git a/examples/texture-array/main.rs b/examples/texture-array/main.rs index 6356332150..ad553ffe12 100644 --- a/examples/texture-array/main.rs +++ b/examples/texture-array/main.rs @@ -1,6 +1,6 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, @@ -9,8 +9,8 @@ use vulkano::{ allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags, }, format::Format, image::{ @@ -42,9 +42,10 @@ use vulkano::{ DeviceSize, Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { @@ -54,9 +55,39 @@ fn main() -> Result<(), impl Error> { // uniform sampler2D array_of_textures[42]; let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + descriptor_set_allocator: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + texture: Arc, + sampler: Arc, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + descriptor_set: Arc, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); let instance = Instance::new( library, InstanceCreateInfo { @@ -81,7 +112,7 @@ fn main() -> Result<(), impl Error> { .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) .map(|i| (p, i as u32)) }) @@ -113,61 +144,30 @@ fn main() -> Result<(), impl Error> { }, ) .unwrap(); - let queue = queues.next().unwrap(); - - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); - let (mut swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let image_format = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; - - Swapchain::new( - device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; + let queue = queues.next().unwrap(); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); let vertices = [ - Vertex { + MyVertex { position: [-0.2, -0.5], }, - Vertex { + MyVertex { position: [-0.5, 0.8], }, - Vertex { + MyVertex { position: [0.4, -0.5], }, - Vertex { + MyVertex { position: [0.5, 0.2], }, ]; @@ -186,32 +186,6 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); - let render_pass = vulkano::single_pass_renderpass!( - device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - let mut uploads = RecordingCommandBuffer::new( command_buffer_allocator.clone(), queue.queue_family_index(), @@ -292,31 +266,103 @@ fn main() -> Result<(), impl Error> { let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap(); + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + let pipeline = { - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -343,97 +389,95 @@ fn main() -> Result<(), impl Error> { .unwrap() }; + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + let layout = &pipeline.layout().set_layouts()[0]; - let set = DescriptorSet::new( - descriptor_set_allocator, + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), layout.clone(), [ - WriteDescriptorSet::sampler(0, sampler), - WriteDescriptorSet::image_view(1, texture), + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view(1, self.texture.clone()), ], [], ) .unwrap(); - let mut viewport = Viewport { - offset: [0.0, 0.0], - extent: [0.0, 0.0], - depth_range: 0.0..=1.0, - }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - - let mut recreate_swapchain = false; - let mut previous_frame_end = Some( - uploads - .end() - .unwrap() - .execute(queue.clone()) - .unwrap() - .boxed(), - ); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - let image_extent: [u32; 2] = window.inner_size().into(); + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( - &new_images, - render_pass.clone(), - &mut viewport, - ); - recreate_swapchain = false; + rcx.swapchain = new_swapchain; + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.viewport.extent = window_size.into(); + rcx.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -447,78 +491,87 @@ fn main() -> Result<(), impl Error> { RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, Default::default(), ) .unwrap() - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, - pipeline.layout().clone(), + rcx.pipeline.layout().clone(), 0, - set.clone(), + rcx.descriptor_set.clone(), ) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { - builder.draw(vertex_buffer.len() as u32, 3, 0, 0).unwrap(); + builder.draw(self.vertex_buffer.len() as u32, 3, 0, 0).unwrap(); } builder.end_render_pass(Default::default()).unwrap(); let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/examples/triangle-util/main.rs b/examples/triangle-util/main.rs index 731209ead1..0cab3f355d 100644 --- a/examples/triangle-util/main.rs +++ b/examples/triangle-util/main.rs @@ -9,7 +9,7 @@ use std::{error::Error, sync::Arc, time::Duration}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, @@ -38,17 +38,40 @@ use vulkano_util::{ window::VulkanoWindows, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::WindowId, }; fn main() -> Result<(), impl Error> { - let context = VulkanoContext::new(VulkanoConfig::default()); let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + + event_loop.run_app(&mut app) +} + +struct App { + context: VulkanoContext, + windows: VulkanoWindows, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + rcx: Option, +} + +struct RenderContext { + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, +} + +impl App { + fn new(_event_loop: &EventLoop<()>) -> Self { + let context = VulkanoContext::new(VulkanoConfig::default()); + // Manages any windows and their rendering. - let mut windows_manager = VulkanoWindows::default(); - windows_manager.create_window(&event_loop, &context, &Default::default(), |_| {}); - let window_renderer = windows_manager.get_primary_renderer_mut().unwrap(); + let windows = VulkanoWindows::default(); // Some little debug infos. println!( @@ -57,24 +80,23 @@ fn main() -> Result<(), impl Error> { context.device().physical_device().properties().device_type, ); - // We now create a buffer that will store the shape of our triangle. We use `#[repr(C)]` here - // to force rustc to use a defined layout for our data, as the default representation has *no - // guarantees*. - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools + // underneath and provides a safe interface for them. + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + context.device().clone(), + Default::default(), + )); + // We now create a buffer that will store the shape of our triangle. let vertices = [ - Vertex { + MyVertex { position: [-0.5, -0.25], }, - Vertex { + MyVertex { position: [0.0, 0.5], }, - Vertex { + MyVertex { position: [0.25, -0.1], }, ]; @@ -93,6 +115,26 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + App { + context, + windows, + command_buffer_allocator, + vertex_buffer, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + if let Some(primary_window_id) = self.windows.primary_window_id() { + self.windows.remove_renderer(primary_window_id); + } + + self.windows.create_window(event_loop, &self.context, &Default::default(), |_| {}); + let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); + let window_size = window_renderer.window().inner_size(); + // The next step is to create the shaders. // // The raw shader creation API provided by the vulkano library is unsafe for various reasons, @@ -137,15 +179,11 @@ fn main() -> Result<(), impl Error> { } } - // At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL - // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this - // manually. - // The next step is to create a *render pass*, which is an object that describes where the // output of the graphics pipeline will go. It describes the layout of the images where the // colors, depth and/or stencil information will be written. let render_pass = vulkano::single_pass_renderpass!( - context.device().clone(), + self.context.device().clone(), attachments: { // `color` is a custom name we give to the first and only attachment. color: { @@ -176,6 +214,16 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + // The render pass we created above only describes the layout of our framebuffers. Before we + // can draw we also need to create the actual framebuffers. + // + // Since we need to draw to multiple images, we are going to create a different framebuffer for + // each image. + let framebuffers = window_size_dependent_setup( + window_renderer.swapchain_image_views(), + &render_pass, + ); + // Before we draw, we have to create what is called a **pipeline**. A pipeline describes how // a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains // many settings for customization, all baked into a single object. For drawing, we create @@ -186,18 +234,18 @@ fn main() -> Result<(), impl Error> { // // A Vulkan shader can in theory contain multiple entry points, so we have to specify which // one. - let vs = vs::load(context.device().clone()) + let vs = vs::load(self.context.device().clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(context.device().clone()) + let fs = fs::load(self.context.device().clone()) .unwrap() .entry_point("main") .unwrap(); // Automatically generate a vertex input state from the vertex shader's input interface, // that takes a single vertex buffer containing `Vertex` structs. - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); // Make a list of the shader stages that the pipeline will have. let stages = [ @@ -215,13 +263,13 @@ fn main() -> Result<(), impl Error> { // layout. Thus, it is a good idea to design shaders so that many pipelines have // common resource locations, which allows them to share pipeline layouts. let layout = PipelineLayout::new( - context.device().clone(), + self.context.device().clone(), // Since we only have one pipeline in this example, and thus one pipeline layout, // we automatically generate the creation info for it from the resources used in the // shaders. In a real application, you would specify this information manually so that // you can re-use one layout in multiple pipelines. PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(context.device().clone()) + .into_pipeline_layout_create_info(self.context.device().clone()) .unwrap(), ) .unwrap(); @@ -232,7 +280,7 @@ fn main() -> Result<(), impl Error> { // Finally, create the pipeline. GraphicsPipeline::new( - context.device().clone(), + self.context.device().clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -270,62 +318,46 @@ fn main() -> Result<(), impl Error> { // Dynamic viewports allow us to recreate just the viewport when the window is resized. // Otherwise we would have to recreate the whole pipeline. - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - // The render pass we created above only describes the layout of our framebuffers. Before we - // can draw we also need to create the actual framebuffers. - // - // Since we need to draw to multiple images, we are going to create a different framebuffer for - // each image. - let mut framebuffers = window_size_dependent_setup( - window_renderer.swapchain_image_views(), - render_pass.clone(), - &mut viewport, - ); - - // Before we can start creating and recording command buffers, we need a way of allocating - // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools - // underneath and provides a safe interface for them. - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - context.device().clone(), - Default::default(), - )); - - // Initialization is finally finished! - - // In the loop below we are going to submit commands to the GPU. Submitting a command produces + // In the `window_event` handler below we are going to submit commands to the GPU. Submitting a command produces // an object that implements the `GpuFuture` trait, which holds the resources for as long as // they are in use by the GPU. - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + self.rcx = Some(RenderContext { + render_pass, + framebuffers, + pipeline, + viewport, + }); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { + WindowEvent::Resized(_) => { window_renderer.resize(); } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { + WindowEvent::RedrawRequested => { + let window_size = window_renderer.window().inner_size(); + // Do not draw the frame when the screen size is zero. On Windows, this can // occur when minimizing the application. - let image_extent: [u32; 2] = window_renderer.window().inner_size().into(); - - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } @@ -336,10 +368,9 @@ fn main() -> Result<(), impl Error> { // on the window size. In this example that // includes the swapchain, the framebuffers // and the dynamic state viewport. - framebuffers = window_size_dependent_setup( + rcx.framebuffers = window_size_dependent_setup( swapchain_images, - render_pass.clone(), - &mut viewport, + &rcx.render_pass, ); }) .unwrap(); @@ -354,8 +385,8 @@ fn main() -> Result<(), impl Error> { // Note that we have to pass a queue family when we create the command buffer. The // command buffer will only be executable on that given queue family. let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - context.graphics_queue().queue_family_index(), + self.command_buffer_allocator.clone(), + self.context.graphics_queue().queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -377,7 +408,7 @@ fn main() -> Result<(), impl Error> { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[window_renderer.image_index() as usize].clone(), + rcx.framebuffers[window_renderer.image_index() as usize].clone(), ) }, SubpassBeginInfo { @@ -392,17 +423,17 @@ fn main() -> Result<(), impl Error> { // We are now inside the first subpass of the render pass. // // TODO: Document state setting and how it affects subsequent draw commands. - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { builder // We add a draw command. - .draw(vertex_buffer.len() as u32, 1, 0, 0) + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) .unwrap(); } @@ -416,7 +447,7 @@ fn main() -> Result<(), impl Error> { let command_buffer = builder.end().unwrap(); let future = previous_frame_end - .then_execute(context.graphics_queue().clone(), command_buffer) + .then_execute(self.context.graphics_queue().clone(), command_buffer) .unwrap() .boxed(); @@ -430,21 +461,30 @@ fn main() -> Result<(), impl Error> { // that draws the triangle. window_renderer.present(future, false); } - Event::AboutToWait => window_renderer.window().request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); + window_renderer.window().request_redraw(); + } +} + +// We use `#[repr(C)]` here to force rustc to use a defined layout for our data, as the default +// representation has *no guarantees*. +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( swapchain_images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = swapchain_images[0].image().extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - swapchain_images .iter() .map(|swapchain_image| { diff --git a/examples/triangle-v1_3/main.rs b/examples/triangle-v1_3/main.rs index 2936a1ba36..a2177d9bdd 100644 --- a/examples/triangle-v1_3/main.rs +++ b/examples/triangle-v1_3/main.rs @@ -14,14 +14,14 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderingAttachmentInfo, RenderingInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, - QueueCreateInfo, QueueFlags, + Queue, QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -48,14 +48,40 @@ use vulkano::{ Validated, Version, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + attachment_image_views: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); // The first step of any Vulkan program is to create an instance. @@ -65,7 +91,7 @@ fn main() -> Result<(), impl Error> { // All the window-drawing functionalities are part of non-core extensions that we need to // enable manually. To do so, we ask `Surface` for the list of extensions required to draw to // a window. - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); // Now creating the instance. let instance = Instance::new( @@ -124,7 +150,7 @@ fn main() -> Result<(), impl Error> { // a window surface, as we do in this example, we also need to check that // queues in this queue family are capable of presenting images to the surface. q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) // The code here searches for the first queue family that is suitable. If none is // found, `None` is returned to `filter_map`, which disqualifies this physical @@ -208,36 +234,88 @@ fn main() -> Result<(), impl Error> { // iterator. let queue = queues.next().unwrap(); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools + // underneath and provides a safe interface for them. + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // We now create a buffer that will store the shape of our triangle. + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + }, + MyVertex { + position: [0.0, 0.5], + }, + MyVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcx: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { // The objective of this example is to draw a triangle on a window. To do so, we first need to // create the window. We use the `WindowBuilder` from the `winit` crate to do that here. // // Before we can render to a window, we must first create a `vulkano::swapchain::Surface` // object from it, which represents the drawable surface of a window. For that we must wrap the // `winit::window::Window` in an `Arc`. - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); // Before we can draw on the surface, we have to create what is called a swapchain. Creating a // swapchain allocates the color buffers that will contain the image that will ultimately be // visible on the screen. These images are returned alongside the swapchain. - let (mut swapchain, images) = { + let (swapchain, images) = { // Querying the capabilities of the surface. When we create the swapchain we can only pass // values that are allowed by the capabilities. - let surface_capabilities = device + let surface_capabilities = self + .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); // Choosing the internal format that the images will have. - let image_format = device + let (image_format, _) = self + .device .physical_device() .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; + .unwrap()[0]; // Please take a look at the docs for the meaning of the parameters we didn't mention. Swapchain::new( - device.clone(), + self.device.clone(), surface, SwapchainCreateInfo { // Some drivers report an `min_image_count` of 1, but fullscreen mode requires at @@ -260,7 +338,7 @@ fn main() -> Result<(), impl Error> { // // Both of these cases need the swapchain to use the window size, so we just // use that. - image_extent: window.inner_size().into(), + image_extent: window_size.into(), image_usage: ImageUsage::COLOR_ATTACHMENT, @@ -278,43 +356,12 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - // We now create a buffer that will store the shape of our triangle. We use `#[repr(C)]` here - // to force rustc to use a defined layout for our data, as the default representation has *no - // guarantees*. - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } - - let vertices = [ - Vertex { - position: [-0.5, -0.25], - }, - Vertex { - position: [0.0, 0.5], - }, - Vertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); + // When creating the swapchain, we only created plain images. To use them as an attachment for + // rendering, we must wrap then in an image view. + // + // Since we need to draw to multiple images, we are going to create a different image view for + // each image. + let attachment_image_views = window_size_dependent_setup(&images); // The next step is to create the shaders. // @@ -360,10 +407,6 @@ fn main() -> Result<(), impl Error> { } } - // At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL - // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this - // manually. - // Before we draw, we have to create what is called a **pipeline**. A pipeline describes how // a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains // many settings for customization, all baked into a single object. For drawing, we create @@ -374,18 +417,18 @@ fn main() -> Result<(), impl Error> { // // A Vulkan shader can in theory contain multiple entry points, so we have to specify which // one. - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); // Automatically generate a vertex input state from the vertex shader's input interface, // that takes a single vertex buffer containing `Vertex` structs. - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); // Make a list of the shader stages that the pipeline will have. let stages = [ @@ -403,13 +446,13 @@ fn main() -> Result<(), impl Error> { // layout. Thus, it is a good idea to design shaders so that many pipelines have // common resource locations, which allows them to share pipeline layouts. let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), // Since we only have one pipeline in this example, and thus one pipeline layout, // we automatically generate the creation info for it from the resources used in the // shaders. In a real application, you would specify this information manually so that // you can re-use one layout in multiple pipelines. PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); @@ -427,7 +470,7 @@ fn main() -> Result<(), impl Error> { // Finally, create the pipeline. GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -465,29 +508,12 @@ fn main() -> Result<(), impl Error> { // Dynamic viewports allow us to recreate just the viewport when the window is resized. // Otherwise we would have to recreate the whole pipeline. - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - // When creating the swapchain, we only created plain images. To use them as an attachment for - // rendering, we must wrap then in an image view. - // - // Since we need to draw to multiple images, we are going to create a different image view for - // each image. - let mut attachment_image_views = window_size_dependent_setup(&images, &mut viewport); - - // Before we can start creating and recording command buffers, we need a way of allocating - // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools - // underneath and provides a safe interface for them. - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // Initialization is finally finished! - // In some situations, the swapchain will become invalid by itself. This includes for example // when the window is resized (as the images of the swapchain will no longer match the // window's) or, on Android, when the application went to the background and goes back to the @@ -497,7 +523,7 @@ fn main() -> Result<(), impl Error> { // Rendering to an image of that swapchain will not produce any error, but may or may not work. // To continue rendering, we need to recreate the swapchain by creating a new swapchain. Here, // we remember that we need to do this for the next loop iteration. - let mut recreate_swapchain = false; + let recreate_swapchain = false; // In the loop below we are going to submit commands to the GPU. Submitting a command produces // an object that implements the `GpuFuture` trait, which holds the resources for as long as @@ -505,33 +531,40 @@ fn main() -> Result<(), impl Error> { // // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid // that, we store the submission of the previous frame here. - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + attachment_image_views, + pipeline, + viewport, + recreate_swapchain, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); + // Do not draw the frame when the screen size is zero. On Windows, this can // occur when minimizing the application. - let image_extent: [u32; 2] = window.inner_size().into(); - - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } @@ -539,27 +572,29 @@ fn main() -> Result<(), impl Error> { // will keep accumulating and you will eventually reach an out of memory error. // Calling this function polls various fences in order to determine what the GPU // has already processed, and frees the resources that are no longer needed. - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); // Whenever the window resizes we need to recreate everything dependent on the // window size. In this example that includes the swapchain, the framebuffers and // the dynamic state viewport. - if recreate_swapchain { - let (new_swapchain, new_images) = swapchain + if rcx.recreate_swapchain { + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; + rcx.swapchain = new_swapchain; // Now that we have new swapchain images, we must create new image views from // them as well. - attachment_image_views = - window_size_dependent_setup(&new_images, &mut viewport); + rcx.attachment_image_views = window_size_dependent_setup(&new_images); + + rcx.viewport.extent = window_size.into(); - recreate_swapchain = false; + rcx.recreate_swapchain = false; } // Before we can draw on the output, we have to *acquire* an image from the @@ -570,10 +605,10 @@ fn main() -> Result<(), impl Error> { // This function can block if no image is available. The parameter is an optional // timeout after which the function call will return an error. let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), @@ -584,7 +619,7 @@ fn main() -> Result<(), impl Error> { // drivers this can be when the window resizes, but it may not cause the swapchain // to become out of date. if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } // In order to draw, we have to record a *command buffer*. The command buffer object @@ -597,8 +632,8 @@ fn main() -> Result<(), impl Error> { // Note that we have to pass a queue family when we create the command buffer. The // command buffer will only be executable on that given queue family. let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -630,7 +665,7 @@ fn main() -> Result<(), impl Error> { ..RenderingAttachmentInfo::image_view( // We specify image view corresponding to the currently acquired // swapchain image, to use for this attachment. - attachment_image_views[image_index as usize].clone(), + rcx.attachment_image_views[image_index as usize].clone(), ) })], ..Default::default() @@ -639,17 +674,17 @@ fn main() -> Result<(), impl Error> { // We are now inside the first subpass of the render pass. // // TODO: Document state setting and how it affects subsequent draw commands. - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { builder // We add a draw command. - .draw(vertex_buffer.len() as u32, 1, 0, 0) + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) .unwrap(); } @@ -661,11 +696,12 @@ fn main() -> Result<(), impl Error> { // Finish recording the command buffer by calling `end`. let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() // The color output is now expected to contain our triangle. But in order to // show it on the screen, we have to *present* the image by calling @@ -676,39 +712,46 @@ fn main() -> Result<(), impl Error> { // only be presented once the GPU has finished executing the command buffer // that draws the triangle. .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { println!("failed to flush future: {e}"); - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } } -/// This function is called once during initialization, then again whenever the window is resized. -fn window_size_dependent_setup( - images: &[Arc], - viewport: &mut Viewport, -) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; +// We use `#[repr(C)]` here to force rustc to use a defined layout for our data, as the default +// representation has *no guarantees*. +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], +} +/// This function is called once during initialization, then again whenever the window is resized. +fn window_size_dependent_setup(images: &[Arc]) -> Vec> { images .iter() .map(|image| ImageView::new_default(image.clone()).unwrap()) diff --git a/examples/triangle/main.rs b/examples/triangle/main.rs index e1f51f0799..4972ab0ed5 100644 --- a/examples/triangle/main.rs +++ b/examples/triangle/main.rs @@ -9,15 +9,15 @@ use std::{error::Error, sync::Arc}; use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, }, device::{ - physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, - QueueFlags, + physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, + QueueCreateInfo, QueueFlags }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -43,14 +43,41 @@ use vulkano::{ Validated, VulkanError, VulkanLibrary, }; use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, }; fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); + let mut app = App::new(&event_loop); + event_loop.run_app(&mut app) +} + +struct App { + instance: Arc, + device: Arc, + queue: Arc, + command_buffer_allocator: Arc, + vertex_buffer: Subbuffer<[MyVertex]>, + rcx: Option, +} + +struct RenderContext { + window: Arc, + swapchain: Arc, + render_pass: Arc, + framebuffers: Vec>, + pipeline: Arc, + viewport: Viewport, + recreate_swapchain: bool, + previous_frame_end: Option>, +} + +impl App { + fn new(event_loop: &EventLoop<()>) -> Self { let library = VulkanLibrary::new().unwrap(); // The first step of any Vulkan program is to create an instance. @@ -60,7 +87,7 @@ fn main() -> Result<(), impl Error> { // All the window-drawing functionalities are part of non-core extensions that we need to // enable manually. To do so, we ask `Surface` for the list of extensions required to draw to // a window. - let required_extensions = Surface::required_extensions(&event_loop).unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); // Now creating the instance. let instance = Instance::new( @@ -114,7 +141,7 @@ fn main() -> Result<(), impl Error> { // a window surface, as we do in this example, we also need to check that // queues in this queue family are capable of presenting images to the surface. q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, &event_loop).unwrap() + && p.presentation_support(i as u32, event_loop).unwrap() }) // The code here searches for the first queue family that is suitable. If none is // found, `None` is returned to `filter_map`, which disqualifies this physical @@ -178,36 +205,90 @@ fn main() -> Result<(), impl Error> { // iterator. let queue = queues.next().unwrap(); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools + // underneath and provides a safe interface for them. + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // We now create a buffer that will store the shape of our triangle. + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + }, + MyVertex { + position: [0.0, 0.5], + }, + MyVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + let rcx = None; + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcx, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { // The objective of this example is to draw a triangle on a window. To do so, we first need to // create the window. We use the `WindowBuilder` from the `winit` crate to do that here. // // Before we can render to a window, we must first create a `vulkano::swapchain::Surface` // object from it, which represents the drawable surface of a window. For that we must wrap the // `winit::window::Window` in an `Arc`. - let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); + let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); // Before we can draw on the surface, we have to create what is called a swapchain. Creating a // swapchain allocates the color buffers that will contain the image that will ultimately be // visible on the screen. These images are returned alongside the swapchain. - let (mut swapchain, images) = { + let (swapchain, images) = { // Querying the capabilities of the surface. When we create the swapchain we can only pass // values that are allowed by the capabilities. - let surface_capabilities = device + let surface_capabilities = self + .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); // Choosing the internal format that the images will have. - let image_format = device + let (image_format, _) = self + .device .physical_device() .surface_formats(&surface, Default::default()) - .unwrap()[0] - .0; + .unwrap()[0]; // Please take a look at the docs for the meaning of the parameters we didn't mention. Swapchain::new( - device.clone(), + self.device.clone(), surface, SwapchainCreateInfo { // Some drivers report an `min_image_count` of 1, but fullscreen mode requires at @@ -230,7 +311,7 @@ fn main() -> Result<(), impl Error> { // // Both of these cases need the swapchain to use the window size, so we just // use that. - image_extent: window.inner_size().into(), + image_extent: window_size.into(), image_usage: ImageUsage::COLOR_ATTACHMENT, @@ -248,44 +329,6 @@ fn main() -> Result<(), impl Error> { .unwrap() }; - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - // We now create a buffer that will store the shape of our triangle. We use `#[repr(C)]` here - // to force rustc to use a defined layout for our data, as the default representation has *no - // guarantees*. - #[derive(BufferContents, Vertex)] - #[repr(C)] - struct Vertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], - } - - let vertices = [ - Vertex { - position: [-0.5, -0.25], - }, - Vertex { - position: [0.0, 0.5], - }, - Vertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - // The next step is to create the shaders. // // The raw shader creation API provided by the vulkano library is unsafe for various reasons, @@ -330,15 +373,11 @@ fn main() -> Result<(), impl Error> { } } - // At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL - // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this - // manually. - // The next step is to create a *render pass*, which is an object that describes where the // output of the graphics pipeline will go. It describes the layout of the images where the // colors, depth and/or stencil information will be written. let render_pass = vulkano::single_pass_renderpass!( - device.clone(), + self.device.clone(), attachments: { // `color` is a custom name we give to the first and only attachment. color: { @@ -369,6 +408,13 @@ fn main() -> Result<(), impl Error> { ) .unwrap(); + // The render pass we created above only describes the layout of our framebuffers. Before we + // can draw we also need to create the actual framebuffers. + // + // Since we need to draw to multiple images, we are going to create a different framebuffer for + // each image. + let framebuffers = window_size_dependent_setup(&images, &render_pass); + // Before we draw, we have to create what is called a **pipeline**. A pipeline describes how // a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains // many settings for customization, all baked into a single object. For drawing, we create @@ -379,18 +425,18 @@ fn main() -> Result<(), impl Error> { // // A Vulkan shader can in theory contain multiple entry points, so we have to specify which // one. - let vs = vs::load(device.clone()) + let vs = vs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(device.clone()) + let fs = fs::load(self.device.clone()) .unwrap() .entry_point("main") .unwrap(); // Automatically generate a vertex input state from the vertex shader's input interface, // that takes a single vertex buffer containing `Vertex` structs. - let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); // Make a list of the shader stages that the pipeline will have. let stages = [ @@ -408,13 +454,13 @@ fn main() -> Result<(), impl Error> { // layout. Thus, it is a good idea to design shaders so that many pipelines have // common resource locations, which allows them to share pipeline layouts. let layout = PipelineLayout::new( - device.clone(), + self.device.clone(), // Since we only have one pipeline in this example, and thus one pipeline layout, // we automatically generate the creation info for it from the resources used in the // shaders. In a real application, you would specify this information manually so that // you can re-use one layout in multiple pipelines. PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) + .into_pipeline_layout_create_info(self.device.clone()) .unwrap(), ) .unwrap(); @@ -425,7 +471,7 @@ fn main() -> Result<(), impl Error> { // Finally, create the pipeline. GraphicsPipeline::new( - device.clone(), + self.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -463,29 +509,12 @@ fn main() -> Result<(), impl Error> { // Dynamic viewports allow us to recreate just the viewport when the window is resized. // Otherwise we would have to recreate the whole pipeline. - let mut viewport = Viewport { + let viewport = Viewport { offset: [0.0, 0.0], - extent: [0.0, 0.0], + extent: window_size.into(), depth_range: 0.0..=1.0, }; - // The render pass we created above only describes the layout of our framebuffers. Before we - // can draw we also need to create the actual framebuffers. - // - // Since we need to draw to multiple images, we are going to create a different framebuffer for - // each image. - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); - - // Before we can start creating and recording command buffers, we need a way of allocating - // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools - // underneath and provides a safe interface for them. - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // Initialization is finally finished! - // In some situations, the swapchain will become invalid by itself. This includes for example // when the window is resized (as the images of the swapchain will no longer match the // window's) or, on Android, when the application went to the background and goes back to the @@ -495,41 +524,49 @@ fn main() -> Result<(), impl Error> { // Rendering to an image of that swapchain will not produce any error, but may or may not work. // To continue rendering, we need to recreate the swapchain by creating a new swapchain. Here, // we remember that we need to do this for the next loop iteration. - let mut recreate_swapchain = false; + let recreate_swapchain = false; - // In the loop below we are going to submit commands to the GPU. Submitting a command produces + // In the `window_event` handler below we are going to submit commands to the GPU. Submitting a command produces // an object that implements the `GpuFuture` trait, which holds the resources for as long as // they are in use by the GPU. // // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid // that, we store the submission of the previous frame here. - let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain, + previous_frame_end, + }); + } - event_loop.run(move |event, elwt| { - elwt.set_control_flow(ControlFlow::Poll); + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let rcx = self.rcx.as_mut().unwrap(); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - elwt.exit(); + WindowEvent::CloseRequested => { + event_loop.exit(); } - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - recreate_swapchain = true; + WindowEvent::Resized(_) => { + rcx.recreate_swapchain = true; } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { + WindowEvent::RedrawRequested => { + let window_size = rcx.window.inner_size(); + // Do not draw the frame when the screen size is zero. On Windows, this can // occur when minimizing the application. - let image_extent: [u32; 2] = window.inner_size().into(); - - if image_extent.contains(&0) { + if window_size.width == 0 || window_size.height == 0 { return; } @@ -537,32 +574,33 @@ fn main() -> Result<(), impl Error> { // will keep accumulating and you will eventually reach an out of memory error. // Calling this function polls various fences in order to determine what the GPU // has already processed, and frees the resources that are no longer needed. - previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); // Whenever the window resizes we need to recreate everything dependent on the // window size. In this example that includes the swapchain, the framebuffers and // the dynamic state viewport. - if recreate_swapchain { + if rcx.recreate_swapchain { // Use the new dimensions of the window. - let (new_swapchain, new_images) = swapchain + let (new_swapchain, new_images) = rcx.swapchain .recreate(SwapchainCreateInfo { - image_extent, - ..swapchain.create_info() + image_extent: window_size.into(), + ..rcx.swapchain.create_info() }) .expect("failed to recreate swapchain"); - swapchain = new_swapchain; + rcx.swapchain = new_swapchain; // Because framebuffers contains a reference to the old swapchain, we need to // recreate framebuffers as well. - framebuffers = window_size_dependent_setup( + rcx.framebuffers = window_size_dependent_setup( &new_images, - render_pass.clone(), - &mut viewport, + &rcx.render_pass, ); - recreate_swapchain = false; + rcx.viewport.extent = window_size.into(); + + rcx.recreate_swapchain = false; } // Before we can draw on the output, we have to *acquire* an image from the @@ -573,10 +611,10 @@ fn main() -> Result<(), impl Error> { // This function can block if no image is available. The parameter is an optional // timeout after which the function call will return an error. let (image_index, suboptimal, acquire_future) = - match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { + match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; + rcx.recreate_swapchain = true; return; } Err(e) => panic!("failed to acquire next image: {e}"), @@ -587,7 +625,7 @@ fn main() -> Result<(), impl Error> { // drivers this can be when the window resizes, but it may not cause the swapchain // to become out of date. if suboptimal { - recreate_swapchain = true; + rcx.recreate_swapchain = true; } // In order to draw, we have to record a *command buffer*. The command buffer object @@ -600,8 +638,8 @@ fn main() -> Result<(), impl Error> { // Note that we have to pass a queue family when we create the command buffer. The // command buffer will only be executable on that given queue family. let mut builder = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), + self.command_buffer_allocator.clone(), + self.queue.queue_family_index(), CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, @@ -623,7 +661,7 @@ fn main() -> Result<(), impl Error> { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( - framebuffers[image_index as usize].clone(), + rcx.framebuffers[image_index as usize].clone(), ) }, SubpassBeginInfo { @@ -638,17 +676,17 @@ fn main() -> Result<(), impl Error> { // We are now inside the first subpass of the render pass. // // TODO: Document state setting and how it affects subsequent draw commands. - .set_viewport(0, [viewport.clone()].into_iter().collect()) + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) .unwrap() - .bind_pipeline_graphics(pipeline.clone()) + .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() - .bind_vertex_buffers(0, vertex_buffer.clone()) + .bind_vertex_buffers(0, self.vertex_buffer.clone()) .unwrap(); unsafe { builder // We add a draw command. - .draw(vertex_buffer.len() as u32, 1, 0, 0) + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) .unwrap(); } @@ -661,11 +699,12 @@ fn main() -> Result<(), impl Error> { // Finish recording the command buffer by calling `end`. let command_buffer = builder.end().unwrap(); - let future = previous_frame_end + let future = rcx + .previous_frame_end .take() .unwrap() .join(acquire_future) - .then_execute(queue.clone(), command_buffer) + .then_execute(self.queue.clone(), command_buffer) .unwrap() // The color output is now expected to contain our triangle. But in order to // show it on the screen, we have to *present* the image by calling @@ -676,18 +715,18 @@ fn main() -> Result<(), impl Error> { // only be presented once the GPU has finished executing the command buffer // that draws the triangle. .then_swapchain_present( - queue.clone(), - SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { - previous_frame_end = Some(future.boxed()); + rcx.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { - recreate_swapchain = true; - previous_frame_end = Some(sync::now(device.clone()).boxed()); + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { panic!("failed to flush future: {e}"); @@ -695,25 +734,35 @@ fn main() -> Result<(), impl Error> { } } } - Event::AboutToWait => window.request_redraw(), - _ => (), + _ => {} } - }) + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.rcx.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} + +// We use `#[repr(C)]` here to force rustc to use a defined layout for our data, as the default +// representation has *no guarantees*. +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], } /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], - render_pass: Arc, - viewport: &mut Viewport, + render_pass: &Arc, ) -> Vec> { - let extent = images[0].extent(); - viewport.extent = [extent[0] as f32, extent[1] as f32]; - images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); + Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { diff --git a/vulkano-taskgraph/src/command_buffer/mod.rs b/vulkano-taskgraph/src/command_buffer/mod.rs index ffeb09715c..380461b851 100644 --- a/vulkano-taskgraph/src/command_buffer/mod.rs +++ b/vulkano-taskgraph/src/command_buffer/mod.rs @@ -4,7 +4,7 @@ pub use self::commands::{clear::*, copy::*, dynamic_state::*, pipeline::*, sync::*}; use crate::{graph::ResourceMap, resource::DeathRow, Id}; use ash::vk; -use std::sync::Arc; +use std::{any::Any, sync::Arc}; use vulkano::{ buffer::Buffer, command_buffer::sys::RawRecordingCommandBuffer, @@ -53,6 +53,22 @@ impl<'a> RecordingCommandBuffer<'a> { pub fn as_raw(&mut self) -> &mut RawRecordingCommandBuffer { self.inner } + + /// Queues the destruction of the given `object` after the destruction of the command buffer. + #[inline] + pub fn destroy_object(&mut self, object: Arc) { + self.death_row.push(object); + } + + /// Queues the destruction of the given `objects` after the destruction of the command buffer. + #[inline] + pub fn destroy_objects( + &mut self, + objects: impl IntoIterator>, + ) { + self.death_row + .extend(objects.into_iter().map(|object| object as _)); + } } unsafe impl DeviceOwned for RecordingCommandBuffer<'_> { diff --git a/vulkano-util/src/context.rs b/vulkano-util/src/context.rs index 6cdfd46919..28dfbcab17 100644 --- a/vulkano-util/src/context.rs +++ b/vulkano-util/src/context.rs @@ -49,15 +49,9 @@ impl Default for VulkanoConfig { }; VulkanoConfig { instance_create_info: InstanceCreateInfo { - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, application_version: Version::V1_3, - enabled_extensions: InstanceExtensions { - #[cfg(target_os = "macos")] - khr_portability_enumeration: true, - ..InstanceExtensions::empty() - }, - ..Default::default() }, debug_create_info: None, diff --git a/vulkano-util/src/window.rs b/vulkano-util/src/window.rs index aaa863365b..5cb96927e2 100644 --- a/vulkano-util/src/window.rs +++ b/vulkano-util/src/window.rs @@ -8,6 +8,7 @@ use std::collections::hash_map::{Iter, IterMut}; use vulkano::swapchain::{PresentMode, SwapchainCreateInfo}; use winit::{ dpi::LogicalSize, + event_loop::ActiveEventLoop, window::{CursorGrabMode, WindowId}, }; @@ -21,19 +22,14 @@ use winit::{ /// context::{VulkanoConfig, VulkanoContext}, /// window::VulkanoWindows, /// }; -/// use winit::event_loop::EventLoop; /// -/// fn test() { -/// let context = VulkanoContext::new(VulkanoConfig::default()); -/// let event_loop = EventLoop::new().unwrap(); -/// let mut vulkano_windows = VulkanoWindows::default(); -/// let _id1 = -/// vulkano_windows.create_window(&event_loop, &context, &Default::default(), |_| {}); -/// let _id2 = -/// vulkano_windows.create_window(&event_loop, &context, &Default::default(), |_| {}); +/// # let event_loop = return; +/// let context = VulkanoContext::new(VulkanoConfig::default()); +/// let mut vulkano_windows = VulkanoWindows::default(); +/// let _id1 = vulkano_windows.create_window(event_loop, &context, &Default::default(), |_| {}); +/// let _id2 = vulkano_windows.create_window(event_loop, &context, &Default::default(), |_| {}); /// -/// // You should now have two windows. -/// } +/// // You should now have two windows. /// ``` #[derive(Default)] pub struct VulkanoWindows { @@ -44,25 +40,25 @@ pub struct VulkanoWindows { impl VulkanoWindows { /// Creates a winit window with [`VulkanoWindowRenderer`] based on the given /// [`WindowDescriptor`] input and swapchain creation modifications. - pub fn create_window( + pub fn create_window( &mut self, - event_loop: &winit::event_loop::EventLoopWindowTarget, + event_loop: &ActiveEventLoop, vulkano_context: &VulkanoContext, window_descriptor: &WindowDescriptor, swapchain_create_info_modify: fn(&mut SwapchainCreateInfo), ) -> WindowId { - let mut winit_window_builder = winit::window::WindowBuilder::new(); + let mut winit_window_attributes = winit::window::Window::default_attributes(); - winit_window_builder = match window_descriptor.mode { - WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some( + winit_window_attributes = match window_descriptor.mode { + WindowMode::BorderlessFullscreen => winit_window_attributes.with_fullscreen(Some( winit::window::Fullscreen::Borderless(event_loop.primary_monitor()), )), WindowMode::Fullscreen => { - winit_window_builder.with_fullscreen(Some(winit::window::Fullscreen::Exclusive( + winit_window_attributes.with_fullscreen(Some(winit::window::Fullscreen::Exclusive( get_best_videomode(&event_loop.primary_monitor().unwrap()), ))) } - WindowMode::SizedFullscreen => winit_window_builder.with_fullscreen(Some( + WindowMode::SizedFullscreen => winit_window_attributes.with_fullscreen(Some( winit::window::Fullscreen::Exclusive(get_fitting_videomode( &event_loop.primary_monitor().unwrap(), window_descriptor.width as u32, @@ -80,7 +76,7 @@ impl VulkanoWindows { if let Some(position) = position { if let Some(sf) = scale_factor_override { - winit_window_builder = winit_window_builder.with_position( + winit_window_attributes = winit_window_attributes.with_position( winit::dpi::LogicalPosition::new( position[0] as f64, position[1] as f64, @@ -88,18 +84,20 @@ impl VulkanoWindows { .to_physical::(*sf), ); } else { - winit_window_builder = - winit_window_builder.with_position(winit::dpi::LogicalPosition::new( + winit_window_attributes = winit_window_attributes.with_position( + winit::dpi::LogicalPosition::new( position[0] as f64, position[1] as f64, - )); + ), + ); } } + if let Some(sf) = scale_factor_override { - winit_window_builder + winit_window_attributes .with_inner_size(LogicalSize::new(*width, *height).to_physical::(*sf)) } else { - winit_window_builder.with_inner_size(LogicalSize::new(*width, *height)) + winit_window_attributes.with_inner_size(LogicalSize::new(*width, *height)) } } .with_resizable(window_descriptor.resizable) @@ -117,19 +115,20 @@ impl VulkanoWindows { height: constraints.max_height, }; - let winit_window_builder = + let winit_window_attributes = if constraints.max_width.is_finite() && constraints.max_height.is_finite() { - winit_window_builder + winit_window_attributes .with_min_inner_size(min_inner_size) .with_max_inner_size(max_inner_size) } else { - winit_window_builder.with_min_inner_size(min_inner_size) + winit_window_attributes.with_min_inner_size(min_inner_size) }; #[allow(unused_mut)] - let mut winit_window_builder = winit_window_builder.with_title(&window_descriptor.title); + let mut winit_window_attributes = + winit_window_attributes.with_title(&window_descriptor.title); - let winit_window = winit_window_builder.build(event_loop).unwrap(); + let winit_window = event_loop.create_window(winit_window_attributes).unwrap(); if window_descriptor.cursor_locked { match winit_window.set_cursor_grab(CursorGrabMode::Confined) { @@ -241,7 +240,7 @@ fn get_fitting_videomode( monitor: &winit::monitor::MonitorHandle, width: u32, height: u32, -) -> winit::monitor::VideoMode { +) -> winit::monitor::VideoModeHandle { let mut modes = monitor.video_modes().collect::>(); fn abs_diff(a: u32, b: u32) -> u32 { @@ -269,7 +268,7 @@ fn get_fitting_videomode( modes.first().unwrap().clone() } -fn get_best_videomode(monitor: &winit::monitor::MonitorHandle) -> winit::monitor::VideoMode { +fn get_best_videomode(monitor: &winit::monitor::MonitorHandle) -> winit::monitor::VideoModeHandle { let mut modes = monitor.video_modes().collect::>(); modes.sort_by(|a, b| { use std::cmp::Ordering::*; From 1bf38153d34a8739fc9452d9a74fc1e40a23116b Mon Sep 17 00:00:00 2001 From: marc0246 <40955683+marc0246@users.noreply.github.com> Date: Sat, 5 Oct 2024 21:48:37 +0200 Subject: [PATCH 2/2] fmt --- examples/async-update/main.rs | 889 +++++++++-------- examples/bloom/main.rs | 6 +- examples/buffer-allocator/main.rs | 472 ++++----- examples/clear-attachments/main.rs | 303 +++--- examples/deferred/main.rs | 315 +++--- examples/gl-interop/main.rs | 889 +++++++++-------- examples/image-self-copy-blit/main.rs | 709 ++++++------- examples/image/main.rs | 607 +++++------ examples/immutable-sampler/main.rs | 628 ++++++------ examples/indirect/main.rs | 624 ++++++------ examples/instancing/main.rs | 590 +++++------ .../fractal_compute_pipeline.rs | 7 +- examples/interactive-fractal/input.rs | 16 +- examples/interactive-fractal/main.rs | 308 +++--- examples/mesh-shader/main.rs | 567 +++++------ .../multi-window-game-of-life/game_of_life.rs | 13 +- examples/multi-window-game-of-life/main.rs | 24 +- .../multi-window-game-of-life/render_pass.rs | 8 +- examples/multi-window/main.rs | 513 +++++----- examples/occlusion-query/main.rs | 638 ++++++------ examples/push-descriptors/main.rs | 590 +++++------ examples/runtime-array/main.rs | 824 +++++++-------- examples/runtime-shader/main.rs | 473 ++++----- examples/simple-particles/main.rs | 735 +++++++------- examples/teapot/main.rs | 478 ++++----- examples/tessellation/main.rs | 539 +++++----- examples/texture-array/main.rs | 624 ++++++------ examples/triangle-util/main.rs | 524 +++++----- examples/triangle-v1_3/main.rs | 914 ++++++++--------- examples/triangle/main.rs | 941 +++++++++--------- 30 files changed, 7534 insertions(+), 7234 deletions(-) diff --git a/examples/async-update/main.rs b/examples/async-update/main.rs index 4b86896934..7669192c29 100644 --- a/examples/async-update/main.rs +++ b/examples/async-update/main.rs @@ -136,69 +136,69 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, graphics_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - // Since we are going to be updating the texture on a separate thread asynchronously from the - // execution of graphics commands, it would make sense to also do the transfer on a dedicated - // transfer queue, if such a queue family exists. That way, the graphics queue is not blocked - // during the transfers either and the two tasks are truly asynchronous. - // - // For this, we need to find the queue family with the fewest queue flags set, since if the - // queue family has more flags than `TRANSFER | SPARSE_BINDING`, that means it is not dedicated - // to transfer operations. - let transfer_family_index = physical_device - .queue_family_properties() - .iter() - .enumerate() - .filter(|(_, q)| { - q.queue_flags.intersects(QueueFlags::TRANSFER) - // Queue families dedicated to transfers are not required to support partial - // transfers of images, reported by a minimum granularity of [0, 0, 0]. If you need - // to do partial transfers of images like we do in this example, you therefore have + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, graphics_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + // Since we are going to be updating the texture on a separate thread asynchronously from + // the execution of graphics commands, it would make sense to also do the transfer on a + // dedicated transfer queue, if such a queue family exists. That way, the graphics queue is + // not blocked during the transfers either and the two tasks are truly asynchronous. + // + // For this, we need to find the queue family with the fewest queue flags set, since if the + // queue family has more flags than `TRANSFER | SPARSE_BINDING`, that means it is not + // dedicated to transfer operations. + let transfer_family_index = physical_device + .queue_family_properties() + .iter() + .enumerate() + .filter(|(_, q)| { + q.queue_flags.intersects(QueueFlags::TRANSFER) + // Queue families dedicated to transfers are not required to support partial + // transfers of images, reported by a minimum granularity of [0, 0, 0]. If you need + // to do partial transfers of images like we do in this example, you therefore have // to make sure the queue family supports that. && q.min_image_transfer_granularity != [0; 3] // Unlike queue families for graphics and/or compute, queue families dedicated to @@ -210,99 +210,82 @@ impl App { && q.min_image_transfer_granularity[0..2] .iter() .all(|&g| TRANSFER_GRANULARITY % g == 0) - }) - .min_by_key(|(_, q)| q.queue_flags.count()) - .unwrap() - .0 as u32; + }) + .min_by_key(|(_, q)| q.queue_flags.count()) + .unwrap() + .0 as u32; - let (device, mut queues) = { - let mut queue_create_infos = vec![QueueCreateInfo { - queue_family_index: graphics_family_index, - ..Default::default() - }]; - - // It's possible that the physical device doesn't have any queue families supporting - // transfers other than the graphics and/or compute queue family. In that case we must make - // sure we don't request the same queue family twice. - if transfer_family_index != graphics_family_index { - queue_create_infos.push(QueueCreateInfo { - queue_family_index: transfer_family_index, + let (device, mut queues) = { + let mut queue_create_infos = vec![QueueCreateInfo { + queue_family_index: graphics_family_index, ..Default::default() - }); - } else { - let queue_family_properties = - &physical_device.queue_family_properties()[graphics_family_index as usize]; - - // Even if we can't get an async transfer queue family, it's still better to use - // different queues on the same queue family. This way, at least the threads on the - // host don't have to lock the same queue when submitting. - if queue_family_properties.queue_count > 1 { - queue_create_infos[0].queues.push(0.5); + }]; + + // It's possible that the physical device doesn't have any queue families supporting + // transfers other than the graphics and/or compute queue family. In that case we must + // make sure we don't request the same queue family twice. + if transfer_family_index != graphics_family_index { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: transfer_family_index, + ..Default::default() + }); + } else { + let queue_family_properties = + &physical_device.queue_family_properties()[graphics_family_index as usize]; + + // Even if we can't get an async transfer queue family, it's still better to use + // different queues on the same queue family. This way, at least the threads on the + // host don't have to lock the same queue when submitting. + if queue_family_properties.queue_count > 1 { + queue_create_infos[0].queues.push(0.5); + } } - } - Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos, - ..Default::default() - }, - ) - .unwrap() - }; + Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos, + ..Default::default() + }, + ) + .unwrap() + }; - let graphics_queue = queues.next().unwrap(); + let graphics_queue = queues.next().unwrap(); - // If we didn't get a dedicated transfer queue, fall back to the graphics queue for transfers. - let transfer_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + // If we didn't get a dedicated transfer queue, fall back to the graphics queue for + // transfers. + let transfer_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); - println!( - "Using queue family {graphics_family_index} for graphics and queue family \ - {transfer_family_index} for transfers", - ); + println!( + "Using queue family {graphics_family_index} for graphics and queue family \ + {transfer_family_index} for transfers", + ); - let resources = Resources::new(&device, &Default::default()); + let resources = Resources::new(&device, &Default::default()); - let graphics_flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); - let transfer_flight_id = resources.create_flight(1).unwrap(); + let graphics_flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); + let transfer_flight_id = resources.create_flight(1).unwrap(); - let vertices = [ - MyVertex { - position: [-0.5, -0.5], - }, - MyVertex { - position: [-0.5, 0.5], - }, - MyVertex { - position: [0.5, -0.5], - }, - MyVertex { - position: [0.5, 0.5], - }, - ]; - let vertex_buffer_id = resources - .create_buffer( - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() + let vertices = [ + MyVertex { + position: [-0.5, -0.5], }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() + MyVertex { + position: [-0.5, 0.5], }, - DeviceLayout::from_layout(Layout::for_value(&vertices)).unwrap(), - ) - .unwrap(); - - // Create a pool of uniform buffers, one per frame in flight. This way we always have an - // available buffer to write during each frame while reusing them as much as possible. - let uniform_buffer_ids = [(); MAX_FRAMES_IN_FLIGHT as usize].map(|_| { - resources + MyVertex { + position: [0.5, -0.5], + }, + MyVertex { + position: [0.5, 0.5], + }, + ]; + let vertex_buffer_id = resources .create_buffer( BufferCreateInfo { - usage: BufferUsage::UNIFORM_BUFFER, + usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { @@ -310,339 +293,361 @@ impl App { | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - DeviceLayout::from_layout(Layout::new::()).unwrap(), + DeviceLayout::from_layout(Layout::for_value(&vertices)).unwrap(), ) - .unwrap() - }); + .unwrap(); - // Create two textures, where at any point in time one is used exclusively for reading and - // one is used exclusively for writing, swapping the two after each update. - let texture_ids = [(); 2].map(|_| { - resources - .create_image( - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::R8G8B8A8_UNORM, - extent: [TRANSFER_GRANULARITY * 2, TRANSFER_GRANULARITY * 2, 1], - usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, - sharing: if graphics_family_index != transfer_family_index { - Sharing::Concurrent( - [graphics_family_index, transfer_family_index] - .into_iter() - .collect(), - ) - } else { - Sharing::Exclusive + // Create a pool of uniform buffers, one per frame in flight. This way we always have an + // available buffer to write during each frame while reusing them as much as possible. + let uniform_buffer_ids = [(); MAX_FRAMES_IN_FLIGHT as usize].map(|_| { + resources + .create_buffer( + BufferCreateInfo { + usage: BufferUsage::UNIFORM_BUFFER, + ..Default::default() }, - ..Default::default() + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + DeviceLayout::from_layout(Layout::new::()).unwrap(), + ) + .unwrap() + }); + + // Create two textures, where at any point in time one is used exclusively for reading and + // one is used exclusively for writing, swapping the two after each update. + let texture_ids = [(); 2].map(|_| { + resources + .create_image( + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_UNORM, + extent: [TRANSFER_GRANULARITY * 2, TRANSFER_GRANULARITY * 2, 1], + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + sharing: if graphics_family_index != transfer_family_index { + Sharing::Concurrent( + [graphics_family_index, transfer_family_index] + .into_iter() + .collect(), + ) + } else { + Sharing::Exclusive + }, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) + .unwrap() + }); + + // The index of the currently most up-to-date texture. The worker thread swaps the index + // after every finished write, which is always done to the, at that point in time, unused + // texture. + let current_texture_index = Arc::new(AtomicBool::new(false)); + + // Initialize the resources. + unsafe { + vulkano_taskgraph::execute( + &graphics_queue, + &resources, + graphics_flight_id, + |cbf, tcx| { + tcx.write_buffer::<[MyVertex]>(vertex_buffer_id, ..)? + .copy_from_slice(&vertices); + + for &texture_id in &texture_ids { + cbf.clear_color_image(&ClearColorImageInfo { + image: texture_id, + ..Default::default() + })?; + } + + Ok(()) }, - AllocationCreateInfo::default(), + [(vertex_buffer_id, HostAccessType::Write)], + [], + [ + ( + texture_ids[0], + AccessType::ClearTransferWrite, + ImageLayoutType::Optimal, + ), + ( + texture_ids[1], + AccessType::ClearTransferWrite, + ImageLayoutType::Optimal, + ), + ], ) - .unwrap() - }); + } + .unwrap(); - // The index of the currently most up-to-date texture. The worker thread swaps the index after - // every finished write, which is always done to the, at that point in time, unused texture. - let current_texture_index = Arc::new(AtomicBool::new(false)); + // Start the worker thread. + let (channel, receiver) = mpsc::channel(); + run_worker( + receiver, + graphics_family_index, + transfer_family_index, + transfer_queue.clone(), + resources.clone(), + graphics_flight_id, + transfer_flight_id, + texture_ids, + current_texture_index.clone(), + ); - // Initialize the resources. - unsafe { - vulkano_taskgraph::execute( - &graphics_queue, - &resources, + App { + instance, + device, + graphics_family_index, + transfer_family_index, + graphics_queue, + resources, graphics_flight_id, - |cbf, tcx| { - tcx.write_buffer::<[MyVertex]>(vertex_buffer_id, ..)? - .copy_from_slice(&vertices); + vertex_buffer_id, + uniform_buffer_ids, + texture_ids, + current_texture_index, + channel, + rcx: None, + } + } +} - for &texture_id in &texture_ids { - cbf.clear_color_image(&ClearColorImageInfo { - image: texture_id, +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let swapchain_format; + let swapchain_id = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + (swapchain_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + self.resources + .create_swapchain( + self.graphics_flight_id, + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(3), + image_format: swapchain_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), ..Default::default() - })?; - } + }, + ) + .unwrap() + }; - Ok(()) + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain_format, + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, }, - [(vertex_buffer_id, HostAccessType::Write)], - [], - [ - ( - texture_ids[0], - AccessType::ClearTransferWrite, - ImageLayoutType::Optimal, - ), - ( - texture_ids[1], - AccessType::ClearTransferWrite, - ImageLayoutType::Optimal, - ), - ], ) - } - .unwrap(); + .unwrap(); - // Start the worker thread. - let (channel, receiver) = mpsc::channel(); - run_worker( - receiver, - graphics_family_index, - transfer_family_index, - transfer_queue.clone(), - resources.clone(), - graphics_flight_id, - transfer_flight_id, - texture_ids, - current_texture_index.clone(), - ); - - App { - instance, - device, - graphics_family_index, - transfer_family_index, - graphics_queue, - resources, - graphics_flight_id, - vertex_buffer_id, - uniform_buffer_ids, - texture_ids, - current_texture_index, - channel, - rcx: None, - } - } -} + let framebuffers = window_size_dependent_setup(&self.resources, swapchain_id, &render_pass); -impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new( - event_loop - .create_window(Window::default_attributes()) - .unwrap(), - ); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let swapchain_format; - let swapchain_id = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - (swapchain_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - self.resources - .create_swapchain( - self.graphics_flight_id, - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(3), - image_format: swapchain_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) }, ) .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain_format, - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + }; - let framebuffers = window_size_dependent_setup(&self.resources, swapchain_id, &render_pass); + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + Default::default(), + )); + + // A byproduct of always using the same set of uniform buffers is that we can also create + // one descriptor set for each, reusing them in the same way as the buffers. + let uniform_buffer_sets = self.uniform_buffer_ids.map(|buffer_id| { + let buffer_state = self.resources.buffer(buffer_id).unwrap(); + let buffer = buffer_state.buffer(); + + DescriptorSet::new( + descriptor_set_allocator.clone(), + pipeline.layout().set_layouts()[0].clone(), + [WriteDescriptorSet::buffer(0, buffer.clone().into())], + [], + ) + .unwrap() + }); - GraphicsPipeline::new( + // Create the descriptor sets for sampling the textures. + let sampler = Sampler::new( self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, + SamplerCreateInfo::simple_repeat_linear(), ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - self.device.clone(), - Default::default(), - )); - - // A byproduct of always using the same set of uniform buffers is that we can also create one - // descriptor set for each, reusing them in the same way as the buffers. - let uniform_buffer_sets = self.uniform_buffer_ids.map(|buffer_id| { - let buffer_state = self.resources.buffer(buffer_id).unwrap(); - let buffer = buffer_state.buffer(); - - DescriptorSet::new( - descriptor_set_allocator.clone(), - pipeline.layout().set_layouts()[0].clone(), - [WriteDescriptorSet::buffer(0, buffer.clone().into())], - [], - ) - .unwrap() - }); - - // Create the descriptor sets for sampling the textures. - let sampler = Sampler::new( - self.device.clone(), - SamplerCreateInfo::simple_repeat_linear(), - ) - .unwrap(); - let sampler_sets = self.texture_ids.map(|texture_id| { - let texture_state = self.resources.image(texture_id).unwrap(); - let texture = texture_state.image(); - - DescriptorSet::new( - descriptor_set_allocator.clone(), - pipeline.layout().set_layouts()[1].clone(), - [ - WriteDescriptorSet::sampler(0, sampler.clone()), - WriteDescriptorSet::image_view(1, ImageView::new_default(texture.clone()).unwrap()), - ], - [], - ) - .unwrap() - }); - - let mut task_graph = TaskGraph::new(&self.resources, 1, 4); - - let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default()); - let virtual_texture_id = task_graph.add_image(&ImageCreateInfo { - sharing: if self.graphics_family_index != self.transfer_family_index { - Sharing::Concurrent( - [self.graphics_family_index, self.transfer_family_index] - .into_iter() - .collect(), + .unwrap(); + let sampler_sets = self.texture_ids.map(|texture_id| { + let texture_state = self.resources.image(texture_id).unwrap(); + let texture = texture_state.image(); + + DescriptorSet::new( + descriptor_set_allocator.clone(), + pipeline.layout().set_layouts()[1].clone(), + [ + WriteDescriptorSet::sampler(0, sampler.clone()), + WriteDescriptorSet::image_view( + 1, + ImageView::new_default(texture.clone()).unwrap(), + ), + ], + [], ) - } else { - Sharing::Exclusive - }, - ..Default::default() - }); - let virtual_uniform_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default()); + .unwrap() + }); - task_graph.add_host_buffer_access(virtual_uniform_buffer_id, HostAccessType::Write); + let mut task_graph = TaskGraph::new(&self.resources, 1, 4); - task_graph - .create_task_node( - "Render", - QueueFamilyType::Graphics, - RenderTask { - swapchain_id: virtual_swapchain_id, - vertex_buffer_id: self.vertex_buffer_id, - current_texture_index: self.current_texture_index.clone(), - pipeline, - uniform_buffer_id: virtual_uniform_buffer_id, - uniform_buffer_sets, - sampler_sets, + let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default()); + let virtual_texture_id = task_graph.add_image(&ImageCreateInfo { + sharing: if self.graphics_family_index != self.transfer_family_index { + Sharing::Concurrent( + [self.graphics_family_index, self.transfer_family_index] + .into_iter() + .collect(), + ) + } else { + Sharing::Exclusive }, - ) - .image_access( - virtual_swapchain_id.current_image_id(), - AccessType::ColorAttachmentWrite, - ImageLayoutType::Optimal, - ) - .buffer_access(self.vertex_buffer_id, AccessType::VertexAttributeRead) - .image_access( - virtual_texture_id, - AccessType::FragmentShaderSampledRead, - ImageLayoutType::Optimal, - ) - .buffer_access( - virtual_uniform_buffer_id, - AccessType::VertexShaderUniformRead, - ); - - let task_graph = unsafe { - task_graph.compile(&CompileInfo { - queues: &[&self.graphics_queue], - present_queue: Some(&self.graphics_queue), - flight_id: self.graphics_flight_id, ..Default::default() - }) - } - .unwrap(); + }); + let virtual_uniform_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default()); + + task_graph.add_host_buffer_access(virtual_uniform_buffer_id, HostAccessType::Write); + + task_graph + .create_task_node( + "Render", + QueueFamilyType::Graphics, + RenderTask { + swapchain_id: virtual_swapchain_id, + vertex_buffer_id: self.vertex_buffer_id, + current_texture_index: self.current_texture_index.clone(), + pipeline, + uniform_buffer_id: virtual_uniform_buffer_id, + uniform_buffer_sets, + sampler_sets, + }, + ) + .image_access( + virtual_swapchain_id.current_image_id(), + AccessType::ColorAttachmentWrite, + ImageLayoutType::Optimal, + ) + .buffer_access(self.vertex_buffer_id, AccessType::VertexAttributeRead) + .image_access( + virtual_texture_id, + AccessType::FragmentShaderSampledRead, + ImageLayoutType::Optimal, + ) + .buffer_access( + virtual_uniform_buffer_id, + AccessType::VertexShaderUniformRead, + ); + + let task_graph = unsafe { + task_graph.compile(&CompileInfo { + queues: &[&self.graphics_queue], + present_queue: Some(&self.graphics_queue), + flight_id: self.graphics_flight_id, + ..Default::default() + }) + } + .unwrap(); - self.rcx = Some(RenderContext { - window, - swapchain_id, - render_pass, - framebuffers, - viewport, - recreate_swapchain: false, - task_graph, - virtual_swapchain_id, - virtual_texture_id, - virtual_uniform_buffer_id, - }); + self.rcx = Some(RenderContext { + window, + swapchain_id, + render_pass, + framebuffers, + viewport, + recreate_swapchain: false, + task_graph, + virtual_swapchain_id, + virtual_texture_id, + virtual_uniform_buffer_id, + }); } fn window_event( diff --git a/examples/bloom/main.rs b/examples/bloom/main.rs index a301ee5cd3..6425be9ca2 100644 --- a/examples/bloom/main.rs +++ b/examples/bloom/main.rs @@ -182,7 +182,11 @@ impl App { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); let window_size = window.inner_size(); diff --git a/examples/buffer-allocator/main.rs b/examples/buffer-allocator/main.rs index 9f2eb75997..2c063d449b 100644 --- a/examples/buffer-allocator/main.rs +++ b/examples/buffer-allocator/main.rs @@ -77,250 +77,250 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // Using a buffer allocator allows multiple buffers to be "in-flight" simultaneously and is - // suited to highly dynamic data like vertex, index and uniform buffers. - let buffer_allocator = SubbufferAllocator::new( - memory_allocator, - SubbufferAllocatorCreateInfo { - // We want to use the allocated subbuffers as vertex buffers. - buffer_usage: BufferUsage::VERTEX_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - - App { - instance, - device, - queue, - command_buffer_allocator, - buffer_allocator, - rcx: None, + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // Using a buffer allocator allows multiple buffers to be "in-flight" simultaneously and is + // suited to highly dynamic data like vertex, index and uniform buffers. + let buffer_allocator = SubbufferAllocator::new( + memory_allocator, + SubbufferAllocatorCreateInfo { + // We want to use the allocated subbuffers as vertex buffers. + buffer_usage: BufferUsage::VERTEX_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); + + App { + instance, + device, + queue, + command_buffer_allocator, + buffer_allocator, + rcx: None, + } } } -} impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new( - event_loop - .create_window(Window::default_attributes()) - .unwrap(), - ); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 - layout(location = 0) in vec2 position; + layout(location = 0) in vec2 position; - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } } - } - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - layout(location = 0) out vec4 f_color; + layout(location = 0) out vec4 f_color; - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - ", + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } } - } - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain: false, - previous_frame_end, - }); + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -362,15 +362,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -409,7 +413,10 @@ impl ApplicationHandler for App { let num_vertices = data.len() as u32; // Allocate a new subbuffer using the buffer allocator. - let buffer = self.buffer_allocator.allocate_slice(data.len() as _).unwrap(); + let buffer = self + .buffer_allocator + .allocate_slice(data.len() as _) + .unwrap(); buffer.write().unwrap().copy_from_slice(&data); let mut builder = RecordingCommandBuffer::new( @@ -458,7 +465,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/clear-attachments/main.rs b/examples/clear-attachments/main.rs index 6facb4f3f4..455514c93e 100644 --- a/examples/clear-attachments/main.rs +++ b/examples/clear-attachments/main.rs @@ -56,156 +56,156 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - App { - instance, - device, - queue, - command_buffer_allocator, - rcx: None, - } + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + App { + instance, + device, + queue, + command_buffer_allocator, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new( - event_loop - .create_window(Window::default_attributes()) - .unwrap(), - ); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let framebuffers = window_size_dependent_setup(&images, &render_pass); - - let [width, height] = window_size.into(); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - width, - height, - recreate_swapchain: false, - previous_frame_end, - }); + ) + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + + let [width, height] = window_size.into(); + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + width, + height, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -247,15 +247,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -330,7 +334,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/deferred/main.rs b/examples/deferred/main.rs index 8b4c0d9f3c..1fc0ae11c1 100644 --- a/examples/deferred/main.rs +++ b/examples/deferred/main.rs @@ -79,156 +79,160 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, - ..Default::default() - }, - )); - - App { - instance, - device, - queue, - memory_allocator, - command_buffer_allocator, - rcx: None, - } + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + StandardCommandBufferAllocatorCreateInfo { + secondary_buffer_count: 32, + ..Default::default() + }, + )); + + App { + instance, + device, + queue, + memory_allocator, + command_buffer_allocator, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + let (swapchain, images) = Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - let (swapchain, images) = Swapchain::new( - self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap(); - (swapchain, images) - }; - - let images = images - .into_iter() - .map(|image| ImageView::new_default(image).unwrap()) - .collect::>(); - - // Here is the basic initialization for the deferred system. - let frame_system = FrameSystem::new( - self.queue.clone(), - swapchain.image_format(), - self.memory_allocator.clone(), - self.command_buffer_allocator.clone(), - ); - let triangle_draw_system = TriangleDrawSystem::new( - self.queue.clone(), - frame_system.deferred_subpass(), - self.memory_allocator.clone(), - self.command_buffer_allocator.clone(), - ); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - images, - frame_system, - triangle_draw_system, - recreate_swapchain: false, - previous_frame_end, - }); + (swapchain, images) + }; + + let images = images + .into_iter() + .map(|image| ImageView::new_default(image).unwrap()) + .collect::>(); + + // Here is the basic initialization for the deferred system. + let frame_system = FrameSystem::new( + self.queue.clone(), + swapchain.image_format(), + self.memory_allocator.clone(), + self.command_buffer_allocator.clone(), + ); + let triangle_draw_system = TriangleDrawSystem::new( + self.queue.clone(), + frame_system.deferred_subpass(), + self.memory_allocator.clone(), + self.command_buffer_allocator.clone(), + ); + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + images, + frame_system, + triangle_draw_system, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -273,15 +277,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -297,7 +305,9 @@ impl ApplicationHandler for App { while let Some(pass) = frame.next_pass() { match pass { Pass::Deferred(mut draw_pass) => { - let cb = rcx.triangle_draw_system.draw(draw_pass.viewport_dimensions()); + let cb = rcx + .triangle_draw_system + .draw(draw_pass.viewport_dimensions()); draw_pass.execute(cb); } Pass::Lighting(mut lighting) => { @@ -317,7 +327,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/gl-interop/main.rs b/examples/gl-interop/main.rs index cc3ae3cd1c..6f65d37641 100644 --- a/examples/gl-interop/main.rs +++ b/examples/gl-interop/main.rs @@ -124,482 +124,496 @@ mod linux { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let event_loop_gl = winit_glium::event_loop::EventLoop::new(); - // For some reason, this must be created before the vulkan window - let hrb = glutin::ContextBuilder::new() - .with_gl_debug_flag(true) - .with_gl(glutin::GlRequest::Latest) - .build_surfaceless(&event_loop_gl) - .unwrap(); + let event_loop_gl = winit_glium::event_loop::EventLoop::new(); + // For some reason, this must be created before the vulkan window + let hrb = glutin::ContextBuilder::new() + .with_gl_debug_flag(true) + .with_gl(glutin::GlRequest::Latest) + .build_surfaceless(&event_loop_gl) + .unwrap(); - let hrb_vk = glutin::ContextBuilder::new() - .with_gl_debug_flag(true) - .with_gl(glutin::GlRequest::Latest) - .build_surfaceless(&event_loop_gl) + let hrb_vk = glutin::ContextBuilder::new() + .with_gl_debug_flag(true) + .with_gl(glutin::GlRequest::Latest) + .build_surfaceless(&event_loop_gl) + .unwrap(); + + // Used for checking device and driver UUIDs. + let display = glium::HeadlessRenderer::with_debug( + hrb_vk, + glium::debug::DebugCallbackBehavior::PrintAll, + ) .unwrap(); - // Used for checking device and driver UUIDs. - let display = glium::HeadlessRenderer::with_debug( - hrb_vk, - glium::debug::DebugCallbackBehavior::PrintAll, - ) - .unwrap(); - - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: InstanceExtensions { - khr_get_physical_device_properties2: true, - khr_external_memory_capabilities: true, - khr_external_semaphore_capabilities: true, - khr_external_fence_capabilities: true, - ext_debug_utils: true, - ..required_extensions - }, - ..Default::default() - }, - ) - .unwrap(); - - let _debug_callback = unsafe { - DebugUtilsMessenger::new( - instance.clone(), - DebugUtilsMessengerCreateInfo::user_callback(DebugUtilsMessengerCallback::new( - |message_severity, message_type, callback_data| { - println!( - "{} {:?} {:?}: {}", - callback_data.message_id_name.unwrap_or("unknown"), - message_type, - message_severity, - callback_data.message, - ); + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: InstanceExtensions { + khr_get_physical_device_properties2: true, + khr_external_memory_capabilities: true, + khr_external_semaphore_capabilities: true, + khr_external_fence_capabilities: true, + ext_debug_utils: true, + ..required_extensions }, - )), + ..Default::default() + }, ) - .unwrap() - }; - - let device_extensions = DeviceExtensions { - khr_external_semaphore: true, - khr_external_semaphore_fd: true, - khr_external_memory: true, - khr_external_memory_fd: true, - khr_external_fence: true, - khr_external_fence_fd: true, - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .filter(|(p, _)| p.properties().driver_uuid.unwrap() == display.driver_uuid().unwrap()) - .filter(|(p, _)| { - display - .device_uuids() - .unwrap() - .contains(&p.properties().device_uuid.unwrap()) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, - ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertices = [ - MyVertex { - position: [-0.5, -0.5], - }, - MyVertex { - position: [-0.5, 0.5], - }, - MyVertex { - position: [0.5, -0.5], - }, - MyVertex { - position: [0.5, 0.5], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let raw_image = RawImage::new( - device.clone(), - ImageCreateInfo { - flags: ImageCreateFlags::MUTABLE_FORMAT, - image_type: ImageType::Dim2d, - format: Format::R16G16B16A16_UNORM, - extent: [200, 200, 1], - usage: ImageUsage::TRANSFER_SRC | ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, - external_memory_handle_types: ExternalMemoryHandleTypes::OPAQUE_FD, - ..Default::default() - }, - ) - .unwrap(); - - let image_requirements = raw_image.memory_requirements()[0]; - - let image_memory = DeviceMemory::allocate( - device.clone(), - MemoryAllocateInfo { - allocation_size: image_requirements.layout.size(), - memory_type_index: memory_allocator - .find_memory_type_index( - image_requirements.memory_type_bits, - MemoryTypeFilter::PREFER_DEVICE, - ) - .unwrap(), - dedicated_allocation: Some(DedicatedAllocation::Image(&raw_image)), - export_handle_types: ExternalMemoryHandleTypes::OPAQUE_FD, - ..Default::default() - }, - ) - .unwrap(); + let _debug_callback = unsafe { + DebugUtilsMessenger::new( + instance.clone(), + DebugUtilsMessengerCreateInfo::user_callback(DebugUtilsMessengerCallback::new( + |message_severity, message_type, callback_data| { + println!( + "{} {:?} {:?}: {}", + callback_data.message_id_name.unwrap_or("unknown"), + message_type, + message_severity, + callback_data.message, + ); + }, + )), + ) + .unwrap() + }; - let allocation_size = image_memory.allocation_size(); - let image_fd = image_memory - .export_fd(ExternalMemoryHandleType::OpaqueFd) - .unwrap(); + let device_extensions = DeviceExtensions { + khr_external_semaphore: true, + khr_external_semaphore_fd: true, + khr_external_memory: true, + khr_external_memory_fd: true, + khr_external_fence: true, + khr_external_fence_fd: true, + khr_swapchain: true, + ..DeviceExtensions::empty() + }; - // SAFETY: we just created this raw image and hasn't bound any memory to it. - let image = Arc::new(unsafe { - raw_image - .bind_memory([ResourceMemory::new_dedicated(image_memory)]) - .map_err(|(err, _, _)| err) + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() .unwrap() - }); + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .filter(|(p, _)| { + p.properties().driver_uuid.unwrap() == display.driver_uuid().unwrap() + }) + .filter(|(p, _)| { + display + .device_uuids() + .unwrap() + .contains(&p.properties().device_uuid.unwrap()) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); - let image_view = ImageView::new_default(image).unwrap(); + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); - let sampler = Sampler::new( - device.clone(), - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - ..Default::default() - }, - ) - .unwrap(); + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() + }, + ) + .unwrap(); - let barrier = Arc::new(Barrier::new(2)); - let barrier_2 = Arc::new(Barrier::new(2)); + let queue = queues.next().unwrap(); - let acquire_sem = Arc::new( - Semaphore::new( + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( device.clone(), - SemaphoreCreateInfo { - export_handle_types: ExternalSemaphoreHandleTypes::OPAQUE_FD, + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-0.5, -0.5], + }, + MyVertex { + position: [-0.5, 0.5], + }, + MyVertex { + position: [0.5, -0.5], + }, + MyVertex { + position: [0.5, 0.5], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, + vertices, ) - .unwrap(), - ); - let release_sem = Arc::new( - Semaphore::new( + .unwrap(); + + let raw_image = RawImage::new( device.clone(), - SemaphoreCreateInfo { - export_handle_types: ExternalSemaphoreHandleTypes::OPAQUE_FD, + ImageCreateInfo { + flags: ImageCreateFlags::MUTABLE_FORMAT, + image_type: ImageType::Dim2d, + format: Format::R16G16B16A16_UNORM, + extent: [200, 200, 1], + usage: ImageUsage::TRANSFER_SRC + | ImageUsage::TRANSFER_DST + | ImageUsage::SAMPLED, + external_memory_handle_types: ExternalMemoryHandleTypes::OPAQUE_FD, ..Default::default() }, ) - .unwrap(), - ); + .unwrap(); - let acquire_fd = unsafe { - acquire_sem - .export_fd(ExternalSemaphoreHandleType::OpaqueFd) - .unwrap() - }; - let release_fd = unsafe { - release_sem - .export_fd(ExternalSemaphoreHandleType::OpaqueFd) - .unwrap() - }; - - let barrier_clone = barrier.clone(); - let barrier_2_clone = barrier_2.clone(); - - build_display(hrb, move |gl_display| { - let gl_tex = unsafe { - glium::texture::Texture2d::new_from_fd( - gl_display.as_ref(), - glium::texture::UncompressedFloatFormat::U16U16U16U16, - glium::texture::MipmapsOption::NoMipmap, - glium::texture::Dimensions::Texture2d { - width: 200, - height: 200, + let image_requirements = raw_image.memory_requirements()[0]; + + let image_memory = DeviceMemory::allocate( + device.clone(), + MemoryAllocateInfo { + allocation_size: image_requirements.layout.size(), + memory_type_index: memory_allocator + .find_memory_type_index( + image_requirements.memory_type_bits, + MemoryTypeFilter::PREFER_DEVICE, + ) + .unwrap(), + dedicated_allocation: Some(DedicatedAllocation::Image(&raw_image)), + export_handle_types: ExternalMemoryHandleTypes::OPAQUE_FD, + ..Default::default() + }, + ) + .unwrap(); + + let allocation_size = image_memory.allocation_size(); + let image_fd = image_memory + .export_fd(ExternalMemoryHandleType::OpaqueFd) + .unwrap(); + + // SAFETY: we just created this raw image and hasn't bound any memory to it. + let image = Arc::new(unsafe { + raw_image + .bind_memory([ResourceMemory::new_dedicated(image_memory)]) + .map_err(|(err, _, _)| err) + .unwrap() + }); + + let image_view = ImageView::new_default(image).unwrap(); + + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + ) + .unwrap(); + + let barrier = Arc::new(Barrier::new(2)); + let barrier_2 = Arc::new(Barrier::new(2)); + + let acquire_sem = Arc::new( + Semaphore::new( + device.clone(), + SemaphoreCreateInfo { + export_handle_types: ExternalSemaphoreHandleTypes::OPAQUE_FD, + ..Default::default() }, - glium::texture::ImportParameters { - dedicated_memory: true, - size: allocation_size, - offset: 0, - tiling: glium::texture::ExternalTilingMode::Optimal, + ) + .unwrap(), + ); + let release_sem = Arc::new( + Semaphore::new( + device.clone(), + SemaphoreCreateInfo { + export_handle_types: ExternalSemaphoreHandleTypes::OPAQUE_FD, + ..Default::default() }, - image_fd, ) - } - .unwrap(); + .unwrap(), + ); - let gl_acquire_sem = unsafe { - glium::semaphore::Semaphore::new_from_fd(gl_display.as_ref(), acquire_fd).unwrap() + let acquire_fd = unsafe { + acquire_sem + .export_fd(ExternalSemaphoreHandleType::OpaqueFd) + .unwrap() }; - - let gl_release_sem = unsafe { - glium::semaphore::Semaphore::new_from_fd(gl_display.as_ref(), release_fd).unwrap() + let release_fd = unsafe { + release_sem + .export_fd(ExternalSemaphoreHandleType::OpaqueFd) + .unwrap() }; - let rotation_start = Instant::now(); + let barrier_clone = barrier.clone(); + let barrier_2_clone = barrier_2.clone(); + + build_display(hrb, move |gl_display| { + let gl_tex = unsafe { + glium::texture::Texture2d::new_from_fd( + gl_display.as_ref(), + glium::texture::UncompressedFloatFormat::U16U16U16U16, + glium::texture::MipmapsOption::NoMipmap, + glium::texture::Dimensions::Texture2d { + width: 200, + height: 200, + }, + glium::texture::ImportParameters { + dedicated_memory: true, + size: allocation_size, + offset: 0, + tiling: glium::texture::ExternalTilingMode::Optimal, + }, + image_fd, + ) + } + .unwrap(); - loop { - barrier_clone.wait(); - gl_acquire_sem - .wait_textures(Some(&[(&gl_tex, glium::semaphore::TextureLayout::General)])); + let gl_acquire_sem = unsafe { + glium::semaphore::Semaphore::new_from_fd(gl_display.as_ref(), acquire_fd) + .unwrap() + }; - gl_display.get_context().flush(); + let gl_release_sem = unsafe { + glium::semaphore::Semaphore::new_from_fd(gl_display.as_ref(), release_fd) + .unwrap() + }; - let elapsed = rotation_start.elapsed(); - let rotation = elapsed.as_nanos() as f64 / 2_000_000_000.0; + let rotation_start = Instant::now(); - use glium::Surface; - { - let mut fb = gl_tex.as_surface(); + loop { + barrier_clone.wait(); + gl_acquire_sem.wait_textures(Some(&[( + &gl_tex, + glium::semaphore::TextureLayout::General, + )])); - fb.clear_color( - 0.0, - (((rotation as f32).sin() + 1.) / 2.).powf(2.2), - 0.0, - 1.0, - ); - } - gl_release_sem - .signal_textures(Some(&[(&gl_tex, glium::semaphore::TextureLayout::General)])); - barrier_2_clone.wait(); + gl_display.get_context().flush(); - gl_display.get_context().finish(); + let elapsed = rotation_start.elapsed(); + let rotation = elapsed.as_nanos() as f64 / 2_000_000_000.0; - gl_display.get_context().assert_no_error(Some("err")); - } - }); + use glium::Surface; + { + let mut fb = gl_tex.as_surface(); - App { - instance, - device, - queue, - descriptor_set_allocator, - command_buffer_allocator, - vertex_buffer, - sampler, - image_view, - barrier, - barrier_2, - acquire_sem, - release_sem, - rcx: None, - } + fb.clear_color( + 0.0, + (((rotation as f32).sin() + 1.) / 2.).powf(2.2), + 0.0, + 1.0, + ); + } + gl_release_sem.signal_textures(Some(&[( + &gl_tex, + glium::semaphore::TextureLayout::General, + )])); + barrier_2_clone.wait(); + + gl_display.get_context().finish(); + + gl_display.get_context().assert_no_error(Some("err")); + } + }); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + sampler, + image_view, + barrier, + barrier_2, + acquire_sem, + release_sem, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let layout = &pipeline.layout().set_layouts()[0]; + + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), + layout.clone(), + [ + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view(1, self.image_view.clone()), + ], + [], ) .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let layout = &pipeline.layout().set_layouts()[0]; - - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - layout.clone(), - [ - WriteDescriptorSet::sampler(0, self.sampler.clone()), - WriteDescriptorSet::image_view(1, self.image_view.clone()), - ], - [], - ) - .unwrap(); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - descriptor_set, - recreate_swapchain: false, - previous_frame_end, - }); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -667,24 +681,23 @@ mod linux { .expect("failed to recreate swapchain"); rcx.swapchain = new_swapchain; - rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); + rcx.framebuffers = + window_size_dependent_setup(&new_images, &rcx.render_pass); rcx.viewport.extent = window_size.into(); rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = match acquire_next_image( - rcx.swapchain.clone(), - None, - ) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(rcx.swapchain.clone(), None) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -727,7 +740,9 @@ mod linux { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); diff --git a/examples/image-self-copy-blit/main.rs b/examples/image-self-copy-blit/main.rs index 1cce399a4c..0f0a4ef5d9 100644 --- a/examples/image-self-copy-blit/main.rs +++ b/examples/image-self-copy-blit/main.rs @@ -86,383 +86,389 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertices = [ - MyVertex { - position: [-0.5, -0.5], - }, - MyVertex { - position: [-0.5, 0.5], - }, - MyVertex { - position: [0.5, -0.5], - }, - MyVertex { - position: [0.5, 0.5], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let mut uploads = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), - CommandBufferLevel::Primary, - CommandBufferBeginInfo { - usage: CommandBufferUsage::OneTimeSubmit, - ..Default::default() - }, - ) - .unwrap(); - - let texture = { - let png_bytes = include_bytes!("image_img.png").as_slice(); - let decoder = png::Decoder::new(png_bytes); - let mut reader = decoder.read_info().unwrap(); - let info = reader.info(); - let img_size = [info.width, info.height]; - let extent = [info.width * 2, info.height * 2, 1]; - - let upload_buffer = Buffer::new_slice( + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-0.5, -0.5], + }, + MyVertex { + position: [-0.5, 0.5], + }, + MyVertex { + position: [0.5, -0.5], + }, + MyVertex { + position: [0.5, 0.5], + }, + ]; + let vertex_buffer = Buffer::from_iter( memory_allocator.clone(), BufferCreateInfo { - usage: BufferUsage::TRANSFER_SRC, + usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - (info.width * info.height * 4) as DeviceSize, + vertices, ) .unwrap(); - reader - .next_frame(&mut upload_buffer.write().unwrap()) - .unwrap(); - - let image = Image::new( - memory_allocator, - ImageCreateInfo { - format: Format::R8G8B8A8_UNORM, - extent, - usage: ImageUsage::TRANSFER_SRC | ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + let mut uploads = RecordingCommandBuffer::new( + command_buffer_allocator.clone(), + queue.queue_family_index(), + CommandBufferLevel::Primary, + CommandBufferBeginInfo { + usage: CommandBufferUsage::OneTimeSubmit, ..Default::default() }, - AllocationCreateInfo::default(), ) .unwrap(); - // Here, we perform image copying and blitting on the same image. - uploads - // Clear the image buffer. - .clear_color_image(ClearColorImageInfo::image(image.clone())) - .unwrap() - // Put our image in the top left corner. - .copy_buffer_to_image(CopyBufferToImageInfo { - regions: [BufferImageCopy { - image_subresource: image.subresource_layers(), - image_extent: [img_size[0], img_size[1], 1], + let texture = { + let png_bytes = include_bytes!("image_img.png").as_slice(); + let decoder = png::Decoder::new(png_bytes); + let mut reader = decoder.read_info().unwrap(); + let info = reader.info(); + let img_size = [info.width, info.height]; + let extent = [info.width * 2, info.height * 2, 1]; + + let upload_buffer = Buffer::new_slice( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, ..Default::default() - }] - .into(), - ..CopyBufferToImageInfo::buffer_image(upload_buffer, image.clone()) - }) - .unwrap() - // Copy from the top left corner to the bottom right corner. - .copy_image(CopyImageInfo { - // Copying within the same image requires the General layout if the source and - // destination subresources overlap. - src_image_layout: ImageLayout::General, - dst_image_layout: ImageLayout::General, - regions: [ImageCopy { - src_subresource: image.subresource_layers(), - src_offset: [0, 0, 0], - dst_subresource: image.subresource_layers(), - dst_offset: [img_size[0], img_size[1], 0], - extent: [img_size[0], img_size[1], 1], + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() - }] - .into(), - ..CopyImageInfo::images(image.clone(), image.clone()) - }) - .unwrap() - // Blit from the bottom right corner to the top right corner (flipped). - .blit_image(BlitImageInfo { - // Same as above applies for blitting. - src_image_layout: ImageLayout::General, - dst_image_layout: ImageLayout::General, - regions: [ImageBlit { - src_subresource: image.subresource_layers(), - src_offsets: [ - [img_size[0], img_size[1], 0], - [img_size[0] * 2, img_size[1] * 2, 1], - ], - dst_subresource: image.subresource_layers(), - // Swapping the two corners results in flipped image. - dst_offsets: [ - [img_size[0] * 2 - 1, img_size[1] - 1, 0], - [img_size[0], 0, 1], - ], + }, + (info.width * info.height * 4) as DeviceSize, + ) + .unwrap(); + + reader + .next_frame(&mut upload_buffer.write().unwrap()) + .unwrap(); + + let image = Image::new( + memory_allocator, + ImageCreateInfo { + format: Format::R8G8B8A8_UNORM, + extent, + usage: ImageUsage::TRANSFER_SRC + | ImageUsage::TRANSFER_DST + | ImageUsage::SAMPLED, ..Default::default() - }] - .into(), - filter: Filter::Nearest, - ..BlitImageInfo::images(image.clone(), image.clone()) - }) + }, + AllocationCreateInfo::default(), + ) .unwrap(); - ImageView::new_default(image).unwrap() - }; + // Here, we perform image copying and blitting on the same image. + uploads + // Clear the image buffer. + .clear_color_image(ClearColorImageInfo::image(image.clone())) + .unwrap() + // Put our image in the top left corner. + .copy_buffer_to_image(CopyBufferToImageInfo { + regions: [BufferImageCopy { + image_subresource: image.subresource_layers(), + image_extent: [img_size[0], img_size[1], 1], + ..Default::default() + }] + .into(), + ..CopyBufferToImageInfo::buffer_image(upload_buffer, image.clone()) + }) + .unwrap() + // Copy from the top left corner to the bottom right corner. + .copy_image(CopyImageInfo { + // Copying within the same image requires the General layout if the source and + // destination subresources overlap. + src_image_layout: ImageLayout::General, + dst_image_layout: ImageLayout::General, + regions: [ImageCopy { + src_subresource: image.subresource_layers(), + src_offset: [0, 0, 0], + dst_subresource: image.subresource_layers(), + dst_offset: [img_size[0], img_size[1], 0], + extent: [img_size[0], img_size[1], 1], + ..Default::default() + }] + .into(), + ..CopyImageInfo::images(image.clone(), image.clone()) + }) + .unwrap() + // Blit from the bottom right corner to the top right corner (flipped). + .blit_image(BlitImageInfo { + // Same as above applies for blitting. + src_image_layout: ImageLayout::General, + dst_image_layout: ImageLayout::General, + regions: [ImageBlit { + src_subresource: image.subresource_layers(), + src_offsets: [ + [img_size[0], img_size[1], 0], + [img_size[0] * 2, img_size[1] * 2, 1], + ], + dst_subresource: image.subresource_layers(), + // Swapping the two corners results in flipped image. + dst_offsets: [ + [img_size[0] * 2 - 1, img_size[1] - 1, 0], + [img_size[0], 0, 1], + ], + ..Default::default() + }] + .into(), + filter: Filter::Nearest, + ..BlitImageInfo::images(image.clone(), image.clone()) + }) + .unwrap(); - let sampler = Sampler::new( - device.clone(), - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - ..Default::default() - }, - ) - .unwrap(); - - let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); - - App { - instance, - device, - queue, - descriptor_set_allocator, - command_buffer_allocator, - vertex_buffer, - texture, - sampler, - rcx: None, - } + ImageView::new_default(image).unwrap() + }; + + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + ) + .unwrap(); + + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let layout = &pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), + layout.clone(), + [ + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view(1, self.texture.clone()), + ], + [], ) .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let layout = &pipeline.layout().set_layouts()[0]; - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - layout.clone(), - [ - WriteDescriptorSet::sampler(0, self.sampler.clone()), - WriteDescriptorSet::image_view(1, self.texture.clone()), - ], - [], - ) - .unwrap(); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - descriptor_set, - recreate_swapchain: false, - previous_frame_end, - }); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -504,15 +510,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -555,7 +565,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -570,7 +582,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/image/main.rs b/examples/image/main.rs index 98d5a3944b..92505b2bab 100644 --- a/examples/image/main.rs +++ b/examples/image/main.rs @@ -84,330 +84,334 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .unwrap(); - - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertices = [ - MyVertex { - position: [-0.5, -0.5], - }, - MyVertex { - position: [-0.5, 0.5], - }, - MyVertex { - position: [0.5, -0.5], - }, - MyVertex { - position: [0.5, 0.5], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let mut uploads = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), - CommandBufferLevel::Primary, - CommandBufferBeginInfo { - usage: CommandBufferUsage::OneTimeSubmit, - ..Default::default() - }, - ) - .unwrap(); + }, + ) + .unwrap(); - let texture = { - let png_bytes = include_bytes!("image_img.png").as_slice(); - let decoder = png::Decoder::new(png_bytes); - let mut reader = decoder.read_info().unwrap(); - let info = reader.info(); - let extent = [info.width, info.height, 1]; + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); - let upload_buffer = Buffer::new_slice( + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() + }, + ) + .unwrap(); + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-0.5, -0.5], + }, + MyVertex { + position: [-0.5, 0.5], + }, + MyVertex { + position: [0.5, -0.5], + }, + MyVertex { + position: [0.5, 0.5], + }, + ]; + let vertex_buffer = Buffer::from_iter( memory_allocator.clone(), BufferCreateInfo { - usage: BufferUsage::TRANSFER_SRC, + usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - (info.width * info.height * 4) as DeviceSize, + vertices, ) .unwrap(); - reader - .next_frame(&mut upload_buffer.write().unwrap()) - .unwrap(); - - let image = Image::new( - memory_allocator, - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::R8G8B8A8_SRGB, - extent, - usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + let mut uploads = RecordingCommandBuffer::new( + command_buffer_allocator.clone(), + queue.queue_family_index(), + CommandBufferLevel::Primary, + CommandBufferBeginInfo { + usage: CommandBufferUsage::OneTimeSubmit, ..Default::default() }, - AllocationCreateInfo::default(), ) .unwrap(); - uploads - .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( - upload_buffer, - image.clone(), - )) + let texture = { + let png_bytes = include_bytes!("image_img.png").as_slice(); + let decoder = png::Decoder::new(png_bytes); + let mut reader = decoder.read_info().unwrap(); + let info = reader.info(); + let extent = [info.width, info.height, 1]; + + let upload_buffer = Buffer::new_slice( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + (info.width * info.height * 4) as DeviceSize, + ) + .unwrap(); + + reader + .next_frame(&mut upload_buffer.write().unwrap()) + .unwrap(); + + let image = Image::new( + memory_allocator, + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_SRGB, + extent, + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) .unwrap(); - ImageView::new_default(image).unwrap() - }; + uploads + .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( + upload_buffer, + image.clone(), + )) + .unwrap(); - let sampler = Sampler::new( - device.clone(), - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - ..Default::default() - }, - ) - .unwrap(); - - let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); - - App { - instance, - device, - queue, - descriptor_set_allocator, - command_buffer_allocator, - vertex_buffer, - texture, - sampler, - rcx: None, - } + ImageView::new_default(image).unwrap() + }; + + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + ) + .unwrap(); + + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window.inner_size().into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let layout = &pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), + layout.clone(), + [ + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view(1, self.texture.clone()), + ], + [], ) .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let layout = &pipeline.layout().set_layouts()[0]; - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - layout.clone(), - [ - WriteDescriptorSet::sampler(0, self.sampler.clone()), - WriteDescriptorSet::image_view(1, self.texture.clone()), - ], - [], - ) - .unwrap(); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - descriptor_set, - recreate_swapchain: false, - previous_frame_end, - }); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -449,15 +453,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -500,7 +508,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -515,7 +525,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/immutable-sampler/main.rs b/examples/immutable-sampler/main.rs index 0a4ea13be6..a4ef4c43f3 100644 --- a/examples/immutable-sampler/main.rs +++ b/examples/immutable-sampler/main.rs @@ -19,7 +19,7 @@ use vulkano::{ }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, - QueueCreateInfo, QueueFlags + QueueCreateInfo, QueueFlags, }, format::Format, image::{ @@ -90,342 +90,347 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .unwrap(); - - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertices = [ - MyVertex { - position: [-0.5, -0.5], - }, - MyVertex { - position: [-0.5, 0.5], - }, - MyVertex { - position: [0.5, -0.5], - }, - MyVertex { - position: [0.5, 0.5], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let mut uploads = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), - CommandBufferLevel::Primary, - CommandBufferBeginInfo { - usage: CommandBufferUsage::OneTimeSubmit, - ..Default::default() - }, - ) - .unwrap(); + }, + ) + .unwrap(); - let texture = { - let png_bytes = include_bytes!("image_img.png").as_slice(); - let decoder = png::Decoder::new(png_bytes); - let mut reader = decoder.read_info().unwrap(); - let info = reader.info(); - let extent = [info.width, info.height, 1]; + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); - let upload_buffer = Buffer::new_slice( + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() + }, + ) + .unwrap(); + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-0.5, -0.5], + }, + MyVertex { + position: [-0.5, 0.5], + }, + MyVertex { + position: [0.5, -0.5], + }, + MyVertex { + position: [0.5, 0.5], + }, + ]; + let vertex_buffer = Buffer::from_iter( memory_allocator.clone(), BufferCreateInfo { - usage: BufferUsage::TRANSFER_SRC, + usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - (info.width * info.height * 4) as DeviceSize, + vertices, ) .unwrap(); - reader - .next_frame(&mut upload_buffer.write().unwrap()) - .unwrap(); - - let image = Image::new( - memory_allocator, - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::R8G8B8A8_SRGB, - extent, - usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + let mut uploads = RecordingCommandBuffer::new( + command_buffer_allocator.clone(), + queue.queue_family_index(), + CommandBufferLevel::Primary, + CommandBufferBeginInfo { + usage: CommandBufferUsage::OneTimeSubmit, ..Default::default() }, - AllocationCreateInfo::default(), ) .unwrap(); - uploads - .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( - upload_buffer, - image.clone(), - )) + let texture = { + let png_bytes = include_bytes!("image_img.png").as_slice(); + let decoder = png::Decoder::new(png_bytes); + let mut reader = decoder.read_info().unwrap(); + let info = reader.info(); + let extent = [info.width, info.height, 1]; + + let upload_buffer = Buffer::new_slice( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + (info.width * info.height * 4) as DeviceSize, + ) .unwrap(); - ImageView::new_default(image).unwrap() - }; + reader + .next_frame(&mut upload_buffer.write().unwrap()) + .unwrap(); - let sampler = Sampler::new( - device.clone(), - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - ..Default::default() - }, - ) - .unwrap(); - - let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); - - App { - instance, - device, - queue, - descriptor_set_allocator, - command_buffer_allocator, - vertex_buffer, - texture, - sampler, - rcx: None, - } + let image = Image::new( + memory_allocator, + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_SRGB, + extent, + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) + .unwrap(); + + uploads + .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( + upload_buffer, + image.clone(), + )) + .unwrap(); + + ImageView::new_default(image).unwrap() + }; + + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + ) + .unwrap(); + + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = { - let mut layout_create_info = - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); - - // Modify the auto-generated layout by setting an immutable sampler to set 0 binding 0. - layout_create_info.set_layouts[0] - .bindings - .get_mut(&0) + let pipeline = { + let vs = vs::load(self.device.clone()) .unwrap() - .immutable_samplers = vec![self.sampler.clone()]; + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = { + let mut layout_create_info = + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); + + // Modify the auto-generated layout by setting an immutable sampler to set 0 + // binding 0. + layout_create_info.set_layouts[0] + .bindings + .get_mut(&0) + .unwrap() + .immutable_samplers = vec![self.sampler.clone()]; + + PipelineLayout::new( + self.device.clone(), + layout_create_info + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) + .unwrap() + }; + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - PipelineLayout::new( + GraphicsPipeline::new( self.device.clone(), - layout_create_info - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, ) .unwrap() }; - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, + let layout = &pipeline.layout().set_layouts()[0]; + + // Use `image_view` instead of `image_view_sampler`, since the sampler is already in the + // layout. + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), + layout.clone(), + [WriteDescriptorSet::image_view(1, self.texture.clone())], + [], ) - .unwrap() - }; - - let layout = &pipeline.layout().set_layouts()[0]; - - // Use `image_view` instead of `image_view_sampler`, since the sampler is already in the - // layout. - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - layout.clone(), - [WriteDescriptorSet::image_view(1, self.texture.clone())], - [], - ) - .unwrap(); - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - descriptor_set, - recreate_swapchain: false, - previous_frame_end, - }); + .unwrap(); + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -467,15 +472,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -518,7 +527,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -533,7 +544,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/indirect/main.rs b/examples/indirect/main.rs index 794208fdaf..f87c4a0b07 100644 --- a/examples/indirect/main.rs +++ b/examples/indirect/main.rs @@ -29,7 +29,7 @@ use vulkano::{ }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, - QueueCreateInfo, QueueFlags + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -96,327 +96,331 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - khr_storage_buffer_storage_class: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .unwrap(); - - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // Each frame we generate a new set of vertices and each frame we need a new - // `DrawIndirectCommand` struct to set the number of vertices to draw. - let indirect_buffer_allocator = SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::INDIRECT_BUFFER | BufferUsage::STORAGE_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - let vertex_buffer_allocator = SubbufferAllocator::new( - memory_allocator, - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::STORAGE_BUFFER | BufferUsage::VERTEX_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - - // A simple compute shader that generates vertices. It has two buffers bound: the first is - // where we output the vertices, the second is the `IndirectDrawArgs` struct we passed the - // `draw_indirect` so we can set the number to vertices to draw. - mod cs { - vulkano_shaders::shader! { - ty: "compute", - src: r" - #version 450 - - layout(local_size_x = 16, local_size_y = 1, local_size_z = 1) in; - - layout(set = 0, binding = 0) buffer Output { - vec2 pos[]; - } triangles; - - layout(set = 0, binding = 1) buffer IndirectDrawArgs { - uint vertices; - uint unused0; - uint unused1; - uint unused2; - }; - - void main() { - uint idx = gl_GlobalInvocationID.x; - - // Each invocation of the compute shader is going to increment the counter, so - // we need to use atomic operations for safety. The previous value of the - // counter is returned so that gives us the offset into the vertex buffer this - // thread can write it's vertices into. - uint offset = atomicAdd(vertices, 6); - - vec2 center = vec2(-0.8, -0.8) + idx * vec2(0.1, 0.1); - triangles.pos[offset + 0] = center + vec2(0.0, 0.0375); - triangles.pos[offset + 1] = center + vec2(0.025, -0.01725); - triangles.pos[offset + 2] = center + vec2(-0.025, -0.01725); - triangles.pos[offset + 3] = center + vec2(0.0, -0.0375); - triangles.pos[offset + 4] = center + vec2(0.025, 0.01725); - triangles.pos[offset + 5] = center + vec2(-0.025, 0.01725); - } - ", - } - } + }, + ) + .unwrap(); - let compute_pipeline = { - let cs = cs::load(device.clone()) + let device_extensions = DeviceExtensions { + khr_swapchain: true, + khr_storage_buffer_storage_class: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() .unwrap() - .entry_point("main") + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) .unwrap(); - let stage = PipelineShaderStageCreateInfo::new(cs); - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage]) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() + }, ) .unwrap(); - ComputePipeline::new( + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( device.clone(), - None, - ComputePipelineCreateInfo::stage_layout(stage, layout), - ) - .unwrap() - }; - - App { - instance, - device, - queue, - descriptor_set_allocator, - command_buffer_allocator, - indirect_buffer_allocator, - vertex_buffer_allocator, - compute_pipeline, - rcx: None, - } + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // Each frame we generate a new set of vertices and each frame we need a new + // `DrawIndirectCommand` struct to set the number of vertices to draw. + let indirect_buffer_allocator = SubbufferAllocator::new( + memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::INDIRECT_BUFFER | BufferUsage::STORAGE_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); + let vertex_buffer_allocator = SubbufferAllocator::new( + memory_allocator, + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::STORAGE_BUFFER | BufferUsage::VERTEX_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); + + // A simple compute shader that generates vertices. It has two buffers bound: the first is + // where we output the vertices, the second is the `IndirectDrawArgs` struct we passed the + // `draw_indirect` so we can set the number to vertices to draw. + mod cs { + vulkano_shaders::shader! { + ty: "compute", + src: r" + #version 450 + + layout(local_size_x = 16, local_size_y = 1, local_size_z = 1) in; + + layout(set = 0, binding = 0) buffer Output { + vec2 pos[]; + } triangles; + + layout(set = 0, binding = 1) buffer IndirectDrawArgs { + uint vertices; + uint unused0; + uint unused1; + uint unused2; + }; + + void main() { + uint idx = gl_GlobalInvocationID.x; + + // Each invocation of the compute shader is going to increment the counter, + // so we need to use atomic operations for safety. The previous value of + // the counter is returned so that gives us the offset into the vertex + // buffer this thread can write it's vertices into. + uint offset = atomicAdd(vertices, 6); + + vec2 center = vec2(-0.8, -0.8) + idx * vec2(0.1, 0.1); + triangles.pos[offset + 0] = center + vec2(0.0, 0.0375); + triangles.pos[offset + 1] = center + vec2(0.025, -0.01725); + triangles.pos[offset + 2] = center + vec2(-0.025, -0.01725); + triangles.pos[offset + 3] = center + vec2(0.0, -0.0375); + triangles.pos[offset + 4] = center + vec2(0.025, 0.01725); + triangles.pos[offset + 5] = center + vec2(-0.025, 0.01725); + } + ", + } + } + + let compute_pipeline = { + let cs = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let stage = PipelineShaderStageCreateInfo::new(cs); + let layout = PipelineLayout::new( + device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage]) + .into_pipeline_layout_create_info(device.clone()) + .unwrap(), + ) + .unwrap(); + ComputePipeline::new( + device.clone(), + None, + ComputePipelineCreateInfo::stage_layout(stage, layout), + ) + .unwrap() + }; + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + indirect_buffer_allocator, + vertex_buffer_allocator, + compute_pipeline, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 - // The triangle vertex positions. - layout(location = 0) in vec2 position; + // The triangle vertex positions. + layout(location = 0) in vec2 position; - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } } - } - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - layout(location = 0) out vec4 f_color; + layout(location = 0) out vec4 f_color; - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - ", + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } } - } - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain: false, - previous_frame_end, - }); + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -458,15 +462,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -493,7 +501,10 @@ impl ApplicationHandler for App { // Allocate a buffer to hold this frame's vertices. This needs to be large enough // to hold the worst case number of vertices generated by the compute shader. let iter = (0..(6 * 16)).map(|_| MyVertex { position: [0.0; 2] }); - let vertices = self.vertex_buffer_allocator.allocate_slice(iter.len() as _).unwrap(); + let vertices = self + .vertex_buffer_allocator + .allocate_slice(iter.len() as _) + .unwrap(); for (o, i) in vertices.write().unwrap().iter_mut().zip(iter) { *o = i; } @@ -576,7 +587,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/instancing/main.rs b/examples/instancing/main.rs index a7f18968ca..d92f653934 100644 --- a/examples/instancing/main.rs +++ b/examples/instancing/main.rs @@ -75,310 +75,314 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // We now create a buffer that will store the shape of our triangle. This triangle is identical - // to the one in the `triangle.rs` example. - let vertices = [ - TriangleVertex { - position: [-0.5, -0.25], - }, - TriangleVertex { - position: [0.0, 0.5], - }, - TriangleVertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - // Now we create another buffer that will store the unique data per instance. For this example, - // we'll have the instances form a 10x10 grid that slowly gets larger. - let instances = { - let rows = 10; - let cols = 10; - let n_instances = rows * cols; - let mut data = Vec::new(); - for c in 0..cols { - for r in 0..rows { - let half_cell_w = 0.5 / cols as f32; - let half_cell_h = 0.5 / rows as f32; - let x = half_cell_w + (c as f32 / cols as f32) * 2.0 - 1.0; - let y = half_cell_h + (r as f32 / rows as f32) * 2.0 - 1.0; - let position_offset = [x, y]; - let scale = (2.0 / rows as f32) * (c * rows + r) as f32 / n_instances as f32; - data.push(InstanceData { - position_offset, - scale, - }); + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // We now create a buffer that will store the shape of our triangle. This triangle is + // identical to the one in the `triangle.rs` example. + let vertices = [ + TriangleVertex { + position: [-0.5, -0.25], + }, + TriangleVertex { + position: [0.0, 0.5], + }, + TriangleVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + // Now we create another buffer that will store the unique data per instance. For this + // example, we'll have the instances form a 10x10 grid that slowly gets larger. + let instances = { + let rows = 10; + let cols = 10; + let n_instances = rows * cols; + let mut data = Vec::new(); + for c in 0..cols { + for r in 0..rows { + let half_cell_w = 0.5 / cols as f32; + let half_cell_h = 0.5 / rows as f32; + let x = half_cell_w + (c as f32 / cols as f32) * 2.0 - 1.0; + let y = half_cell_h + (r as f32 / rows as f32) * 2.0 - 1.0; + let position_offset = [x, y]; + let scale = (2.0 / rows as f32) * (c * rows + r) as f32 / n_instances as f32; + data.push(InstanceData { + position_offset, + scale, + }); + } } + data + }; + let instance_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + instances, + ) + .unwrap(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + instance_buffer, + rcx: None, } - data - }; - let instance_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - instances, - ) - .unwrap(); - - App { - instance, - device, - queue, - command_buffer_allocator, - vertex_buffer, - instance_buffer, - rcx: None, - } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 - // The triangle vertex positions. - layout(location = 0) in vec2 position; + // The triangle vertex positions. + layout(location = 0) in vec2 position; - // The per-instance data. - layout(location = 1) in vec2 position_offset; - layout(location = 2) in float scale; + // The per-instance data. + layout(location = 1) in vec2 position_offset; + layout(location = 2) in float scale; - void main() { - // Apply the scale and offset for the instance. - gl_Position = vec4(position * scale + position_offset, 0.0, 1.0); - } - ", + void main() { + // Apply the scale and offset for the instance. + gl_Position = vec4(position * scale + position_offset, 0.0, 1.0); + } + ", + } } - } - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - layout(location = 0) out vec4 f_color; + layout(location = 0) out vec4 f_color; - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - ", + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } } - } - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = [TriangleVertex::per_vertex(), InstanceData::per_instance()] + .definition(&vs) + .unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + // Use the implementations of the `Vertex` trait to describe to vulkano how the + // two vertex types are expected to be used. + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = [TriangleVertex::per_vertex(), InstanceData::per_instance()] - .definition(&vs) - .unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - // Use the implementations of the `Vertex` trait to describe to vulkano how the two - // vertex types are expected to be used. - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain: false, - previous_frame_end, - }); + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -420,15 +424,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -461,7 +469,10 @@ impl ApplicationHandler for App { .bind_pipeline_graphics(rcx.pipeline.clone()) .unwrap() // We pass both our lists of vertices here. - .bind_vertex_buffers(0, (self.vertex_buffer.clone(), self.instance_buffer.clone())) + .bind_vertex_buffers( + 0, + (self.vertex_buffer.clone(), self.instance_buffer.clone()), + ) .unwrap(); unsafe { @@ -487,7 +498,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/interactive-fractal/fractal_compute_pipeline.rs b/examples/interactive-fractal/fractal_compute_pipeline.rs index efe4d5adec..c54cdaa79e 100644 --- a/examples/interactive-fractal/fractal_compute_pipeline.rs +++ b/examples/interactive-fractal/fractal_compute_pipeline.rs @@ -171,7 +171,12 @@ impl FractalComputePipeline { builder .bind_pipeline_compute(self.pipeline.clone()) .unwrap() - .bind_descriptor_sets(PipelineBindPoint::Compute, self.pipeline.layout().clone(), 0, descriptor_set) + .bind_descriptor_sets( + PipelineBindPoint::Compute, + self.pipeline.layout().clone(), + 0, + descriptor_set, + ) .unwrap() .push_constants(self.pipeline.layout().clone(), 0, push_constants) .unwrap(); diff --git a/examples/interactive-fractal/input.rs b/examples/interactive-fractal/input.rs index 5413f4a1b8..6fc65b7ac0 100644 --- a/examples/interactive-fractal/input.rs +++ b/examples/interactive-fractal/input.rs @@ -73,15 +73,15 @@ impl InputState { pub fn handle_input(&mut self, window_size: PhysicalSize, event: &WindowEvent) { self.window_size = window_size.into(); - match event { - WindowEvent::KeyboardInput { event, .. } => self.on_keyboard_event(event), - WindowEvent::MouseInput { state, button, .. } => { - self.on_mouse_click_event(*state, *button) - } - WindowEvent::CursorMoved { position, .. } => self.on_cursor_moved_event(position), - WindowEvent::MouseWheel { delta, .. } => self.on_mouse_wheel_event(delta), - _ => {} + match event { + WindowEvent::KeyboardInput { event, .. } => self.on_keyboard_event(event), + WindowEvent::MouseInput { state, button, .. } => { + self.on_mouse_click_event(*state, *button) } + WindowEvent::CursorMoved { position, .. } => self.on_cursor_moved_event(position), + WindowEvent::MouseWheel { delta, .. } => self.on_mouse_wheel_event(delta), + _ => {} + } } /// Matches keyboard events to our defined inputs. diff --git a/examples/interactive-fractal/main.rs b/examples/interactive-fractal/main.rs index 60cf7c788c..215544c845 100644 --- a/examples/interactive-fractal/main.rs +++ b/examples/interactive-fractal/main.rs @@ -10,15 +10,18 @@ // - A simple `FractalApp` to handle runtime state. // - A simple `InputState` to interact with the application. -use std::{error::Error, sync::Arc, time::{Duration, Instant}}; -use input::InputState; use fractal_compute_pipeline::FractalComputePipeline; use glam::Vec2; +use input::InputState; use place_over_frame::RenderPassPlaceOverFrame; +use std::{ + error::Error, + sync::Arc, + time::{Duration, Instant}, +}; use vulkano::{ command_buffer::allocator::{ - StandardCommandBufferAllocator, - StandardCommandBufferAllocatorCreateInfo, + StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo, }, descriptor_set::allocator::StandardDescriptorSetAllocator, image::ImageUsage, @@ -106,85 +109,85 @@ struct RenderContext { impl App { fn new(_event_loop: &EventLoop<()>) -> Self { - let context = VulkanoContext::new(VulkanoConfig::default()); - let windows = VulkanoWindows::default(); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - context.device().clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - context.device().clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, - ..Default::default() - }, - )); - - App { - context, - windows, - descriptor_set_allocator, - command_buffer_allocator, - rcx: None, - } + let context = VulkanoContext::new(VulkanoConfig::default()); + let windows = VulkanoWindows::default(); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + context.device().clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + context.device().clone(), + StandardCommandBufferAllocatorCreateInfo { + secondary_buffer_count: 32, + ..Default::default() + }, + )); + + App { + context, + windows, + descriptor_set_allocator, + command_buffer_allocator, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let _id = self.windows.create_window( - event_loop, - &self.context, - &WindowDescriptor { - title: "Fractal".to_string(), - present_mode: PresentMode::Fifo, - ..Default::default() - }, - |_| {}, - ); - - // Add our render target image onto which we'll be rendering our fractals. - let render_target_id = 0; - let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); - - // Make sure the image usage is correct (based on your pipeline). - window_renderer.add_additional_image_view( - render_target_id, - DEFAULT_IMAGE_FORMAT, - ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST, - ); + let _id = self.windows.create_window( + event_loop, + &self.context, + &WindowDescriptor { + title: "Fractal".to_string(), + present_mode: PresentMode::Fifo, + ..Default::default() + }, + |_| {}, + ); + + // Add our render target image onto which we'll be rendering our fractals. + let render_target_id = 0; + let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); - let gfx_queue = self.context.graphics_queue(); - - self.rcx = Some(RenderContext { - render_target_id, - fractal_pipeline: FractalComputePipeline::new( - gfx_queue.clone(), - self.context.memory_allocator().clone(), - self.command_buffer_allocator.clone(), - self.descriptor_set_allocator.clone(), - ), - place_over_frame: RenderPassPlaceOverFrame::new( - gfx_queue.clone(), - self.command_buffer_allocator.clone(), - self.descriptor_set_allocator.clone(), - window_renderer.swapchain_format(), - window_renderer.swapchain_image_views(), - ), - is_julia: false, - is_c_paused: false, - c: Vec2::new(0.0, 0.0), - scale: Vec2::new(4.0, 4.0), - translation: Vec2::new(0.0, 0.0), - max_iters: MAX_ITERS_INIT, - time: Instant::now(), - dt: 0.0, - dt_sum: 0.0, - frame_count: 0.0, - avg_fps: 0.0, - input_state: InputState::new(), - }); + // Make sure the image usage is correct (based on your pipeline). + window_renderer.add_additional_image_view( + render_target_id, + DEFAULT_IMAGE_FORMAT, + ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST, + ); + + let gfx_queue = self.context.graphics_queue(); + + self.rcx = Some(RenderContext { + render_target_id, + fractal_pipeline: FractalComputePipeline::new( + gfx_queue.clone(), + self.context.memory_allocator().clone(), + self.command_buffer_allocator.clone(), + self.descriptor_set_allocator.clone(), + ), + place_over_frame: RenderPassPlaceOverFrame::new( + gfx_queue.clone(), + self.command_buffer_allocator.clone(), + self.descriptor_set_allocator.clone(), + window_renderer.swapchain_format(), + window_renderer.swapchain_image_views(), + ), + is_julia: false, + is_c_paused: false, + c: Vec2::new(0.0, 0.0), + scale: Vec2::new(4.0, 4.0), + translation: Vec2::new(0.0, 0.0), + max_iters: MAX_ITERS_INIT, + time: Instant::now(), + dt: 0.0, + dt_sum: 0.0, + frame_count: 0.0, + avg_fps: 0.0, + input_state: InputState::new(), + }); } fn window_event( @@ -193,37 +196,39 @@ impl ApplicationHandler for App { _window_id: WindowId, event: WindowEvent, ) { - let renderer = self.windows.get_primary_renderer_mut().unwrap(); - let rcx = self.rcx.as_mut().unwrap(); - let window_size = renderer.window().inner_size(); + let renderer = self.windows.get_primary_renderer_mut().unwrap(); + let rcx = self.rcx.as_mut().unwrap(); + let window_size = renderer.window().inner_size(); - match event { - WindowEvent::CloseRequested => { - event_loop.exit(); - } - WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. } => { - renderer.resize(); - } - WindowEvent::RedrawRequested => { - // Tasks for redrawing: - // 1. Update state based on events - // 2. Compute & Render - // 3. Reset input state - // 4. Update time & title - - // Skip this frame when minimized. - if window_size.width == 0 || window_size.height == 0 { - return; + match event { + WindowEvent::CloseRequested => { + event_loop.exit(); } - - rcx.update_state_after_inputs(renderer); - - // Start the frame. - let before_pipeline_future = - match renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| { - rcx.place_over_frame - .recreate_framebuffers(swapchain_image_views) - }) { + WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. } => { + renderer.resize(); + } + WindowEvent::RedrawRequested => { + // Tasks for redrawing: + // 1. Update state based on events + // 2. Compute & Render + // 3. Reset input state + // 4. Update time & title + + // Skip this frame when minimized. + if window_size.width == 0 || window_size.height == 0 { + return; + } + + rcx.update_state_after_inputs(renderer); + + // Start the frame. + let before_pipeline_future = match renderer.acquire( + Some(Duration::from_millis(1000)), + |swapchain_image_views| { + rcx.place_over_frame + .recreate_framebuffers(swapchain_image_views) + }, + ) { Err(e) => { println!("{e}"); return; @@ -231,53 +236,54 @@ impl ApplicationHandler for App { Ok(future) => future, }; - // Retrieve the target image. - let image = renderer.get_additional_image_view(rcx.render_target_id); - - // Compute our fractal (writes to target image). Join future with `before_pipeline_future`. - let after_compute = rcx - .fractal_pipeline - .compute( - image.clone(), - rcx.c, - rcx.scale, - rcx.translation, - rcx.max_iters, - rcx.is_julia, - ) - .join(before_pipeline_future); - - // Render the image over the swapchain image, inputting the previous future. - let after_renderpass_future = rcx.place_over_frame.render( - after_compute, - image, - renderer.swapchain_image_view(), - renderer.image_index(), - ); - - // Finish the frame (which presents the view), inputting the last future. Wait for the future - // so resources are not in use when we render. - renderer.present(after_renderpass_future, true); - - rcx.input_state.reset(); - rcx.update_time(); - renderer.window().set_title(&format!( - "{} fps: {:.2} dt: {:.2}, Max Iterations: {}", - if rcx.is_julia { "Julia" } else { "Mandelbrot" }, - rcx.avg_fps(), - rcx.dt(), - rcx.max_iters - )); - } - _ => { - // Pass event for the app to handle our inputs. - rcx.input_state.handle_input(window_size, &event); + // Retrieve the target image. + let image = renderer.get_additional_image_view(rcx.render_target_id); + + // Compute our fractal (writes to target image). Join future with + // `before_pipeline_future`. + let after_compute = rcx + .fractal_pipeline + .compute( + image.clone(), + rcx.c, + rcx.scale, + rcx.translation, + rcx.max_iters, + rcx.is_julia, + ) + .join(before_pipeline_future); + + // Render the image over the swapchain image, inputting the previous future. + let after_renderpass_future = rcx.place_over_frame.render( + after_compute, + image, + renderer.swapchain_image_view(), + renderer.image_index(), + ); + + // Finish the frame (which presents the view), inputting the last future. Wait for + // the future so resources are not in use when we render. + renderer.present(after_renderpass_future, true); + + rcx.input_state.reset(); + rcx.update_time(); + renderer.window().set_title(&format!( + "{} fps: {:.2} dt: {:.2}, Max Iterations: {}", + if rcx.is_julia { "Julia" } else { "Mandelbrot" }, + rcx.avg_fps(), + rcx.dt(), + rcx.max_iters + )); + } + _ => { + // Pass event for the app to handle our inputs. + rcx.input_state.handle_input(window_size, &event); + } } - } - if rcx.input_state.should_quit { - event_loop.exit(); - } + if rcx.input_state.should_quit { + event_loop.exit(); + } } fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { diff --git a/examples/mesh-shader/main.rs b/examples/mesh-shader/main.rs index 9644470bc6..a27368c2c1 100644 --- a/examples/mesh-shader/main.rs +++ b/examples/mesh-shader/main.rs @@ -92,297 +92,301 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ext_mesh_shader: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) + .unwrap(); + + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ext_mesh_shader: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + enabled_features: DeviceFeatures { + mesh_shader: true, + ..DeviceFeatures::default() + }, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - mesh_shader: true, - ..DeviceFeatures::default() + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // We now create a buffer that will store the shape of our triangle. This triangle is + // identical to the one in the `triangle.rs` example. + let vertices = [ + TriangleVertex { + position: [-0.5, -0.25], + }, + TriangleVertex { + position: [0.0, 0.5], + }, + TriangleVertex { + position: [0.25, -0.1], }, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::STORAGE_BUFFER, ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // We now create a buffer that will store the shape of our triangle. This triangle is identical - // to the one in the `triangle.rs` example. - let vertices = [ - TriangleVertex { - position: [-0.5, -0.25], - }, - TriangleVertex { - position: [0.0, 0.5], - }, - TriangleVertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::STORAGE_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - // Now we create another buffer that will store the unique data per instance. For this example, - // we'll have the instances form a 10x10 grid that slowly gets larger. - let rows = 10; - let cols = 10; - let instances = { - let n_instances = rows * cols; - let mut data = Vec::new(); - for c in 0..cols { - for r in 0..rows { - let half_cell_w = 0.5 / cols as f32; - let half_cell_h = 0.5 / rows as f32; - let x = half_cell_w + (c as f32 / cols as f32) * 2.0 - 1.0; - let y = half_cell_h + (r as f32 / rows as f32) * 2.0 - 1.0; - let position_offset = [x, y]; - let scale = (2.0 / rows as f32) * (c * rows + r) as f32 / n_instances as f32; - data.push(InstanceData { - position_offset, - scale, - }); + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + // Now we create another buffer that will store the unique data per instance. For this + // example, we'll have the instances form a 10x10 grid that slowly gets larger. + let rows = 10; + let cols = 10; + let instances = { + let n_instances = rows * cols; + let mut data = Vec::new(); + for c in 0..cols { + for r in 0..rows { + let half_cell_w = 0.5 / cols as f32; + let half_cell_h = 0.5 / rows as f32; + let x = half_cell_w + (c as f32 / cols as f32) * 2.0 - 1.0; + let y = half_cell_h + (r as f32 / rows as f32) * 2.0 - 1.0; + let position_offset = [x, y]; + let scale = (2.0 / rows as f32) * (c * rows + r) as f32 / n_instances as f32; + data.push(InstanceData { + position_offset, + scale, + }); + } + } + data + }; + let instance_buffer = Buffer::new_unsized::( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::STORAGE_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + instances.len() as DeviceSize, + ) + .unwrap(); + { + let mut guard = instance_buffer.write().unwrap(); + for (i, instance) in instances.iter().enumerate() { + guard.instance[i] = Padded(*instance); } } - data - }; - let instance_buffer = Buffer::new_unsized::( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::STORAGE_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - instances.len() as DeviceSize, - ) - .unwrap(); - { - let mut guard = instance_buffer.write().unwrap(); - for (i, instance) in instances.iter().enumerate() { - guard.instance[i] = Padded(*instance); - } - } - App { - instance, - device, - queue, - vertex_buffer, - instance_buffer, - rows, - cols, - descriptor_set_allocator, - command_buffer_allocator, - rcx: None, - } + App { + instance, + device, + queue, + vertex_buffer, + instance_buffer, + rows, + cols, + descriptor_set_allocator, + command_buffer_allocator, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let mesh = mesh::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let mesh = mesh::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(mesh), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(mesh), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), + pipeline.layout().set_layouts()[0].clone(), + [ + WriteDescriptorSet::buffer(0, self.vertex_buffer.clone()), + WriteDescriptorSet::buffer(1, self.instance_buffer.clone()), + ], + [], ) .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - pipeline.layout().set_layouts()[0].clone(), - [ - WriteDescriptorSet::buffer(0, self.vertex_buffer.clone()), - WriteDescriptorSet::buffer(1, self.instance_buffer.clone()), - ], - [], - ) - .unwrap(); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - descriptor_set, - recreate_swapchain: false, - previous_frame_end, - }); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -424,15 +428,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -489,7 +497,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/multi-window-game-of-life/game_of_life.rs b/examples/multi-window-game-of-life/game_of_life.rs index fe0bb742ae..eab55b2957 100644 --- a/examples/multi-window-game-of-life/game_of_life.rs +++ b/examples/multi-window-game-of-life/game_of_life.rs @@ -196,9 +196,18 @@ impl GameOfLifeComputePipeline { builder .bind_pipeline_compute(self.compute_life_pipeline.clone()) .unwrap() - .bind_descriptor_sets(PipelineBindPoint::Compute, self.compute_life_pipeline.layout().clone(), 0, descriptor_set) + .bind_descriptor_sets( + PipelineBindPoint::Compute, + self.compute_life_pipeline.layout().clone(), + 0, + descriptor_set, + ) .unwrap() - .push_constants(self.compute_life_pipeline.layout().clone(), 0, push_constants) + .push_constants( + self.compute_life_pipeline.layout().clone(), + 0, + push_constants, + ) .unwrap(); unsafe { diff --git a/examples/multi-window-game-of-life/main.rs b/examples/multi-window-game-of-life/main.rs index 4ef4d1d368..aeab5a584b 100644 --- a/examples/multi-window-game-of-life/main.rs +++ b/examples/multi-window-game-of-life/main.rs @@ -10,15 +10,18 @@ use game_of_life::GameOfLifeComputePipeline; use glam::{f32::Vec2, IVec2}; use render_pass::RenderPassPlaceOverFrame; +use std::{ + collections::HashMap, + error::Error, + sync::Arc, + time::{Duration, Instant}, +}; use vulkano::{ command_buffer::allocator::{ StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo, }, descriptor_set::allocator::StandardDescriptorSetAllocator, }; -use std::{ - collections::HashMap, error::Error, sync::Arc, time::{Duration, Instant} -}; use vulkano_util::{ context::{VulkanoConfig, VulkanoContext}, renderer::VulkanoWindowRenderer, @@ -141,7 +144,7 @@ impl ApplicationHandler for App { life_color: [1.0, 0.0, 0.0, 1.0], dead_color: [0.0; 4], mouse_is_pressed: false, - } + }, ); self.rcxs.insert( id2, @@ -158,7 +161,7 @@ impl ApplicationHandler for App { life_color: [0.0, 0.0, 0.0, 1.0], dead_color: [1.0; 4], mouse_is_pressed: false, - } + }, ); } @@ -247,10 +250,7 @@ fn draw_life( } /// Compute game of life, then display result on target image. -fn compute_then_render( - window_renderer: &mut VulkanoWindowRenderer, - rcx: &mut RenderContext, -) { +fn compute_then_render(window_renderer: &mut VulkanoWindowRenderer, rcx: &mut RenderContext) { // Start the frame. let before_pipeline_future = match window_renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| { @@ -265,9 +265,9 @@ fn compute_then_render( }; // Compute. - let after_compute = rcx - .compute_pipeline - .compute(before_pipeline_future, rcx.life_color, rcx.dead_color); + let after_compute = + rcx.compute_pipeline + .compute(before_pipeline_future, rcx.life_color, rcx.dead_color); // Render. let color_image = rcx.compute_pipeline.color_image(); diff --git a/examples/multi-window-game-of-life/render_pass.rs b/examples/multi-window-game-of-life/render_pass.rs index 28032bf4b6..63f67d83aa 100644 --- a/examples/multi-window-game-of-life/render_pass.rs +++ b/examples/multi-window-game-of-life/render_pass.rs @@ -1,4 +1,4 @@ -use crate::{App, pixels_draw::PixelsDrawPipeline}; +use crate::{pixels_draw::PixelsDrawPipeline, App}; use std::sync::Arc; use vulkano::{ command_buffer::{ @@ -23,11 +23,7 @@ pub struct RenderPassPlaceOverFrame { } impl RenderPassPlaceOverFrame { - pub fn new( - app: &App, - gfx_queue: Arc, - window_id: WindowId, - ) -> RenderPassPlaceOverFrame { + pub fn new(app: &App, gfx_queue: Arc, window_id: WindowId) -> RenderPassPlaceOverFrame { let window_renderer = app.windows.get_renderer(window_id).unwrap(); let render_pass = vulkano::single_pass_renderpass!( gfx_queue.device().clone(), diff --git a/examples/multi-window/main.rs b/examples/multi-window/main.rs index f77d314509..28f60c8d18 100644 --- a/examples/multi-window/main.rs +++ b/examples/multi-window/main.rs @@ -77,274 +77,276 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); + }, + ) + .unwrap(); - let queue = queues.next().unwrap(); + let queue = queues.next().unwrap(); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); - let vertices = [ - MyVertex { - position: [-0.5, -0.25], - }, - MyVertex { - position: [0.0, 0.5], - }, - MyVertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - // A hashmap that contains all of our created windows and their resources. - let rcxs = HashMap::new(); - - App { - instance, - device, - queue, - command_buffer_allocator, - vertex_buffer, - rcxs, - } - } - - fn create_rcx(&self, window: Window) -> RenderContext { - let window = Arc::new(window); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - // The swapchain and framebuffer images for this particular window. - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - Swapchain::new( - self.device.clone(), - surface.clone(), - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + }, + MyVertex { + position: [0.0, 0.5], + }, + MyVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, ) - .unwrap() - }; - - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 + .unwrap(); - layout(location = 0) in vec2 position; + // A hashmap that contains all of our created windows and their resources. + let rcxs = HashMap::new(); - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcxs, } } - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 + fn create_rcx(&self, window: Window) -> RenderContext { + let window = Arc::new(window); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + // The swapchain and framebuffer images for this particular window. + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 - layout(location = 0) out vec4 f_color; + layout(location = 0) in vec2 position; - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - ", + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } } - } - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - let framebuffers = window_size_dependent_setup(&images, &render_pass); + layout(location = 0) out vec4 f_color; - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } + } - GraphicsPipeline::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, }, ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain: false, - previous_frame_end, - } + .unwrap(); + + let framebuffers = window_size_dependent_setup(&images, &render_pass); + + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = event_loop.create_window(Window::default_attributes()).unwrap(); + let window = event_loop + .create_window(Window::default_attributes()) + .unwrap(); - // Use the window's id as a means to access it from the hashmap. - let window_id = window.id(); + // Use the window's id as a means to access it from the hashmap. + let window_id = window.id(); - let rcx = self.create_rcx(window); + let rcx = self.create_rcx(window); - self.rcxs.insert(window_id, rcx); + self.rcxs.insert(window_id, rcx); } fn window_event( @@ -368,7 +370,9 @@ impl ApplicationHandler for App { }, .. } => { - let window = event_loop.create_window(Window::default_attributes()).unwrap(); + let window = event_loop + .create_window(Window::default_attributes()) + .unwrap(); let window_id = window.id(); let rcx = self.create_rcx(window); @@ -400,15 +404,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -444,7 +452,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -459,7 +469,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); @@ -482,9 +495,7 @@ impl ApplicationHandler for App { } fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - self.rcxs - .values() - .for_each(|s| s.window.request_redraw()); + self.rcxs.values().for_each(|s| s.window.request_redraw()); } } diff --git a/examples/occlusion-query/main.rs b/examples/occlusion-query/main.rs index 8c034dc36b..01bb004c05 100644 --- a/examples/occlusion-query/main.rs +++ b/examples/occlusion-query/main.rs @@ -80,336 +80,341 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertices = [ - // The first triangle (red) is the same one as in the triangle example. - MyVertex { - position: [-0.5, -0.25, 0.5], - color: [1.0, 0.0, 0.0], - }, - MyVertex { - position: [0.0, 0.5, 0.5], - color: [1.0, 0.0, 0.0], - }, - MyVertex { - position: [0.25, -0.1, 0.5], - color: [1.0, 0.0, 0.0], - }, - // The second triangle (cyan) is the same shape and position as the first, but smaller, and - // moved behind a bit. It should be completely occluded by the first triangle. (You can - // lower its z value to put it in front.) - MyVertex { - position: [-0.25, -0.125, 0.6], - color: [0.0, 1.0, 1.0], - }, - MyVertex { - position: [0.0, 0.25, 0.6], - color: [0.0, 1.0, 1.0], - }, - MyVertex { - position: [0.125, -0.05, 0.6], - color: [0.0, 1.0, 1.0], - }, - // The third triangle (green) is the same shape and size as the first, but moved to the - // left and behind the second. It is partially occluded by the first two. - MyVertex { - position: [-0.25, -0.25, 0.7], - color: [0.0, 1.0, 0.0], - }, - MyVertex { - position: [0.25, 0.5, 0.7], - color: [0.0, 1.0, 0.0], - }, - MyVertex { - position: [0.5, -0.1, 0.7], - color: [0.0, 1.0, 0.0], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); + }, + ) + .unwrap(); + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + // The first triangle (red) is the same one as in the triangle example. + MyVertex { + position: [-0.5, -0.25, 0.5], + color: [1.0, 0.0, 0.0], + }, + MyVertex { + position: [0.0, 0.5, 0.5], + color: [1.0, 0.0, 0.0], + }, + MyVertex { + position: [0.25, -0.1, 0.5], + color: [1.0, 0.0, 0.0], + }, + // The second triangle (cyan) is the same shape and position as the first, but smaller, + // and moved behind a bit. It should be completely occluded by the first triangle. (You + // can lower its z value to put it in front.) + MyVertex { + position: [-0.25, -0.125, 0.6], + color: [0.0, 1.0, 1.0], + }, + MyVertex { + position: [0.0, 0.25, 0.6], + color: [0.0, 1.0, 1.0], + }, + MyVertex { + position: [0.125, -0.05, 0.6], + color: [0.0, 1.0, 1.0], + }, + // The third triangle (green) is the same shape and size as the first, but moved to the + // left and behind the second. It is partially occluded by the first two. + MyVertex { + position: [-0.25, -0.25, 0.7], + color: [0.0, 1.0, 0.0], + }, + MyVertex { + position: [0.25, 0.5, 0.7], + color: [0.0, 1.0, 0.0], + }, + MyVertex { + position: [0.5, -0.1, 0.7], + color: [0.0, 1.0, 0.0], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); - // Create three buffer slices, one for each triangle. - let triangle1 = vertex_buffer.clone().slice(0..3); - let triangle2 = vertex_buffer.clone().slice(3..6); - let triangle3 = vertex_buffer.slice(6..9); - - // Create a query pool for occlusion queries, with 3 slots. - let query_pool = QueryPool::new( - device.clone(), - QueryPoolCreateInfo { - query_count: 3, - ..QueryPoolCreateInfo::query_type(QueryType::Occlusion) - }, - ) - .unwrap(); + // Create three buffer slices, one for each triangle. + let triangle1 = vertex_buffer.clone().slice(0..3); + let triangle2 = vertex_buffer.clone().slice(3..6); + let triangle3 = vertex_buffer.slice(6..9); + + // Create a query pool for occlusion queries, with 3 slots. + let query_pool = QueryPool::new( + device.clone(), + QueryPoolCreateInfo { + query_count: 3, + ..QueryPoolCreateInfo::query_type(QueryType::Occlusion) + }, + ) + .unwrap(); - // Create a buffer on the CPU to hold the results of the three queries. Query results are - // always represented as either `u32` or `u64`. For occlusion queries, you always need one - // element per query. You can ask for the number of elements needed at runtime by calling - // `QueryType::result_len`. If you retrieve query results with `with_availability` enabled, - // then this array needs to be 6 elements long instead of 3. - let query_results = [0u32; 3]; - - App { - instance, - device, - queue, - memory_allocator, - command_buffer_allocator, - triangle1, - triangle2, - triangle3, - query_pool, - query_results, - rcx: None, - } + // Create a buffer on the CPU to hold the results of the three queries. Query results are + // always represented as either `u32` or `u64`. For occlusion queries, you always need one + // element per query. You can ask for the number of elements needed at runtime by calling + // `QueryType::result_len`. If you retrieve query results with `with_availability` enabled, + // then this array needs to be 6 elements long instead of 3. + let query_results = [0u32; 3]; + + App { + instance, + device, + queue, + memory_allocator, + command_buffer_allocator, + triangle1, + triangle2, + triangle3, + query_pool, + query_results, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + depth_stencil: { + format: Format::D16_UNORM, + samples: 1, + load_op: Clear, + store_op: DontCare, + }, }, - depth_stencil: { - format: Format::D16_UNORM, - samples: 1, - load_op: Clear, - store_op: DontCare, + pass: { + color: [color], + depth_stencil: {depth_stencil}, }, - }, - pass: { - color: [color], - depth_stencil: {depth_stencil}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass, &self.memory_allocator); + let framebuffers = + window_size_dependent_setup(&images, &render_pass, &self.memory_allocator); - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 - layout(location = 0) in vec3 position; - layout(location = 1) in vec3 color; + layout(location = 0) in vec3 position; + layout(location = 1) in vec3 color; - layout(location = 0) out vec3 v_color; + layout(location = 0) out vec3 v_color; - void main() { - v_color = color; - gl_Position = vec4(position, 1.0); - } - ", + void main() { + v_color = color; + gl_Position = vec4(position, 1.0); + } + ", + } } - } - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - layout(location = 0) in vec3 v_color; - layout(location = 0) out vec4 f_color; + layout(location = 0) in vec3 v_color; + layout(location = 0) out vec4 f_color; - void main() { - f_color = vec4(v_color, 1.0); - } - ", + void main() { + f_color = vec4(v_color, 1.0); + } + ", + } } - } - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + // Enable depth testing, which is needed for occlusion queries to make sense at + // all. If you disable depth testing, every pixel is considered to pass the + // depth test, so every query will return a nonzero result. + depth_stencil_state: Some(DepthStencilState { + depth: Some(DepthState::simple()), + ..Default::default() + }), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - // Enable depth testing, which is needed for occlusion queries to make sense at - // all. If you disable depth testing, every pixel is considered to pass the depth - // test, so every query will return a nonzero result. - depth_stencil_state: Some(DepthStencilState { - depth: Some(DepthState::simple()), - ..Default::default() - }), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain: false, - previous_frame_end, - }); + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -455,15 +460,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -554,7 +563,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/push-descriptors/main.rs b/examples/push-descriptors/main.rs index ef9560198f..e1ccf0baca 100644 --- a/examples/push-descriptors/main.rs +++ b/examples/push-descriptors/main.rs @@ -77,321 +77,326 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - khr_push_descriptor: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .expect("no suitable physical device found"); - - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertices = [ - MyVertex { - position: [-0.5, -0.5], - }, - MyVertex { - position: [-0.5, 0.5], - }, - MyVertex { - position: [0.5, -0.5], - }, - MyVertex { - position: [0.5, 0.5], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let mut uploads = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), - CommandBufferLevel::Primary, - CommandBufferBeginInfo { - usage: CommandBufferUsage::OneTimeSubmit, - ..Default::default() - }, - ) - .unwrap(); + }, + ) + .unwrap(); + + let device_extensions = DeviceExtensions { + khr_swapchain: true, + khr_push_descriptor: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .expect("no suitable physical device found"); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() + }, + ) + .unwrap(); + let queue = queues.next().unwrap(); - let texture = { - let png_bytes = include_bytes!("image_img.png").as_slice(); - let decoder = png::Decoder::new(png_bytes); - let mut reader = decoder.read_info().unwrap(); - let info = reader.info(); - let extent = [info.width, info.height, 1]; + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); - let upload_buffer = Buffer::new_slice( + let vertices = [ + MyVertex { + position: [-0.5, -0.5], + }, + MyVertex { + position: [-0.5, 0.5], + }, + MyVertex { + position: [0.5, -0.5], + }, + MyVertex { + position: [0.5, 0.5], + }, + ]; + let vertex_buffer = Buffer::from_iter( memory_allocator.clone(), BufferCreateInfo { - usage: BufferUsage::TRANSFER_SRC, + usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - (info.width * info.height * 4) as DeviceSize, + vertices, ) .unwrap(); - reader - .next_frame(&mut upload_buffer.write().unwrap()) - .unwrap(); - - let image = Image::new( - memory_allocator, - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::R8G8B8A8_SRGB, - extent, - usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + let mut uploads = RecordingCommandBuffer::new( + command_buffer_allocator.clone(), + queue.queue_family_index(), + CommandBufferLevel::Primary, + CommandBufferBeginInfo { + usage: CommandBufferUsage::OneTimeSubmit, ..Default::default() }, - AllocationCreateInfo::default(), ) .unwrap(); - uploads - .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( - upload_buffer, - image.clone(), - )) + let texture = { + let png_bytes = include_bytes!("image_img.png").as_slice(); + let decoder = png::Decoder::new(png_bytes); + let mut reader = decoder.read_info().unwrap(); + let info = reader.info(); + let extent = [info.width, info.height, 1]; + + let upload_buffer = Buffer::new_slice( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + (info.width * info.height * 4) as DeviceSize, + ) .unwrap(); - ImageView::new_default(image).unwrap() - }; + reader + .next_frame(&mut upload_buffer.write().unwrap()) + .unwrap(); - let sampler = Sampler::new( - device.clone(), - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - ..Default::default() - }, - ) - .unwrap(); - - let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); - - App { - instance, - device, - queue, - command_buffer_allocator, - vertex_buffer, - texture, - sampler, - rcx: None, - } + let image = Image::new( + memory_allocator, + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_SRGB, + extent, + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) + .unwrap(); + + uploads + .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( + upload_buffer, + image.clone(), + )) + .unwrap(); + + ImageView::new_default(image).unwrap() + }; + + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + ) + .unwrap(); + + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; - Swapchain::new( + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = { - let mut layout_create_info = - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); - let set_layout = &mut layout_create_info.set_layouts[0]; - set_layout.flags |= DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR; - set_layout.bindings.get_mut(&0).unwrap().immutable_samplers = vec![self.sampler.clone()]; - - PipelineLayout::new( + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = { + let mut layout_create_info = + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); + let set_layout = &mut layout_create_info.set_layouts[0]; + set_layout.flags |= DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR; + set_layout.bindings.get_mut(&0).unwrap().immutable_samplers = + vec![self.sampler.clone()]; + + PipelineLayout::new( + self.device.clone(), + layout_create_info + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) + .unwrap() + }; + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( self.device.clone(), - layout_create_info - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, ) .unwrap() }; - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain: false, - previous_frame_end, - }); + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -433,15 +438,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -491,7 +500,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -506,7 +517,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/runtime-array/main.rs b/examples/runtime-array/main.rs index 4974a4907f..0e4cbb7678 100644 --- a/examples/runtime-array/main.rs +++ b/examples/runtime-array/main.rs @@ -86,448 +86,459 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + enabled_extensions: device_extensions, + enabled_features: DeviceFeatures { + descriptor_indexing: true, + shader_sampled_image_array_non_uniform_indexing: true, + runtime_descriptor_array: true, + descriptor_binding_variable_descriptor_count: true, + ..DeviceFeatures::empty() + }, ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - descriptor_indexing: true, - shader_sampled_image_array_non_uniform_indexing: true, - runtime_descriptor_array: true, - descriptor_binding_variable_descriptor_count: true, - ..DeviceFeatures::empty() }, - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertices = [ - MyVertex { - position: [-0.1, -0.9], - tex_i: 0, - coords: [1.0, 0.0], - }, - MyVertex { - position: [-0.9, -0.9], - tex_i: 0, - coords: [0.0, 0.0], - }, - MyVertex { - position: [-0.9, -0.1], - tex_i: 0, - coords: [0.0, 1.0], - }, - MyVertex { - position: [-0.1, -0.9], - tex_i: 0, - coords: [1.0, 0.0], - }, - MyVertex { - position: [-0.9, -0.1], - tex_i: 0, - coords: [0.0, 1.0], - }, - MyVertex { - position: [-0.1, -0.1], - tex_i: 0, - coords: [1.0, 1.0], - }, - MyVertex { - position: [0.9, -0.9], - tex_i: 1, - coords: [1.0, 0.0], - }, - MyVertex { - position: [0.1, -0.9], - tex_i: 1, - coords: [0.0, 0.0], - }, - MyVertex { - position: [0.1, -0.1], - tex_i: 1, - coords: [0.0, 1.0], - }, - MyVertex { - position: [0.9, -0.9], - tex_i: 1, - coords: [1.0, 0.0], - }, - MyVertex { - position: [0.1, -0.1], - tex_i: 1, - coords: [0.0, 1.0], - }, - MyVertex { - position: [0.9, -0.1], - tex_i: 1, - coords: [1.0, 1.0], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let mut uploads = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), - CommandBufferLevel::Primary, - CommandBufferBeginInfo { - usage: CommandBufferUsage::OneTimeSubmit, - ..Default::default() - }, - ) - .unwrap(); - - let mascot_texture = { - let png_bytes = include_bytes!("rust_mascot.png").to_vec(); - let cursor = Cursor::new(png_bytes); - let decoder = png::Decoder::new(cursor); - let mut reader = decoder.read_info().unwrap(); - let info = reader.info(); - let extent = [info.width, info.height, 1]; - - let upload_buffer = Buffer::new_slice( + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-0.1, -0.9], + tex_i: 0, + coords: [1.0, 0.0], + }, + MyVertex { + position: [-0.9, -0.9], + tex_i: 0, + coords: [0.0, 0.0], + }, + MyVertex { + position: [-0.9, -0.1], + tex_i: 0, + coords: [0.0, 1.0], + }, + MyVertex { + position: [-0.1, -0.9], + tex_i: 0, + coords: [1.0, 0.0], + }, + MyVertex { + position: [-0.9, -0.1], + tex_i: 0, + coords: [0.0, 1.0], + }, + MyVertex { + position: [-0.1, -0.1], + tex_i: 0, + coords: [1.0, 1.0], + }, + MyVertex { + position: [0.9, -0.9], + tex_i: 1, + coords: [1.0, 0.0], + }, + MyVertex { + position: [0.1, -0.9], + tex_i: 1, + coords: [0.0, 0.0], + }, + MyVertex { + position: [0.1, -0.1], + tex_i: 1, + coords: [0.0, 1.0], + }, + MyVertex { + position: [0.9, -0.9], + tex_i: 1, + coords: [1.0, 0.0], + }, + MyVertex { + position: [0.1, -0.1], + tex_i: 1, + coords: [0.0, 1.0], + }, + MyVertex { + position: [0.9, -0.1], + tex_i: 1, + coords: [1.0, 1.0], + }, + ]; + let vertex_buffer = Buffer::from_iter( memory_allocator.clone(), BufferCreateInfo { - usage: BufferUsage::TRANSFER_SRC, + usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - (info.width * info.height * 4) as DeviceSize, + vertices, ) .unwrap(); - reader - .next_frame(&mut upload_buffer.write().unwrap()) - .unwrap(); - - let image = Image::new( - memory_allocator.clone(), - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::R8G8B8A8_SRGB, - extent, - usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + let mut uploads = RecordingCommandBuffer::new( + command_buffer_allocator.clone(), + queue.queue_family_index(), + CommandBufferLevel::Primary, + CommandBufferBeginInfo { + usage: CommandBufferUsage::OneTimeSubmit, ..Default::default() }, - AllocationCreateInfo::default(), ) .unwrap(); - uploads - .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( - upload_buffer, - image.clone(), - )) + let mascot_texture = { + let png_bytes = include_bytes!("rust_mascot.png").to_vec(); + let cursor = Cursor::new(png_bytes); + let decoder = png::Decoder::new(cursor); + let mut reader = decoder.read_info().unwrap(); + let info = reader.info(); + let extent = [info.width, info.height, 1]; + + let upload_buffer = Buffer::new_slice( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + (info.width * info.height * 4) as DeviceSize, + ) .unwrap(); - ImageView::new_default(image).unwrap() - }; + reader + .next_frame(&mut upload_buffer.write().unwrap()) + .unwrap(); - let vulkano_texture = { - let png_bytes = include_bytes!("vulkano_logo.png").as_slice(); - let decoder = png::Decoder::new(png_bytes); - let mut reader = decoder.read_info().unwrap(); - let info = reader.info(); - let extent = [info.width, info.height, 1]; + let image = Image::new( + memory_allocator.clone(), + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_SRGB, + extent, + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) + .unwrap(); - let upload_buffer = Buffer::new_slice( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::TRANSFER_SRC, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - (info.width * info.height * 4) as DeviceSize, - ) - .unwrap(); + uploads + .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( + upload_buffer, + image.clone(), + )) + .unwrap(); + + ImageView::new_default(image).unwrap() + }; - reader - .next_frame(&mut upload_buffer.write().unwrap()) + let vulkano_texture = { + let png_bytes = include_bytes!("vulkano_logo.png").as_slice(); + let decoder = png::Decoder::new(png_bytes); + let mut reader = decoder.read_info().unwrap(); + let info = reader.info(); + let extent = [info.width, info.height, 1]; + + let upload_buffer = Buffer::new_slice( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + (info.width * info.height * 4) as DeviceSize, + ) + .unwrap(); + + reader + .next_frame(&mut upload_buffer.write().unwrap()) + .unwrap(); + + let image = Image::new( + memory_allocator, + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_SRGB, + extent, + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) .unwrap(); - let image = Image::new( - memory_allocator, - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::R8G8B8A8_SRGB, - extent, - usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + uploads + .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( + upload_buffer, + image.clone(), + )) + .unwrap(); + + ImageView::new_default(image).unwrap() + }; + + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], ..Default::default() }, - AllocationCreateInfo::default(), ) .unwrap(); - uploads - .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( - upload_buffer, - image.clone(), - )) - .unwrap(); - - ImageView::new_default(image).unwrap() - }; - - let sampler = Sampler::new( - device.clone(), - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - ..Default::default() - }, - ) - .unwrap(); - - let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); - - App { - instance, - device, - queue, - descriptor_set_allocator, - command_buffer_allocator, - vertex_buffer, - vulkano_texture, - mascot_texture, - sampler, - rcx: None, - } + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + vulkano_texture, + mascot_texture, + sampler, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; - Swapchain::new( + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = { - let mut layout_create_info = - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); - - // Adjust the info for set 0, binding 1 to make it variable with 2 descriptors. - let binding = layout_create_info.set_layouts[0] - .bindings - .get_mut(&1) + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") .unwrap(); - binding.binding_flags |= DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT; - binding.descriptor_count = 2; + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = { + let mut layout_create_info = + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); + + // Adjust the info for set 0, binding 1 to make it variable with 2 descriptors. + let binding = layout_create_info.set_layouts[0] + .bindings + .get_mut(&1) + .unwrap(); + binding.binding_flags |= DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT; + binding.descriptor_count = 2; + + PipelineLayout::new( + self.device.clone(), + layout_create_info + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) + .unwrap() + }; + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - PipelineLayout::new( + GraphicsPipeline::new( self.device.clone(), - layout_create_info - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, ) .unwrap() }; - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let layout = &pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new_variable( + self.descriptor_set_allocator.clone(), + layout.clone(), + 2, + [ + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view_array( + 1, + 0, + [ + self.mascot_texture.clone() as _, + self.vulkano_texture.clone() as _, + ], + ), + ], + [], ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let layout = &pipeline.layout().set_layouts()[0]; - let descriptor_set = DescriptorSet::new_variable( - self.descriptor_set_allocator.clone(), - layout.clone(), - 2, - [ - WriteDescriptorSet::sampler(0, self.sampler.clone()), - WriteDescriptorSet::image_view_array(1, 0, [self.mascot_texture.clone() as _, self.vulkano_texture.clone() as _]), - ], - [], - ) - .unwrap(); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - descriptor_set, - recreate_swapchain: false, - previous_frame_end, - }); + .unwrap(); + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -569,15 +580,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -620,7 +635,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -635,7 +652,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/runtime-shader/main.rs b/examples/runtime-shader/main.rs index 1857263a2d..bddccba547 100644 --- a/examples/runtime-shader/main.rs +++ b/examples/runtime-shader/main.rs @@ -83,250 +83,256 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); + }, + ) + .unwrap(); - let queue = queues.next().unwrap(); + let queue = queues.next().unwrap(); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); - let vertices = [ - MyVertex { - position: [-1.0, 1.0], - color: [1.0, 0.0, 0.0], - }, - MyVertex { - position: [0.0, -1.0], - color: [0.0, 1.0, 0.0], - }, - MyVertex { - position: [1.0, 1.0], - color: [0.0, 0.0, 1.0], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - App { - instance, - device, - queue, - command_buffer_allocator, - vertex_buffer, - rcx: None, - } + let vertices = [ + MyVertex { + position: [-1.0, 1.0], + color: [1.0, 0.0, 0.0], + }, + MyVertex { + position: [0.0, -1.0], + color: [0.0, 1.0, 0.0], + }, + MyVertex { + position: [1.0, 1.0], + color: [0.0, 0.0, 1.0], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = { - let code = read_spirv_words_from_file("vert.spv"); + let pipeline = { + let vs = { + let code = read_spirv_words_from_file("vert.spv"); - // Create a ShaderModule on a device the same Shader::load does it. - // NOTE: You will have to verify correctness of the data by yourself! - let module = unsafe { - ShaderModule::new(self.device.clone(), ShaderModuleCreateInfo::new(&code)).unwrap() + // Create a ShaderModule on a device the same Shader::load does it. + // NOTE: You will have to verify correctness of the data by yourself! + let module = unsafe { + ShaderModule::new(self.device.clone(), ShaderModuleCreateInfo::new(&code)) + .unwrap() + }; + module.entry_point("main").unwrap() }; - module.entry_point("main").unwrap() - }; - let fs = { - let code = read_spirv_words_from_file("frag.spv"); + let fs = { + let code = read_spirv_words_from_file("frag.spv"); - let module = unsafe { - ShaderModule::new(self.device.clone(), ShaderModuleCreateInfo::new(&code)).unwrap() + let module = unsafe { + ShaderModule::new(self.device.clone(), ShaderModuleCreateInfo::new(&code)) + .unwrap() + }; + module.entry_point("main").unwrap() }; - module.entry_point("main").unwrap() + + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState { + cull_mode: CullMode::Front, + front_face: FrontFace::CounterClockwise, + ..Default::default() + }), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() }; - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState { - cull_mode: CullMode::Front, - front_face: FrontFace::CounterClockwise, - ..Default::default() - }), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain: false, - previous_frame_end, - }); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); } - + fn window_event( &mut self, event_loop: &ActiveEventLoop, @@ -366,15 +372,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -410,7 +420,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -425,7 +437,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/simple-particles/main.rs b/examples/simple-particles/main.rs index 2799f965bc..eff0632027 100644 --- a/examples/simple-particles/main.rs +++ b/examples/simple-particles/main.rs @@ -89,403 +89,404 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - enabled_extensions: required_extensions, - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .unwrap(); - - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, - ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // Apply scoped logic to create `DeviceLocalBuffer` initialized with vertex data. - let vertex_buffer = { - // Initialize vertex data as an iterator. - let vertices = (0..PARTICLE_COUNT).map(|i| { - let f = i as f32 / (PARTICLE_COUNT / 10) as f32; - MyVertex { - pos: [2. * f.fract() - 1., 0.2 * f.floor() - 1.], - vel: [0.; 2], - } - }); - - // Create a CPU-accessible buffer initialized with the vertex data. - let temporary_accessible_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - // Specify this buffer will be used as a transfer source. - usage: BufferUsage::TRANSFER_SRC, + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + enabled_extensions: required_extensions, + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, ..Default::default() }, - AllocationCreateInfo { - // Specify this buffer will be used for uploading to the GPU. - memory_type_filter: MemoryTypeFilter::PREFER_HOST - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, ) .unwrap(); - // Create a buffer in device-local memory with enough space for `PARTICLE_COUNT` number of - // `Vertex`. - let device_local_buffer = Buffer::new_slice::( - memory_allocator, - BufferCreateInfo { - // Specify use as a storage buffer, vertex buffer, and transfer destination. - usage: BufferUsage::STORAGE_BUFFER - | BufferUsage::TRANSFER_DST - | BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - // Specify this buffer will only be used by the device. - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE, - ..Default::default() - }, - PARTICLE_COUNT as DeviceSize, - ) - .unwrap(); + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); - // Create one-time command to copy between the buffers. - let mut cbb = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), - CommandBufferLevel::Primary, - CommandBufferBeginInfo { - usage: CommandBufferUsage::OneTimeSubmit, + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() }, ) .unwrap(); - cbb.copy_buffer(CopyBufferInfo::buffers( - temporary_accessible_buffer, - device_local_buffer.clone(), - )) - .unwrap(); - let cb = cbb.end().unwrap(); - // Execute copy and wait for copy to complete before proceeding. - cb.execute(queue.clone()) - .unwrap() - .then_signal_fence_and_flush() - .unwrap() - .wait(None /* timeout */) + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // Apply scoped logic to create `DeviceLocalBuffer` initialized with vertex data. + let vertex_buffer = { + // Initialize vertex data as an iterator. + let vertices = (0..PARTICLE_COUNT).map(|i| { + let f = i as f32 / (PARTICLE_COUNT / 10) as f32; + MyVertex { + pos: [2. * f.fract() - 1., 0.2 * f.floor() - 1.], + vel: [0.; 2], + } + }); + + // Create a CPU-accessible buffer initialized with the vertex data. + let temporary_accessible_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + // Specify this buffer will be used as a transfer source. + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + // Specify this buffer will be used for uploading to the GPU. + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) .unwrap(); - device_local_buffer - }; + // Create a buffer in device-local memory with enough space for `PARTICLE_COUNT` + // number of `Vertex`. + let device_local_buffer = Buffer::new_slice::( + memory_allocator, + BufferCreateInfo { + // Specify use as a storage buffer, vertex buffer, and transfer destination. + usage: BufferUsage::STORAGE_BUFFER + | BufferUsage::TRANSFER_DST + | BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + // Specify this buffer will only be used by the device. + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE, + ..Default::default() + }, + PARTICLE_COUNT as DeviceSize, + ) + .unwrap(); - // Create a compute-pipeline for applying the compute shader to vertices. - let compute_pipeline = { - let cs = cs::load(device.clone()) - .unwrap() - .entry_point("main") + // Create one-time command to copy between the buffers. + let mut cbb = RecordingCommandBuffer::new( + command_buffer_allocator.clone(), + queue.queue_family_index(), + CommandBufferLevel::Primary, + CommandBufferBeginInfo { + usage: CommandBufferUsage::OneTimeSubmit, + ..Default::default() + }, + ) .unwrap(); - let stage = PipelineShaderStageCreateInfo::new(cs); - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage]) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), + cbb.copy_buffer(CopyBufferInfo::buffers( + temporary_accessible_buffer, + device_local_buffer.clone(), + )) + .unwrap(); + let cb = cbb.end().unwrap(); + + // Execute copy and wait for copy to complete before proceeding. + cb.execute(queue.clone()) + .unwrap() + .then_signal_fence_and_flush() + .unwrap() + .wait(None /* timeout */) + .unwrap(); + + device_local_buffer + }; + + // Create a compute-pipeline for applying the compute shader to vertices. + let compute_pipeline = { + let cs = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let stage = PipelineShaderStageCreateInfo::new(cs); + let layout = PipelineLayout::new( + device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage]) + .into_pipeline_layout_create_info(device.clone()) + .unwrap(), + ) + .unwrap(); + + ComputePipeline::new( + device.clone(), + None, + ComputePipelineCreateInfo::stage_layout(stage, layout), + ) + .unwrap() + }; + + // Create a new descriptor set for binding vertices as a storage buffer. + let descriptor_set = DescriptorSet::new( + descriptor_set_allocator.clone(), + // 0 is the index of the descriptor set. + compute_pipeline.layout().set_layouts()[0].clone(), + [ + // 0 is the binding of the data in this set. We bind the `Buffer` of vertices here. + WriteDescriptorSet::buffer(0, vertex_buffer.clone()), + ], + [], ) .unwrap(); - ComputePipeline::new( - device.clone(), - None, - ComputePipelineCreateInfo::stage_layout(stage, layout), - ) - .unwrap() - }; - - // Create a new descriptor set for binding vertices as a storage buffer. - let descriptor_set = DescriptorSet::new( - descriptor_set_allocator.clone(), - // 0 is the index of the descriptor set. - compute_pipeline.layout().set_layouts()[0].clone(), - [ - // 0 is the binding of the data in this set. We bind the `Buffer` of vertices here. - WriteDescriptorSet::buffer(0, vertex_buffer.clone()), - ], - [], - ) - .unwrap(); - - App { - instance, - device, - queue, - command_buffer_allocator, - vertex_buffer, - compute_pipeline, - descriptor_set, - rcx: None, - } + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + compute_pipeline, + descriptor_set, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new( - event_loop.create_window( - Window::default_attributes() - // For simplicity, we are going to assert that the window size is static. - .with_resizable(false) - .with_title("simple particles") - .with_inner_size(PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)), - ) - .unwrap(), - ); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - Swapchain::new( - self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: [WINDOW_WIDTH, WINDOW_HEIGHT], - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - present_mode: PresentMode::Fifo, - ..Default::default() - }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, - }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); - - let framebuffers = images - .into_iter() - .map(|img| { - let view = ImageView::new_default(img).unwrap(); - Framebuffer::new( - render_pass.clone(), - FramebufferCreateInfo { - attachments: vec![view], + let window = Arc::new( + event_loop + .create_window( + Window::default_attributes() + // For simplicity, we are going to assert that the window size is static. + .with_resizable(false) + .with_title("simple particles") + .with_inner_size(PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)), + ) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: [WINDOW_WIDTH, WINDOW_HEIGHT], + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + present_mode: PresentMode::Fifo, ..Default::default() }, ) .unwrap() - }) - .collect(); - - // The vertex shader determines color and is run once per particle. The vertices will be - // updated by the compute shader each frame. - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 pos; - layout(location = 1) in vec2 vel; - - layout(location = 0) out vec4 outColor; - - // Keep this value in sync with the `maxSpeed` const in the compute shader. - const float maxSpeed = 10.0; - - void main() { - gl_Position = vec4(pos, 0.0, 1.0); - gl_PointSize = 1.0; - - // Mix colors based on position and velocity. - outColor = mix( - 0.2 * vec4(pos, abs(vel.x) + abs(vel.y), 1.0), - vec4(1.0, 0.5, 0.8, 1.0), - sqrt(length(vel) / maxSpeed) - ); - } - ", + }; + + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + let framebuffers = images + .into_iter() + .map(|img| { + let view = ImageView::new_default(img).unwrap(); + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() + }) + .collect(); + + // The vertex shader determines color and is run once per particle. The vertices will be + // updated by the compute shader each frame. + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 pos; + layout(location = 1) in vec2 vel; + + layout(location = 0) out vec4 outColor; + + // Keep this value in sync with the `maxSpeed` const in the compute shader. + const float maxSpeed = 10.0; + + void main() { + gl_Position = vec4(pos, 0.0, 1.0); + gl_PointSize = 1.0; + + // Mix colors based on position and velocity. + outColor = mix( + 0.2 * vec4(pos, abs(vel.x) + abs(vel.y), 1.0), + vec4(1.0, 0.5, 0.8, 1.0), + sqrt(length(vel) / maxSpeed) + ); + } + ", + } } - } - // The fragment shader will only need to apply the color forwarded by the vertex shader, - // because the color of a particle should be identical over all pixels. - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 + // The fragment shader will only need to apply the color forwarded by the vertex shader, + // because the color of a particle should be identical over all pixels. + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - layout(location = 0) in vec4 outColor; + layout(location = 0) in vec4 outColor; - layout(location = 0) out vec4 fragColor; + layout(location = 0) out vec4 fragColor; - void main() { - fragColor = outColor; - } - ", + void main() { + fragColor = outColor; + } + ", + } } - } - // Create a basic graphics pipeline for rendering particles. - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + // Create a basic graphics pipeline for rendering particles. + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass, 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + // Vertices will be rendered as a list of points. + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::PointList, + ..Default::default() + }), + viewport_state: Some(ViewportState { + viewports: [Viewport { + offset: [0.0, 0.0], + extent: [WINDOW_WIDTH as f32, WINDOW_HEIGHT as f32], + depth_range: 0.0..=1.0, + }] + .into_iter() + .collect(), + ..Default::default() + }), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass, 0).unwrap(); + }; - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - // Vertices will be rendered as a list of points. - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::PointList, - ..Default::default() - }), - viewport_state: Some(ViewportState { - viewports: [Viewport { - offset: [0.0, 0.0], - extent: [WINDOW_WIDTH as f32, WINDOW_HEIGHT as f32], - depth_range: 0.0..=1.0, - }] - .into_iter() - .collect(), - ..Default::default() - }), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - let start_time = SystemTime::now(); - - self.rcx = Some(RenderContext { - window, - swapchain, - framebuffers, - pipeline, - previous_frame_end, - start_time, - last_frame_time: start_time, - }); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + let start_time = SystemTime::now(); + + self.rcx = Some(RenderContext { + window, + swapchain, + framebuffers, + pipeline, + previous_frame_end, + start_time, + last_frame_time: start_time, + }); } fn window_event( @@ -512,7 +513,10 @@ impl ApplicationHandler for App { // Update per-frame variables. let now = SystemTime::now(); let time = now.duration_since(rcx.start_time).unwrap().as_secs_f32(); - let delta_time = now.duration_since(rcx.last_frame_time).unwrap().as_secs_f32(); + let delta_time = now + .duration_since(rcx.last_frame_time) + .unwrap() + .as_secs_f32(); rcx.last_frame_time = now; // Create push constants to be passed to compute shader. @@ -531,8 +535,8 @@ impl ApplicationHandler for App { Err(e) => panic!("failed to acquire next image: {e}"), }; - // Since we disallow resizing, assert that the swapchain and surface are optimally - // configured. + // Since we disallow resizing, assert that the swapchain and surface are + // optimally configured. assert!( !suboptimal, "not handling sub-optimal swapchains in this sample code", @@ -603,7 +607,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/teapot/main.rs b/examples/teapot/main.rs index a93a2aa009..ad2d12fb9b 100644 --- a/examples/teapot/main.rs +++ b/examples/teapot/main.rs @@ -96,240 +96,244 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); + }, + ) + .unwrap(); - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - POSITIONS, - ) - .unwrap(); - let normals_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - NORMALS, - ) - .unwrap(); - let index_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::INDEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - INDICES, - ) - .unwrap(); + let queue = queues.next().unwrap(); - let uniform_buffer_allocator = SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - - App { - instance, - device, - queue, - memory_allocator, - descriptor_set_allocator, - command_buffer_allocator, - vertex_buffer, - normals_buffer, - index_buffer, - uniform_buffer_allocator, - rcx: None, - } + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertex_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + POSITIONS, + ) + .unwrap(); + let normals_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + NORMALS, + ) + .unwrap(); + let index_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::INDEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + INDICES, + ) + .unwrap(); + + let uniform_buffer_allocator = SubbufferAllocator::new( + memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::UNIFORM_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + ); + + App { + instance, + device, + queue, + memory_allocator, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + normals_buffer, + index_buffer, + uniform_buffer_allocator, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() - }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, + depth_stencil: { + format: Format::D16_UNORM, + samples: 1, + load_op: Clear, + store_op: DontCare, + }, }, - depth_stencil: { - format: Format::D16_UNORM, - samples: 1, - load_op: Clear, - store_op: DontCare, + pass: { + color: [color], + depth_stencil: {depth_stencil}, }, - }, - pass: { - color: [color], - depth_stencil: {depth_stencil}, - }, - ) - .unwrap(); - - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.device.clone()) - .unwrap() - .entry_point("main") + ) .unwrap(); - let (framebuffers, pipeline) = window_size_dependent_setup( - window_size, - &images, - &render_pass, - &self.memory_allocator, - &vs, - &fs, - ); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - let rotation_start = Instant::now(); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - vs, - fs, - pipeline, - recreate_swapchain: false, - previous_frame_end, - rotation_start, - }); + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + + let (framebuffers, pipeline) = window_size_dependent_setup( + window_size, + &images, + &render_pass, + &self.memory_allocator, + &vs, + &fs, + ); + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + let rotation_start = Instant::now(); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + vs, + fs, + pipeline, + recreate_swapchain: false, + previous_frame_end, + rotation_start, + }); } fn window_event( @@ -339,7 +343,7 @@ impl ApplicationHandler for App { event: WindowEvent, ) { let rcx = self.rcx.as_mut().unwrap(); - + match event { WindowEvent::CloseRequested => { event_loop.exit(); @@ -385,8 +389,8 @@ impl ApplicationHandler for App { // NOTE: This teapot was meant for OpenGL where the origin is at the lower left // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. - let aspect_ratio = - rcx.swapchain.image_extent()[0] as f32 / rcx.swapchain.image_extent()[1] as f32; + let aspect_ratio = rcx.swapchain.image_extent()[0] as f32 + / rcx.swapchain.image_extent()[1] as f32; let proj = Mat4::perspective_rh_gl( std::f32::consts::FRAC_PI_2, @@ -422,15 +426,19 @@ impl ApplicationHandler for App { ) .unwrap(); - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -470,7 +478,10 @@ impl ApplicationHandler for App { descriptor_set, ) .unwrap() - .bind_vertex_buffers(0, (self.vertex_buffer.clone(), self.normals_buffer.clone())) + .bind_vertex_buffers( + 0, + (self.vertex_buffer.clone(), self.normals_buffer.clone()), + ) .unwrap() .bind_index_buffer(self.index_buffer.clone()) .unwrap(); @@ -493,7 +504,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/tessellation/main.rs b/examples/tessellation/main.rs index 1c0e3fe110..f3f70ef918 100644 --- a/examples/tessellation/main.rs +++ b/examples/tessellation/main.rs @@ -83,277 +83,281 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let device_features = DeviceFeatures { - tessellation_shader: true, - fill_mode_non_solid: true, - ..DeviceFeatures::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter(|p| p.supported_features().contains(&device_features)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let device_features = DeviceFeatures { + tessellation_shader: true, + fill_mode_non_solid: true, + ..DeviceFeatures::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter(|p| p.supported_features().contains(&device_features)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + enabled_extensions: device_extensions, + enabled_features: device_features, ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: device_features, - ..Default::default() - }, - ) - .unwrap(); + }, + ) + .unwrap(); - let queue = queues.next().unwrap(); + let queue = queues.next().unwrap(); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); - let vertices = [ - MyVertex { - position: [-0.5, -0.25], - }, - MyVertex { - position: [0.0, 0.5], - }, - MyVertex { - position: [0.25, -0.1], - }, - MyVertex { - position: [0.9, 0.9], - }, - MyVertex { - position: [0.9, 0.8], - }, - MyVertex { - position: [0.8, 0.8], - }, - MyVertex { - position: [-0.9, 0.9], - }, - MyVertex { - position: [-0.7, 0.6], - }, - MyVertex { - position: [-0.5, 0.9], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - App { - instance, - device, - queue, - command_buffer_allocator, - vertex_buffer, - rcx: None, - } + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + }, + MyVertex { + position: [0.0, 0.5], + }, + MyVertex { + position: [0.25, -0.1], + }, + MyVertex { + position: [0.9, 0.9], + }, + MyVertex { + position: [0.9, 0.8], + }, + MyVertex { + position: [0.8, 0.8], + }, + MyVertex { + position: [-0.9, 0.9], + }, + MyVertex { + position: [-0.7, 0.6], + }, + MyVertex { + position: [-0.5, 0.9], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window.inner_size().into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let tcs = tcs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let tes = tes::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let tcs = tcs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let tes = tes::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(tcs), + PipelineShaderStageCreateInfo::new(tes), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::PatchList, + ..Default::default() + }), + tessellation_state: Some(TessellationState { + // Use a patch_control_points of 3, because we want to convert one + // *triangle* into lots of little ones. A value of 4 would convert a + // *rectangle* into lots of little triangles. + patch_control_points: 3, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState { + polygon_mode: PolygonMode::Line, + ..Default::default() + }), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(tcs), - PipelineShaderStageCreateInfo::new(tes), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::PatchList, - ..Default::default() - }), - tessellation_state: Some(TessellationState { - // Use a patch_control_points of 3, because we want to convert one *triangle* - // into lots of little ones. - // A value of 4 would convert a *rectangle* into lots of little triangles. - patch_control_points: 3, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState { - polygon_mode: PolygonMode::Line, - ..Default::default() - }), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain: false, - previous_frame_end, - }); + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -395,15 +399,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -439,7 +447,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 1, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -454,7 +464,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/texture-array/main.rs b/examples/texture-array/main.rs index ad553ffe12..7509cafaed 100644 --- a/examples/texture-array/main.rs +++ b/examples/texture-array/main.rs @@ -86,340 +86,345 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) + let library = VulkanLibrary::new().unwrap(); + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) .unwrap(); - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - enabled_extensions: device_extensions, - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter_map(|p| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) + .unwrap(); + + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + enabled_extensions: device_extensions, + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let vertices = [ - MyVertex { - position: [-0.2, -0.5], - }, - MyVertex { - position: [-0.5, 0.8], - }, - MyVertex { - position: [0.4, -0.5], - }, - MyVertex { - position: [0.5, 0.2], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let mut uploads = RecordingCommandBuffer::new( - command_buffer_allocator.clone(), - queue.queue_family_index(), - CommandBufferLevel::Primary, - CommandBufferBeginInfo { - usage: CommandBufferUsage::OneTimeSubmit, - ..Default::default() - }, - ) - .unwrap(); - - let texture = { - // Replace with your actual image array dimensions. - let format = Format::R8G8B8A8_SRGB; - let extent: [u32; 3] = [128, 128, 1]; - let array_layers = 3u32; - - let buffer_size = format.block_size() - * extent - .into_iter() - .map(|e| e as DeviceSize) - .product::() - * array_layers as DeviceSize; - let upload_buffer = Buffer::new_slice( + }, + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let vertices = [ + MyVertex { + position: [-0.2, -0.5], + }, + MyVertex { + position: [-0.5, 0.8], + }, + MyVertex { + position: [0.4, -0.5], + }, + MyVertex { + position: [0.5, 0.2], + }, + ]; + let vertex_buffer = Buffer::from_iter( memory_allocator.clone(), BufferCreateInfo { - usage: BufferUsage::TRANSFER_SRC, + usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_HOST + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - buffer_size, + vertices, ) .unwrap(); - { - let mut image_data = &mut *upload_buffer.write().unwrap(); - - for png_bytes in [ - include_bytes!("square.png").as_slice(), - include_bytes!("star.png").as_slice(), - include_bytes!("asterisk.png").as_slice(), - ] { - let decoder = png::Decoder::new(png_bytes); - let mut reader = decoder.read_info().unwrap(); - reader.next_frame(image_data).unwrap(); - let info = reader.info(); - image_data = &mut image_data[(info.width * info.height * 4) as usize..]; - } - } - - let image = Image::new( - memory_allocator, - ImageCreateInfo { - image_type: ImageType::Dim2d, - format, - extent, - array_layers, - usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + let mut uploads = RecordingCommandBuffer::new( + command_buffer_allocator.clone(), + queue.queue_family_index(), + CommandBufferLevel::Primary, + CommandBufferBeginInfo { + usage: CommandBufferUsage::OneTimeSubmit, ..Default::default() }, - AllocationCreateInfo::default(), ) .unwrap(); - uploads - .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( - upload_buffer, - image.clone(), - )) + let texture = { + // Replace with your actual image array dimensions. + let format = Format::R8G8B8A8_SRGB; + let extent: [u32; 3] = [128, 128, 1]; + let array_layers = 3u32; + + let buffer_size = format.block_size() + * extent + .into_iter() + .map(|e| e as DeviceSize) + .product::() + * array_layers as DeviceSize; + let upload_buffer = Buffer::new_slice( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + buffer_size, + ) + .unwrap(); + + { + let mut image_data = &mut *upload_buffer.write().unwrap(); + + for png_bytes in [ + include_bytes!("square.png").as_slice(), + include_bytes!("star.png").as_slice(), + include_bytes!("asterisk.png").as_slice(), + ] { + let decoder = png::Decoder::new(png_bytes); + let mut reader = decoder.read_info().unwrap(); + reader.next_frame(image_data).unwrap(); + let info = reader.info(); + image_data = &mut image_data[(info.width * info.height * 4) as usize..]; + } + } + + let image = Image::new( + memory_allocator, + ImageCreateInfo { + image_type: ImageType::Dim2d, + format, + extent, + array_layers, + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) .unwrap(); - ImageView::new_default(image).unwrap() - }; + uploads + .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( + upload_buffer, + image.clone(), + )) + .unwrap(); - let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap(); + ImageView::new_default(image).unwrap() + }; - let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + let sampler = + Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap(); - App { - instance, - device, - queue, - descriptor_set_allocator, - command_buffer_allocator, - vertex_buffer, - texture, - sampler, - rcx: None, - } + let _ = uploads.end().unwrap().execute(queue.clone()).unwrap(); + + App { + instance, + device, + queue, + descriptor_set_allocator, + command_buffer_allocator, + vertex_buffer, + texture, + sampler, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - Swapchain::new( + let render_pass = vulkano::single_pass_renderpass!( self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), - ..Default::default() + attachments: { + color: { + format: swapchain.image_format(), + samples: 1, + load_op: Clear, + store_op: Store, + }, }, - ) - .unwrap() - }; - - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - color: { - format: swapchain.image_format(), - samples: 1, - load_op: Clear, - store_op: Store, + pass: { + color: [color], + depth_stencil: {}, }, - }, - pass: { - color: [color], - depth_stencil: {}, - }, - ) - .unwrap(); + ) + .unwrap(); - let framebuffers = window_size_dependent_setup(&images, &render_pass); + let framebuffers = window_size_dependent_setup(&images, &render_pass); - let pipeline = { - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") + let pipeline = { + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = PipelineLayout::new( + self.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) .unwrap(); - let fs = fs::load(self.device.clone()) + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) .unwrap() - .entry_point("main") - .unwrap(); - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let layout = &pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new( + self.descriptor_set_allocator.clone(), + layout.clone(), + [ + WriteDescriptorSet::sampler(0, self.sampler.clone()), + WriteDescriptorSet::image_view(1, self.texture.clone()), + ], + [], ) .unwrap(); - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState { - topology: PrimitiveTopology::TriangleStrip, - ..Default::default() - }), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - let layout = &pipeline.layout().set_layouts()[0]; - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - layout.clone(), - [ - WriteDescriptorSet::sampler(0, self.sampler.clone()), - WriteDescriptorSet::image_view(1, self.texture.clone()), - ], - [], - ) - .unwrap(); - - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - descriptor_set, - recreate_swapchain: false, - previous_frame_end, - }); + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + descriptor_set, + recreate_swapchain: false, + previous_frame_end, + }); } fn window_event( @@ -461,15 +466,19 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -512,7 +521,9 @@ impl ApplicationHandler for App { .unwrap(); unsafe { - builder.draw(self.vertex_buffer.len() as u32, 3, 0, 0).unwrap(); + builder + .draw(self.vertex_buffer.len() as u32, 3, 0, 0) + .unwrap(); } builder.end_render_pass(Default::default()).unwrap(); @@ -527,7 +538,10 @@ impl ApplicationHandler for App { .unwrap() .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/triangle-util/main.rs b/examples/triangle-util/main.rs index 0cab3f355d..cbd3236def 100644 --- a/examples/triangle-util/main.rs +++ b/examples/triangle-util/main.rs @@ -68,272 +68,272 @@ struct RenderContext { impl App { fn new(_event_loop: &EventLoop<()>) -> Self { - let context = VulkanoContext::new(VulkanoConfig::default()); - - // Manages any windows and their rendering. - let windows = VulkanoWindows::default(); - - // Some little debug infos. - println!( - "Using device: {} (type: {:?})", - context.device().physical_device().properties().device_name, - context.device().physical_device().properties().device_type, - ); - - // Before we can start creating and recording command buffers, we need a way of allocating - // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools - // underneath and provides a safe interface for them. - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - context.device().clone(), - Default::default(), - )); - - // We now create a buffer that will store the shape of our triangle. - let vertices = [ - MyVertex { - position: [-0.5, -0.25], - }, - MyVertex { - position: [0.0, 0.5], - }, - MyVertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - context.memory_allocator().clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - App { - context, - windows, - command_buffer_allocator, - vertex_buffer, - rcx: None, - } + let context = VulkanoContext::new(VulkanoConfig::default()); + + // Manages any windows and their rendering. + let windows = VulkanoWindows::default(); + + // Some little debug infos. + println!( + "Using device: {} (type: {:?})", + context.device().physical_device().properties().device_name, + context.device().physical_device().properties().device_type, + ); + + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command + // pools underneath and provides a safe interface for them. + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + context.device().clone(), + Default::default(), + )); + + // We now create a buffer that will store the shape of our triangle. + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + }, + MyVertex { + position: [0.0, 0.5], + }, + MyVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + context.memory_allocator().clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + App { + context, + windows, + command_buffer_allocator, + vertex_buffer, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - if let Some(primary_window_id) = self.windows.primary_window_id() { - self.windows.remove_renderer(primary_window_id); - } - - self.windows.create_window(event_loop, &self.context, &Default::default(), |_| {}); - let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); - let window_size = window_renderer.window().inner_size(); - - // The next step is to create the shaders. - // - // The raw shader creation API provided by the vulkano library is unsafe for various reasons, - // so The `shader!` macro provides a way to generate a Rust module from GLSL source - in the - // example below, the source is provided as a string input directly to the shader, but a path - // to a source file can be provided as well. Note that the user must specify the type of shader - // (e.g. "vertex", "fragment", etc.) using the `ty` option of the macro. - // - // The items generated by the `shader!` macro include a `load` function which loads the shader - // using an input logical device. The module also includes type definitions for layout - // structures defined in the shader source, for example uniforms and push constants. - // - // A more detailed overview of what the `shader!` macro generates can be found in the - // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/ - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 position; - - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", + if let Some(primary_window_id) = self.windows.primary_window_id() { + self.windows.remove_renderer(primary_window_id); } - } - - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 - layout(location = 0) out vec4 f_color; + self.windows + .create_window(event_loop, &self.context, &Default::default(), |_| {}); + let window_renderer = self.windows.get_primary_renderer_mut().unwrap(); + let window_size = window_renderer.window().inner_size(); - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - ", + // The next step is to create the shaders. + // + // The raw shader creation API provided by the vulkano library is unsafe for various + // reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL + // source - in the example below, the source is provided as a string input directly to the + // shader, but a path to a source file can be provided as well. Note that the user must + // specify the type of shader (e.g. "vertex", "fragment", etc.) using the `ty` option of + // the macro. + // + // The items generated by the `shader!` macro include a `load` function which loads the + // shader using an input logical device. The module also includes type definitions for + // layout structures defined in the shader source, for example uniforms and push constants. + // + // A more detailed overview of what the `shader!` macro generates can be found in the + // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/ + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 position; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } } - } - // The next step is to create a *render pass*, which is an object that describes where the - // output of the graphics pipeline will go. It describes the layout of the images where the - // colors, depth and/or stencil information will be written. - let render_pass = vulkano::single_pass_renderpass!( - self.context.device().clone(), - attachments: { - // `color` is a custom name we give to the first and only attachment. - color: { - // `format: ` indicates the type of the format of the image. This has to be one - // of the types of the `vulkano::format` module (or alternatively one of your - // structs that implements the `FormatDesc` trait). Here we use the same format as - // the swapchain. - format: window_renderer.swapchain_format(), - // `samples: 1` means that we ask the GPU to use one sample to determine the value - // of each pixel in the color attachment. We could use a larger value - // (multisampling) for antialiasing. An example of this can be found in - // msaa-renderpass.rs. - samples: 1, - // `load_op: Clear` means that we ask the GPU to clear the content of this - // attachment at the start of the drawing. - load_op: Clear, - // `store_op: Store` means that we ask the GPU to store the output of the draw in - // the actual image. We could also ask it to discard the result. - store_op: Store, - }, - }, - pass: { - // We use the attachment named `color` as the one and only color attachment. - color: [color], - // No depth-stencil attachment is indicated with empty brackets. - depth_stencil: {}, - }, - ) - .unwrap(); - - // The render pass we created above only describes the layout of our framebuffers. Before we - // can draw we also need to create the actual framebuffers. - // - // Since we need to draw to multiple images, we are going to create a different framebuffer for - // each image. - let framebuffers = window_size_dependent_setup( - window_renderer.swapchain_image_views(), - &render_pass, - ); - - // Before we draw, we have to create what is called a **pipeline**. A pipeline describes how - // a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains - // many settings for customization, all baked into a single object. For drawing, we create - // a **graphics** pipeline, but there are also other types of pipeline. - let pipeline = { - // First, we load the shaders that the pipeline will use: - // the vertex shader and the fragment shader. - // - // A Vulkan shader can in theory contain multiple entry points, so we have to specify which - // one. - let vs = vs::load(self.context.device().clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.context.device().clone()) - .unwrap() - .entry_point("main") - .unwrap(); + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - // Automatically generate a vertex input state from the vertex shader's input interface, - // that takes a single vertex buffer containing `Vertex` structs. - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + layout(location = 0) out vec4 f_color; - // Make a list of the shader stages that the pipeline will have. - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } + } - // We must now create a **pipeline layout** object, which describes the locations and types - // of descriptor sets and push constants used by the shaders in the pipeline. - // - // Multiple pipelines can share a common layout object, which is more efficient. - // The shaders in a pipeline must use a subset of the resources described in its pipeline - // layout, but the pipeline layout is allowed to contain resources that are not present in - // the shaders; they can be used by shaders in other pipelines that share the same - // layout. Thus, it is a good idea to design shaders so that many pipelines have - // common resource locations, which allows them to share pipeline layouts. - let layout = PipelineLayout::new( + // The next step is to create a *render pass*, which is an object that describes where the + // output of the graphics pipeline will go. It describes the layout of the images where the + // colors, depth and/or stencil information will be written. + let render_pass = vulkano::single_pass_renderpass!( self.context.device().clone(), - // Since we only have one pipeline in this example, and thus one pipeline layout, - // we automatically generate the creation info for it from the resources used in the - // shaders. In a real application, you would specify this information manually so that - // you can re-use one layout in multiple pipelines. - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.context.device().clone()) - .unwrap(), + attachments: { + // `color` is a custom name we give to the first and only attachment. + color: { + // `format: ` indicates the type of the format of the image. This has to be + // one of the types of the `vulkano::format` module (or alternatively one of + // your structs that implements the `FormatDesc` trait). Here we use the same + // format as the swapchain. + format: window_renderer.swapchain_format(), + // `samples: 1` means that we ask the GPU to use one sample to determine the + // value of each pixel in the color attachment. We could use a larger value + // (multisampling) for antialiasing. An example of this can be found in + // msaa-renderpass.rs. + samples: 1, + // `load_op: Clear` means that we ask the GPU to clear the content of this + // attachment at the start of the drawing. + load_op: Clear, + // `store_op: Store` means that we ask the GPU to store the output of the draw + // in the actual image. We could also ask it to discard the result. + store_op: Store, + }, + }, + pass: { + // We use the attachment named `color` as the one and only color attachment. + color: [color], + // No depth-stencil attachment is indicated with empty brackets. + depth_stencil: {}, + }, ) .unwrap(); - // We have to indicate which subpass of which render pass this pipeline is going to be used - // in. The pipeline will only be usable from this particular subpass. - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + // The render pass we created above only describes the layout of our framebuffers. Before + // we can draw we also need to create the actual framebuffers. + // + // Since we need to draw to multiple images, we are going to create a different framebuffer + // for each image. + let framebuffers = + window_size_dependent_setup(window_renderer.swapchain_image_views(), &render_pass); + + // Before we draw, we have to create what is called a **pipeline**. A pipeline describes + // how a GPU operation is to be performed. It is similar to an OpenGL program, but it also + // contains many settings for customization, all baked into a single object. For drawing, + // we create a **graphics** pipeline, but there are also other types of pipeline. + let pipeline = { + // First, we load the shaders that the pipeline will use: the vertex shader and the + // fragment shader. + // + // A Vulkan shader can in theory contain multiple entry points, so we have to specify + // which one. + let vs = vs::load(self.context.device().clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.context.device().clone()) + .unwrap() + .entry_point("main") + .unwrap(); - // Finally, create the pipeline. - GraphicsPipeline::new( - self.context.device().clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - // How vertex data is read from the vertex buffers into the vertex shader. - vertex_input_state: Some(vertex_input_state), - // How vertices are arranged into primitive shapes. - // The default primitive shape is a triangle. - input_assembly_state: Some(InputAssemblyState::default()), - // How primitives are transformed and clipped to fit the framebuffer. - // We use a resizable viewport, set to draw over the entire window. - viewport_state: Some(ViewportState::default()), - // How polygons are culled and converted into a raster of pixels. - // The default value does not perform any culling. - rasterization_state: Some(RasterizationState::default()), - // How multiple fragment shader samples are converted to a single pixel value. - // The default value does not perform any multisampling. - multisample_state: Some(MultisampleState::default()), - // How pixel values are combined with the values already present in the framebuffer. - // The default value overwrites the old value with the new one, without any - // blending. - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - // Dynamic states allows us to specify parts of the pipeline settings when - // recording the command buffer, before we perform drawing. - // Here, we specify that the viewport should be dynamic. - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - // Dynamic viewports allow us to recreate just the viewport when the window is resized. - // Otherwise we would have to recreate the whole pipeline. - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - // In the `window_event` handler below we are going to submit commands to the GPU. Submitting a command produces - // an object that implements the `GpuFuture` trait, which holds the resources for as long as - // they are in use by the GPU. - - self.rcx = Some(RenderContext { - render_pass, - framebuffers, - pipeline, - viewport, - }); + // Automatically generate a vertex input state from the vertex shader's input + // interface, that takes a single vertex buffer containing `Vertex` structs. + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + + // Make a list of the shader stages that the pipeline will have. + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + + // We must now create a **pipeline layout** object, which describes the locations and + // types of descriptor sets and push constants used by the shaders in the pipeline. + // + // Multiple pipelines can share a common layout object, which is more efficient. The + // shaders in a pipeline must use a subset of the resources described in its pipeline + // layout, but the pipeline layout is allowed to contain resources that are not present + // in the shaders; they can be used by shaders in other pipelines that share the same + // layout. Thus, it is a good idea to design shaders so that many pipelines have common + // resource locations, which allows them to share pipeline layouts. + let layout = PipelineLayout::new( + self.context.device().clone(), + // Since we only have one pipeline in this example, and thus one pipeline layout, + // we automatically generate the creation info for it from the resources used in + // the shaders. In a real application, you would specify this information manually + // so that you can re-use one layout in multiple pipelines. + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.context.device().clone()) + .unwrap(), + ) + .unwrap(); + + // We have to indicate which subpass of which render pass this pipeline is going to be + // used in. The pipeline will only be usable from this particular subpass. + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + // Finally, create the pipeline. + GraphicsPipeline::new( + self.context.device().clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + // How vertex data is read from the vertex buffers into the vertex shader. + vertex_input_state: Some(vertex_input_state), + // How vertices are arranged into primitive shapes. The default primitive shape + // is a triangle. + input_assembly_state: Some(InputAssemblyState::default()), + // How primitives are transformed and clipped to fit the framebuffer. We use a + // resizable viewport, set to draw over the entire window. + viewport_state: Some(ViewportState::default()), + // How polygons are culled and converted into a raster of pixels. The default + // value does not perform any culling. + rasterization_state: Some(RasterizationState::default()), + // How multiple fragment shader samples are converted to a single pixel value. + // The default value does not perform any multisampling. + multisample_state: Some(MultisampleState::default()), + // How pixel values are combined with the values already present in the + // framebuffer. The default value overwrites the old value with the new one, + // without any blending. + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + // Dynamic states allows us to specify parts of the pipeline settings when + // recording the command buffer, before we perform drawing. Here, we specify + // that the viewport should be dynamic. + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() + }; + + // Dynamic viewports allow us to recreate just the viewport when the window is resized. + // Otherwise we would have to recreate the whole pipeline. + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + // In the `window_event` handler below we are going to submit commands to the GPU. + // Submitting a command produces an object that implements the `GpuFuture` trait, which + // holds the resources for as long as they are in use by the GPU. + + self.rcx = Some(RenderContext { + render_pass, + framebuffers, + pipeline, + viewport, + }); } fn window_event( @@ -368,15 +368,13 @@ impl ApplicationHandler for App { // on the window size. In this example that // includes the swapchain, the framebuffers // and the dynamic state viewport. - rcx.framebuffers = window_size_dependent_setup( - swapchain_images, - &rcx.render_pass, - ); + rcx.framebuffers = + window_size_dependent_setup(swapchain_images, &rcx.render_pass); }) .unwrap(); - // In order to draw, we have to record a *command buffer*. The command buffer object - // holds the list of commands that are going to be executed. + // In order to draw, we have to record a *command buffer*. The command buffer + // object holds the list of commands that are going to be executed. // // Recording a command buffer is an expensive operation (usually a few hundred // microseconds), but it is known to be a hot path in the driver and is expected to @@ -412,9 +410,9 @@ impl ApplicationHandler for App { ) }, SubpassBeginInfo { - // The contents of the first (and only) subpass. - // This can be either `Inline` or `SecondaryCommandBuffers`. - // The latter is a bit more advanced and is not covered here. + // The contents of the first (and only) subpass. This can be either + // `Inline` or `SecondaryCommandBuffers`. The latter is a bit more + // advanced and is not covered here. contents: SubpassContents::Inline, ..Default::default() }, @@ -451,14 +449,14 @@ impl ApplicationHandler for App { .unwrap() .boxed(); - // The color output is now expected to contain our triangle. But in order to - // show it on the screen, we have to *present* the image by calling - // `present` on the window renderer. + // The color output is now expected to contain our triangle. But in order to show + // it on the screen, we have to *present* the image by calling `present` on the + // window renderer. // // This function does not actually present the image immediately. Instead it - // submits a present command at the end of the queue. This means that it will - // only be presented once the GPU has finished executing the command buffer - // that draws the triangle. + // submits a present command at the end of the queue. This means that it will only + // be presented once the GPU has finished executing the command buffer that draws + // the triangle. window_renderer.present(future, false); } _ => {} diff --git a/examples/triangle-v1_3/main.rs b/examples/triangle-v1_3/main.rs index a2177d9bdd..2a74e1ed53 100644 --- a/examples/triangle-v1_3/main.rs +++ b/examples/triangle-v1_3/main.rs @@ -82,466 +82,475 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - - // The first step of any Vulkan program is to create an instance. - // - // When we create an instance, we have to pass a list of extensions that we want to enable. - // - // All the window-drawing functionalities are part of non-core extensions that we need to - // enable manually. To do so, we ask `Surface` for the list of extensions required to draw to - // a window. - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - - // Now creating the instance. - let instance = Instance::new( - library, - InstanceCreateInfo { - // Enable enumerating devices that use non-conformant Vulkan implementations. - // (e.g. MoltenVK) - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - // Choose device extensions that we're going to use. In order to present images to a surface, - // we need a `Swapchain`, which is provided by the `khr_swapchain` extension. - let mut device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - // We then choose which physical device to use. First, we enumerate all the available physical - // devices, then apply filters to narrow them down to those that can support our needs. - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| { - // For this example, we require at least Vulkan 1.3, or a device that has the - // `khr_dynamic_rendering` extension available. - p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering - }) - .filter(|p| { - // Some devices may not support the extensions or features that your application, or - // report properties and limits that are not sufficient for your application. These - // should be filtered out here. - p.supported_extensions().contains(&device_extensions) - }) - .filter_map(|p| { - // For each physical device, we try to find a suitable queue family that will execute - // our draw commands. - // - // Devices can provide multiple queues to run commands in parallel (for example a draw - // queue and a compute queue), similar to CPU threads. This is something you have to - // have to manage manually in Vulkan. Queues of the same type belong to the same queue - // family. + let library = VulkanLibrary::new().unwrap(); + + // The first step of any Vulkan program is to create an instance. + // + // When we create an instance, we have to pass a list of extensions that we want to enable. + // + // All the window-drawing functionalities are part of non-core extensions that we need to + // enable manually. To do so, we ask `Surface` for the list of extensions required to draw + // to a window. + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + + // Now creating the instance. + let instance = Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) + .unwrap(); + + // Choose device extensions that we're going to use. In order to present images to a + // surface, we need a `Swapchain`, which is provided by the `khr_swapchain` extension. + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + // We then choose which physical device to use. First, we enumerate all the available + // physical devices, then apply filters to narrow them down to those that can support our + // needs. + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + // For this example, we require at least Vulkan 1.3, or a device that has the + // `khr_dynamic_rendering` extension available. + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| { + // Some devices may not support the extensions or features that your application, + // or report properties and limits that are not sufficient for your application. + // These should be filtered out here. + p.supported_extensions().contains(&device_extensions) + }) + .filter_map(|p| { + // For each physical device, we try to find a suitable queue family that will + // execute our draw commands. + // + // Devices can provide multiple queues to run commands in parallel (for example a + // draw queue and a compute queue), similar to CPU threads. This is something you + // have to have to manage manually in Vulkan. Queues of the same type belong to the + // same queue family. + // + // Here, we look for a single queue family that is suitable for our purposes. In a + // real-world application, you may want to use a separate dedicated transfer queue + // to handle data transfers in parallel with graphics operations. You may also need + // a separate queue for compute operations, if your application uses those. + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + // We select a queue family that supports graphics operations. When drawing + // to a window surface, as we do in this example, we also need to check + // that queues in this queue family are capable of presenting images to the + // surface. + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + // The code here searches for the first queue family that is suitable. If none + // is found, `None` is returned to `filter_map`, which disqualifies this + // physical device. + .map(|i| (p, i as u32)) + }) + // All the physical devices that pass the filters above are suitable for the + // application. However, not every device is equal, some are preferred over others. + // Now, we assign each physical device a score, and pick the device with the lowest + // ("best") score. // - // Here, we look for a single queue family that is suitable for our purposes. In a - // real-world application, you may want to use a separate dedicated transfer queue to - // handle data transfers in parallel with graphics operations. You may also need a - // separate queue for compute operations, if your application uses those. - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - // We select a queue family that supports graphics operations. When drawing to - // a window surface, as we do in this example, we also need to check that - // queues in this queue family are capable of presenting images to the surface. - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - // The code here searches for the first queue family that is suitable. If none is - // found, `None` is returned to `filter_map`, which disqualifies this physical - // device. - .map(|i| (p, i as u32)) - }) - // All the physical devices that pass the filters above are suitable for the application. - // However, not every device is equal, some are preferred over others. Now, we assign each - // physical device a score, and pick the device with the lowest ("best") score. + // In this example, we simply select the best-scoring device to use in the application. + // In a real-world setting, you may want to use the best-scoring device only as a + // "default" or "recommended" device, and let the user choose the device themself. + .min_by_key(|(p, _)| { + // We assign a lower score to device types that are likely to be faster/better. + match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + } + }) + .expect("no suitable physical device found"); + + // Some little debug infos. + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + // If the selected device doesn't have Vulkan 1.3 available, then we need to enable the + // `khr_dynamic_rendering` extension manually. This extension became a core part of Vulkan + // in version 1.3 and later, so it's always available then and it does not need to be + // enabled. We can be sure that this extension will be available on the selected physical + // device, because we filtered out unsuitable devices in the device selection code above. + if physical_device.api_version() < Version::V1_3 { + device_extensions.khr_dynamic_rendering = true; + } + + // Now initializing the device. This is probably the most important object of Vulkan. // - // In this example, we simply select the best-scoring device to use in the application. - // In a real-world setting, you may want to use the best-scoring device only as a "default" - // or "recommended" device, and let the user choose the device themself. - .min_by_key(|(p, _)| { - // We assign a lower score to device types that are likely to be faster/better. - match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - } - }) - .expect("no suitable physical device found"); - - // Some little debug infos. - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - // If the selected device doesn't have Vulkan 1.3 available, then we need to enable the - // `khr_dynamic_rendering` extension manually. This extension became a core part of Vulkan - // in version 1.3 and later, so it's always available then and it does not need to be enabled. - // We can be sure that this extension will be available on the selected physical device, - // because we filtered out unsuitable devices in the device selection code above. - if physical_device.api_version() < Version::V1_3 { - device_extensions.khr_dynamic_rendering = true; - } + // An iterator of created queues is returned by the function alongside the device. + let (device, mut queues) = Device::new( + // Which physical device to connect to. + physical_device, + DeviceCreateInfo { + // The list of queues that we are going to use. Here we only use one queue, from + // the previously chosen queue family. + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + + // A list of optional features and extensions that our program needs to work + // correctly. Some parts of the Vulkan specs are optional and must be enabled + // manually at device creation. In this example the only things we are going to + // need are the `khr_swapchain` extension that allows us to draw to a window, and + // `khr_dynamic_rendering` if we don't have Vulkan 1.3 available. + enabled_extensions: device_extensions, + + // In order to render with Vulkan 1.3's dynamic rendering, we need to enable it + // here. Otherwise, we are only allowed to render with a render pass object, as in + // the standard triangle example. The feature is required to be supported by the + // device if it supports Vulkan 1.3 and higher, or if the `khr_dynamic_rendering` + // extension is available, so we don't need to check for support. + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, - // Now initializing the device. This is probably the most important object of Vulkan. - // - // An iterator of created queues is returned by the function alongside the device. - let (device, mut queues) = Device::new( - // Which physical device to connect to. - physical_device, - DeviceCreateInfo { - // The list of queues that we are going to use. Here we only use one queue, from the - // previously chosen queue family. - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, ..Default::default() - }], - - // A list of optional features and extensions that our program needs to work correctly. - // Some parts of the Vulkan specs are optional and must be enabled manually at device - // creation. In this example the only things we are going to need are the - // `khr_swapchain` extension that allows us to draw to a window, and - // `khr_dynamic_rendering` if we don't have Vulkan 1.3 available. - enabled_extensions: device_extensions, - - // In order to render with Vulkan 1.3's dynamic rendering, we need to enable it here. - // Otherwise, we are only allowed to render with a render pass object, as in the - // standard triangle example. The feature is required to be supported by the device if - // it supports Vulkan 1.3 and higher, or if the `khr_dynamic_rendering` extension is - // available, so we don't need to check for support. - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() }, + ) + .unwrap(); - ..Default::default() - }, - ) - .unwrap(); - - // Since we can request multiple queues, the `queues` variable is in fact an iterator. We only - // use one queue in this example, so we just retrieve the first and only element of the - // iterator. - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - // Before we can start creating and recording command buffers, we need a way of allocating - // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools - // underneath and provides a safe interface for them. - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // We now create a buffer that will store the shape of our triangle. - let vertices = [ - MyVertex { - position: [-0.5, -0.25], - }, - MyVertex { - position: [0.0, 0.5], - }, - MyVertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - App { - instance, - device, - queue, - command_buffer_allocator, - vertex_buffer, - rcx: None, - } + // Since we can request multiple queues, the `queues` variable is in fact an iterator. We + // only use one queue in this example, so we just retrieve the first and only element of + // the iterator. + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command + // pools underneath and provides a safe interface for them. + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // We now create a buffer that will store the shape of our triangle. + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + }, + MyVertex { + position: [0.0, 0.5], + }, + MyVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcx: None, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - // The objective of this example is to draw a triangle on a window. To do so, we first need to - // create the window. We use the `WindowBuilder` from the `winit` crate to do that here. - // - // Before we can render to a window, we must first create a `vulkano::swapchain::Surface` - // object from it, which represents the drawable surface of a window. For that we must wrap the - // `winit::window::Window` in an `Arc`. - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - // Before we can draw on the surface, we have to create what is called a swapchain. Creating a - // swapchain allocates the color buffers that will contain the image that will ultimately be - // visible on the screen. These images are returned alongside the swapchain. - let (swapchain, images) = { - // Querying the capabilities of the surface. When we create the swapchain we can only pass - // values that are allowed by the capabilities. - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); + // The objective of this example is to draw a triangle on a window. To do so, we first need + // to create the window. We use the `WindowBuilder` from the `winit` crate to do that here. + // + // Before we can render to a window, we must first create a `vulkano::swapchain::Surface` + // object from it, which represents the drawable surface of a window. For that we must wrap + // the `winit::window::Window` in an `Arc`. + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + // Before we can draw on the surface, we have to create what is called a swapchain. + // Creating a swapchain allocates the color buffers that will contain the image that will + // ultimately be visible on the screen. These images are returned alongside the swapchain. + let (swapchain, images) = { + // Querying the capabilities of the surface. When we create the swapchain we can only + // pass values that are allowed by the capabilities. + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); - // Choosing the internal format that the images will have. - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - // Please take a look at the docs for the meaning of the parameters we didn't mention. - Swapchain::new( - self.device.clone(), - surface, - SwapchainCreateInfo { - // Some drivers report an `min_image_count` of 1, but fullscreen mode requires at - // least 2. Therefore we must ensure the count is at least 2, otherwise the program - // would crash when entering fullscreen mode on those drivers. - min_image_count: surface_capabilities.min_image_count.max(2), - - image_format, - - // The size of the window, only used to initially setup the swapchain. - // - // NOTE: - // On some drivers the swapchain extent is specified by - // `surface_capabilities.current_extent` and the swapchain size must use this - // extent. This extent is always the same as the window size. - // - // However, other drivers don't specify a value, i.e. - // `surface_capabilities.current_extent` is `None`. These drivers will allow - // anything, but the only sensible value is the window size. - // - // Both of these cases need the swapchain to use the window size, so we just - // use that. - image_extent: window_size.into(), - - image_usage: ImageUsage::COLOR_ATTACHMENT, - - // The alpha mode indicates how the alpha value of the final image will behave. For - // example, you can choose whether the window will be opaque or transparent. - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), + // Choosing the internal format that the images will have. + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + // Please take a look at the docs for the meaning of the parameters we didn't mention. + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + // Some drivers report an `min_image_count` of 1, but fullscreen mode requires + // at least 2. Therefore we must ensure the count is at least 2, otherwise the + // program would crash when entering fullscreen mode on those drivers. + min_image_count: surface_capabilities.min_image_count.max(2), + + image_format, + + // The size of the window, only used to initially setup the swapchain. + // + // NOTE: + // On some drivers the swapchain extent is specified by + // `surface_capabilities.current_extent` and the swapchain size must use this + // extent. This extent is always the same as the window size. + // + // However, other drivers don't specify a value, i.e. + // `surface_capabilities.current_extent` is `None`. These drivers will allow + // anything, but the only sensible value is the window size. + // + // Both of these cases need the swapchain to use the window size, so we just + // use that. + image_extent: window_size.into(), + + image_usage: ImageUsage::COLOR_ATTACHMENT, + + // The alpha mode indicates how the alpha value of the final image will behave. + // For example, you can choose whether the window will be opaque or + // transparent. + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() + }; - ..Default::default() - }, - ) - .unwrap() - }; - - // When creating the swapchain, we only created plain images. To use them as an attachment for - // rendering, we must wrap then in an image view. - // - // Since we need to draw to multiple images, we are going to create a different image view for - // each image. - let attachment_image_views = window_size_dependent_setup(&images); - - // The next step is to create the shaders. - // - // The raw shader creation API provided by the vulkano library is unsafe for various reasons, - // so The `shader!` macro provides a way to generate a Rust module from GLSL source - in the - // example below, the source is provided as a string input directly to the shader, but a path - // to a source file can be provided as well. Note that the user must specify the type of shader - // (e.g. "vertex", "fragment", etc.) using the `ty` option of the macro. - // - // The items generated by the `shader!` macro include a `load` function which loads the shader - // using an input logical device. The module also includes type definitions for layout - // structures defined in the shader source, for example uniforms and push constants. - // - // A more detailed overview of what the `shader!` macro generates can be found in the - // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/ - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 position; - - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", + // When creating the swapchain, we only created plain images. To use them as an attachment + // for rendering, we must wrap then in an image view. + // + // Since we need to draw to multiple images, we are going to create a different image view + // for each image. + let attachment_image_views = window_size_dependent_setup(&images); + + // The next step is to create the shaders. + // + // The raw shader creation API provided by the vulkano library is unsafe for various + // reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL + // source - in the example below, the source is provided as a string input directly to the + // shader, but a path to a source file can be provided as well. Note that the user must + // specify the type of shader (e.g. "vertex", "fragment", etc.) using the `ty` option of + // the macro. + // + // The items generated by the `shader!` macro include a `load` function which loads the + // shader using an input logical device. The module also includes type definitions for + // layout structures defined in the shader source, for example uniforms and push constants. + // + // A more detailed overview of what the `shader!` macro generates can be found in the + // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/ + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 position; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } } - } - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - layout(location = 0) out vec4 f_color; + layout(location = 0) out vec4 f_color; - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - ", + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } } - } - // Before we draw, we have to create what is called a **pipeline**. A pipeline describes how - // a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains - // many settings for customization, all baked into a single object. For drawing, we create - // a **graphics** pipeline, but there are also other types of pipeline. - let pipeline = { - // First, we load the shaders that the pipeline will use: - // the vertex shader and the fragment shader. - // - // A Vulkan shader can in theory contain multiple entry points, so we have to specify which - // one. - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); + // Before we draw, we have to create what is called a **pipeline**. A pipeline describes + // how a GPU operation is to be performed. It is similar to an OpenGL program, but it also + // contains many settings for customization, all baked into a single object. For drawing, + // we create a **graphics** pipeline, but there are also other types of pipeline. + let pipeline = { + // First, we load the shaders that the pipeline will use: the vertex shader and the + // fragment shader. + // + // A Vulkan shader can in theory contain multiple entry points, so we have to specify + // which one. + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); - // Automatically generate a vertex input state from the vertex shader's input interface, - // that takes a single vertex buffer containing `Vertex` structs. - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + // Automatically generate a vertex input state from the vertex shader's input + // interface, that takes a single vertex buffer containing `Vertex` structs. + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - // Make a list of the shader stages that the pipeline will have. - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; + // Make a list of the shader stages that the pipeline will have. + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; - // We must now create a **pipeline layout** object, which describes the locations and types - // of descriptor sets and push constants used by the shaders in the pipeline. - // - // Multiple pipelines can share a common layout object, which is more efficient. - // The shaders in a pipeline must use a subset of the resources described in its pipeline - // layout, but the pipeline layout is allowed to contain resources that are not present in - // the shaders; they can be used by shaders in other pipelines that share the same - // layout. Thus, it is a good idea to design shaders so that many pipelines have - // common resource locations, which allows them to share pipeline layouts. - let layout = PipelineLayout::new( - self.device.clone(), - // Since we only have one pipeline in this example, and thus one pipeline layout, - // we automatically generate the creation info for it from the resources used in the - // shaders. In a real application, you would specify this information manually so that - // you can re-use one layout in multiple pipelines. - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); + // We must now create a **pipeline layout** object, which describes the locations and + // types of descriptor sets and push constants used by the shaders in the pipeline. + // + // Multiple pipelines can share a common layout object, which is more efficient. The + // shaders in a pipeline must use a subset of the resources described in its pipeline + // layout, but the pipeline layout is allowed to contain resources that are not present + // in the shaders; they can be used by shaders in other pipelines that share the same + // layout. Thus, it is a good idea to design shaders so that many pipelines have common + // resource locations, which allows them to share pipeline layouts. + let layout = PipelineLayout::new( + self.device.clone(), + // Since we only have one pipeline in this example, and thus one pipeline layout, + // we automatically generate the creation info for it from the resources used in + // the shaders. In a real application, you would specify this information manually + // so that you can re-use one layout in multiple pipelines. + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) + .unwrap(); - // We describe the formats of attachment images where the colors, depth and/or stencil - // information will be written. The pipeline will only be usable with this particular - // configuration of the attachment images. - let subpass = PipelineRenderingCreateInfo { - // We specify a single color attachment that will be rendered to. When we begin - // rendering, we will specify a swapchain image to be used as this attachment, so here - // we set its format to be the same format as the swapchain. - color_attachment_formats: vec![Some(swapchain.image_format())], - ..Default::default() + // We describe the formats of attachment images where the colors, depth and/or stencil + // information will be written. The pipeline will only be usable with this particular + // configuration of the attachment images. + let subpass = PipelineRenderingCreateInfo { + // We specify a single color attachment that will be rendered to. When we begin + // rendering, we will specify a swapchain image to be used as this attachment, so + // here we set its format to be the same format as the swapchain. + color_attachment_formats: vec![Some(swapchain.image_format())], + ..Default::default() + }; + + // Finally, create the pipeline. + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + // How vertex data is read from the vertex buffers into the vertex shader. + vertex_input_state: Some(vertex_input_state), + // How vertices are arranged into primitive shapes. The default primitive shape + // is a triangle. + input_assembly_state: Some(InputAssemblyState::default()), + // How primitives are transformed and clipped to fit the framebuffer. We use a + // resizable viewport, set to draw over the entire window. + viewport_state: Some(ViewportState::default()), + // How polygons are culled and converted into a raster of pixels. The default + // value does not perform any culling. + rasterization_state: Some(RasterizationState::default()), + // How multiple fragment shader samples are converted to a single pixel value. + // The default value does not perform any multisampling. + multisample_state: Some(MultisampleState::default()), + // How pixel values are combined with the values already present in the + // framebuffer. The default value overwrites the old value with the new one, + // without any blending. + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.color_attachment_formats.len() as u32, + ColorBlendAttachmentState::default(), + )), + // Dynamic states allows us to specify parts of the pipeline settings when + // recording the command buffer, before we perform drawing. Here, we specify + // that the viewport should be dynamic. + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() }; - // Finally, create the pipeline. - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - // How vertex data is read from the vertex buffers into the vertex shader. - vertex_input_state: Some(vertex_input_state), - // How vertices are arranged into primitive shapes. - // The default primitive shape is a triangle. - input_assembly_state: Some(InputAssemblyState::default()), - // How primitives are transformed and clipped to fit the framebuffer. - // We use a resizable viewport, set to draw over the entire window. - viewport_state: Some(ViewportState::default()), - // How polygons are culled and converted into a raster of pixels. - // The default value does not perform any culling. - rasterization_state: Some(RasterizationState::default()), - // How multiple fragment shader samples are converted to a single pixel value. - // The default value does not perform any multisampling. - multisample_state: Some(MultisampleState::default()), - // How pixel values are combined with the values already present in the framebuffer. - // The default value overwrites the old value with the new one, without any - // blending. - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.color_attachment_formats.len() as u32, - ColorBlendAttachmentState::default(), - )), - // Dynamic states allows us to specify parts of the pipeline settings when - // recording the command buffer, before we perform drawing. - // Here, we specify that the viewport should be dynamic. - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - // Dynamic viewports allow us to recreate just the viewport when the window is resized. - // Otherwise we would have to recreate the whole pipeline. - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - // In some situations, the swapchain will become invalid by itself. This includes for example - // when the window is resized (as the images of the swapchain will no longer match the - // window's) or, on Android, when the application went to the background and goes back to the - // foreground. - // - // In this situation, acquiring a swapchain image or presenting it will return an error. - // Rendering to an image of that swapchain will not produce any error, but may or may not work. - // To continue rendering, we need to recreate the swapchain by creating a new swapchain. Here, - // we remember that we need to do this for the next loop iteration. - let recreate_swapchain = false; - - // In the loop below we are going to submit commands to the GPU. Submitting a command produces - // an object that implements the `GpuFuture` trait, which holds the resources for as long as - // they are in use by the GPU. - // - // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid - // that, we store the submission of the previous frame here. - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - attachment_image_views, - pipeline, - viewport, - recreate_swapchain, - previous_frame_end, - }); + // Dynamic viewports allow us to recreate just the viewport when the window is resized. + // Otherwise we would have to recreate the whole pipeline. + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + // In some situations, the swapchain will become invalid by itself. This includes for + // example when the window is resized (as the images of the swapchain will no longer match + // the window's) or, on Android, when the application went to the background and goes back + // to the foreground. + // + // In this situation, acquiring a swapchain image or presenting it will return an error. + // Rendering to an image of that swapchain will not produce any error, but may or may not + // work. To continue rendering, we need to recreate the swapchain by creating a new + // swapchain. Here, we remember that we need to do this for the next loop iteration. + let recreate_swapchain = false; + + // In the loop below we are going to submit commands to the GPU. Submitting a command + // produces an object that implements the `GpuFuture` trait, which holds the resources for + // as long as they are in use by the GPU. + // + // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to + // avoid that, we store the submission of the previous frame here. + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + attachment_image_views, + pipeline, + viewport, + recreate_swapchain, + previous_frame_end, + }); } fn window_event( @@ -562,8 +571,8 @@ impl ApplicationHandler for App { WindowEvent::RedrawRequested => { let window_size = rcx.window.inner_size(); - // Do not draw the frame when the screen size is zero. On Windows, this can - // occur when minimizing the application. + // Do not draw the frame when the screen size is zero. On Windows, this can occur + // when minimizing the application. if window_size.width == 0 || window_size.height == 0 { return; } @@ -604,15 +613,19 @@ impl ApplicationHandler for App { // // This function can block if no image is available. The parameter is an optional // timeout after which the function call will return an error. - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; // `acquire_next_image` can be successful, but suboptimal. This means that the // swapchain image will still work, but it may not display correctly. With some @@ -622,8 +635,8 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = true; } - // In order to draw, we have to record a *command buffer*. The command buffer object - // holds the list of commands that are going to be executed. + // In order to draw, we have to record a *command buffer*. The command buffer + // object holds the list of commands that are going to be executed. // // Recording a command buffer is an expensive operation (usually a few hundred // microseconds), but it is known to be a hot path in the driver and is expected to @@ -713,7 +726,10 @@ impl ApplicationHandler for App { // that draws the triangle. .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush(); diff --git a/examples/triangle/main.rs b/examples/triangle/main.rs index 4972ab0ed5..cc02a145d5 100644 --- a/examples/triangle/main.rs +++ b/examples/triangle/main.rs @@ -17,7 +17,7 @@ use vulkano::{ }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, - QueueCreateInfo, QueueFlags + QueueCreateInfo, QueueFlags, }, image::{view::ImageView, Image, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, @@ -78,472 +78,482 @@ struct RenderContext { impl App { fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - - // The first step of any Vulkan program is to create an instance. - // - // When we create an instance, we have to pass a list of extensions that we want to enable. - // - // All the window-drawing functionalities are part of non-core extensions that we need to - // enable manually. To do so, we ask `Surface` for the list of extensions required to draw to - // a window. - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - - // Now creating the instance. - let instance = Instance::new( - library, - InstanceCreateInfo { - // Enable enumerating devices that use non-conformant Vulkan implementations. - // (e.g. MoltenVK) - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - ..Default::default() - }, - ) - .unwrap(); - - // Choose device extensions that we're going to use. In order to present images to a surface, - // we need a `Swapchain`, which is provided by the `khr_swapchain` extension. - let device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - // We then choose which physical device to use. First, we enumerate all the available physical - // devices, then apply filters to narrow them down to those that can support our needs. - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| { - // Some devices may not support the extensions or features that your application, or - // report properties and limits that are not sufficient for your application. These - // should be filtered out here. - p.supported_extensions().contains(&device_extensions) - }) - .filter_map(|p| { - // For each physical device, we try to find a suitable queue family that will execute - // our draw commands. - // - // Devices can provide multiple queues to run commands in parallel (for example a draw - // queue and a compute queue), similar to CPU threads. This is something you have to - // have to manage manually in Vulkan. Queues of the same type belong to the same queue - // family. + let library = VulkanLibrary::new().unwrap(); + + // The first step of any Vulkan program is to create an instance. + // + // When we create an instance, we have to pass a list of extensions that we want to enable. + // + // All the window-drawing functionalities are part of non-core extensions that we need to + // enable manually. To do so, we ask `Surface` for the list of extensions required to draw + // to a window. + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + + // Now creating the instance. + let instance = Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + ..Default::default() + }, + ) + .unwrap(); + + // Choose device extensions that we're going to use. In order to present images to a + // surface, we need a `Swapchain`, which is provided by the `khr_swapchain` extension. + let device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + // We then choose which physical device to use. First, we enumerate all the available + // physical devices, then apply filters to narrow them down to those that can support our + // needs. + let (physical_device, queue_family_index) = instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + // Some devices may not support the extensions or features that your application, + // or report properties and limits that are not sufficient for your application. + // These should be filtered out here. + p.supported_extensions().contains(&device_extensions) + }) + .filter_map(|p| { + // For each physical device, we try to find a suitable queue family that will + // execute our draw commands. + // + // Devices can provide multiple queues to run commands in parallel (for example a + // draw queue and a compute queue), similar to CPU threads. This is + // something you have to have to manage manually in Vulkan. Queues + // of the same type belong to the same queue family. + // + // Here, we look for a single queue family that is suitable for our purposes. In a + // real-world application, you may want to use a separate dedicated transfer queue + // to handle data transfers in parallel with graphics operations. + // You may also need a separate queue for compute operations, if + // your application uses those. + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + // We select a queue family that supports graphics operations. When drawing + // to a window surface, as we do in this example, we also need to check + // that queues in this queue family are capable of presenting images to the + // surface. + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + // The code here searches for the first queue family that is suitable. If none + // is found, `None` is returned to `filter_map`, which + // disqualifies this physical device. + .map(|i| (p, i as u32)) + }) + // All the physical devices that pass the filters above are suitable for the + // application. However, not every device is equal, some are preferred over others. + // Now, we assign each physical device a score, and pick the device with the lowest + // ("best") score. // - // Here, we look for a single queue family that is suitable for our purposes. In a - // real-world application, you may want to use a separate dedicated transfer queue to - // handle data transfers in parallel with graphics operations. You may also need a - // separate queue for compute operations, if your application uses those. - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - // We select a queue family that supports graphics operations. When drawing to - // a window surface, as we do in this example, we also need to check that - // queues in this queue family are capable of presenting images to the surface. - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - // The code here searches for the first queue family that is suitable. If none is - // found, `None` is returned to `filter_map`, which disqualifies this physical - // device. - .map(|i| (p, i as u32)) - }) - // All the physical devices that pass the filters above are suitable for the application. - // However, not every device is equal, some are preferred over others. Now, we assign each - // physical device a score, and pick the device with the lowest ("best") score. + // In this example, we simply select the best-scoring device to use in the application. + // In a real-world setting, you may want to use the best-scoring device only as a + // "default" or "recommended" device, and let the user choose the device themself. + .min_by_key(|(p, _)| { + // We assign a lower score to device types that are likely to be faster/better. + match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + } + }) + .expect("no suitable physical device found"); + + // Some little debug infos. + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + // Now initializing the device. This is probably the most important object of Vulkan. // - // In this example, we simply select the best-scoring device to use in the application. - // In a real-world setting, you may want to use the best-scoring device only as a "default" - // or "recommended" device, and let the user choose the device themself. - .min_by_key(|(p, _)| { - // We assign a lower score to device types that are likely to be faster/better. - match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - } - }) - .expect("no suitable physical device found"); - - // Some little debug infos. - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - // Now initializing the device. This is probably the most important object of Vulkan. - // - // An iterator of created queues is returned by the function alongside the device. - let (device, mut queues) = Device::new( - // Which physical device to connect to. - physical_device, - DeviceCreateInfo { - // A list of optional features and extensions that our program needs to work correctly. - // Some parts of the Vulkan specs are optional and must be enabled manually at device - // creation. In this example the only thing we are going to need is the `khr_swapchain` - // extension that allows us to draw to a window. - enabled_extensions: device_extensions, - - // The list of queues that we are going to use. Here we only use one queue, from the - // previously chosen queue family. - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + // An iterator of created queues is returned by the function alongside the device. + let (device, mut queues) = Device::new( + // Which physical device to connect to. + physical_device, + DeviceCreateInfo { + // A list of optional features and extensions that our program needs to work + // correctly. Some parts of the Vulkan specs are optional and must be enabled + // manually at device creation. In this example the only thing we are going to need + // is the `khr_swapchain` extension that allows us to draw to a window. + enabled_extensions: device_extensions, + + // The list of queues that we are going to use. Here we only use one queue, from + // the previously chosen queue family. + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + ..Default::default() - }], + }, + ) + .unwrap(); - ..Default::default() - }, - ) - .unwrap(); - - // Since we can request multiple queues, the `queues` variable is in fact an iterator. We only - // use one queue in this example, so we just retrieve the first and only element of the - // iterator. - let queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - // Before we can start creating and recording command buffers, we need a way of allocating - // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools - // underneath and provides a safe interface for them. - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - // We now create a buffer that will store the shape of our triangle. - let vertices = [ - MyVertex { - position: [-0.5, -0.25], - }, - MyVertex { - position: [0.0, 0.5], - }, - MyVertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .unwrap(); - - let rcx = None; - - App { - instance, - device, - queue, - command_buffer_allocator, - vertex_buffer, - rcx, - } + // Since we can request multiple queues, the `queues` variable is in fact an iterator. We + // only use one queue in this example, so we just retrieve the first and only element of + // the iterator. + let queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + // Before we can start creating and recording command buffers, we need a way of allocating + // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command + // pools underneath and provides a safe interface for them. + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + // We now create a buffer that will store the shape of our triangle. + let vertices = [ + MyVertex { + position: [-0.5, -0.25], + }, + MyVertex { + position: [0.0, 0.5], + }, + MyVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer = Buffer::from_iter( + memory_allocator, + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + vertices, + ) + .unwrap(); + + let rcx = None; + + App { + instance, + device, + queue, + command_buffer_allocator, + vertex_buffer, + rcx, + } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - // The objective of this example is to draw a triangle on a window. To do so, we first need to - // create the window. We use the `WindowBuilder` from the `winit` crate to do that here. - // - // Before we can render to a window, we must first create a `vulkano::swapchain::Surface` - // object from it, which represents the drawable surface of a window. For that we must wrap the - // `winit::window::Window` in an `Arc`. - let window = Arc::new(event_loop.create_window(Window::default_attributes()).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - let window_size = window.inner_size(); - - // Before we can draw on the surface, we have to create what is called a swapchain. Creating a - // swapchain allocates the color buffers that will contain the image that will ultimately be - // visible on the screen. These images are returned alongside the swapchain. - let (swapchain, images) = { - // Querying the capabilities of the surface. When we create the swapchain we can only pass - // values that are allowed by the capabilities. - let surface_capabilities = self - .device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); + // The objective of this example is to draw a triangle on a window. To do so, we first need + // to create the window. We use the `WindowBuilder` from the `winit` crate to do that here. + // + // Before we can render to a window, we must first create a `vulkano::swapchain::Surface` + // object from it, which represents the drawable surface of a window. For that we must wrap + // the `winit::window::Window` in an `Arc`. + let window = Arc::new( + event_loop + .create_window(Window::default_attributes()) + .unwrap(), + ); + let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let window_size = window.inner_size(); + + // Before we can draw on the surface, we have to create what is called a swapchain. + // Creating a swapchain allocates the color buffers that will contain the image that will + // ultimately be visible on the screen. These images are returned alongside the swapchain. + let (swapchain, images) = { + // Querying the capabilities of the surface. When we create the swapchain we can only + // pass values that are allowed by the capabilities. + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); - // Choosing the internal format that the images will have. - let (image_format, _) = self - .device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; + // Choosing the internal format that the images will have. + let (image_format, _) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + // Please take a look at the docs for the meaning of the parameters we didn't mention. + Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + // Some drivers report an `min_image_count` of 1, but fullscreen mode requires + // at least 2. Therefore we must ensure the count is at least 2, otherwise the + // program would crash when entering fullscreen mode on those drivers. + min_image_count: surface_capabilities.min_image_count.max(2), + + image_format, + + // The size of the window, only used to initially setup the swapchain. + // + // NOTE: + // On some drivers the swapchain extent is specified by + // `surface_capabilities.current_extent` and the swapchain size must use this + // extent. This extent is always the same as the window size. + // + // However, other drivers don't specify a value, i.e. + // `surface_capabilities.current_extent` is `None`. These drivers will allow + // anything, but the only sensible value is the window size. + // + // Both of these cases need the swapchain to use the window size, so we just + // use that. + image_extent: window_size.into(), - // Please take a look at the docs for the meaning of the parameters we didn't mention. - Swapchain::new( - self.device.clone(), - surface, - SwapchainCreateInfo { - // Some drivers report an `min_image_count` of 1, but fullscreen mode requires at - // least 2. Therefore we must ensure the count is at least 2, otherwise the program - // would crash when entering fullscreen mode on those drivers. - min_image_count: surface_capabilities.min_image_count.max(2), + image_usage: ImageUsage::COLOR_ATTACHMENT, - image_format, + // The alpha mode indicates how the alpha value of the final image will behave. + // For example, you can choose whether the window will be + // opaque or transparent. + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), - // The size of the window, only used to initially setup the swapchain. - // - // NOTE: - // On some drivers the swapchain extent is specified by - // `surface_capabilities.current_extent` and the swapchain size must use this - // extent. This extent is always the same as the window size. - // - // However, other drivers don't specify a value, i.e. - // `surface_capabilities.current_extent` is `None`. These drivers will allow - // anything, but the only sensible value is the window size. - // - // Both of these cases need the swapchain to use the window size, so we just - // use that. - image_extent: window_size.into(), - - image_usage: ImageUsage::COLOR_ATTACHMENT, - - // The alpha mode indicates how the alpha value of the final image will behave. For - // example, you can choose whether the window will be opaque or transparent. - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; - ..Default::default() - }, - ) - .unwrap() - }; - - // The next step is to create the shaders. - // - // The raw shader creation API provided by the vulkano library is unsafe for various reasons, - // so The `shader!` macro provides a way to generate a Rust module from GLSL source - in the - // example below, the source is provided as a string input directly to the shader, but a path - // to a source file can be provided as well. Note that the user must specify the type of shader - // (e.g. "vertex", "fragment", etc.) using the `ty` option of the macro. - // - // The items generated by the `shader!` macro include a `load` function which loads the shader - // using an input logical device. The module also includes type definitions for layout - // structures defined in the shader source, for example uniforms and push constants. - // - // A more detailed overview of what the `shader!` macro generates can be found in the - // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/ - mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 position; - - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", + // The next step is to create the shaders. + // + // The raw shader creation API provided by the vulkano library is unsafe for various + // reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL + // source - in the example below, the source is provided as a string input directly to the + // shader, but a path to a source file can be provided as well. Note that the user must + // specify the type of shader (e.g. "vertex", "fragment", etc.) using the `ty` option of + // the macro. + // + // The items generated by the `shader!` macro include a `load` function which loads the + // shader using an input logical device. The module also includes type definitions for + // layout structures defined in the shader source, for example uniforms and push constants. + // + // A more detailed overview of what the `shader!` macro generates can be found in the + // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/ + mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 position; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } } - } - mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 + mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 - layout(location = 0) out vec4 f_color; + layout(location = 0) out vec4 f_color; - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - ", + void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); + } + ", + } } - } - // The next step is to create a *render pass*, which is an object that describes where the - // output of the graphics pipeline will go. It describes the layout of the images where the - // colors, depth and/or stencil information will be written. - let render_pass = vulkano::single_pass_renderpass!( - self.device.clone(), - attachments: { - // `color` is a custom name we give to the first and only attachment. - color: { - // `format: ` indicates the type of the format of the image. This has to be one - // of the types of the `vulkano::format` module (or alternatively one of your - // structs that implements the `FormatDesc` trait). Here we use the same format as - // the swapchain. - format: swapchain.image_format(), - // `samples: 1` means that we ask the GPU to use one sample to determine the value - // of each pixel in the color attachment. We could use a larger value - // (multisampling) for antialiasing. An example of this can be found in - // msaa-renderpass.rs. - samples: 1, - // `load_op: Clear` means that we ask the GPU to clear the content of this - // attachment at the start of the drawing. - load_op: Clear, - // `store_op: Store` means that we ask the GPU to store the output of the draw in - // the actual image. We could also ask it to discard the result. - store_op: Store, + // The next step is to create a *render pass*, which is an object that describes where the + // output of the graphics pipeline will go. It describes the layout of the images where the + // colors, depth and/or stencil information will be written. + let render_pass = vulkano::single_pass_renderpass!( + self.device.clone(), + attachments: { + // `color` is a custom name we give to the first and only attachment. + color: { + // `format: ` indicates the type of the format of the image. This has to be + // one of the types of the `vulkano::format` module (or alternatively one of + // your structs that implements the `FormatDesc` trait). Here we use the same + // format as the swapchain. + format: swapchain.image_format(), + // `samples: 1` means that we ask the GPU to use one sample to determine the + // value of each pixel in the color attachment. We could use a larger value + // (multisampling) for antialiasing. An example of this can be found in + // msaa-renderpass.rs. + samples: 1, + // `load_op: Clear` means that we ask the GPU to clear the content of this + // attachment at the start of the drawing. + load_op: Clear, + // `store_op: Store` means that we ask the GPU to store the output of the draw + // in the actual image. We could also ask it to discard the result. + store_op: Store, + }, }, - }, - pass: { - // We use the attachment named `color` as the one and only color attachment. - color: [color], - // No depth-stencil attachment is indicated with empty brackets. - depth_stencil: {}, - }, - ) - .unwrap(); - - // The render pass we created above only describes the layout of our framebuffers. Before we - // can draw we also need to create the actual framebuffers. - // - // Since we need to draw to multiple images, we are going to create a different framebuffer for - // each image. - let framebuffers = window_size_dependent_setup(&images, &render_pass); - - // Before we draw, we have to create what is called a **pipeline**. A pipeline describes how - // a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains - // many settings for customization, all baked into a single object. For drawing, we create - // a **graphics** pipeline, but there are also other types of pipeline. - let pipeline = { - // First, we load the shaders that the pipeline will use: - // the vertex shader and the fragment shader. - // - // A Vulkan shader can in theory contain multiple entry points, so we have to specify which - // one. - let vs = vs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); - let fs = fs::load(self.device.clone()) - .unwrap() - .entry_point("main") - .unwrap(); + pass: { + // We use the attachment named `color` as the one and only color attachment. + color: [color], + // No depth-stencil attachment is indicated with empty brackets. + depth_stencil: {}, + }, + ) + .unwrap(); - // Automatically generate a vertex input state from the vertex shader's input interface, - // that takes a single vertex buffer containing `Vertex` structs. - let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); + // The render pass we created above only describes the layout of our framebuffers. Before + // we can draw we also need to create the actual framebuffers. + // + // Since we need to draw to multiple images, we are going to create a different framebuffer + // for each image. + let framebuffers = window_size_dependent_setup(&images, &render_pass); + + // Before we draw, we have to create what is called a **pipeline**. A pipeline describes + // how a GPU operation is to be performed. It is similar to an OpenGL program, but it also + // contains many settings for customization, all baked into a single object. For drawing, + // we create a **graphics** pipeline, but there are also other types of pipeline. + let pipeline = { + // First, we load the shaders that the pipeline will use: the vertex shader and the + // fragment shader. + // + // A Vulkan shader can in theory contain multiple entry points, so we have to specify + // which one. + let vs = vs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(self.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); - // Make a list of the shader stages that the pipeline will have. - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; + // Automatically generate a vertex input state from the vertex shader's input + // interface, that takes a single vertex buffer containing `Vertex` structs. + let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap(); - // We must now create a **pipeline layout** object, which describes the locations and types - // of descriptor sets and push constants used by the shaders in the pipeline. - // - // Multiple pipelines can share a common layout object, which is more efficient. - // The shaders in a pipeline must use a subset of the resources described in its pipeline - // layout, but the pipeline layout is allowed to contain resources that are not present in - // the shaders; they can be used by shaders in other pipelines that share the same - // layout. Thus, it is a good idea to design shaders so that many pipelines have - // common resource locations, which allows them to share pipeline layouts. - let layout = PipelineLayout::new( - self.device.clone(), - // Since we only have one pipeline in this example, and thus one pipeline layout, - // we automatically generate the creation info for it from the resources used in the - // shaders. In a real application, you would specify this information manually so that - // you can re-use one layout in multiple pipelines. - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); + // Make a list of the shader stages that the pipeline will have. + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; - // We have to indicate which subpass of which render pass this pipeline is going to be used - // in. The pipeline will only be usable from this particular subpass. - let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + // We must now create a **pipeline layout** object, which describes the locations and + // types of descriptor sets and push constants used by the shaders in the pipeline. + // + // Multiple pipelines can share a common layout object, which is more efficient. The + // shaders in a pipeline must use a subset of the resources described in its pipeline + // layout, but the pipeline layout is allowed to contain resources that are not present + // in the shaders; they can be used by shaders in other pipelines that share the same + // layout. Thus, it is a good idea to design shaders so that many pipelines have common + // resource locations, which allows them to share pipeline layouts. + let layout = PipelineLayout::new( + self.device.clone(), + // Since we only have one pipeline in this example, and thus one pipeline layout, + // we automatically generate the creation info for it from the resources used in + // the shaders. In a real application, you would specify this information manually + // so that you can re-use one layout in multiple pipelines. + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(self.device.clone()) + .unwrap(), + ) + .unwrap(); - // Finally, create the pipeline. - GraphicsPipeline::new( - self.device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - // How vertex data is read from the vertex buffers into the vertex shader. - vertex_input_state: Some(vertex_input_state), - // How vertices are arranged into primitive shapes. - // The default primitive shape is a triangle. - input_assembly_state: Some(InputAssemblyState::default()), - // How primitives are transformed and clipped to fit the framebuffer. - // We use a resizable viewport, set to draw over the entire window. - viewport_state: Some(ViewportState::default()), - // How polygons are culled and converted into a raster of pixels. - // The default value does not perform any culling. - rasterization_state: Some(RasterizationState::default()), - // How multiple fragment shader samples are converted to a single pixel value. - // The default value does not perform any multisampling. - multisample_state: Some(MultisampleState::default()), - // How pixel values are combined with the values already present in the framebuffer. - // The default value overwrites the old value with the new one, without any - // blending. - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - // Dynamic states allows us to specify parts of the pipeline settings when - // recording the command buffer, before we perform drawing. - // Here, we specify that the viewport should be dynamic. - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - }; - - // Dynamic viewports allow us to recreate just the viewport when the window is resized. - // Otherwise we would have to recreate the whole pipeline. - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - // In some situations, the swapchain will become invalid by itself. This includes for example - // when the window is resized (as the images of the swapchain will no longer match the - // window's) or, on Android, when the application went to the background and goes back to the - // foreground. - // - // In this situation, acquiring a swapchain image or presenting it will return an error. - // Rendering to an image of that swapchain will not produce any error, but may or may not work. - // To continue rendering, we need to recreate the swapchain by creating a new swapchain. Here, - // we remember that we need to do this for the next loop iteration. - let recreate_swapchain = false; - - // In the `window_event` handler below we are going to submit commands to the GPU. Submitting a command produces - // an object that implements the `GpuFuture` trait, which holds the resources for as long as - // they are in use by the GPU. - // - // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid - // that, we store the submission of the previous frame here. - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - - self.rcx = Some(RenderContext { - window, - swapchain, - render_pass, - framebuffers, - pipeline, - viewport, - recreate_swapchain, - previous_frame_end, - }); + // We have to indicate which subpass of which render pass this pipeline is going to be + // used in. The pipeline will only be usable from this particular subpass. + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + + // Finally, create the pipeline. + GraphicsPipeline::new( + self.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + // How vertex data is read from the vertex buffers into the vertex shader. + vertex_input_state: Some(vertex_input_state), + // How vertices are arranged into primitive shapes. The default primitive shape + // is a triangle. + input_assembly_state: Some(InputAssemblyState::default()), + // How primitives are transformed and clipped to fit the framebuffer. We use a + // resizable viewport, set to draw over the entire window. + viewport_state: Some(ViewportState::default()), + // How polygons are culled and converted into a raster of pixels. The default + // value does not perform any culling. + rasterization_state: Some(RasterizationState::default()), + // How multiple fragment shader samples are converted to a single pixel value. + // The default value does not perform any multisampling. + multisample_state: Some(MultisampleState::default()), + // How pixel values are combined with the values already present in the + // framebuffer. The default value overwrites the old value with the new one, + // without any blending. + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + // Dynamic states allows us to specify parts of the pipeline settings when + // recording the command buffer, before we perform drawing. Here, we specify + // that the viewport should be dynamic. + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() + }; + + // Dynamic viewports allow us to recreate just the viewport when the window is resized. + // Otherwise we would have to recreate the whole pipeline. + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + // In some situations, the swapchain will become invalid by itself. This includes for + // example when the window is resized (as the images of the swapchain will no longer match + // the window's) or, on Android, when the application went to the background and goes back + // to the foreground. + // + // In this situation, acquiring a swapchain image or presenting it will return an error. + // Rendering to an image of that swapchain will not produce any error, but may or may not + // work. To continue rendering, we need to recreate the swapchain by creating a new + // swapchain. Here, we remember that we need to do this for the next loop iteration. + let recreate_swapchain = false; + + // In the `window_event` handler below we are going to submit commands to the GPU. + // Submitting a command produces an object that implements the `GpuFuture` trait, which + // holds the resources for as long as they are in use by the GPU. + // + // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to + // avoid that, we store the submission of the previous frame here. + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + + self.rcx = Some(RenderContext { + window, + swapchain, + render_pass, + framebuffers, + pipeline, + viewport, + recreate_swapchain, + previous_frame_end, + }); } fn window_event( @@ -564,8 +574,8 @@ impl ApplicationHandler for App { WindowEvent::RedrawRequested => { let window_size = rcx.window.inner_size(); - // Do not draw the frame when the screen size is zero. On Windows, this can - // occur when minimizing the application. + // Do not draw the frame when the screen size is zero. On Windows, this can occur + // when minimizing the application. if window_size.width == 0 || window_size.height == 0 { return; } @@ -582,7 +592,8 @@ impl ApplicationHandler for App { if rcx.recreate_swapchain { // Use the new dimensions of the window. - let (new_swapchain, new_images) = rcx.swapchain + let (new_swapchain, new_images) = rcx + .swapchain .recreate(SwapchainCreateInfo { image_extent: window_size.into(), ..rcx.swapchain.create_info() @@ -593,10 +604,7 @@ impl ApplicationHandler for App { // Because framebuffers contains a reference to the old swapchain, we need to // recreate framebuffers as well. - rcx.framebuffers = window_size_dependent_setup( - &new_images, - &rcx.render_pass, - ); + rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass); rcx.viewport.extent = window_size.into(); @@ -610,15 +618,19 @@ impl ApplicationHandler for App { // // This function can block if no image is available. The parameter is an optional // timeout after which the function call will return an error. - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None).map_err(Validated::unwrap) { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; // `acquire_next_image` can be successful, but suboptimal. This means that the // swapchain image will still work, but it may not display correctly. With some @@ -628,8 +640,8 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = true; } - // In order to draw, we have to record a *command buffer*. The command buffer object - // holds the list of commands that are going to be executed. + // In order to draw, we have to record a *command buffer*. The command buffer + // object holds the list of commands that are going to be executed. // // Recording a command buffer is an expensive operation (usually a few hundred // microseconds), but it is known to be a hot path in the driver and is expected to @@ -665,9 +677,9 @@ impl ApplicationHandler for App { ) }, SubpassBeginInfo { - // The contents of the first (and only) subpass. - // This can be either `Inline` or `SecondaryCommandBuffers`. - // The latter is a bit more advanced and is not covered here. + // The contents of the first (and only) subpass. This can be either + // `Inline` or `SecondaryCommandBuffers`. The latter is a bit more + // advanced and is not covered here. contents: SubpassContents::Inline, ..Default::default() }, @@ -716,7 +728,10 @@ impl ApplicationHandler for App { // that draws the triangle. .then_swapchain_present( self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(rcx.swapchain.clone(), image_index), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), ) .then_signal_fence_and_flush();