Update dependencies (#2571)

* Update dependencies

* fmt
This commit is contained in:
marc0246 2024-10-10 12:16:14 +02:00 committed by GitHub
parent 9033311653
commit f6bc05df94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 11420 additions and 9943 deletions

414
Cargo.lock generated
View File

@ -48,9 +48,9 @@ dependencies = [
[[package]] [[package]]
name = "android-activity" name = "android-activity"
version = "0.5.2" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046"
dependencies = [ dependencies = [
"android-properties", "android-properties",
"bitflags 2.5.0", "bitflags 2.5.0",
@ -60,9 +60,9 @@ dependencies = [
"jni-sys", "jni-sys",
"libc", "libc",
"log", "log",
"ndk 0.8.0", "ndk 0.9.0",
"ndk-context", "ndk-context",
"ndk-sys 0.5.0+25.2.9519653", "ndk-sys 0.6.0+11769913",
"num_enum 0.7.2", "num_enum 0.7.2",
"thiserror", "thiserror",
] ]
@ -115,7 +115,7 @@ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"vulkano-taskgraph", "vulkano-taskgraph",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -180,32 +180,13 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 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]] [[package]]
name = "block2" name = "block2"
version = "0.5.1" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
dependencies = [ dependencies = [
"objc2 0.5.2", "objc2",
] ]
[[package]] [[package]]
@ -215,7 +196,7 @@ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"vulkano-taskgraph", "vulkano-taskgraph",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -224,7 +205,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -324,9 +305,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "cfg_aliases" name = "cfg_aliases"
version = "0.1.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "cgl" name = "cgl"
@ -342,7 +323,7 @@ name = "clear-attachments"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -421,8 +402,9 @@ dependencies = [
[[package]] [[package]]
name = "concurrent-slotmap" name = "concurrent-slotmap"
version = "0.1.0" version = "0.1.0-alpha.1"
source = "git+https://github.com/vulkano-rs/concurrent-slotmap?rev=fa906d916d8d126d3cc3a2b4ab9a29fa27bee62d#fa906d916d8d126d3cc3a2b4ab9a29fa27bee62d" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "288086ecfcd80978f795a7ba8ffd9777d6dfcb6e5c6c74bba10e3d9a1eb52169"
dependencies = [ dependencies = [
"virtual-buffer", "virtual-buffer",
] ]
@ -606,7 +588,7 @@ dependencies = [
"glam", "glam",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -631,10 +613,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]] [[package]]
name = "dwrote" name = "dpi"
version = "0.11.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53"
[[package]]
name = "dwrote"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da3498378ed373237bdef1eddcc64e7be2d3ba4841f4c22a998e81cadeea83c"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"libc", "libc",
@ -811,7 +799,7 @@ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.27.5", "winit 0.27.5",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -827,9 +815,9 @@ dependencies = [
[[package]] [[package]]
name = "glam" name = "glam"
version = "0.25.0" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" checksum = "c28091a37a5d09b555cb6628fd954da299b536433834f5b8e59eba78e0cbbf8a"
[[package]] [[package]]
name = "glium" name = "glium"
@ -925,9 +913,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]] [[package]]
name = "heck" name = "heck"
@ -941,17 +929,6 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 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]] [[package]]
name = "ident_case" name = "ident_case"
version = "1.0.1" version = "1.0.1"
@ -965,7 +942,7 @@ dependencies = [
"png", "png",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -975,7 +952,7 @@ dependencies = [
"png", "png",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -985,14 +962,14 @@ dependencies = [
"png", "png",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.2.6" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -1004,7 +981,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1013,7 +990,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1037,7 +1014,7 @@ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"vulkano-util", "vulkano-util",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1094,9 +1071,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
@ -1205,7 +1182,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1251,7 +1228,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1263,7 +1240,7 @@ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"vulkano-util", "vulkano-util",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1291,14 +1268,14 @@ dependencies = [
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.8.0" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
"jni-sys", "jni-sys",
"log", "log",
"ndk-sys 0.5.0+25.2.9519653", "ndk-sys 0.6.0+11769913",
"num_enum 0.7.2", "num_enum 0.7.2",
"raw-window-handle 0.6.2", "raw-window-handle 0.6.2",
"thiserror", "thiserror",
@ -1350,9 +1327,9 @@ dependencies = [
[[package]] [[package]]
name = "ndk-sys" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
dependencies = [ dependencies = [
"jni-sys", "jni-sys",
] ]
@ -1428,7 +1405,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
dependencies = [ dependencies = [
"proc-macro-crate 2.0.2", "proc-macro-crate 3.2.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.66", "syn 2.0.66",
@ -1449,16 +1426,6 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
[[package]]
name = "objc2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d"
dependencies = [
"objc-sys",
"objc2-encode 3.0.0",
]
[[package]] [[package]]
name = "objc2" name = "objc2"
version = "0.5.2" version = "0.5.2"
@ -1466,14 +1433,84 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
dependencies = [ dependencies = [
"objc-sys", "objc-sys",
"objc2-encode 4.0.3", "objc2-encode",
] ]
[[package]] [[package]]
name = "objc2-encode" name = "objc2-app-kit"
version = "3.0.0" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
"bitflags 2.5.0",
"block2",
"libc",
"objc2",
"objc2-core-data",
"objc2-core-image",
"objc2-foundation",
"objc2-quartz-core",
]
[[package]]
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 = "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]] [[package]]
name = "objc2-encode" name = "objc2-encode"
@ -1488,9 +1525,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
"block2 0.5.1", "block2",
"dispatch",
"libc", "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]] [[package]]
@ -1500,8 +1550,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
"block2 0.5.1", "block2",
"objc2 0.5.2", "objc2",
"objc2-foundation", "objc2-foundation",
] ]
@ -1512,12 +1562,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
"block2 0.5.1", "block2",
"objc2 0.5.2", "objc2",
"objc2-foundation", "objc2-foundation",
"objc2-metal", "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]] [[package]]
name = "object" name = "object"
version = "0.35.0" version = "0.35.0"
@ -1533,7 +1638,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1543,7 +1648,7 @@ dependencies = [
"png", "png",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1608,6 +1713,26 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 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]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.14" version = "0.2.14"
@ -1674,12 +1799,11 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "2.0.2" version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
dependencies = [ dependencies = [
"toml_datetime", "toml_edit 0.22.22",
"toml_edit 0.20.2",
] ]
[[package]] [[package]]
@ -1706,7 +1830,7 @@ dependencies = [
"png", "png",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1784,20 +1908,11 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2000e45d7daa9b6d946e88dfa1d7ae330424a81918a6545741821c989eb80a9" checksum = "b2000e45d7daa9b6d946e88dfa1d7ae330424a81918a6545741821c989eb80a9"
dependencies = [ dependencies = [
"objc2 0.5.2", "objc2",
"objc2-foundation", "objc2-foundation",
"objc2-quartz-core", "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]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.4.1" version = "0.4.1"
@ -1844,7 +1959,7 @@ dependencies = [
"png", "png",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1852,7 +1967,7 @@ name = "runtime-shader"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -1924,9 +2039,9 @@ dependencies = [
[[package]] [[package]]
name = "sctk-adwaita" name = "sctk-adwaita"
version = "0.8.1" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" checksum = "7555fcb4f753d095d734fdefebb0ad8c98478a21db500492d87c55913d3b0086"
dependencies = [ dependencies = [
"ab_glyph", "ab_glyph",
"log", "log",
@ -2011,7 +2126,7 @@ dependencies = [
"serde", "serde",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2065,7 +2180,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2206,7 +2321,7 @@ dependencies = [
"glam", "glam",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2215,7 +2330,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2225,7 +2340,7 @@ dependencies = [
"png", "png",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2310,9 +2425,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.3" version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
@ -2322,18 +2437,18 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"toml_datetime", "toml_datetime",
"winnow", "winnow 0.5.40",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.20.2" version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"toml_datetime", "toml_datetime",
"winnow", "winnow 0.6.20",
] ]
[[package]] [[package]]
@ -2358,7 +2473,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2368,7 +2483,7 @@ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"vulkano-util", "vulkano-util",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2377,7 +2492,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2422,9 +2537,9 @@ dependencies = [
[[package]] [[package]]
name = "vk-parse" name = "vk-parse"
version = "0.12.0" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81086c28be67a8759cd80cbb3c8f7b520e0874605fc5eb74d5a1c9c2d1878e79" checksum = "3859da4d7b98bec73e68fb65815d47a263819c415c90eed42b80440a02cbce8c"
dependencies = [ dependencies = [
"xml-rs", "xml-rs",
] ]
@ -2464,7 +2579,7 @@ dependencies = [
name = "vulkano-macros" name = "vulkano-macros"
version = "0.34.0" version = "0.34.0"
dependencies = [ dependencies = [
"proc-macro-crate 2.0.2", "proc-macro-crate 3.2.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.66", "syn 2.0.66",
@ -2502,7 +2617,7 @@ version = "0.34.0"
dependencies = [ dependencies = [
"ahash", "ahash",
"vulkano", "vulkano",
"winit 0.29.15", "winit 0.30.3",
] ]
[[package]] [[package]]
@ -2791,9 +2906,9 @@ dependencies = [
[[package]] [[package]]
name = "web-time" name = "web-time"
version = "0.2.4" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@ -3113,37 +3228,41 @@ dependencies = [
[[package]] [[package]]
name = "winit" name = "winit"
version = "0.29.15" version = "0.30.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" checksum = "49f45a7b7e2de6af35448d7718dab6d95acec466eb3bb7a56f4d31d1af754004"
dependencies = [ dependencies = [
"ahash", "ahash",
"android-activity", "android-activity",
"atomic-waker", "atomic-waker",
"bitflags 2.5.0", "bitflags 2.5.0",
"block2",
"bytemuck", "bytemuck",
"calloop 0.12.4", "calloop 0.12.4",
"cfg_aliases", "cfg_aliases",
"concurrent-queue",
"core-foundation", "core-foundation",
"core-graphics 0.23.2", "core-graphics 0.23.2",
"cursor-icon", "cursor-icon",
"icrate", "dpi",
"js-sys", "js-sys",
"libc", "libc",
"log",
"memmap2 0.9.4", "memmap2 0.9.4",
"ndk 0.8.0", "ndk 0.9.0",
"ndk-sys 0.5.0+25.2.9519653", "objc2",
"objc2 0.4.1", "objc2-app-kit",
"once_cell", "objc2-foundation",
"objc2-ui-kit",
"orbclient", "orbclient",
"percent-encoding", "percent-encoding",
"pin-project",
"raw-window-handle 0.6.2", "raw-window-handle 0.6.2",
"redox_syscall 0.3.5", "redox_syscall 0.4.1",
"rustix", "rustix",
"sctk-adwaita 0.8.1", "sctk-adwaita 0.9.1",
"smithay-client-toolkit 0.18.1", "smithay-client-toolkit 0.18.1",
"smol_str", "smol_str",
"tracing",
"unicode-segmentation", "unicode-segmentation",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
@ -3153,7 +3272,7 @@ dependencies = [
"wayland-protocols-plasma", "wayland-protocols-plasma",
"web-sys", "web-sys",
"web-time", "web-time",
"windows-sys 0.48.0", "windows-sys 0.52.0",
"x11-dl", "x11-dl",
"x11rb", "x11rb",
"xkbcommon-dl", "xkbcommon-dl",
@ -3168,6 +3287,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winnow"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "wio" name = "wio"
version = "0.2.2" version = "0.2.2"

View File

@ -46,7 +46,7 @@ ahash = "0.8"
# https://github.com/KhronosGroup/Vulkan-Headers/commits/main/registry/vk.xml # https://github.com/KhronosGroup/Vulkan-Headers/commits/main/registry/vk.xml
ash = "0.38.0" ash = "0.38.0"
bytemuck = "1.9" 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" crossbeam-queue = "0.3"
half = "2.0" half = "2.0"
heck = "0.4" heck = "0.4"
@ -56,7 +56,7 @@ nom = "7.1"
once_cell = "1.17" once_cell = "1.17"
parking_lot = "0.12" parking_lot = "0.12"
proc-macro2 = "1.0" proc-macro2 = "1.0"
proc-macro-crate = "2.0" proc-macro-crate = "3.0"
quote = "1.0" quote = "1.0"
rangemap = "1.5" rangemap = "1.5"
raw-window-handle = "0.6" raw-window-handle = "0.6"
@ -68,13 +68,13 @@ slabbin = "1.0"
smallvec = "1.8" smallvec = "1.8"
syn = "2.0" syn = "2.0"
thread_local = "1.1" thread_local = "1.1"
vk-parse = "0.12" vk-parse = "0.15"
winit = { version = "0.29", default-features = false } winit = { version = "0.30", default-features = false }
x11-dl = "2.0" x11-dl = "2.0"
x11rb = "0.13" x11rb = "0.13"
# Only used in examples # Only used in examples
glam = "0.25" glam = "0.29"
png = "0.17" png = "0.17"
rand = "0.8" rand = "0.8"
ron = "0.8" ron = "0.8"

View File

@ -81,15 +81,16 @@ use vulkano_taskgraph::{
command_buffer::{ command_buffer::{
BufferImageCopy, ClearColorImageInfo, CopyBufferToImageInfo, RecordingCommandBuffer, BufferImageCopy, ClearColorImageInfo, CopyBufferToImageInfo, RecordingCommandBuffer,
}, },
graph::{CompileInfo, ExecuteError, TaskGraph}, graph::{CompileInfo, ExecutableTaskGraph, ExecuteError, TaskGraph},
resource::{AccessType, Flight, HostAccessType, ImageLayoutType, Resources}, resource::{AccessType, Flight, HostAccessType, ImageLayoutType, Resources},
resource_map, Id, QueueFamilyType, Task, TaskContext, TaskResult, resource_map, Id, QueueFamilyType, Task, TaskContext, TaskResult,
}; };
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::{ElementState, KeyEvent, WindowEvent},
event_loop::{ActiveEventLoop, EventLoop},
keyboard::{Key, NamedKey}, keyboard::{Key, NamedKey},
window::WindowBuilder, window::{Window, WindowId},
}; };
const TRANSFER_GRANULARITY: u32 = 4096; const TRANSFER_GRANULARITY: u32 = 4096;
@ -97,9 +98,46 @@ const MAX_FRAMES_IN_FLIGHT: u32 = 2;
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); 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<Instance>,
device: Arc<Device>,
graphics_family_index: u32,
transfer_family_index: u32,
graphics_queue: Arc<Queue>,
resources: Arc<Resources>,
graphics_flight_id: Id<Flight>,
vertex_buffer_id: Id<Buffer>,
uniform_buffer_ids: [Id<Buffer>; MAX_FRAMES_IN_FLIGHT as usize],
texture_ids: [Id<Image>; 2],
current_texture_index: Arc<AtomicBool>,
channel: mpsc::Sender<()>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain_id: Id<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
viewport: Viewport,
recreate_swapchain: bool,
task_graph: ExecutableTaskGraph<Self>,
virtual_swapchain_id: Id<Swapchain>,
virtual_texture_id: Id<Image>,
virtual_uniform_buffer_id: Id<Buffer>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -124,7 +162,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -144,14 +182,14 @@ fn main() -> Result<(), impl Error> {
physical_device.properties().device_type, physical_device.properties().device_type,
); );
// Since we are going to be updating the texture on a separate thread asynchronously from the // Since we are going to be updating the texture on a separate thread asynchronously from
// execution of graphics commands, it would make sense to also do the transfer on a dedicated // the execution of graphics commands, it would make sense to also do the transfer on a
// transfer queue, if such a queue family exists. That way, the graphics queue is not blocked // dedicated transfer queue, if such a queue family exists. That way, the graphics queue is
// during the transfers either and the two tasks are truly asynchronous. // 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 // 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 // queue family has more flags than `TRANSFER | SPARSE_BINDING`, that means it is not
// to transfer operations. // dedicated to transfer operations.
let transfer_family_index = physical_device let transfer_family_index = physical_device
.queue_family_properties() .queue_family_properties()
.iter() .iter()
@ -184,8 +222,8 @@ fn main() -> Result<(), impl Error> {
}]; }];
// It's possible that the physical device doesn't have any queue families supporting // 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 // transfers other than the graphics and/or compute queue family. In that case we must
// sure we don't request the same queue family twice. // make sure we don't request the same queue family twice.
if transfer_family_index != graphics_family_index { if transfer_family_index != graphics_family_index {
queue_create_infos.push(QueueCreateInfo { queue_create_infos.push(QueueCreateInfo {
queue_family_index: transfer_family_index, queue_family_index: transfer_family_index,
@ -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 // 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 // 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 { if queue_family_properties.queue_count > 1 {
queue_create_infos[0].queues.push(0.5); queue_create_infos[0].queues.push(0.5);
} }
@ -216,7 +254,8 @@ fn main() -> Result<(), impl Error> {
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. // 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()); let transfer_queue = queues.next().unwrap_or_else(|| graphics_queue.clone());
println!( println!(
@ -229,40 +268,6 @@ fn main() -> Result<(), impl Error> {
let graphics_flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); let graphics_flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap();
let transfer_flight_id = resources.create_flight(1).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 = [ let vertices = [
MyVertex { MyVertex {
position: [-0.5, -0.5], position: [-0.5, -0.5],
@ -311,7 +316,12 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}); });
let texture_create_info = ImageCreateInfo { // 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, image_type: ImageType::Dim2d,
format: Format::R8G8B8A8_UNORM, format: Format::R8G8B8A8_UNORM,
extent: [TRANSFER_GRANULARITY * 2, TRANSFER_GRANULARITY * 2, 1], extent: [TRANSFER_GRANULARITY * 2, TRANSFER_GRANULARITY * 2, 1],
@ -326,18 +336,15 @@ fn main() -> Result<(), impl Error> {
Sharing::Exclusive Sharing::Exclusive
}, },
..Default::default() ..Default::default()
}; },
AllocationCreateInfo::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.
let texture_ids = [(); 2].map(|_| {
resources
.create_image(texture_create_info.clone(), AllocationCreateInfo::default())
.unwrap() .unwrap()
}); });
// The index of the currently most up-to-date texture. The worker thread swaps the index after // The index of the currently most up-to-date texture. The worker thread swaps the index
// every finished write, which is always done to the, at that point in time, unused texture. // 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)); let current_texture_index = Arc::new(AtomicBool::new(false));
// Initialize the resources. // Initialize the resources.
@ -381,17 +388,79 @@ fn main() -> Result<(), impl Error> {
let (channel, receiver) = mpsc::channel(); let (channel, receiver) = mpsc::channel();
run_worker( run_worker(
receiver, receiver,
transfer_queue, graphics_family_index,
transfer_family_index,
transfer_queue.clone(),
resources.clone(), resources.clone(),
graphics_flight_id, graphics_flight_id,
transfer_flight_id, transfer_flight_id,
&texture_create_info,
texture_ids, texture_ids,
current_texture_index.clone(), 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!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), self.device.clone(),
attachments: { attachments: {
color: { color: {
format: swapchain_format, format: swapchain_format,
@ -407,12 +476,14 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
let framebuffers = window_size_dependent_setup(&self.resources, swapchain_id, &render_pass);
let pipeline = { let pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
@ -422,16 +493,16 @@ fn main() -> Result<(), impl Error> {
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -455,23 +526,21 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.0, 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( let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
device.clone(), self.device.clone(),
Default::default(), Default::default(),
)); ));
// A byproduct of always using the same set of uniform buffers is that we can also create one // A byproduct of always using the same set of uniform buffers is that we can also create
// descriptor set for each, reusing them in the same way as the buffers. // 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 uniform_buffer_sets = self.uniform_buffer_ids.map(|buffer_id| {
let buffer_state = resources.buffer(buffer_id).unwrap(); let buffer_state = self.resources.buffer(buffer_id).unwrap();
let buffer = buffer_state.buffer(); let buffer = buffer_state.buffer();
DescriptorSet::new( DescriptorSet::new(
@ -484,9 +553,13 @@ fn main() -> Result<(), impl Error> {
}); });
// Create the descriptor sets for sampling the textures. // Create the descriptor sets for sampling the textures.
let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap(); let sampler = Sampler::new(
let sampler_sets = texture_ids.map(|texture_id| { self.device.clone(),
let texture_state = resources.image(texture_id).unwrap(); 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(); let texture = texture_state.image();
DescriptorSet::new( DescriptorSet::new(
@ -494,22 +567,31 @@ fn main() -> Result<(), impl Error> {
pipeline.layout().set_layouts()[1].clone(), pipeline.layout().set_layouts()[1].clone(),
[ [
WriteDescriptorSet::sampler(0, sampler.clone()), WriteDescriptorSet::sampler(0, sampler.clone()),
WriteDescriptorSet::image_view(1, ImageView::new_default(texture.clone()).unwrap()), WriteDescriptorSet::image_view(
1,
ImageView::new_default(texture.clone()).unwrap(),
),
], ],
[], [],
) )
.unwrap() .unwrap()
}); });
let mut rcx = RenderContext { let mut task_graph = TaskGraph::new(&self.resources, 1, 4);
viewport,
framebuffers,
};
let mut task_graph = TaskGraph::new(&resources, 1, 4);
let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default()); 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()); 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.add_host_buffer_access(virtual_uniform_buffer_id, HostAccessType::Write);
@ -520,12 +602,12 @@ fn main() -> Result<(), impl Error> {
QueueFamilyType::Graphics, QueueFamilyType::Graphics,
RenderTask { RenderTask {
swapchain_id: virtual_swapchain_id, swapchain_id: virtual_swapchain_id,
vertex_buffer_id, vertex_buffer_id: self.vertex_buffer_id,
current_texture_index: current_texture_index.clone(), current_texture_index: self.current_texture_index.clone(),
pipeline: pipeline.clone(), pipeline,
uniform_buffer_id: virtual_uniform_buffer_id, uniform_buffer_id: virtual_uniform_buffer_id,
uniform_buffer_sets: uniform_buffer_sets.clone(), uniform_buffer_sets,
sampler_sets: sampler_sets.clone(), sampler_sets,
}, },
) )
.image_access( .image_access(
@ -533,7 +615,7 @@ fn main() -> Result<(), impl Error> {
AccessType::ColorAttachmentWrite, AccessType::ColorAttachmentWrite,
ImageLayoutType::Optimal, ImageLayoutType::Optimal,
) )
.buffer_access(vertex_buffer_id, AccessType::VertexAttributeRead) .buffer_access(self.vertex_buffer_id, AccessType::VertexAttributeRead)
.image_access( .image_access(
virtual_texture_id, virtual_texture_id,
AccessType::FragmentShaderSampledRead, AccessType::FragmentShaderSampledRead,
@ -546,36 +628,43 @@ fn main() -> Result<(), impl Error> {
let task_graph = unsafe { let task_graph = unsafe {
task_graph.compile(&CompileInfo { task_graph.compile(&CompileInfo {
queues: &[&graphics_queue], queues: &[&self.graphics_queue],
present_queue: Some(&graphics_queue), present_queue: Some(&self.graphics_queue),
flight_id: graphics_flight_id, flight_id: self.graphics_flight_id,
..Default::default() ..Default::default()
}) })
} }
.unwrap(); .unwrap();
let mut recreate_swapchain = false; 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,
});
}
println!("\nPress space to update part of the texture"); fn window_event(
&mut self,
event_loop.run(move |event, elwt| { event_loop: &ActiveEventLoop,
elwt.set_control_flow(ControlFlow::Poll); _window_id: WindowId,
event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent {
event:
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: event:
KeyEvent { KeyEvent {
@ -584,83 +673,72 @@ fn main() -> Result<(), impl Error> {
.. ..
}, },
.. ..
},
..
} => { } => {
channel.send(()).unwrap(); self.channel.send(()).unwrap();
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
let flight = resources.flight(graphics_flight_id).unwrap(); let flight = self.resources.flight(self.graphics_flight_id).unwrap();
if recreate_swapchain { if rcx.recreate_swapchain {
swapchain_id = resources rcx.swapchain_id = self
.recreate_swapchain(swapchain_id, |create_info| SwapchainCreateInfo { .resources
image_extent, .recreate_swapchain(rcx.swapchain_id, |create_info| SwapchainCreateInfo {
image_extent: window_size.into(),
..create_info ..create_info
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
flight.destroy_objects(rcx.framebuffers.drain(..));
rcx.framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(
&resources, &self.resources,
swapchain_id, rcx.swapchain_id,
&render_pass, &rcx.render_pass,
&mut rcx.viewport,
); );
rcx.viewport.extent = window_size.into();
recreate_swapchain = false; rcx.recreate_swapchain = false;
} }
let frame_index = flight.current_frame_index(); 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!( let resource_map = resource_map!(
&task_graph, &rcx.task_graph,
virtual_swapchain_id => swapchain_id, rcx.virtual_swapchain_id => rcx.swapchain_id,
virtual_texture_id => texture_ids[texture_index as usize], rcx.virtual_texture_id => self.texture_ids[texture_index as usize],
virtual_uniform_buffer_id => uniform_buffer_ids[frame_index as usize], rcx.virtual_uniform_buffer_id => self.uniform_buffer_ids[frame_index as usize],
) )
.unwrap(); .unwrap();
flight.wait(None).unwrap(); flight.wait(None).unwrap();
match unsafe { 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(()) => {} Ok(()) => {}
Err(ExecuteError::Swapchain { Err(ExecuteError::Swapchain {
error: Validated::Error(VulkanError::OutOfDate), error: Validated::Error(VulkanError::OutOfDate),
.. ..
}) => { }) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
Err(e) => { Err(e) => {
panic!("failed to execute next frame: {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)] #[derive(Clone, Copy, BufferContents, Vertex)]
@ -710,11 +788,6 @@ mod fs {
} }
} }
struct RenderContext {
viewport: Viewport,
framebuffers: Vec<Arc<Framebuffer>>,
}
struct RenderTask { struct RenderTask {
swapchain_id: Id<Swapchain>, swapchain_id: Id<Swapchain>,
vertex_buffer_id: Id<Buffer>, vertex_buffer_id: Id<Buffer>,
@ -785,6 +858,10 @@ impl Task for RenderTask {
cbf.as_raw().end_render_pass(&Default::default())?; 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(()) Ok(())
} }
} }
@ -792,11 +869,12 @@ impl Task for RenderTask {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn run_worker( fn run_worker(
channel: mpsc::Receiver<()>, channel: mpsc::Receiver<()>,
graphics_family_index: u32,
transfer_family_index: u32,
transfer_queue: Arc<Queue>, transfer_queue: Arc<Queue>,
resources: Arc<Resources>, resources: Arc<Resources>,
graphics_flight_id: Id<Flight>, graphics_flight_id: Id<Flight>,
transfer_flight_id: Id<Flight>, transfer_flight_id: Id<Flight>,
texture_create_info: &ImageCreateInfo,
texture_ids: [Id<Image>; 2], texture_ids: [Id<Image>; 2],
current_texture_index: Arc<AtomicBool>, current_texture_index: Arc<AtomicBool>,
) { ) {
@ -834,7 +912,18 @@ fn run_worker(
let virtual_front_staging_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default()); 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_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); task_graph.add_host_buffer_access(virtual_front_staging_buffer_id, HostAccessType::Write);
@ -1009,17 +1098,15 @@ fn window_size_dependent_setup(
resources: &Resources, resources: &Resources,
swapchain_id: Id<Swapchain>, swapchain_id: Id<Swapchain>,
render_pass: &Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let swapchain_state = resources.swapchain(swapchain_id).unwrap(); let swapchain_state = resources.swapchain(swapchain_id).unwrap();
let images = swapchain_state.images(); let images = swapchain_state.images();
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -1,9 +1,9 @@
use crate::RenderContext; use crate::{App, RenderContext};
use std::{slice, sync::Arc}; use std::{slice, sync::Arc};
use vulkano::{ use vulkano::{
image::{mip_level_extent, Image}, image::{mip_level_extent, Image},
pipeline::{ pipeline::{
compute::ComputePipelineCreateInfo, ComputePipeline, PipelineBindPoint, compute::ComputePipelineCreateInfo, ComputePipeline, PipelineBindPoint, PipelineLayout,
PipelineShaderStageCreateInfo, PipelineShaderStageCreateInfo,
}, },
sync::{AccessFlags, PipelineStages}, sync::{AccessFlags, PipelineStages},
@ -24,33 +24,37 @@ pub struct BloomTask {
} }
impl BloomTask { impl BloomTask {
pub fn new(rcx: &RenderContext, bloom_image_id: Id<Image>) -> Self { pub fn new(
app: &App,
pipeline_layout: &Arc<PipelineLayout>,
virtual_bloom_image_id: Id<Image>,
) -> Self {
let downsample_pipeline = { let downsample_pipeline = {
let cs = downsample::load(rcx.device.clone()) let cs = downsample::load(app.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let stage = PipelineShaderStageCreateInfo::new(cs); let stage = PipelineShaderStageCreateInfo::new(cs);
ComputePipeline::new( ComputePipeline::new(
rcx.device.clone(), app.device.clone(),
None, None,
ComputePipelineCreateInfo::stage_layout(stage, rcx.pipeline_layout.clone()), ComputePipelineCreateInfo::stage_layout(stage, pipeline_layout.clone()),
) )
.unwrap() .unwrap()
}; };
let upsample_pipeline = { let upsample_pipeline = {
let cs = upsample::load(rcx.device.clone()) let cs = upsample::load(app.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let stage = PipelineShaderStageCreateInfo::new(cs); let stage = PipelineShaderStageCreateInfo::new(cs);
ComputePipeline::new( ComputePipeline::new(
rcx.device.clone(), app.device.clone(),
None, None,
ComputePipelineCreateInfo::stage_layout(stage, rcx.pipeline_layout.clone()), ComputePipelineCreateInfo::stage_layout(stage, pipeline_layout.clone()),
) )
.unwrap() .unwrap()
}; };
@ -58,7 +62,7 @@ impl BloomTask {
BloomTask { BloomTask {
downsample_pipeline, downsample_pipeline,
upsample_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(()) Ok(())
} }
} }

View File

@ -37,14 +37,15 @@ use vulkano::{
Validated, Version, VulkanError, VulkanLibrary, Validated, Version, VulkanError, VulkanLibrary,
}; };
use vulkano_taskgraph::{ use vulkano_taskgraph::{
graph::{CompileInfo, ExecuteError, TaskGraph}, graph::{CompileInfo, ExecutableTaskGraph, ExecuteError, NodeId, TaskGraph},
resource::{AccessType, Flight, ImageLayoutType, Resources}, resource::{AccessType, Flight, ImageLayoutType, Resources},
resource_map, Id, QueueFamilyType, resource_map, Id, QueueFamilyType,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::{Window, WindowBuilder}, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
mod bloom; mod bloom;
@ -56,9 +57,41 @@ const MAX_BLOOM_MIP_LEVELS: u32 = 6;
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
resources: Arc<Resources>,
flight_id: Id<Flight>,
rcx: Option<RenderContext>,
}
pub struct RenderContext {
window: Arc<Window>,
swapchain_id: Id<Swapchain>,
bloom_image_id: Id<Image>,
viewport: Viewport,
pipeline_layout: Arc<PipelineLayout>,
recreate_swapchain: bool,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
sampler: Arc<Sampler>,
descriptor_set: DescriptorSetWithOffsets,
task_graph: ExecutableTaskGraph<Self>,
scene_node_id: NodeId,
tonemap_node_id: NodeId,
virtual_swapchain_id: Id<Swapchain>,
virtual_bloom_image_id: Id<Image>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -69,188 +102,6 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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::<SceneTask>()
.unwrap()
.handle_resize(&rcx);
task_graph
.task_node_mut(tonemap_node_id)
.unwrap()
.task_mut()
.downcast_mut::<TonemapTask>()
.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::<SceneTask>()
.unwrap()
.cleanup(&rcx);
task_graph
.task_node_mut(tonemap_node_id)
.unwrap()
.task_mut()
.downcast_mut::<TonemapTask>()
.unwrap()
.cleanup(&rcx);
}
_ => (),
}
})
}
pub struct RenderContext {
device: Arc<Device>,
queue: Arc<Queue>,
resources: Arc<Resources>,
flight_id: Id<Flight>,
window: Arc<Window>,
swapchain_id: Id<Swapchain>,
swapchain_format: Format,
bloom_image_id: Id<Image>,
viewport: Viewport,
pipeline_layout: Arc<PipelineLayout>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
sampler: Arc<Sampler>,
descriptor_set: DescriptorSetWithOffsets,
}
impl RenderContext {
fn new(event_loop: &EventLoop<()>, instance: &Arc<Instance>) -> Self {
let mut device_extensions = DeviceExtensions { let mut device_extensions = DeviceExtensions {
khr_swapchain: true, khr_swapchain: true,
..DeviceExtensions::empty() ..DeviceExtensions::empty()
@ -318,16 +169,36 @@ impl RenderContext {
let flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); let flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap();
let window = Arc::new(WindowBuilder::new().build(event_loop).unwrap()); App {
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); 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_format;
let swapchain_id = { let swapchain_id = {
let surface_capabilities = device let surface_capabilities = self
.device
.physical_device() .physical_device()
.surface_capabilities(&surface, Default::default()) .surface_capabilities(&surface, Default::default())
.unwrap(); .unwrap();
(swapchain_format, _) = device (swapchain_format, _) = self
.device
.physical_device() .physical_device()
.surface_formats(&surface, Default::default()) .surface_formats(&surface, Default::default())
.unwrap() .unwrap()
@ -338,9 +209,9 @@ impl RenderContext {
}) })
.unwrap(); .unwrap();
resources self.resources
.create_swapchain( .create_swapchain(
flight_id, self.flight_id,
surface, surface,
SwapchainCreateInfo { SwapchainCreateInfo {
min_image_count: surface_capabilities.min_image_count.max(3), min_image_count: surface_capabilities.min_image_count.max(3),
@ -360,15 +231,15 @@ impl RenderContext {
let viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: window.inner_size().into(), extent: window_size.into(),
depth_range: 0.0..=1.0, depth_range: 0.0..=1.0,
}; };
let pipeline_layout = PipelineLayout::new( let pipeline_layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineLayoutCreateInfo { PipelineLayoutCreateInfo {
set_layouts: vec![DescriptorSetLayout::new( set_layouts: vec![DescriptorSetLayout::new(
device.clone(), self.device.clone(),
DescriptorSetLayoutCreateInfo { DescriptorSetLayoutCreateInfo {
bindings: [ bindings: [
( (
@ -417,12 +288,12 @@ impl RenderContext {
.unwrap(); .unwrap();
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
device.clone(), self.device.clone(),
Default::default(), Default::default(),
)); ));
let sampler = Sampler::new( let sampler = Sampler::new(
device.clone(), self.device.clone(),
SamplerCreateInfo { SamplerCreateInfo {
mag_filter: Filter::Linear, mag_filter: Filter::Linear,
min_filter: Filter::Linear, min_filter: Filter::Linear,
@ -434,61 +305,191 @@ impl RenderContext {
.unwrap(); .unwrap();
let (bloom_image_id, descriptor_set) = window_size_dependent_setup( let (bloom_image_id, descriptor_set) = window_size_dependent_setup(
&resources, &self.resources,
swapchain_id, swapchain_id,
&pipeline_layout, &pipeline_layout,
&sampler, &sampler,
&descriptor_set_allocator, &descriptor_set_allocator,
); );
RenderContext { let mut task_graph = TaskGraph::new(&self.resources, 3, 2);
device,
queue, 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, window,
resources,
flight_id,
swapchain_id, swapchain_id,
swapchain_format,
bloom_image_id, bloom_image_id,
viewport, viewport,
pipeline_layout, pipeline_layout,
recreate_swapchain: false,
sampler, sampler,
descriptor_set_allocator, descriptor_set_allocator,
descriptor_set, descriptor_set,
} task_graph,
scene_node_id,
tonemap_node_id,
virtual_swapchain_id,
virtual_bloom_image_id,
});
} }
fn handle_resize(&mut self) { fn window_event(
let window_size = self.window.inner_size(); &mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
self.swapchain_id = self 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();
if rcx.recreate_swapchain {
rcx.swapchain_id = self
.resources .resources
.recreate_swapchain(self.swapchain_id, |create_info| SwapchainCreateInfo { .recreate_swapchain(rcx.swapchain_id, |create_info| SwapchainCreateInfo {
image_extent: window_size.into(), image_extent: window_size.into(),
..create_info ..create_info
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
let flight = self.resources.flight(self.flight_id).unwrap(); rcx.viewport.extent = window_size.into();
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());
(self.bloom_image_id, self.descriptor_set) = window_size_dependent_setup( unsafe { self.resources.remove_image(rcx.bloom_image_id) }.unwrap();
(rcx.bloom_image_id, rcx.descriptor_set) = window_size_dependent_setup(
&self.resources, &self.resources,
self.swapchain_id, rcx.swapchain_id,
&self.pipeline_layout, &rcx.pipeline_layout,
&self.sampler, &rcx.sampler,
&self.descriptor_set_allocator, &rcx.descriptor_set_allocator,
); );
self.viewport.extent = window_size.into(); rcx.task_graph
.task_node_mut(rcx.scene_node_id)
.unwrap()
.task_mut()
.downcast_mut::<SceneTask>()
.unwrap()
.handle_resize(&self.resources, rcx.bloom_image_id);
rcx.task_graph
.task_node_mut(rcx.tonemap_node_id)
.unwrap()
.task_mut()
.downcast_mut::<TonemapTask>()
.unwrap()
.handle_resize(&self.resources, rcx.swapchain_id);
rcx.recreate_swapchain = false;
} }
fn cleanup(&mut self) { flight.wait(None).unwrap();
let flight = self.resources.flight(self.flight_id).unwrap();
flight.destroy_object(self.descriptor_set.as_ref().0.clone()); 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();
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 about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
let rcx = self.rcx.as_mut().unwrap();
rcx.window.request_redraw();
} }
} }

View File

@ -1,12 +1,12 @@
use crate::RenderContext; use crate::{App, RenderContext};
use std::{alloc::Layout, mem, slice, sync::Arc}; use std::{alloc::Layout, slice, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage},
command_buffer::RenderPassBeginInfo, command_buffer::RenderPassBeginInfo,
format::Format, format::Format,
image::{ image::{
view::{ImageView, ImageViewCreateInfo}, view::{ImageView, ImageViewCreateInfo},
ImageAspects, ImageSubresourceRange, ImageUsage, Image, ImageAspects, ImageSubresourceRange, ImageUsage,
}, },
memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter}, memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter},
pipeline::{ pipeline::{
@ -19,13 +19,14 @@ use vulkano::{
viewport::ViewportState, viewport::ViewportState,
GraphicsPipelineCreateInfo, GraphicsPipelineCreateInfo,
}, },
DynamicState, GraphicsPipeline, PipelineShaderStageCreateInfo, DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
}; };
use vulkano_taskgraph::{ use vulkano_taskgraph::{
command_buffer::RecordingCommandBuffer, resource::HostAccessType, Id, Task, TaskContext, command_buffer::RecordingCommandBuffer,
TaskResult, resource::{HostAccessType, Resources},
Id, Task, TaskContext, TaskResult,
}; };
pub struct SceneTask { pub struct SceneTask {
@ -36,9 +37,13 @@ pub struct SceneTask {
} }
impl SceneTask { impl SceneTask {
pub fn new(rcx: &RenderContext) -> Self { pub fn new(
app: &App,
pipeline_layout: &Arc<PipelineLayout>,
bloom_image_id: Id<Image>,
) -> Self {
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
rcx.device.clone(), app.device.clone(),
attachments: { attachments: {
color: { color: {
format: Format::R32_UINT, format: Format::R32_UINT,
@ -55,11 +60,11 @@ impl SceneTask {
.unwrap(); .unwrap();
let pipeline = { let pipeline = {
let vs = vs::load(rcx.device.clone()) let vs = vs::load(app.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(rcx.device.clone()) let fs = fs::load(app.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
@ -71,7 +76,7 @@ impl SceneTask {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
rcx.device.clone(), app.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -86,13 +91,13 @@ impl SceneTask {
)), )),
dynamic_state: [DynamicState::Viewport].into_iter().collect(), dynamic_state: [DynamicState::Viewport].into_iter().collect(),
subpass: Some(subpass.into()), subpass: Some(subpass.into()),
..GraphicsPipelineCreateInfo::layout(rcx.pipeline_layout.clone()) ..GraphicsPipelineCreateInfo::layout(pipeline_layout.clone())
}, },
) )
.unwrap() .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 = [ let vertices = [
MyVertex { MyVertex {
@ -105,7 +110,7 @@ impl SceneTask {
position: [0.0, -0.5], position: [0.0, -0.5],
}, },
]; ];
let vertex_buffer_id = rcx let vertex_buffer_id = app
.resources .resources
.create_buffer( .create_buffer(
BufferCreateInfo { BufferCreateInfo {
@ -123,9 +128,9 @@ impl SceneTask {
unsafe { unsafe {
vulkano_taskgraph::execute( vulkano_taskgraph::execute(
&rcx.queue, &app.queue,
&rcx.resources, &app.resources,
rcx.flight_id, app.flight_id,
|_cbf, tcx| { |_cbf, tcx| {
tcx.write_buffer::<[MyVertex]>(vertex_buffer_id, ..)? tcx.write_buffer::<[MyVertex]>(vertex_buffer_id, ..)?
.copy_from_slice(&vertices); .copy_from_slice(&vertices);
@ -147,16 +152,9 @@ impl SceneTask {
} }
} }
pub fn handle_resize(&mut self, rcx: &RenderContext) { pub fn handle_resize(&mut self, resources: &Resources, bloom_image_id: Id<Image>) {
let framebuffer = window_size_dependent_setup(rcx, &self.render_pass); self.framebuffer =
window_size_dependent_setup(resources, bloom_image_id, &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());
} }
} }
@ -184,6 +182,8 @@ impl Task for SceneTask {
cbf.as_raw().end_render_pass(&Default::default())?; cbf.as_raw().end_render_pass(&Default::default())?;
cbf.destroy_object(self.framebuffer.clone());
Ok(()) Ok(())
} }
} }
@ -195,6 +195,38 @@ struct MyVertex {
position: [f32; 2], position: [f32; 2],
} }
fn window_size_dependent_setup(
resources: &Resources,
bloom_image_id: Id<Image>,
render_pass: &Arc<RenderPass>,
) -> Arc<Framebuffer> {
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 { mod vs {
vulkano_shaders::shader! { vulkano_shaders::shader! {
ty: "vertex", ty: "vertex",
@ -226,34 +258,3 @@ mod fs {
include: ["."], include: ["."],
} }
} }
fn window_size_dependent_setup(
rcx: &RenderContext,
render_pass: &Arc<RenderPass>,
) -> Arc<Framebuffer> {
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()
}

View File

@ -1,5 +1,5 @@
use crate::RenderContext; use crate::{App, RenderContext};
use std::{mem, slice, sync::Arc}; use std::{slice, sync::Arc};
use vulkano::{ use vulkano::{
command_buffer::RenderPassBeginInfo, command_buffer::RenderPassBeginInfo,
image::view::ImageView, image::view::ImageView,
@ -13,13 +13,14 @@ use vulkano::{
viewport::ViewportState, viewport::ViewportState,
GraphicsPipelineCreateInfo, GraphicsPipelineCreateInfo,
}, },
DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineShaderStageCreateInfo, DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineLayout,
PipelineShaderStageCreateInfo,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
swapchain::Swapchain, swapchain::Swapchain,
}; };
use vulkano_taskgraph::{ use vulkano_taskgraph::{
command_buffer::RecordingCommandBuffer, Id, Task, TaskContext, TaskResult, command_buffer::RecordingCommandBuffer, resource::Resources, Id, Task, TaskContext, TaskResult,
}; };
const EXPOSURE: f32 = 1.0; const EXPOSURE: f32 = 1.0;
@ -32,12 +33,20 @@ pub struct TonemapTask {
} }
impl TonemapTask { impl TonemapTask {
pub fn new(rcx: &RenderContext, swapchain_id: Id<Swapchain>) -> Self { pub fn new(
app: &App,
pipeline_layout: &Arc<PipelineLayout>,
swapchain_id: Id<Swapchain>,
virtual_swapchain_id: Id<Swapchain>,
) -> 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!( let render_pass = vulkano::single_pass_renderpass!(
rcx.device.clone(), app.device.clone(),
attachments: { attachments: {
color: { color: {
format: rcx.swapchain_format, format: swapchain_format,
samples: 1, samples: 1,
load_op: DontCare, load_op: DontCare,
store_op: Store, store_op: Store,
@ -51,11 +60,11 @@ impl TonemapTask {
.unwrap(); .unwrap();
let pipeline = { let pipeline = {
let vs = vs::load(rcx.device.clone()) let vs = vs::load(app.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(rcx.device.clone()) let fs = fs::load(app.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
@ -66,7 +75,7 @@ impl TonemapTask {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
rcx.device.clone(), app.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -81,32 +90,24 @@ impl TonemapTask {
)), )),
dynamic_state: [DynamicState::Viewport].into_iter().collect(), dynamic_state: [DynamicState::Viewport].into_iter().collect(),
subpass: Some(subpass.into()), subpass: Some(subpass.into()),
..GraphicsPipelineCreateInfo::layout(rcx.pipeline_layout.clone()) ..GraphicsPipelineCreateInfo::layout(pipeline_layout.clone())
}, },
) )
.unwrap() .unwrap()
}; };
let framebuffers = window_size_dependent_setup(rcx, &render_pass); let framebuffers = window_size_dependent_setup(&app.resources, swapchain_id, &render_pass);
TonemapTask { TonemapTask {
render_pass, render_pass,
pipeline, pipeline,
framebuffers, framebuffers,
swapchain_id, swapchain_id: virtual_swapchain_id,
} }
} }
pub fn handle_resize(&mut self, rcx: &RenderContext) { pub fn handle_resize(&mut self, resources: &Resources, swapchain_id: Id<Swapchain>) {
let framebuffers = window_size_dependent_setup(rcx, &self.render_pass); self.framebuffers = window_size_dependent_setup(resources, swapchain_id, &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(..));
} }
} }
@ -148,10 +149,37 @@ impl Task for TonemapTask {
cbf.as_raw().end_render_pass(&Default::default())?; cbf.as_raw().end_render_pass(&Default::default())?;
cbf.destroy_objects(self.framebuffers.iter().cloned());
Ok(()) Ok(())
} }
} }
fn window_size_dependent_setup(
resources: &Resources,
swapchain_id: Id<Swapchain>,
render_pass: &Arc<RenderPass>,
) -> Vec<Arc<Framebuffer>> {
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::<Vec<_>>()
}
mod vs { mod vs {
vulkano_shaders::shader! { vulkano_shaders::shader! {
ty: "vertex", ty: "vertex",
@ -187,26 +215,3 @@ mod fs {
include: ["."], include: ["."],
} }
} }
fn window_size_dependent_setup(
rcx: &RenderContext,
render_pass: &Arc<RenderPass>,
) -> Vec<Arc<Framebuffer>> {
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::<Vec<_>>()
}

View File

@ -15,8 +15,8 @@ use vulkano::{
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -42,16 +42,43 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
buffer_allocator: SubbufferAllocator,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -76,7 +103,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -111,47 +138,11 @@ fn main() -> Result<(), impl Error> {
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
#[derive(Clone, Copy, BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32_SFLOAT)]
position: [f32; 2],
}
// Using a buffer allocator allows multiple buffers to be "in-flight" simultaneously and is // 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. // suited to highly dynamic data like vertex, index and uniform buffers.
@ -166,6 +157,77 @@ fn main() -> Result<(), impl Error> {
}, },
); );
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];
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);
mod vs { mod vs {
vulkano_shaders::shader! { vulkano_shaders::shader! {
ty: "vertex", ty: "vertex",
@ -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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -258,77 +303,81 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.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( let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
device.clone(),
Default::default(),
));
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); 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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
// Rotate once (PI*2) every 5 seconds // Rotate once (PI*2) every 5 seconds
@ -345,16 +394,16 @@ fn main() -> Result<(), impl Error> {
const ANGLE_OFFSET: f32 = (std::f32::consts::PI * 2.0) / 3.0; const ANGLE_OFFSET: f32 = (std::f32::consts::PI * 2.0) / 3.0;
// Calculate vertices // Calculate vertices
let data = [ let data = [
Vertex { MyVertex {
position: [angle.cos() * RADIUS, angle.sin() * RADIUS], position: [angle.cos() * RADIUS, angle.sin() * RADIUS],
}, },
Vertex { MyVertex {
position: [ position: [
(angle + ANGLE_OFFSET).cos() * RADIUS, (angle + ANGLE_OFFSET).cos() * RADIUS,
(angle + ANGLE_OFFSET).sin() * RADIUS, (angle + ANGLE_OFFSET).sin() * RADIUS,
], ],
}, },
Vertex { MyVertex {
position: [ position: [
(angle - ANGLE_OFFSET).cos() * RADIUS, (angle - ANGLE_OFFSET).cos() * RADIUS,
(angle - ANGLE_OFFSET).sin() * RADIUS, (angle - ANGLE_OFFSET).sin() * RADIUS,
@ -364,12 +413,15 @@ fn main() -> Result<(), impl Error> {
let num_vertices = data.len() as u32; let num_vertices = data.len() as u32;
// Allocate a new subbuffer using the buffer allocator. // 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); buffer.write().unwrap().copy_from_slice(&data);
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -383,16 +435,16 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
// Draw our buffer // Draw our buffer
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_vertex_buffers(0, buffer) .bind_vertex_buffers(0, buffer)
.unwrap(); .unwrap();
@ -404,51 +456,63 @@ fn main() -> Result<(), impl Error> {
builder.end_render_pass(Default::default()).unwrap(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(Box::new(future) as Box<_>); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<_>); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -6,8 +6,8 @@ use vulkano::{
RenderPassBeginInfo, RenderPassBeginInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -19,9 +19,10 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
@ -29,9 +30,34 @@ fn main() -> Result<(), impl Error> {
// example if you haven't done so yet. // example if you haven't done so yet.
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
width: u32,
height: u32,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -56,7 +82,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -88,29 +114,53 @@ fn main() -> Result<(), impl Error> {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); device.clone(),
Default::default(),
));
let (mut swapchain, images) = { App {
let surface_capabilities = device 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() .physical_device()
.surface_capabilities(&surface, Default::default()) .surface_capabilities(&surface, Default::default())
.unwrap(); .unwrap();
let image_format = device let (image_format, _) = self
.device
.physical_device() .physical_device()
.surface_formats(&surface, Default::default()) .surface_formats(&surface, Default::default())
.unwrap()[0] .unwrap()[0];
.0;
Swapchain::new( Swapchain::new(
device.clone(), self.device.clone(),
surface, surface,
SwapchainCreateInfo { SwapchainCreateInfo {
min_image_count: surface_capabilities.min_image_count.max(2), min_image_count: surface_capabilities.min_image_count.max(2),
image_format, image_format,
image_extent: window.inner_size().into(), image_extent: window_size.into(),
image_usage: ImageUsage::COLOR_ATTACHMENT, image_usage: ImageUsage::COLOR_ATTACHMENT,
composite_alpha: surface_capabilities composite_alpha: surface_capabilities
.supported_composite_alpha .supported_composite_alpha
@ -123,7 +173,8 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let render_pass = vulkano::single_pass_renderpass!(device.clone(), let render_pass = vulkano::single_pass_renderpass!(
self.device.clone(),
attachments: { attachments: {
color: { color: {
format: swapchain.image_format(), format: swapchain.image_format(),
@ -139,78 +190,84 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( let framebuffers = window_size_dependent_setup(&images, &render_pass);
device.clone(),
Default::default(),
));
let mut width = swapchain.image_extent()[0]; let [width, height] = window_size.into();
let mut height = swapchain.image_extent()[1];
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone());
let mut recreate_swapchain = false; let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); window,
swapchain,
render_pass,
framebuffers,
width,
height,
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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
width = swapchain.image_extent()[0]; rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
height = swapchain.image_extent()[1]; [rcx.width, rcx.height] = window_size.into();
framebuffers = window_size_dependent_setup(&new_images, render_pass.clone()); rcx.recreate_swapchain = false;
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -223,7 +280,7 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
@ -250,13 +307,13 @@ fn main() -> Result<(), impl Error> {
// Fixed offset, relative extent. // Fixed offset, relative extent.
ClearRect { ClearRect {
offset: [100, 150], offset: [100, 150],
extent: [width / 4, height / 4], extent: [rcx.width / 4, rcx.height / 4],
array_layers: 0..1, array_layers: 0..1,
}, },
// Relative offset and extent. // Relative offset and extent.
ClearRect { ClearRect {
offset: [width / 2, height / 2], offset: [rcx.width / 2, rcx.height / 2],
extent: [width / 3, height / 5], extent: [rcx.width / 3, rcx.height / 5],
array_layers: 0..1, array_layers: 0..1,
}, },
] ]
@ -268,47 +325,56 @@ fn main() -> Result<(), impl Error> {
.unwrap(); .unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -27,8 +27,8 @@ use vulkano::{
StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo, StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, ImageUsage}, image::{view::ImageView, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -40,9 +40,10 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
mod frame; mod frame;
@ -52,9 +53,34 @@ fn main() -> Result<(), impl Error> {
// Basic initialization. See the triangle example if you want more details about this. // Basic initialization. See the triangle example if you want more details about this.
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
memory_allocator: Arc<StandardMemoryAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
images: Vec<Arc<ImageView>>,
frame_system: FrameSystem,
triangle_draw_system: TriangleDrawSystem,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -79,7 +105,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -111,46 +137,9 @@ fn main() -> Result<(), impl Error> {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().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, mut 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;
let (swapchain, images) = 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 images = images
.into_iter()
.map(|image| ImageView::new_default(image).unwrap())
.collect::<Vec<_>>();
(swapchain, images)
};
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
device.clone(), device.clone(),
@ -160,56 +149,122 @@ fn main() -> Result<(), impl Error> {
}, },
)); ));
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, _) = 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::<Vec<_>>();
// Here is the basic initialization for the deferred system. // Here is the basic initialization for the deferred system.
let mut frame_system = FrameSystem::new( let frame_system = FrameSystem::new(
queue.clone(), self.queue.clone(),
swapchain.image_format(), swapchain.image_format(),
memory_allocator.clone(), self.memory_allocator.clone(),
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
); );
let triangle_draw_system = TriangleDrawSystem::new( let triangle_draw_system = TriangleDrawSystem::new(
queue.clone(), self.queue.clone(),
frame_system.deferred_subpass(), frame_system.deferred_subpass(),
memory_allocator.clone(), self.memory_allocator.clone(),
command_buffer_allocator, self.command_buffer_allocator.clone(),
); );
let mut recreate_swapchain = false; let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); window,
swapchain,
images,
frame_system,
triangle_draw_system,
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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
let new_images = new_images let new_images = new_images
@ -217,36 +272,42 @@ fn main() -> Result<(), impl Error> {
.map(|image| ImageView::new_default(image).unwrap()) .map(|image| ImageView::new_default(image).unwrap())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
images = new_images; rcx.images = new_images;
recreate_swapchain = false; rcx.recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let future = previous_frame_end.take().unwrap().join(acquire_future); let future = rcx.previous_frame_end.take().unwrap().join(acquire_future);
let mut frame = frame_system.frame( let mut frame = rcx.frame_system.frame(
future, future,
images[image_index as usize].clone(), rcx.images[image_index as usize].clone(),
Mat4::IDENTITY, Mat4::IDENTITY,
); );
let mut after_future = None; let mut after_future = None;
while let Some(pass) = frame.next_pass() { while let Some(pass) = frame.next_pass() {
match pass { match pass {
Pass::Deferred(mut draw_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); draw_pass.execute(cb);
} }
Pass::Lighting(mut lighting) => { Pass::Lighting(mut lighting) => {
@ -265,27 +326,34 @@ fn main() -> Result<(), impl Error> {
let future = after_future let future = after_future
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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();
} }
})
} }

View File

@ -83,6 +83,7 @@ fn main() {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
mod cs { mod cs {

View File

@ -16,5 +16,4 @@ glium = "0.32.1"
vulkano = { workspace = true, default-features = true } vulkano = { workspace = true, default-features = true }
vulkano-shaders = { workspace = true } vulkano-shaders = { workspace = true }
winit = { workspace = true, default-features = 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" } winit_glium = { package = "winit", version = "0.27.1" }

View File

@ -70,7 +70,7 @@ mod linux {
acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo,
}, },
sync::{ sync::{
now, self,
semaphore::{ semaphore::{
ExternalSemaphoreHandleType, ExternalSemaphoreHandleTypes, Semaphore, ExternalSemaphoreHandleType, ExternalSemaphoreHandleTypes, Semaphore,
SemaphoreCreateInfo, SemaphoreCreateInfo,
@ -80,12 +80,50 @@ mod linux {
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, error::EventLoopError,
window::{Window, WindowBuilder}, 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<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
image_view: Arc<ImageView>,
sampler: Arc<Sampler>,
barrier: Arc<Barrier>,
barrier_2: Arc<Barrier>,
acquire_sem: Arc<Semaphore>,
release_sem: Arc<Semaphore>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
descriptor_set: Arc<DescriptorSet>,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let event_loop_gl = winit_glium::event_loop::EventLoop::new(); let event_loop_gl = winit_glium::event_loop::EventLoop::new();
// For some reason, this must be created before the vulkan window // For some reason, this must be created before the vulkan window
let hrb = glutin::ContextBuilder::new() let hrb = glutin::ContextBuilder::new()
@ -107,21 +145,146 @@ mod linux {
) )
.unwrap(); .unwrap();
let event_loop = EventLoop::new().unwrap(); let library = VulkanLibrary::new().unwrap();
let ( let required_extensions = Surface::required_extensions(event_loop).unwrap();
device, let instance = Instance::new(
_instance, library,
mut swapchain, InstanceCreateInfo {
window, flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
mut viewport, enabled_extensions: InstanceExtensions {
queue, khr_get_physical_device_properties2: true,
render_pass, khr_external_memory_capabilities: true,
mut framebuffers, khr_external_semaphore_capabilities: true,
sampler, khr_external_fence_capabilities: true,
pipeline, ext_debug_utils: true,
memory_allocator, ..required_extensions
vertex_buffer, },
) = vk_setup(display, &event_loop); ..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( let raw_image = RawImage::new(
device.clone(), device.clone(),
@ -130,7 +293,9 @@ mod linux {
image_type: ImageType::Dim2d, image_type: ImageType::Dim2d,
format: Format::R16G16B16A16_UNORM, format: Format::R16G16B16A16_UNORM,
extent: [200, 200, 1], extent: [200, 200, 1],
usage: ImageUsage::TRANSFER_SRC | ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, usage: ImageUsage::TRANSFER_SRC
| ImageUsage::TRANSFER_DST
| ImageUsage::SAMPLED,
external_memory_handle_types: ExternalMemoryHandleTypes::OPAQUE_FD, external_memory_handle_types: ExternalMemoryHandleTypes::OPAQUE_FD,
..Default::default() ..Default::default()
}, },
@ -171,6 +336,17 @@ mod linux {
let image_view = ImageView::new_default(image).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 = Arc::new(Barrier::new(2));
let barrier_2 = Arc::new(Barrier::new(2)); let barrier_2 = Arc::new(Barrier::new(2));
@ -231,19 +407,23 @@ mod linux {
.unwrap(); .unwrap();
let gl_acquire_sem = unsafe { let gl_acquire_sem = unsafe {
glium::semaphore::Semaphore::new_from_fd(gl_display.as_ref(), acquire_fd).unwrap() glium::semaphore::Semaphore::new_from_fd(gl_display.as_ref(), acquire_fd)
.unwrap()
}; };
let gl_release_sem = unsafe { let gl_release_sem = unsafe {
glium::semaphore::Semaphore::new_from_fd(gl_display.as_ref(), release_fd).unwrap() glium::semaphore::Semaphore::new_from_fd(gl_display.as_ref(), release_fd)
.unwrap()
}; };
let rotation_start = Instant::now(); let rotation_start = Instant::now();
loop { loop {
barrier_clone.wait(); barrier_clone.wait();
gl_acquire_sem gl_acquire_sem.wait_textures(Some(&[(
.wait_textures(Some(&[(&gl_tex, glium::semaphore::TextureLayout::General)])); &gl_tex,
glium::semaphore::TextureLayout::General,
)]));
gl_display.get_context().flush(); gl_display.get_context().flush();
@ -261,8 +441,10 @@ mod linux {
1.0, 1.0,
); );
} }
gl_release_sem gl_release_sem.signal_textures(Some(&[(
.signal_textures(Some(&[(&gl_tex, glium::semaphore::TextureLayout::General)])); &gl_tex,
glium::semaphore::TextureLayout::General,
)]));
barrier_2_clone.wait(); barrier_2_clone.wait();
gl_display.get_context().finish(); gl_display.get_context().finish();
@ -271,350 +453,53 @@ mod linux {
} }
}); });
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( App {
device.clone(), instance,
Default::default(), device,
)); queue,
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
device.clone(),
Default::default(),
));
let layout = &pipeline.layout().set_layouts()[0];
let set = DescriptorSet::new(
descriptor_set_allocator, descriptor_set_allocator,
layout.clone(), command_buffer_allocator,
[ vertex_buffer,
WriteDescriptorSet::sampler(0, sampler), sampler,
WriteDescriptorSet::image_view(1, image_view), image_view,
], barrier,
[], barrier_2,
) acquire_sem,
.unwrap(); release_sem,
rcx: None,
let mut recreate_swapchain = false;
let mut previous_frame_end: Option<Box<dyn GpuFuture>> =
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();
barrier.wait();
barrier_2.wait();
queue
.with(|mut q| unsafe {
q.submit(
&[SubmitInfo {
wait_semaphores: vec![SemaphoreSubmitInfo::new(
release_sem.clone(),
)],
..Default::default()
}],
None,
)
})
.unwrap();
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) {
return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if recreate_swapchain { let window = Arc::new(
let (new_swapchain, new_images) = swapchain event_loop
.recreate(SwapchainCreateInfo { .create_window(Window::default_attributes())
image_extent, .unwrap(),
..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; let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
} let window_size = window.inner_size();
let (image_index, suboptimal, acquire_future) = match acquire_next_image(
swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r,
Err(VulkanError::OutOfDate) => {
recreate_swapchain = true;
return;
}
Err(e) => panic!("failed to acquire next image: {e}"),
};
if suboptimal {
recreate_swapchain = true;
}
let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(),
queue.queue_family_index(),
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit,
..Default::default()
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(),
)
},
Default::default(),
)
.unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect())
.unwrap()
.bind_pipeline_graphics(pipeline.clone())
.unwrap()
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
pipeline.layout().clone(),
0,
set.clone(),
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap();
unsafe {
builder.draw(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)
.unwrap()
.then_swapchain_present(
queue.clone(),
SwapchainPresentInfo::swapchain_image_index(
swapchain.clone(),
image_index,
),
)
.then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) {
Ok(future) => {
future.wait(None).unwrap();
previous_frame_end = Some(future.boxed());
}
Err(VulkanError::OutOfDate) => {
recreate_swapchain = true;
previous_frame_end = Some(now(device.clone()).boxed());
}
Err(e) => {
println!("failed to flush future: {e}");
previous_frame_end = Some(now(device.clone()).boxed());
}
};
}
Event::AboutToWait => window.request_redraw(),
_ => (),
};
})
}
#[derive(BufferContents, Vertex)]
#[repr(C)]
struct MyVertex {
#[format(R32G32_SFLOAT)]
position: [f32; 2],
}
#[allow(clippy::type_complexity)]
fn vk_setup(
display: glium::HeadlessRenderer,
event_loop: &EventLoop<()>,
) -> (
Arc<Device>,
Arc<Instance>,
Arc<Swapchain>,
Arc<Window>,
Viewport,
Arc<Queue>,
Arc<RenderPass>,
Vec<Arc<Framebuffer>>,
Arc<Sampler>,
Arc<GraphicsPipeline>,
Arc<StandardMemoryAllocator>,
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 (swapchain, images) = {
let surface_capabilities = device let surface_capabilities = self
.device
.physical_device() .physical_device()
.surface_capabilities(&surface, Default::default()) .surface_capabilities(&surface, Default::default())
.unwrap(); .unwrap();
let image_format = device let (image_format, _) = self
.device
.physical_device() .physical_device()
.surface_formats(&surface, Default::default()) .surface_formats(&surface, Default::default())
.unwrap()[0] .unwrap()[0];
.0;
Swapchain::new( Swapchain::new(
device.clone(), self.device.clone(),
surface, surface,
SwapchainCreateInfo { SwapchainCreateInfo {
min_image_count: surface_capabilities.min_image_count.max(2), min_image_count: surface_capabilities.min_image_count.max(2),
image_format, image_format,
image_extent: window.inner_size().into(), image_extent: window_size.into(),
image_usage: ImageUsage::COLOR_ATTACHMENT, image_usage: ImageUsage::COLOR_ATTACHMENT,
composite_alpha: surface_capabilities composite_alpha: surface_capabilities
.supported_composite_alpha .supported_composite_alpha
@ -627,38 +512,8 @@ mod linux {
.unwrap() .unwrap()
}; };
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let render_pass = vulkano::single_pass_renderpass!(
self.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: { attachments: {
color: { color: {
format: swapchain.image_format(), format: swapchain.image_format(),
@ -674,23 +529,14 @@ mod linux {
) )
.unwrap(); .unwrap();
let sampler = Sampler::new( let framebuffers = window_size_dependent_setup(&images, &render_pass);
device.clone(),
SamplerCreateInfo {
mag_filter: Filter::Linear,
min_filter: Filter::Linear,
address_mode: [SamplerAddressMode::Repeat; 3],
..Default::default()
},
)
.unwrap();
let pipeline = { let pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
@ -700,16 +546,16 @@ mod linux {
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -736,27 +582,219 @@ mod linux {
.unwrap() .unwrap()
}; };
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.0, depth_range: 0.0..=1.0,
}; };
let framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
( let layout = &pipeline.layout().set_layouts()[0];
device,
instance, let descriptor_set = DescriptorSet::new(
swapchain, 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, window,
viewport, swapchain,
queue,
render_pass, render_pass,
framebuffers, framebuffers,
sampler,
pipeline, pipeline,
memory_allocator, viewport,
vertex_buffer, 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");
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(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;
}
let mut builder = RecordingCommandBuffer::new(
self.command_buffer_allocator.clone(),
self.queue.queue_family_index(),
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit,
..Default::default()
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer(
rcx.framebuffers[image_index as usize].clone(),
)
},
Default::default(),
)
.unwrap()
.set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap()
.bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap()
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
rcx.pipeline.layout().clone(),
0,
rcx.descriptor_set.clone(),
)
.unwrap()
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap();
unsafe {
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 = rcx
.previous_frame_end
.take()
.unwrap()
.join(acquire_future)
.then_execute(self.queue.clone(), command_buffer)
.unwrap()
.then_swapchain_present(
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) => {
future.wait(None).unwrap();
rcx.previous_frame_end = Some(future.boxed());
}
Err(VulkanError::OutOfDate) => {
rcx.recreate_swapchain = true;
rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
}
Err(e) => {
println!("failed to flush future: {e}");
rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
}
};
}
_ => {}
}
}
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],
} }
fn build_display<F>(ctx: glutin::Context<glutin::NotCurrent>, f: F) fn build_display<F>(ctx: glutin::Context<glutin::NotCurrent>, f: F)
@ -779,12 +817,8 @@ mod linux {
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {

View File

@ -1,6 +1,6 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, BlitImageInfo, BufferImageCopy, allocator::StandardCommandBufferAllocator, BlitImageInfo, BufferImageCopy,
ClearColorImageInfo, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, ClearColorImageInfo, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage,
@ -11,8 +11,8 @@ use vulkano::{
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
format::Format, format::Format,
image::{ image::{
@ -44,9 +44,10 @@ use vulkano::{
DeviceSize, Validated, VulkanError, VulkanLibrary, DeviceSize, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
@ -54,9 +55,39 @@ fn main() -> Result<(), impl Error> {
// example if you haven't done so yet. // example if you haven't done so yet.
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
texture: Arc<ImageView>,
sampler: Arc<Sampler>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
descriptor_set: Arc<DescriptorSet>,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -81,7 +112,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -113,61 +144,30 @@ fn main() -> Result<(), impl Error> {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
#[derive(BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32_SFLOAT)] let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
position: [f32; 2], device.clone(),
} Default::default(),
));
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.5, -0.5], position: [-0.5, -0.5],
}, },
Vertex { MyVertex {
position: [-0.5, 0.5], position: [-0.5, 0.5],
}, },
Vertex { MyVertex {
position: [0.5, -0.5], position: [0.5, -0.5],
}, },
Vertex { MyVertex {
position: [0.5, 0.5], position: [0.5, 0.5],
}, },
]; ];
@ -186,31 +186,6 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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( let mut uploads = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), command_buffer_allocator.clone(),
queue.queue_family_index(), queue.queue_family_index(),
@ -254,7 +229,9 @@ fn main() -> Result<(), impl Error> {
ImageCreateInfo { ImageCreateInfo {
format: Format::R8G8B8A8_UNORM, format: Format::R8G8B8A8_UNORM,
extent, extent,
usage: ImageUsage::TRANSFER_SRC | ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, usage: ImageUsage::TRANSFER_SRC
| ImageUsage::TRANSFER_DST
| ImageUsage::SAMPLED,
..Default::default() ..Default::default()
}, },
AllocationCreateInfo::default(), AllocationCreateInfo::default(),
@ -334,31 +311,107 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -385,97 +438,99 @@ fn main() -> Result<(), impl Error> {
.unwrap() .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 layout = &pipeline.layout().set_layouts()[0];
let set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
descriptor_set_allocator, self.descriptor_set_allocator.clone(),
layout.clone(), layout.clone(),
[ [
WriteDescriptorSet::sampler(0, sampler), WriteDescriptorSet::sampler(0, self.sampler.clone()),
WriteDescriptorSet::image_view(1, texture), WriteDescriptorSet::image_view(1, self.texture.clone()),
], ],
[], [],
) )
.unwrap(); .unwrap();
let mut viewport = Viewport { let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
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; self.rcx = Some(RenderContext {
let mut previous_frame_end = Some( window,
uploads swapchain,
.end() render_pass,
.unwrap() framebuffers,
.execute(queue.clone()) pipeline,
.unwrap() viewport,
.boxed(), descriptor_set,
); recreate_swapchain: false,
previous_frame_end,
});
}
event_loop.run(move |event, elwt| { fn window_event(
elwt.set_control_flow(ControlFlow::Poll); &mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -489,78 +544,92 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), rcx.pipeline.layout().clone(),
0, 0,
set.clone(), rcx.descriptor_set.clone(),
) )
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -1,6 +1,6 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo,
@ -9,8 +9,8 @@ use vulkano::{
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
format::Format, format::Format,
image::{ image::{
@ -42,9 +42,10 @@ use vulkano::{
DeviceSize, Validated, VulkanError, VulkanLibrary, DeviceSize, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
@ -52,9 +53,39 @@ fn main() -> Result<(), impl Error> {
// example if you haven't done so yet. // example if you haven't done so yet.
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
texture: Arc<ImageView>,
sampler: Arc<Sampler>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
descriptor_set: Arc<DescriptorSet>,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -79,7 +110,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -113,59 +144,27 @@ fn main() -> Result<(), impl Error> {
.unwrap(); .unwrap();
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
#[derive(BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32_SFLOAT)] let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
position: [f32; 2], device.clone(),
} Default::default(),
));
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.5, -0.5], position: [-0.5, -0.5],
}, },
Vertex { MyVertex {
position: [-0.5, 0.5], position: [-0.5, 0.5],
}, },
Vertex { MyVertex {
position: [0.5, -0.5], position: [0.5, -0.5],
}, },
Vertex { MyVertex {
position: [0.5, 0.5], position: [0.5, 0.5],
}, },
]; ];
@ -184,32 +183,6 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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( let mut uploads = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), command_buffer_allocator.clone(),
queue.queue_family_index(), queue.queue_family_index(),
@ -281,31 +254,107 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -332,97 +381,99 @@ fn main() -> Result<(), impl Error> {
.unwrap() .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 layout = &pipeline.layout().set_layouts()[0];
let set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
descriptor_set_allocator, self.descriptor_set_allocator.clone(),
layout.clone(), layout.clone(),
[ [
WriteDescriptorSet::sampler(0, sampler), WriteDescriptorSet::sampler(0, self.sampler.clone()),
WriteDescriptorSet::image_view(1, texture), WriteDescriptorSet::image_view(1, self.texture.clone()),
], ],
[], [],
) )
.unwrap(); .unwrap();
let mut viewport = Viewport { let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
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; self.rcx = Some(RenderContext {
let mut previous_frame_end = Some( window,
uploads swapchain,
.end() render_pass,
.unwrap() framebuffers,
.execute(queue.clone()) pipeline,
.unwrap() viewport,
.boxed(), descriptor_set,
); recreate_swapchain: false,
previous_frame_end,
});
}
event_loop.run(move |event, elwt| { fn window_event(
elwt.set_control_flow(ControlFlow::Poll); &mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -436,78 +487,92 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), rcx.pipeline.layout().clone(),
0, 0,
set.clone(), rcx.descriptor_set.clone(),
) )
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -9,7 +9,7 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo,
@ -18,8 +18,8 @@ use vulkano::{
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
format::Format, format::Format,
image::{ image::{
@ -51,16 +51,47 @@ use vulkano::{
DeviceSize, Validated, VulkanError, VulkanLibrary, DeviceSize, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
texture: Arc<ImageView>,
sampler: Arc<Sampler>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
descriptor_set: Arc<DescriptorSet>,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -85,7 +116,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -119,59 +150,27 @@ fn main() -> Result<(), impl Error> {
.unwrap(); .unwrap();
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
#[derive(BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32_SFLOAT)] let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
position: [f32; 2], device.clone(),
} Default::default(),
));
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.5, -0.5], position: [-0.5, -0.5],
}, },
Vertex { MyVertex {
position: [-0.5, 0.5], position: [-0.5, 0.5],
}, },
Vertex { MyVertex {
position: [0.5, -0.5], position: [0.5, -0.5],
}, },
Vertex { MyVertex {
position: [0.5, 0.5], position: [0.5, 0.5],
}, },
]; ];
@ -190,32 +189,6 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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( let mut uploads = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), command_buffer_allocator.clone(),
queue.queue_family_index(), queue.queue_family_index(),
@ -287,16 +260,92 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
@ -305,17 +354,18 @@ fn main() -> Result<(), impl Error> {
let mut layout_create_info = let mut layout_create_info =
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages);
// Modify the auto-generated layout by setting an immutable sampler to set 0 binding 0. // Modify the auto-generated layout by setting an immutable sampler to set 0
// binding 0.
layout_create_info.set_layouts[0] layout_create_info.set_layouts[0]
.bindings .bindings
.get_mut(&0) .get_mut(&0)
.unwrap() .unwrap()
.immutable_samplers = vec![sampler]; .immutable_samplers = vec![self.sampler.clone()];
PipelineLayout::new( PipelineLayout::new(
device.clone(), self.device.clone(),
layout_create_info layout_create_info
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap() .unwrap()
@ -323,7 +373,7 @@ fn main() -> Result<(), impl Error> {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -354,93 +404,95 @@ fn main() -> Result<(), impl Error> {
// Use `image_view` instead of `image_view_sampler`, since the sampler is already in the // Use `image_view` instead of `image_view_sampler`, since the sampler is already in the
// layout. // layout.
let set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
descriptor_set_allocator, self.descriptor_set_allocator.clone(),
layout.clone(), layout.clone(),
[WriteDescriptorSet::image_view(1, texture)], [WriteDescriptorSet::image_view(1, self.texture.clone())],
[], [],
) )
.unwrap(); .unwrap();
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.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 previous_frame_end = Some(sync::now(self.device.clone()).boxed());
let mut previous_frame_end = Some(
uploads
.end()
.unwrap()
.execute(queue.clone())
.unwrap()
.boxed(),
);
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); 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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -454,78 +506,92 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), rcx.pipeline.layout().clone(),
0, 0,
set.clone(), rcx.descriptor_set.clone(),
) )
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -28,8 +28,8 @@ use vulkano::{
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -58,16 +58,46 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
indirect_buffer_allocator: SubbufferAllocator,
vertex_buffer_allocator: SubbufferAllocator,
compute_pipeline: Arc<ComputePipeline>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -93,7 +123,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -128,69 +158,36 @@ fn main() -> Result<(), impl Error> {
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
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(), device.clone(),
surface, Default::default(),
SwapchainCreateInfo { ));
min_image_count: surface_capabilities.min_image_count.max(2), let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
image_format, device.clone(),
image_extent: window.inner_size().into(), Default::default(),
image_usage: ImageUsage::COLOR_ATTACHMENT, ));
composite_alpha: surface_capabilities
.supported_composite_alpha // Each frame we generate a new set of vertices and each frame we need a new
.into_iter() // `DrawIndirectCommand` struct to set the number of vertices to draw.
.next() let indirect_buffer_allocator = SubbufferAllocator::new(
.unwrap(), memory_allocator.clone(),
SubbufferAllocatorCreateInfo {
buffer_usage: BufferUsage::INDIRECT_BUFFER | BufferUsage::STORAGE_BUFFER,
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default() ..Default::default()
}, },
) );
.unwrap() let vertex_buffer_allocator = SubbufferAllocator::new(
}; memory_allocator,
SubbufferAllocatorCreateInfo {
mod vs { buffer_usage: BufferUsage::STORAGE_BUFFER | BufferUsage::VERTEX_BUFFER,
vulkano_shaders::shader! { memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
ty: "vertex", | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
src: r" ..Default::default()
#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);
}
"#,
}
}
// A simple compute shader that generates vertices. It has two buffers bound: the first is // 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 // where we output the vertices, the second is the `IndirectDrawArgs` struct we passed the
@ -217,10 +214,10 @@ fn main() -> Result<(), impl Error> {
void main() { void main() {
uint idx = gl_GlobalInvocationID.x; uint idx = gl_GlobalInvocationID.x;
// Each invocation of the compute shader is going to increment the counter, so // Each invocation of the compute shader is going to increment the counter,
// we need to use atomic operations for safety. The previous value of the // so we need to use atomic operations for safety. The previous value of
// counter is returned so that gives us the offset into the vertex buffer this // the counter is returned so that gives us the offset into the vertex
// thread can write it's vertices into. // buffer this thread can write it's vertices into.
uint offset = atomicAdd(vertices, 6); uint offset = atomicAdd(vertices, 6);
vec2 center = vec2(-0.8, -0.8) + idx * vec2(0.1, 0.1); vec2 center = vec2(-0.8, -0.8) + idx * vec2(0.1, 0.1);
@ -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 compute_pipeline = {
let cs = cs::load(device.clone()) let cs = cs::load(device.clone())
.unwrap() .unwrap()
@ -279,8 +253,63 @@ fn main() -> Result<(), impl Error> {
.unwrap() .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!( let render_pass = single_pass_renderpass!(
device.clone(), self.device.clone(),
attachments: { attachments: {
color: { color: {
format: swapchain.image_format(), format: swapchain.image_format(),
@ -296,40 +325,64 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// `Vertex` is the vertex type that will be output from the compute shader and be input to the let framebuffers = window_size_dependent_setup(&images, &render_pass);
// vertex shader.
#[derive(BufferContents, Vertex)] mod vs {
#[repr(C)] vulkano_shaders::shader! {
struct Vertex { ty: "vertex",
#[format(R32G32_SFLOAT)] src: r"
position: [f32; 2], #version 450
// The triangle vertex positions.
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
",
}
} }
let render_pipeline = { mod fs {
let vs = vs::load(device.clone()) 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 pipeline = {
let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -350,81 +403,81 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.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 descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
device.clone(),
Default::default(),
));
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
device.clone(),
Default::default(),
));
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); 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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
// Allocate a buffer to hold the arguments for this frame's draw call. The compute // Allocate a buffer to hold the arguments for this frame's draw call. The compute
@ -436,7 +489,8 @@ fn main() -> Result<(), impl Error> {
first_vertex: 0, first_vertex: 0,
first_instance: 0, first_instance: 0,
}]; }];
let indirect_buffer = indirect_args_pool let indirect_buffer = self
.indirect_buffer_allocator
.allocate_slice(indirect_commands.len() as _) .allocate_slice(indirect_commands.len() as _)
.unwrap(); .unwrap();
indirect_buffer indirect_buffer
@ -446,16 +500,19 @@ fn main() -> Result<(), impl Error> {
// Allocate a buffer to hold this frame's vertices. This needs to be large enough // 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. // 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 iter = (0..(6 * 16)).map(|_| MyVertex { position: [0.0; 2] });
let vertices = vertex_pool.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) { for (o, i) in vertices.write().unwrap().iter_mut().zip(iter) {
*o = i; *o = i;
} }
// Pass the two buffers to the compute shader. // 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( let cs_descriptor_set = DescriptorSet::new(
descriptor_set_allocator.clone(), self.descriptor_set_allocator.clone(),
layout.clone(), layout.clone(),
[ [
WriteDescriptorSet::buffer(0, vertices.clone()), WriteDescriptorSet::buffer(0, vertices.clone()),
@ -466,8 +523,8 @@ fn main() -> Result<(), impl Error> {
.unwrap(); .unwrap();
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -479,11 +536,11 @@ fn main() -> Result<(), impl Error> {
// First in the command buffer we dispatch the compute shader to generate the // First in the command buffer we dispatch the compute shader to generate the
// vertices and fill out the draw call arguments. // vertices and fill out the draw call arguments.
builder builder
.bind_pipeline_compute(compute_pipeline.clone()) .bind_pipeline_compute(self.compute_pipeline.clone())
.unwrap() .unwrap()
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Compute, PipelineBindPoint::Compute,
compute_pipeline.layout().clone(), self.compute_pipeline.layout().clone(),
0, 0,
cs_descriptor_set, cs_descriptor_set,
) )
@ -498,15 +555,15 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(render_pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertices) .bind_vertex_buffers(0, vertices)
.unwrap(); .unwrap();
@ -521,51 +578,65 @@ fn main() -> Result<(), impl Error> {
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -5,14 +5,14 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -39,34 +39,44 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, 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],
}
/// 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,
}
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[TriangleVertex]>,
instance_buffer: Subbuffer<[InstanceData]>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -91,7 +101,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -126,43 +136,14 @@ fn main() -> Result<(), impl Error> {
let queue = queues.next().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())); 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 // We now create a buffer that will store the shape of our triangle. This triangle is
// to the one in the `triangle.rs` example. // identical to the one in the `triangle.rs` example.
let vertices = [ let vertices = [
TriangleVertex { TriangleVertex {
position: [-0.5, -0.25], position: [-0.5, -0.25],
@ -189,8 +170,8 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// Now we create another buffer that will store the unique data per instance. For this example, // Now we create another buffer that will store the unique data per instance. For this
// we'll have the instances form a 10x10 grid that slowly gets larger. // example, we'll have the instances form a 10x10 grid that slowly gets larger.
let instances = { let instances = {
let rows = 10; let rows = 10;
let cols = 10; let cols = 10;
@ -227,6 +208,78 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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 { mod vs {
vulkano_shaders::shader! { vulkano_shaders::shader! {
ty: "vertex", ty: "vertex",
@ -263,29 +316,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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
@ -297,21 +333,21 @@ fn main() -> Result<(), impl Error> {
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
// Use the implementations of the `Vertex` trait to describe to vulkano how the two // Use the implementations of the `Vertex` trait to describe to vulkano how the
// vertex types are expected to be used. // two vertex types are expected to be used.
vertex_input_state: Some(vertex_input_state), vertex_input_state: Some(vertex_input_state),
input_assembly_state: Some(InputAssemblyState::default()), input_assembly_state: Some(InputAssemblyState::default()),
viewport_state: Some(ViewportState::default()), viewport_state: Some(ViewportState::default()),
@ -329,82 +365,86 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.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( let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
device.clone(),
Default::default(),
));
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); 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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -418,25 +458,28 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
// We pass both our lists of vertices here. // 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(); .unwrap();
unsafe { unsafe {
builder builder
.draw( .draw(
vertex_buffer.len() as u32, self.vertex_buffer.len() as u32,
instance_buffer.len() as u32, self.instance_buffer.len() as u32,
0, 0,
0, 0,
) )
@ -446,51 +489,74 @@ fn main() -> Result<(), impl Error> {
builder.end_render_pass(Default::default()).unwrap(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -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<Queue>,
image_format: vulkano::format::Format,
swapchain_image_views: &[Arc<ImageView>],
) -> 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<ImageView>) -> Box<dyn GpuFuture> {
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<f64>) {
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)
}
}
}

View File

@ -136,11 +136,10 @@ impl FractalComputePipeline {
) -> Box<dyn GpuFuture> { ) -> Box<dyn GpuFuture> {
// Resize image if needed. // Resize image if needed.
let image_extent = image_view.image().extent(); let image_extent = image_view.image().extent();
let pipeline_layout = self.pipeline.layout(); let layout = &self.pipeline.layout().set_layouts()[0];
let desc_layout = &pipeline_layout.set_layouts()[0]; let descriptor_set = DescriptorSet::new(
let set = DescriptorSet::new(
self.descriptor_set_allocator.clone(), self.descriptor_set_allocator.clone(),
desc_layout.clone(), layout.clone(),
[ [
WriteDescriptorSet::image_view(0, image_view), WriteDescriptorSet::image_view(0, image_view),
WriteDescriptorSet::buffer(1, self.palette.clone()), WriteDescriptorSet::buffer(1, self.palette.clone()),
@ -172,9 +171,14 @@ impl FractalComputePipeline {
builder builder
.bind_pipeline_compute(self.pipeline.clone()) .bind_pipeline_compute(self.pipeline.clone())
.unwrap() .unwrap()
.bind_descriptor_sets(PipelineBindPoint::Compute, pipeline_layout.clone(), 0, set) .bind_descriptor_sets(
PipelineBindPoint::Compute,
self.pipeline.layout().clone(),
0,
descriptor_set,
)
.unwrap() .unwrap()
.push_constants(pipeline_layout.clone(), 0, push_constants) .push_constants(self.pipeline.layout().clone(), 0, push_constants)
.unwrap(); .unwrap();
unsafe { unsafe {

View File

@ -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<u32>, 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<f64>) {
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();
}
}
}

View File

@ -10,32 +10,135 @@
// - A simple `FractalApp` to handle runtime state. // - A simple `FractalApp` to handle runtime state.
// - A simple `InputState` to interact with the application. // - A simple `InputState` to interact with the application.
use crate::app::FractalApp; use fractal_compute_pipeline::FractalComputePipeline;
use std::{error::Error, time::Duration}; use glam::Vec2;
use vulkano::{image::ImageUsage, swapchain::PresentMode, sync::GpuFuture}; use input::InputState;
use place_over_frame::RenderPassPlaceOverFrame;
use std::{
error::Error,
sync::Arc,
time::{Duration, Instant},
};
use vulkano::{
command_buffer::allocator::{
StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo,
},
descriptor_set::allocator::StandardDescriptorSetAllocator,
image::ImageUsage,
swapchain::PresentMode,
sync::GpuFuture,
};
use vulkano_util::{ use vulkano_util::{
context::{VulkanoConfig, VulkanoContext}, context::{VulkanoConfig, VulkanoContext},
renderer::{VulkanoWindowRenderer, DEFAULT_IMAGE_FORMAT}, renderer::{VulkanoWindowRenderer, DEFAULT_IMAGE_FORMAT},
window::{VulkanoWindows, WindowDescriptor}, window::{VulkanoWindows, WindowDescriptor},
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::{Fullscreen, WindowId},
}; };
mod app;
mod fractal_compute_pipeline; mod fractal_compute_pipeline;
mod input;
mod pixels_draw_pipeline; mod pixels_draw_pipeline;
mod place_over_frame; mod place_over_frame;
const MAX_ITERS_INIT: u32 = 200;
const MOVE_SPEED: f32 = 0.5;
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
// Create the event loop. // Create the event loop.
let event_loop = EventLoop::new().unwrap(); 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<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
rcx: Option<RenderContext>,
}
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 context = VulkanoContext::new(VulkanoConfig::default());
let mut windows = VulkanoWindows::default(); let windows = VulkanoWindows::default();
let _id = windows.create_window(
&event_loop, let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
&context, 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 { &WindowDescriptor {
title: "Fractal".to_string(), title: "Fractal".to_string(),
present_mode: PresentMode::Fifo, present_mode: PresentMode::Fifo,
@ -46,110 +149,86 @@ fn main() -> Result<(), impl Error> {
// Add our render target image onto which we'll be rendering our fractals. // Add our render target image onto which we'll be rendering our fractals.
let render_target_id = 0; 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). // 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, render_target_id,
DEFAULT_IMAGE_FORMAT, DEFAULT_IMAGE_FORMAT,
ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST, ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST,
); );
// Create app to hold the logic of our fractal explorer. let gfx_queue = self.context.graphics_queue();
let gfx_queue = context.graphics_queue();
// We intend to eventually render on our swapchain, thus we use that format when creating the self.rcx = Some(RenderContext {
// app here. render_target_id,
let mut app = FractalApp::new( fractal_pipeline: FractalComputePipeline::new(
gfx_queue.clone(), gfx_queue.clone(),
primary_window_renderer.swapchain_format(), self.context.memory_allocator().clone(),
primary_window_renderer.swapchain_image_views(), self.command_buffer_allocator.clone(),
); self.descriptor_set_allocator.clone(),
app.print_guide(); ),
place_over_frame: RenderPassPlaceOverFrame::new(
event_loop.run(move |event, elwt| { gfx_queue.clone(),
elwt.set_control_flow(ControlFlow::Poll); self.command_buffer_allocator.clone(),
self.descriptor_set_allocator.clone(),
let renderer = windows.get_primary_renderer_mut().unwrap(); window_renderer.swapchain_format(),
window_renderer.swapchain_image_views(),
if process_event(renderer, &event, &mut app, render_target_id) { ),
elwt.exit(); is_julia: false,
return; 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(),
});
} }
// Pass event for the app to handle our inputs. fn window_event(
app.handle_input(renderer.window_size(), &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();
/// Processes a single event for an event loop. match event {
/// Returns true only if the window is to be closed. WindowEvent::CloseRequested => {
pub fn process_event( event_loop.exit();
renderer: &mut VulkanoWindowRenderer,
event: &Event<()>,
app: &mut FractalApp,
render_target_id: usize,
) -> bool {
match &event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
return true;
} }
Event::WindowEvent { WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. } => {
event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. }, renderer.resize();
.. }
} => renderer.resize(), WindowEvent::RedrawRequested => {
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} => 'redraw: {
// Tasks for redrawing: // Tasks for redrawing:
// 1. Update state based on events // 1. Update state based on events
// 2. Compute & Render // 2. Compute & Render
// 3. Reset input state // 3. Reset input state
// 4. Update time & title // 4. Update time & title
// The rendering part goes here:
match renderer.window_size() {
[w, h] => {
// Skip this frame when minimized. // Skip this frame when minimized.
if w == 0.0 || h == 0.0 { if window_size.width == 0 || window_size.height == 0 {
break 'redraw; return;
} }
}
}
app.update_state_after_inputs(renderer);
compute_then_render(renderer, app, render_target_id);
app.reset_input_state();
app.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
));
}
Event::AboutToWait => renderer.window().request_redraw(),
_ => (),
}
!app.is_running()
}
/// Orchestrates rendering. rcx.update_state_after_inputs(renderer);
fn compute_then_render(
renderer: &mut VulkanoWindowRenderer,
app: &mut FractalApp,
target_image_id: usize,
) {
// Start the frame. // Start the frame.
let before_pipeline_future = let before_pipeline_future = match renderer.acquire(
match renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| { Some(Duration::from_millis(1000)),
app.place_over_frame |swapchain_image_views| {
rcx.place_over_frame
.recreate_framebuffers(swapchain_image_views) .recreate_framebuffers(swapchain_image_views)
}) { },
) {
Err(e) => { Err(e) => {
println!("{e}"); println!("{e}");
return; return;
@ -158,20 +237,156 @@ fn compute_then_render(
}; };
// Retrieve the target image. // Retrieve the target image.
let image = renderer.get_additional_image_view(target_image_id); let image = renderer.get_additional_image_view(rcx.render_target_id);
// Compute our fractal (writes to target image). Join future with `before_pipeline_future`. // Compute our fractal (writes to target image). Join future with
let after_compute = app.compute(image.clone()).join(before_pipeline_future); // `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. // Render the image over the swapchain image, inputting the previous future.
let after_renderpass_future = app.place_over_frame.render( let after_renderpass_future = rcx.place_over_frame.render(
after_compute, after_compute,
image, image,
renderer.swapchain_image_view(), renderer.swapchain_image_view(),
renderer.image_index(), renderer.image_index(),
); );
// Finish the frame (which presents the view), inputting the last future. Wait for the future // Finish the frame (which presents the view), inputting the last future. Wait for
// so resources are not in use when we render. // the future so resources are not in use when we render.
renderer.present(after_renderpass_future, true); 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();
}
}
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
let window_renderer = self.windows.get_primary_renderer_mut().unwrap();
window_renderer.window().request_redraw();
}
}
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;
}
}
// 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
});
}
}
/// Returns the average FPS.
fn avg_fps(&self) -> f32 {
self.avg_fps
}
/// 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();
}
} }

View File

@ -15,7 +15,7 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo,
@ -25,7 +25,7 @@ use vulkano::{
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures,
QueueCreateInfo, QueueFlags, Queue, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -52,41 +52,48 @@ use vulkano::{
DeviceSize, Validated, VulkanError, VulkanLibrary, DeviceSize, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, 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;
mod mesh {
vulkano_shaders::shader! {
ty: "mesh",
path: "mesh.glsl",
vulkan_version: "1.2",
}
}
mod fs {
vulkano_shaders::shader! {
ty: "fragment",
path: "frag.glsl",
}
}
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
vertex_buffer: Subbuffer<[TriangleVertex]>,
instance_buffer: Subbuffer<mesh::InstanceBuffer>,
rows: u32,
cols: u32,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
descriptor_set: Arc<DescriptorSet>,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -112,7 +119,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -151,47 +158,18 @@ fn main() -> Result<(), impl Error> {
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
device.clone(), device.clone(),
Default::default(), 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 // We now create a buffer that will store the shape of our triangle. This triangle is
// to the one in the `triangle.rs` example. // identical to the one in the `triangle.rs` example.
let vertices = [ let vertices = [
TriangleVertex { TriangleVertex {
position: [-0.5, -0.25], position: [-0.5, -0.25],
@ -218,8 +196,8 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// Now we create another buffer that will store the unique data per instance. For this example, // Now we create another buffer that will store the unique data per instance. For this
// we'll have the instances form a 10x10 grid that slowly gets larger. // example, we'll have the instances form a 10x10 grid that slowly gets larger.
let rows = 10; let rows = 10;
let cols = 10; let cols = 10;
let instances = { let instances = {
@ -262,8 +240,64 @@ 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!( let render_pass = single_pass_renderpass!(
device.clone(), self.device.clone(),
attachments: { attachments: {
color: { color: {
format: swapchain.image_format(), format: swapchain.image_format(),
@ -279,12 +313,14 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
let framebuffers = window_size_dependent_setup(&images, &render_pass);
let pipeline = { let pipeline = {
let mesh = mesh::load(device.clone()) let mesh = mesh::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
@ -293,16 +329,16 @@ fn main() -> Result<(), impl Error> {
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -321,93 +357,98 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let viewport = Viewport {
offset: [0.0, 0.0],
extent: window_size.into(),
depth_range: 0.0..=1.0,
};
let descriptor_set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
descriptor_set_allocator, self.descriptor_set_allocator.clone(),
pipeline.layout().set_layouts()[0].clone(), pipeline.layout().set_layouts()[0].clone(),
[ [
WriteDescriptorSet::buffer(0, vertex_buffer.clone()), WriteDescriptorSet::buffer(0, self.vertex_buffer.clone()),
WriteDescriptorSet::buffer(1, instance_buffer.clone()), WriteDescriptorSet::buffer(1, self.instance_buffer.clone()),
], ],
[], [],
) )
.unwrap(); .unwrap();
let mut viewport = Viewport { let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
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( self.rcx = Some(RenderContext {
device.clone(), window,
Default::default(), swapchain,
)); render_pass,
framebuffers,
pipeline,
viewport,
descriptor_set,
recreate_swapchain: false,
previous_frame_end,
});
}
event_loop.run(move |event, elwt| { fn window_event(
elwt.set_control_flow(ControlFlow::Poll); &mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -421,77 +462,92 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
// Instead of binding vertex attributes, bind buffers as descriptor sets // Instead of binding vertex attributes, bind buffers as descriptor sets
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), rcx.pipeline.layout().clone(),
0, 0,
descriptor_set.clone(), rcx.descriptor_set.clone(),
) )
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {
@ -503,3 +559,18 @@ fn window_size_dependent_setup(
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
mod mesh {
vulkano_shaders::shader! {
ty: "mesh",
path: "mesh.glsl",
vulkan_version: "1.2",
}
}
mod fs {
vulkano_shaders::shader! {
ty: "fragment",
path: "frag.glsl",
}
}

View File

@ -139,6 +139,7 @@ fn main() {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));

View File

@ -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<Queue>,
gfx_queue: Arc<Queue>,
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<StandardCommandBufferAllocator>,
pub descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
pub pipelines: HashMap<WindowId, RenderPipeline>,
}
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(),
}
}
}

View File

@ -1,4 +1,4 @@
use crate::app::App; use crate::App;
use glam::IVec2; use glam::IVec2;
use rand::Rng; use rand::Rng;
use std::sync::Arc; use std::sync::Arc;
@ -175,11 +175,10 @@ impl GameOfLifeComputePipeline {
) { ) {
// Resize image if needed. // Resize image if needed.
let image_extent = self.image.image().extent(); let image_extent = self.image.image().extent();
let pipeline_layout = self.compute_life_pipeline.layout(); let layout = &self.compute_life_pipeline.layout().set_layouts()[0];
let desc_layout = &pipeline_layout.set_layouts()[0]; let descriptor_set = DescriptorSet::new(
let set = DescriptorSet::new(
self.descriptor_set_allocator.clone(), self.descriptor_set_allocator.clone(),
desc_layout.clone(), layout.clone(),
[ [
WriteDescriptorSet::image_view(0, self.image.clone()), WriteDescriptorSet::image_view(0, self.image.clone()),
WriteDescriptorSet::buffer(1, self.life_in.clone()), WriteDescriptorSet::buffer(1, self.life_in.clone()),
@ -197,9 +196,18 @@ impl GameOfLifeComputePipeline {
builder builder
.bind_pipeline_compute(self.compute_life_pipeline.clone()) .bind_pipeline_compute(self.compute_life_pipeline.clone())
.unwrap() .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() .unwrap()
.push_constants(pipeline_layout.clone(), 0, push_constants) .push_constants(
self.compute_life_pipeline.layout().clone(),
0,
push_constants,
)
.unwrap(); .unwrap();
unsafe { unsafe {

View File

@ -7,151 +7,233 @@
// //
// The possibilities are limitless. ;) // The possibilities are limitless. ;)
mod app; 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 vulkano_util::{
context::{VulkanoConfig, VulkanoContext},
renderer::VulkanoWindowRenderer,
window::{VulkanoWindows, WindowDescriptor},
};
use winit::{
application::ApplicationHandler,
event::{MouseButton, WindowEvent},
event_loop::{ActiveEventLoop, EventLoop},
window::WindowId,
};
mod game_of_life; mod game_of_life;
mod pixels_draw; mod pixels_draw;
mod render_pass; mod render_pass;
use crate::app::{App, RenderPipeline}; const WINDOW_WIDTH: f32 = 1024.0;
use glam::{f32::Vec2, IVec2}; const WINDOW_HEIGHT: f32 = 1024.0;
use std::{ const WINDOW2_WIDTH: f32 = 512.0;
error::Error, const WINDOW2_HEIGHT: f32 = 512.0;
time::{Duration, Instant}, const SCALING: f32 = 2.0;
};
use vulkano_util::renderer::VulkanoWindowRenderer;
use winit::{
event::{ElementState, Event, MouseButton, WindowEvent},
event_loop::{ControlFlow, EventLoop},
};
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;
fn main() -> Result<(), impl Error> { 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"); println!("Welcome to Vulkano Game of Life\nUse the mouse to draw life on the grid(s)\n");
// Create event loop. event_loop.run_app(&mut app)
let event_loop = EventLoop::new().unwrap();
// 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();
}
}
// Draw life on windows if mouse is down.
draw_life(
&mut app,
cursor_pos,
mouse_is_pressed_w1,
mouse_is_pressed_w2,
);
// 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();
}
})
} }
/// Processes a single event for an event loop. struct App {
/// Returns true only if the window is to be closed. context: VulkanoContext,
pub fn process_event( windows: VulkanoWindows,
event: &Event<()>, descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
app: &mut App, command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
cursor_pos: &mut Vec2, rcxs: HashMap<WindowId, RenderContext>,
mouse_pressed_w1: &mut bool, time: Instant,
mouse_pressed_w2: &mut bool, cursor_pos: Vec2,
) -> bool { }
if let Event::WindowEvent {
event, window_id, .. struct RenderContext {
} = &event compute_pipeline: GameOfLifeComputePipeline,
{ place_over_frame: RenderPassPlaceOverFrame,
life_color: [f32; 4],
dead_color: [f32; 4],
mouse_is_pressed: bool,
}
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,
}
}
}
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 { match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
if *window_id == app.windows.primary_window_id().unwrap() { if window_id == self.windows.primary_window_id().unwrap() {
return true; event_loop.exit();
} else { } else {
// Destroy window by removing its renderer. // Destroy window by removing its renderer.
app.windows.remove_renderer(*window_id); self.windows.remove_renderer(window_id);
app.pipelines.remove(window_id); self.rcxs.remove(&window_id);
} }
} }
// Resize window and its images. // Resize window and its images.
WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. } => { WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. } => {
let vulkano_window = app.windows.get_renderer_mut(*window_id).unwrap(); let window_renderer = self.windows.get_renderer_mut(window_id).unwrap();
vulkano_window.resize(); window_renderer.resize();
} }
// Handle mouse position events. // Handle mouse position events.
WindowEvent::CursorMoved { position, .. } => { 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. // Handle mouse button events.
WindowEvent::MouseInput { state, button, .. } => { WindowEvent::MouseInput { state, button, .. } => {
let mut mouse_pressed = false; let rcx = self.rcxs.get_mut(&window_id).unwrap();
if button == &MouseButton::Left && state == &ElementState::Pressed {
mouse_pressed = true; if button == MouseButton::Left {
} rcx.mouse_is_pressed = state.is_pressed();
if button == &MouseButton::Left && state == &ElementState::Released {
mouse_pressed = false;
}
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( fn draw_life(
app: &mut App, window_renderer: &mut VulkanoWindowRenderer,
rcx: &mut RenderContext,
cursor_pos: Vec2, cursor_pos: Vec2,
mouse_is_pressed_w1: bool,
mouse_is_pressed_w2: bool,
) { ) {
let primary_window_id = app.windows.primary_window_id().unwrap(); if rcx.mouse_is_pressed {
for (id, window) in app.windows.iter_mut() { let window_size = window_renderer.window_size();
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;
let mut normalized_pos = Vec2::new( let mut normalized_pos = Vec2::new(
(cursor_pos.x / window_size[0]).clamp(0.0, 1.0), (cursor_pos.x / window_size[0]).clamp(0.0, 1.0),
(cursor_pos.y / window_size[1]).clamp(0.0, 1.0), (cursor_pos.y / window_size[1]).clamp(0.0, 1.0),
@ -159,48 +241,20 @@ fn draw_life(
// Flip y. // Flip y.
normalized_pos.y = 1.0 - normalized_pos.y; normalized_pos.y = 1.0 - normalized_pos.y;
let image_extent = compute_pipeline.color_image().image().extent(); let image_extent = rcx.compute_pipeline.color_image().image().extent();
compute_pipeline.draw_life(IVec2::new( rcx.compute_pipeline.draw_life(IVec2::new(
(image_extent[0] as f32 * normalized_pos.x) as i32, (image_extent[0] as f32 * normalized_pos.x) as i32,
(image_extent[1] as f32 * normalized_pos.y) 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. /// Compute game of life, then display result on target image.
fn compute_then_render( fn compute_then_render(window_renderer: &mut VulkanoWindowRenderer, rcx: &mut RenderContext) {
window_renderer: &mut VulkanoWindowRenderer,
pipeline: &mut RenderPipeline,
life_color: [f32; 4],
dead_color: [f32; 4],
) {
// Skip this window when minimized.
match window_renderer.window_size() {
[w, h] => {
if w == 0.0 || h == 0.0 {
return;
}
}
}
// Start the frame. // Start the frame.
let before_pipeline_future = let before_pipeline_future =
match window_renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| { match window_renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| {
pipeline rcx.place_over_frame
.place_over_frame
.recreate_framebuffers(swapchain_image_views) .recreate_framebuffers(swapchain_image_views)
}) { }) {
Err(e) => { Err(e) => {
@ -211,15 +265,15 @@ fn compute_then_render(
}; };
// Compute. // Compute.
let after_compute = pipeline let after_compute =
.compute rcx.compute_pipeline
.compute(before_pipeline_future, life_color, dead_color); .compute(before_pipeline_future, rcx.life_color, rcx.dead_color);
// Render. // 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 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, after_compute,
color_image, color_image,
target_image, target_image,

View File

@ -1,4 +1,4 @@
use crate::app::App; use crate::App;
use std::sync::Arc; use std::sync::Arc;
use vulkano::{ use vulkano::{
command_buffer::{ command_buffer::{

View File

@ -1,4 +1,4 @@
use crate::{app::App, pixels_draw::PixelsDrawPipeline}; use crate::{pixels_draw::PixelsDrawPipeline, App};
use std::sync::Arc; use std::sync::Arc;
use vulkano::{ use vulkano::{
command_buffer::{ command_buffer::{
@ -11,7 +11,7 @@ use vulkano::{
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
sync::GpuFuture, sync::GpuFuture,
}; };
use vulkano_util::renderer::VulkanoWindowRenderer; use winit::window::WindowId;
/// A render pass which places an incoming image over the frame, filling it. /// A render pass which places an incoming image over the frame, filling it.
pub struct RenderPassPlaceOverFrame { pub struct RenderPassPlaceOverFrame {
@ -23,11 +23,8 @@ pub struct RenderPassPlaceOverFrame {
} }
impl RenderPassPlaceOverFrame { impl RenderPassPlaceOverFrame {
pub fn new( pub fn new(app: &App, gfx_queue: Arc<Queue>, window_id: WindowId) -> RenderPassPlaceOverFrame {
app: &App, let window_renderer = app.windows.get_renderer(window_id).unwrap();
gfx_queue: Arc<Queue>,
window_renderer: &VulkanoWindowRenderer,
) -> RenderPassPlaceOverFrame {
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
gfx_queue.device().clone(), gfx_queue.device().clone(),
attachments: { attachments: {

View File

@ -9,14 +9,14 @@
use std::{collections::HashMap, error::Error, sync::Arc}; use std::{collections::HashMap, error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -42,25 +42,43 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::{ElementState, KeyEvent, WindowEvent},
window::{Window, WindowBuilder}, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
/// A struct to contain resources related to a window. fn main() -> Result<(), impl Error> {
struct WindowSurface { let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
rcxs: HashMap<WindowId, RenderContext>,
}
struct RenderContext {
window: Arc<Window>, window: Arc<Window>,
swapchain: Arc<Swapchain>, swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>, framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool, recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>, previous_frame_end: Option<Box<dyn GpuFuture>>,
} }
fn main() -> Result<(), impl Error> { impl App {
let event_loop = EventLoop::new().unwrap(); fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -85,7 +103,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -117,65 +135,23 @@ fn main() -> Result<(), impl Error> {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().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();
// A hashmap that contains all of our created windows and their resources.
let mut window_surfaces = HashMap::new();
// Use the window's id as a means to access it from the hashmap.
let window_id = window.id();
// The swapchain and framebuffer images for this particular window.
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.clone(),
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 memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
#[derive(BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32_SFLOAT)]
position: [f32; 2],
}
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.5, -0.25], position: [-0.5, -0.25],
}, },
Vertex { MyVertex {
position: [0.0, 0.5], position: [0.0, 0.5],
}, },
Vertex { MyVertex {
position: [0.25, -0.1], position: [0.25, -0.1],
}, },
]; ];
@ -194,6 +170,56 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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(),
..Default::default()
},
)
.unwrap()
};
mod vs { mod vs {
vulkano_shaders::shader! { vulkano_shaders::shader! {
ty: "vertex", ty: "vertex",
@ -225,7 +251,7 @@ fn main() -> Result<(), impl Error> {
} }
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), self.device.clone(),
attachments: { attachments: {
color: { color: {
format: swapchain.image_format(), format: swapchain.image_format(),
@ -241,31 +267,33 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
let framebuffers = window_size_dependent_setup(&images, &render_pass);
let pipeline = { let pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -286,50 +314,54 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.0, depth_range: 0.0..=1.0,
}; };
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
device.clone(),
Default::default(),
));
window_surfaces.insert( RenderContext {
window_id,
WindowSurface {
window, window,
swapchain, swapchain,
render_pass,
framebuffers,
pipeline,
viewport,
recreate_swapchain: false, recreate_swapchain: false,
framebuffers: window_size_dependent_setup(&images, render_pass.clone(), &mut viewport), previous_frame_end,
previous_frame_end: Some(sync::now(device.clone()).boxed()), }
}, }
); }
event_loop.run(move |event, elwt| { impl ApplicationHandler for App {
elwt.set_control_flow(ControlFlow::Poll); 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();
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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
window_id, self.rcxs.get_mut(&window_id).unwrap().recreate_swapchain = true;
event: WindowEvent::Resized(_),
..
} => {
window_surfaces
.get_mut(&window_id)
.unwrap()
.recreate_swapchain = true;
} }
Event::WindowEvent {
event:
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: event:
KeyEvent { KeyEvent {
@ -337,111 +369,62 @@ fn main() -> Result<(), impl Error> {
.. ..
}, },
.. ..
},
..
} => { } => {
let window = Arc::new(WindowBuilder::new().build(elwt).unwrap()); let window = event_loop
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); .create_window(Window::default_attributes())
let window_id = window.id();
let (swapchain, images) = {
let surface_capabilities = device
.physical_device()
.surface_capabilities(&surface, Default::default())
.unwrap(); .unwrap();
let image_format = device let window_id = window.id();
.physical_device() let rcx = self.create_rcx(window);
.surface_formats(&surface, Default::default())
.unwrap()[0]
.0;
Swapchain::new( self.rcxs.insert(window_id, rcx);
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()),
},
);
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let rcx = self.rcxs.get_mut(&window_id).unwrap();
window_id,
} => {
let WindowSurface {
window,
swapchain,
recreate_swapchain,
framebuffers,
previous_frame_end,
} = window_surfaces.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; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if *recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
*swapchain = new_swapchain; rcx.swapchain = new_swapchain;
*framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
*recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
*recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
*recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -455,74 +438,83 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
*previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
*recreate_swapchain = true; rcx.recreate_swapchain = true;
*previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -4,14 +4,14 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
format::Format, format::Format,
image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage}, image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage},
@ -40,16 +40,48 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
memory_allocator: Arc<StandardMemoryAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
triangle1: Subbuffer<[MyVertex]>,
triangle2: Subbuffer<[MyVertex]>,
triangle3: Subbuffer<[MyVertex]>,
query_pool: Arc<QueryPool>,
query_results: [u32; 3],
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -74,7 +106,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -108,90 +140,52 @@ fn main() -> Result<(), impl Error> {
.unwrap(); .unwrap();
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
#[derive(BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32B32_SFLOAT)]
position: [f32; 3],
#[format(R32G32B32_SFLOAT)]
color: [f32; 3],
}
let vertices = [ let vertices = [
// The first triangle (red) is the same one as in the triangle example. // The first triangle (red) is the same one as in the triangle example.
Vertex { MyVertex {
position: [-0.5, -0.25, 0.5], position: [-0.5, -0.25, 0.5],
color: [1.0, 0.0, 0.0], color: [1.0, 0.0, 0.0],
}, },
Vertex { MyVertex {
position: [0.0, 0.5, 0.5], position: [0.0, 0.5, 0.5],
color: [1.0, 0.0, 0.0], color: [1.0, 0.0, 0.0],
}, },
Vertex { MyVertex {
position: [0.25, -0.1, 0.5], position: [0.25, -0.1, 0.5],
color: [1.0, 0.0, 0.0], color: [1.0, 0.0, 0.0],
}, },
// The second triangle (cyan) is the same shape and position as the first, but smaller, and // The second triangle (cyan) is the same shape and position as the first, but smaller,
// moved behind a bit. It should be completely occluded by the first triangle. (You can // and moved behind a bit. It should be completely occluded by the first triangle. (You
// lower its z value to put it in front.) // can lower its z value to put it in front.)
Vertex { MyVertex {
position: [-0.25, -0.125, 0.6], position: [-0.25, -0.125, 0.6],
color: [0.0, 1.0, 1.0], color: [0.0, 1.0, 1.0],
}, },
Vertex { MyVertex {
position: [0.0, 0.25, 0.6], position: [0.0, 0.25, 0.6],
color: [0.0, 1.0, 1.0], color: [0.0, 1.0, 1.0],
}, },
Vertex { MyVertex {
position: [0.125, -0.05, 0.6], position: [0.125, -0.05, 0.6],
color: [0.0, 1.0, 1.0], color: [0.0, 1.0, 1.0],
}, },
// The third triangle (green) is the same shape and size as the first, but moved to the // 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. // left and behind the second. It is partially occluded by the first two.
Vertex { MyVertex {
position: [-0.25, -0.25, 0.7], position: [-0.25, -0.25, 0.7],
color: [0.0, 1.0, 0.0], color: [0.0, 1.0, 0.0],
}, },
Vertex { MyVertex {
position: [0.25, 0.5, 0.7], position: [0.25, 0.5, 0.7],
color: [0.0, 1.0, 0.0], color: [0.0, 1.0, 0.0],
}, },
Vertex { MyVertex {
position: [0.5, -0.1, 0.7], position: [0.5, -0.1, 0.7],
color: [0.0, 1.0, 0.0], color: [0.0, 1.0, 0.0],
}, },
@ -231,7 +225,90 @@ fn main() -> Result<(), impl Error> {
// element per query. You can ask for the number of elements needed at runtime by calling // 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, // `QueryType::result_len`. If you retrieve query results with `with_availability` enabled,
// then this array needs to be 6 elements long instead of 3. // 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 { mod vs {
vulkano_shaders::shader! { vulkano_shaders::shader! {
@ -268,54 +345,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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -325,8 +379,8 @@ fn main() -> Result<(), impl Error> {
rasterization_state: Some(RasterizationState::default()), rasterization_state: Some(RasterizationState::default()),
multisample_state: Some(MultisampleState::default()), multisample_state: Some(MultisampleState::default()),
// Enable depth testing, which is needed for occlusion queries to make sense at // 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 // all. If you disable depth testing, every pixel is considered to pass the
// test, so every query will return a nonzero result. // depth test, so every query will return a nonzero result.
depth_stencil_state: Some(DepthStencilState { depth_stencil_state: Some(DepthStencilState {
depth: Some(DepthState::simple()), depth: Some(DepthState::simple()),
..Default::default() ..Default::default()
@ -343,90 +397,90 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.0, depth_range: 0.0..=1.0,
}; };
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
device.clone(),
Default::default(),
));
let mut framebuffers = window_size_dependent_setup( self.rcx = Some(RenderContext {
&images, window,
render_pass.clone(), swapchain,
&mut viewport, render_pass,
memory_allocator.clone(), framebuffers,
); pipeline,
viewport,
recreate_swapchain: false,
previous_frame_end,
});
}
let mut recreate_swapchain = false; fn window_event(
let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); &mut self,
event_loop: &ActiveEventLoop,
event_loop.run(move |event, elwt| { _window_id: WindowId,
elwt.set_control_flow(ControlFlow::Poll); event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(
&new_images, &new_images,
render_pass.clone(), &rcx.render_pass,
&mut viewport, &self.memory_allocator,
memory_allocator.clone(),
); );
recreate_swapchain = false; rcx.viewport.extent = window_size.into();
rcx.recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -440,11 +494,11 @@ fn main() -> Result<(), impl Error> {
builder builder
// A query must be reset before each use, including the first use. This // A query must be reset before each use, including the first use. This
// must be done outside a render pass. // 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() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.begin_render_pass( .begin_render_pass(
RenderPassBeginInfo { RenderPassBeginInfo {
@ -453,7 +507,7 @@ fn main() -> Result<(), impl Error> {
Some(1.0.into()), Some(1.0.into()),
], ],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
@ -463,36 +517,36 @@ fn main() -> Result<(), impl Error> {
// `QueryControlFlags::PRECISE` flag would give exact numeric results. This // `QueryControlFlags::PRECISE` flag would give exact numeric results. This
// needs the `occlusion_query_precise` feature to be enabled on the device. // needs the `occlusion_query_precise` feature to be enabled on the device.
.begin_query( .begin_query(
query_pool.clone(), self.query_pool.clone(),
0, 0,
QueryControlFlags::empty(), QueryControlFlags::empty(),
// QueryControlFlags::PRECISE, // QueryControlFlags::PRECISE,
) )
.unwrap() .unwrap()
.bind_vertex_buffers(0, triangle1.clone()) .bind_vertex_buffers(0, self.triangle1.clone())
.unwrap() .unwrap()
.draw(triangle1.len() as u32, 1, 0, 0) .draw(self.triangle1.len() as u32, 1, 0, 0)
.unwrap() .unwrap()
// End query 0. // End query 0.
.end_query(query_pool.clone(), 0) .end_query(self.query_pool.clone(), 0)
.unwrap() .unwrap()
// Begin query 1 for the cyan triangle. // 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() .unwrap()
.bind_vertex_buffers(0, triangle2.clone()) .bind_vertex_buffers(0, self.triangle2.clone())
.unwrap() .unwrap()
.draw(triangle2.len() as u32, 1, 0, 0) .draw(self.triangle2.len() as u32, 1, 0, 0)
.unwrap() .unwrap()
.end_query(query_pool.clone(), 1) .end_query(self.query_pool.clone(), 1)
.unwrap() .unwrap()
// Finally, query 2 for the green triangle. // 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() .unwrap()
.bind_vertex_buffers(0, triangle3.clone()) .bind_vertex_buffers(0, self.triangle3.clone())
.unwrap() .unwrap()
.draw(triangle3.len() as u32, 1, 0, 0) .draw(self.triangle3.len() as u32, 1, 0, 0)
.unwrap() .unwrap()
.end_query(query_pool.clone(), 2) .end_query(self.query_pool.clone(), 2)
.unwrap() .unwrap()
.end_render_pass(Default::default()) .end_render_pass(Default::default())
.unwrap(); .unwrap();
@ -500,29 +554,33 @@ fn main() -> Result<(), impl Error> {
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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 +589,9 @@ fn main() -> Result<(), impl Error> {
// write results to a Vulkano buffer. This could then be used to influence draw // 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. // operations further down the line, either in the same frame or a future frame.
#[rustfmt::skip] #[rustfmt::skip]
query_pool.get_results( self.query_pool.get_results(
0..3, 0..3,
&mut query_results, &mut self.query_results,
// Block the function call until the results are available. // Block the function call until the results are available.
// NOTE: If not all the queries have actually been executed, then this will // NOTE: If not all the queries have actually been executed, then this will
// wait forever for something that never happens! // wait forever for something that never happens!
@ -560,33 +618,42 @@ fn main() -> Result<(), impl Error> {
// Query 0 (red triangle) will always succeed, because the depth buffer starts // Query 0 (red triangle) will always succeed, because the depth buffer starts
// empty and will never occlude anything. // 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 // Query 1 (cyan triangle) will fail, because it's drawn completely behind the
// first. // 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. // 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( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport, memory_allocator: &Arc<StandardMemoryAllocator>,
memory_allocator: Arc<StandardMemoryAllocator>,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
let depth_attachment = ImageView::new_default( let depth_attachment = ImageView::new_default(
Image::new( Image::new(
memory_allocator, memory_allocator.clone(),
ImageCreateInfo { ImageCreateInfo {
image_type: ImageType::Dim2d, image_type: ImageType::Dim2d,
format: Format::D16_UNORM, format: Format::D16_UNORM,
@ -604,6 +671,7 @@ fn window_size_dependent_setup(
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -1,14 +1,14 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo,
}, },
descriptor_set::{layout::DescriptorSetLayoutCreateFlags, WriteDescriptorSet}, descriptor_set::{layout::DescriptorSetLayoutCreateFlags, WriteDescriptorSet},
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
format::Format, format::Format,
image::{ image::{
@ -40,16 +40,45 @@ use vulkano::{
DeviceSize, Validated, VulkanError, VulkanLibrary, DeviceSize, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
texture: Arc<ImageView>,
sampler: Arc<Sampler>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -75,7 +104,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -109,59 +138,23 @@ fn main() -> Result<(), impl Error> {
.unwrap(); .unwrap();
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
#[derive(BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32_SFLOAT)]
position: [f32; 2],
}
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.5, -0.5], position: [-0.5, -0.5],
}, },
Vertex { MyVertex {
position: [-0.5, 0.5], position: [-0.5, 0.5],
}, },
Vertex { MyVertex {
position: [0.5, -0.5], position: [0.5, -0.5],
}, },
Vertex { MyVertex {
position: [0.5, 0.5], position: [0.5, 0.5],
}, },
]; ];
@ -180,27 +173,6 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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( let mut uploads = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), command_buffer_allocator.clone(),
queue.queue_family_index(), queue.queue_family_index(),
@ -272,16 +244,91 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
@ -291,12 +338,13 @@ fn main() -> Result<(), impl Error> {
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages);
let set_layout = &mut layout_create_info.set_layouts[0]; let set_layout = &mut layout_create_info.set_layouts[0];
set_layout.flags |= DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR; 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( PipelineLayout::new(
device.clone(), self.device.clone(),
layout_create_info layout_create_info
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap() .unwrap()
@ -304,7 +352,7 @@ fn main() -> Result<(), impl Error> {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -331,85 +379,86 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.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 previous_frame_end = Some(sync::now(self.device.clone()).boxed());
let mut previous_frame_end = Some(
uploads
.end()
.unwrap()
.execute(queue.clone())
.unwrap()
.boxed(),
);
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); 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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -423,85 +472,99 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.push_descriptor_set( .push_descriptor_set(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), rcx.pipeline.layout().clone(),
0, 0,
[ [
// If the binding is an immutable sampler, using push descriptors // If the binding is an immutable sampler, using push descriptors
// you must write a dummy value to the binding. // you must write a dummy value to the binding.
WriteDescriptorSet::none(0), WriteDescriptorSet::none(0),
WriteDescriptorSet::image_view(1, texture.clone()), WriteDescriptorSet::image_view(1, self.texture.clone()),
] ]
.into_iter() .into_iter()
.collect(), .collect(),
) )
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -1,6 +1,6 @@
use std::{error::Error, io::Cursor, sync::Arc}; use std::{error::Error, io::Cursor, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo,
@ -11,7 +11,7 @@ use vulkano::{
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures,
QueueCreateInfo, QueueFlags, Queue, QueueCreateInfo, QueueFlags,
}, },
format::Format, format::Format,
image::{ image::{
@ -43,9 +43,10 @@ use vulkano::{
DeviceSize, Validated, VulkanError, VulkanLibrary, DeviceSize, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
@ -53,9 +54,40 @@ fn main() -> Result<(), impl Error> {
// example if you haven't done so yet. // example if you haven't done so yet.
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
vulkano_texture: Arc<ImageView>,
mascot_texture: Arc<ImageView>,
sampler: Arc<Sampler>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
descriptor_set: Arc<DescriptorSet>,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -80,7 +112,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -110,7 +142,7 @@ fn main() -> Result<(), impl Error> {
enabled_extensions: device_extensions, enabled_extensions: device_extensions,
enabled_features: DeviceFeatures { enabled_features: DeviceFeatures {
descriptor_indexing: true, descriptor_indexing: true,
shader_uniform_buffer_array_non_uniform_indexing: true, shader_sampled_image_array_non_uniform_indexing: true,
runtime_descriptor_array: true, runtime_descriptor_array: true,
descriptor_binding_variable_descriptor_count: true, descriptor_binding_variable_descriptor_count: true,
..DeviceFeatures::empty() ..DeviceFeatures::empty()
@ -119,111 +151,76 @@ fn main() -> Result<(), impl Error> {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
#[derive(BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32_SFLOAT)] let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
position: [f32; 2], device.clone(),
#[format(R32_UINT)] Default::default(),
tex_i: u32, ));
#[format(R32G32_SFLOAT)]
coords: [f32; 2],
}
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.1, -0.9], position: [-0.1, -0.9],
tex_i: 0, tex_i: 0,
coords: [1.0, 0.0], coords: [1.0, 0.0],
}, },
Vertex { MyVertex {
position: [-0.9, -0.9], position: [-0.9, -0.9],
tex_i: 0, tex_i: 0,
coords: [0.0, 0.0], coords: [0.0, 0.0],
}, },
Vertex { MyVertex {
position: [-0.9, -0.1], position: [-0.9, -0.1],
tex_i: 0, tex_i: 0,
coords: [0.0, 1.0], coords: [0.0, 1.0],
}, },
Vertex { MyVertex {
position: [-0.1, -0.9], position: [-0.1, -0.9],
tex_i: 0, tex_i: 0,
coords: [1.0, 0.0], coords: [1.0, 0.0],
}, },
Vertex { MyVertex {
position: [-0.9, -0.1], position: [-0.9, -0.1],
tex_i: 0, tex_i: 0,
coords: [0.0, 1.0], coords: [0.0, 1.0],
}, },
Vertex { MyVertex {
position: [-0.1, -0.1], position: [-0.1, -0.1],
tex_i: 0, tex_i: 0,
coords: [1.0, 1.0], coords: [1.0, 1.0],
}, },
Vertex { MyVertex {
position: [0.9, -0.9], position: [0.9, -0.9],
tex_i: 1, tex_i: 1,
coords: [1.0, 0.0], coords: [1.0, 0.0],
}, },
Vertex { MyVertex {
position: [0.1, -0.9], position: [0.1, -0.9],
tex_i: 1, tex_i: 1,
coords: [0.0, 0.0], coords: [0.0, 0.0],
}, },
Vertex { MyVertex {
position: [0.1, -0.1], position: [0.1, -0.1],
tex_i: 1, tex_i: 1,
coords: [0.0, 1.0], coords: [0.0, 1.0],
}, },
Vertex { MyVertex {
position: [0.9, -0.9], position: [0.9, -0.9],
tex_i: 1, tex_i: 1,
coords: [1.0, 0.0], coords: [1.0, 0.0],
}, },
Vertex { MyVertex {
position: [0.1, -0.1], position: [0.1, -0.1],
tex_i: 1, tex_i: 1,
coords: [0.0, 1.0], coords: [0.0, 1.0],
}, },
Vertex { MyVertex {
position: [0.9, -0.1], position: [0.9, -0.1],
tex_i: 1, tex_i: 1,
coords: [1.0, 1.0], coords: [1.0, 1.0],
@ -244,32 +241,6 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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( let mut uploads = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), command_buffer_allocator.clone(),
queue.queue_family_index(), queue.queue_family_index(),
@ -391,16 +362,93 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
@ -418,9 +466,9 @@ fn main() -> Result<(), impl Error> {
binding.descriptor_count = 2; binding.descriptor_count = 2;
PipelineLayout::new( PipelineLayout::new(
device.clone(), self.device.clone(),
layout_create_info layout_create_info
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap() .unwrap()
@ -428,7 +476,7 @@ fn main() -> Result<(), impl Error> {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -452,98 +500,107 @@ fn main() -> Result<(), impl Error> {
.unwrap() .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 layout = &pipeline.layout().set_layouts()[0];
let set = DescriptorSet::new_variable( let descriptor_set = DescriptorSet::new_variable(
descriptor_set_allocator, self.descriptor_set_allocator.clone(),
layout.clone(), layout.clone(),
2, 2,
[ [
WriteDescriptorSet::sampler(0, sampler), WriteDescriptorSet::sampler(0, self.sampler.clone()),
WriteDescriptorSet::image_view_array(1, 0, [mascot_texture as _, vulkano_texture as _]), WriteDescriptorSet::image_view_array(
1,
0,
[
self.mascot_texture.clone() as _,
self.vulkano_texture.clone() as _,
],
),
], ],
[], [],
) )
.unwrap(); .unwrap();
let mut viewport = Viewport { let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
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; self.rcx = Some(RenderContext {
let mut previous_frame_end = Some( window,
uploads swapchain,
.end() render_pass,
.unwrap() framebuffers,
.execute(queue.clone()) pipeline,
.unwrap() viewport,
.boxed(), descriptor_set,
); recreate_swapchain: false,
previous_frame_end,
});
}
event_loop.run(move |event, elwt| { fn window_event(
elwt.set_control_flow(ControlFlow::Poll); &mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -557,78 +614,96 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), rcx.pipeline.layout().clone(),
0, 0,
set.clone(), rcx.descriptor_set.clone(),
) )
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -14,14 +14,14 @@
use std::{error::Error, fs::File, io::Read, path::Path, sync::Arc}; use std::{error::Error, fs::File, io::Read, path::Path, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -48,16 +48,43 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -82,7 +109,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -114,29 +141,84 @@ fn main() -> Result<(), impl Error> {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
device.clone(),
Default::default(),
));
let (mut swapchain, images) = { let vertices = [
let surface_capabilities = device 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() .physical_device()
.surface_capabilities(&surface, Default::default()) .surface_capabilities(&surface, Default::default())
.unwrap(); .unwrap();
let image_format = device let (image_format, _) = self
.device
.physical_device() .physical_device()
.surface_formats(&surface, Default::default()) .surface_formats(&surface, Default::default())
.unwrap()[0] .unwrap()[0];
.0;
Swapchain::new( Swapchain::new(
device.clone(), self.device.clone(),
surface, surface,
SwapchainCreateInfo { SwapchainCreateInfo {
min_image_count: surface_capabilities.min_image_count.max(2), min_image_count: surface_capabilities.min_image_count.max(2),
image_format, image_format,
image_extent: window.inner_size().into(), image_extent: window_size.into(),
image_usage: ImageUsage::COLOR_ATTACHMENT, image_usage: ImageUsage::COLOR_ATTACHMENT,
composite_alpha: surface_capabilities composite_alpha: surface_capabilities
.supported_composite_alpha .supported_composite_alpha
@ -150,7 +232,7 @@ fn main() -> Result<(), impl Error> {
}; };
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), self.device.clone(),
attachments: { attachments: {
color: { color: {
format: swapchain.image_format(), format: swapchain.image_format(),
@ -166,14 +248,17 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
let graphics_pipeline = { let framebuffers = window_size_dependent_setup(&images, &render_pass);
let pipeline = {
let vs = { let vs = {
let code = read_spirv_words_from_file("vert.spv"); let code = read_spirv_words_from_file("vert.spv");
// Create a ShaderModule on a device the same Shader::load does it. // Create a ShaderModule on a device the same Shader::load does it.
// NOTE: You will have to verify correctness of the data by yourself! // NOTE: You will have to verify correctness of the data by yourself!
let module = unsafe { 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() module.entry_point("main").unwrap()
}; };
@ -182,27 +267,28 @@ fn main() -> Result<(), impl Error> {
let code = read_spirv_words_from_file("frag.spv"); let code = read_spirv_words_from_file("frag.spv");
let module = unsafe { 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() 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 = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -227,128 +313,86 @@ fn main() -> Result<(), impl Error> {
.unwrap() .unwrap()
}; };
let mut recreate_swapchain = false; let viewport = Viewport {
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 {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.0, 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( let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
device.clone(),
Default::default(),
));
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); 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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::MultipleSubmit, usage: CommandBufferUsage::MultipleSubmit,
@ -362,71 +406,87 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(graphics_pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -5,7 +5,7 @@
use std::{error::Error, sync::Arc, time::SystemTime}; use std::{error::Error, sync::Arc, time::SystemTime};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, CopyBufferInfo, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, CopyBufferInfo, RecordingCommandBuffer, RenderPassBeginInfo,
@ -14,8 +14,8 @@ use vulkano::{
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, ImageUsage}, image::{view::ImageView, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -32,7 +32,7 @@ use vulkano::{
GraphicsPipelineCreateInfo, GraphicsPipelineCreateInfo,
}, },
layout::PipelineDescriptorSetLayoutCreateInfo, layout::PipelineDescriptorSetLayoutCreateInfo,
ComputePipeline, GraphicsPipeline, PipelineBindPoint, PipelineLayout, ComputePipeline, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout,
PipelineShaderStageCreateInfo, PipelineShaderStageCreateInfo,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, Subpass},
@ -40,13 +40,15 @@ use vulkano::{
acquire_next_image, PresentMode, Surface, Swapchain, SwapchainCreateInfo, acquire_next_image, PresentMode, Surface, Swapchain, SwapchainCreateInfo,
SwapchainPresentInfo, SwapchainPresentInfo,
}, },
sync::{self, future::FenceSignalFuture, GpuFuture}, sync::{self, GpuFuture},
Validated, VulkanLibrary, DeviceSize, Validated, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, dpi::PhysicalSize,
window::WindowBuilder, event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
const WINDOW_WIDTH: u32 = 800; const WINDOW_WIDTH: u32 = 800;
@ -55,12 +57,40 @@ const WINDOW_HEIGHT: u32 = 600;
const PARTICLE_COUNT: usize = 100_000; const PARTICLE_COUNT: usize = 100_000;
fn main() -> Result<(), impl Error> { 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. // commentation is provided.
let event_loop = EventLoop::new().unwrap();
let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
compute_pipeline: Arc<ComputePipeline>,
descriptor_set: Arc<DescriptorSet>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
previous_frame_end: Option<Box<dyn GpuFuture>>,
start_time: SystemTime,
last_frame_time: SystemTime,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -85,7 +115,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -117,33 +147,175 @@ fn main() -> Result<(), impl Error> {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().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,
..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::<MyVertex>(
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 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();
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();
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( let window = Arc::new(
WindowBuilder::new() event_loop
.create_window(
Window::default_attributes()
// For simplicity, we are going to assert that the window size is static. // For simplicity, we are going to assert that the window size is static.
.with_resizable(false) .with_resizable(false)
.with_title("simple particles") .with_title("simple particles")
.with_inner_size(winit::dpi::PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)) .with_inner_size(PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)),
.build(&event_loop) )
.unwrap(), .unwrap(),
); );
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
let (swapchain, images) = { let (swapchain, images) = {
let surface_capabilities = device let surface_capabilities = self
.device
.physical_device() .physical_device()
.surface_capabilities(&surface, Default::default()) .surface_capabilities(&surface, Default::default())
.unwrap(); .unwrap();
let (image_format, _) = self
let image_format = device .device
.physical_device() .physical_device()
.surface_formats(&surface, Default::default()) .surface_formats(&surface, Default::default())
.unwrap()[0] .unwrap()[0];
.0;
Swapchain::new( Swapchain::new(
device.clone(), self.device.clone(),
surface, surface,
SwapchainCreateInfo { SwapchainCreateInfo {
min_image_count: surface_capabilities.min_image_count.max(2), min_image_count: surface_capabilities.min_image_count.max(2),
@ -163,7 +335,7 @@ fn main() -> Result<(), impl Error> {
}; };
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), self.device.clone(),
attachments: { attachments: {
color: { color: {
format: swapchain.image_format(), format: swapchain.image_format(),
@ -179,7 +351,7 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
let framebuffers: Vec<Arc<Framebuffer>> = images let framebuffers = images
.into_iter() .into_iter()
.map(|img| { .map(|img| {
let view = ImageView::new_default(img).unwrap(); let view = ImageView::new_default(img).unwrap();
@ -194,8 +366,282 @@ fn main() -> Result<(), impl Error> {
}) })
.collect(); .collect();
// Compute shader for updating the position and velocity of each particle every frame. // The vertex shader determines color and is run once per particle. The vertices will be
mod cs { // 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 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, 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,
});
}
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::RedrawRequested => {
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();
// 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();
rcx.last_frame_time = now;
// Create push constants to be passed to compute shader.
let push_constants = cs::PushConstants {
attractor: [0.75 * (3. * time).cos(), 0.6 * (0.75 * time).sin()],
attractor_strength: 1.2 * (2. * time).cos(),
delta_time,
};
// Acquire information on the next swapchain target.
let (image_index, suboptimal, acquire_future) = match acquire_next_image(
rcx.swapchain.clone(),
None, // timeout
) {
Ok(tuple) => tuple,
Err(e) => panic!("failed to acquire next image: {e}"),
};
// Since we disallow resizing, assert that the swapchain and surface are
// optimally configured.
assert!(
!suboptimal,
"not handling sub-optimal swapchains in this sample code",
);
let mut builder = RecordingCommandBuffer::new(
self.command_buffer_allocator.clone(),
self.queue.queue_family_index(),
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit,
..Default::default()
},
)
.unwrap();
builder
// Push constants for compute shader.
.push_constants(self.compute_pipeline.layout().clone(), 0, push_constants)
.unwrap()
// Perform compute operation to update particle positions.
.bind_pipeline_compute(self.compute_pipeline.clone())
.unwrap()
.bind_descriptor_sets(
PipelineBindPoint::Compute,
self.compute_pipeline.layout().clone(),
0, // Bind this descriptor set to index 0.
self.descriptor_set.clone(),
)
.unwrap();
unsafe {
builder
.dispatch([PARTICLE_COUNT as u32 / 128, 1, 1])
.unwrap();
}
// Use render-pass to draw particles to swapchain.
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0., 0., 0., 1.].into())],
..RenderPassBeginInfo::framebuffer(
rcx.framebuffers[image_index as usize].clone(),
)
},
Default::default(),
)
.unwrap()
.bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap();
unsafe {
builder.draw(PARTICLE_COUNT as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = rcx
.previous_frame_end
.take()
.unwrap()
.join(acquire_future)
.then_execute(self.queue.clone(), command_buffer)
.unwrap()
.then_swapchain_present(
self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
)
.then_signal_fence_and_flush();
rcx.previous_frame_end = match future.map_err(Validated::unwrap) {
// Success, store result into vector.
Ok(future) => Some(future.boxed()),
// Unknown failure.
Err(e) => panic!("failed to flush future: {e}"),
};
}
_ => {}
}
}
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! { vulkano_shaders::shader! {
ty: "compute", ty: "compute",
src: r" src: r"
@ -265,397 +711,4 @@ fn main() -> Result<(), impl Error> {
} }
", ",
} }
}
// 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 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(),
));
#[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 {
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();
// Create a buffer in device-local memory with enough space for `PARTICLE_COUNT` number of
// `Vertex`.
let device_local_buffer = Buffer::new_slice::<Vertex>(
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 vulkano::DeviceSize,
)
.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,
..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 */)
.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())
.expect("failed to create descriptor set layouts"),
)
.expect("failed to create pipeline layout");
ComputePipeline::new(
device.clone(),
None,
ComputePipelineCreateInfo::stage_layout(stage, layout),
)
.expect("failed to create compute shader")
};
// 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.
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();
// Create a basic graphics pipeline for rendering particles.
let graphics_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 = Vertex::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, 0).unwrap();
GraphicsPipeline::new(
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 mut fences: Vec<Option<FenceSignalFuture<_>>> =
(0..framebuffers.len()).map(|_| None).collect();
let mut previous_fence_index = 0u32;
let start_time = SystemTime::now();
let mut last_frame_time = start_time;
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::RedrawRequested,
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) {
return;
}
// 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;
// Create push constants to be passed to compute shader.
let push_constants = cs::PushConstants {
attractor: [0.75 * (3. * time).cos(), 0.6 * (0.75 * time).sin()],
attractor_strength: 1.2 * (2. * time).cos(),
delta_time,
};
// Acquire information on the next swapchain target.
let (image_index, suboptimal, acquire_future) = match acquire_next_image(
swapchain.clone(),
None, // timeout
) {
Ok(tuple) => tuple,
Err(e) => panic!("failed to acquire next image: {e}"),
};
// Since we disallow resizing, assert that the swapchain and surface are optimally
// configured.
assert!(
!suboptimal,
"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(),
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit,
..Default::default()
},
)
.unwrap();
builder
// Push constants for compute shader.
.push_constants(compute_pipeline.layout().clone(), 0, push_constants)
.unwrap()
// Perform compute operation to update particle positions.
.bind_pipeline_compute(compute_pipeline.clone())
.unwrap()
.bind_descriptor_sets(
PipelineBindPoint::Compute,
compute_pipeline.layout().clone(),
0, // Bind this descriptor set to index 0.
descriptor_set.clone(),
)
.unwrap();
unsafe {
builder
.dispatch([PARTICLE_COUNT as u32 / 128, 1, 1])
.unwrap();
}
// Use render-pass to draw particles to swapchain.
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0., 0., 0., 1.].into())],
..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(),
)
},
Default::default(),
)
.unwrap()
.bind_pipeline_graphics(graphics_pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap();
unsafe {
builder.draw(PARTICLE_COUNT as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_future
.join(acquire_future)
.then_execute(queue.clone(), command_buffer)
.unwrap()
.then_swapchain_present(
queue.clone(),
SwapchainPresentInfo::swapchain_image_index(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) {
// Success, store result into vector.
Ok(future) => Some(future),
// Unknown failure.
Err(e) => panic!("failed to flush future: {e}"),
};
previous_fence_index = image_index;
}
Event::AboutToWait => window.request_redraw(),
_ => (),
}
})
} }

View File

@ -78,6 +78,7 @@ fn main() {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
mod cs { mod cs {
@ -126,6 +127,7 @@ fn main() {
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
ComputePipeline::new( ComputePipeline::new(
device.clone(), device.clone(),
None, None,
@ -160,7 +162,7 @@ fn main() {
.unwrap(); .unwrap();
let layout = &pipeline.layout().set_layouts()[0]; let layout = &pipeline.layout().set_layouts()[0];
let set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
descriptor_set_allocator, descriptor_set_allocator,
layout.clone(), layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())], [WriteDescriptorSet::buffer(0, data_buffer.clone())],
@ -186,7 +188,7 @@ fn main() {
PipelineBindPoint::Compute, PipelineBindPoint::Compute,
pipeline.layout().clone(), pipeline.layout().clone(),
0, 0,
set, descriptor_set,
) )
.unwrap(); .unwrap();

View File

@ -7,7 +7,7 @@ use std::{error::Error, sync::Arc, time::Instant};
use vulkano::{ use vulkano::{
buffer::{ buffer::{
allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
Buffer, BufferCreateInfo, BufferUsage, Buffer, BufferCreateInfo, BufferUsage, Subbuffer,
}, },
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
@ -18,7 +18,7 @@ use vulkano::{
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceOwned, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceOwned,
QueueCreateInfo, QueueFlags, Queue, QueueCreateInfo, QueueFlags,
}, },
format::Format, format::Format,
image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage}, image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage},
@ -48,9 +48,11 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::EventLoop, dpi::PhysicalSize,
window::WindowBuilder, event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
mod model; mod model;
@ -60,9 +62,42 @@ fn main() -> Result<(), impl Error> {
// example if you haven't done so yet. // example if you haven't done so yet.
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
memory_allocator: Arc<StandardMemoryAllocator>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[Position]>,
normals_buffer: Subbuffer<[Normal]>,
index_buffer: Subbuffer<[u16]>,
uniform_buffer_allocator: SubbufferAllocator,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
vs: EntryPoint,
fs: EntryPoint,
pipeline: Arc<GraphicsPipeline>,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
rotation_start: Instant,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -87,7 +122,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -122,40 +157,15 @@ fn main() -> Result<(), impl Error> {
let queue = queues.next().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())); 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( let vertex_buffer = Buffer::from_iter(
memory_allocator.clone(), memory_allocator.clone(),
@ -200,7 +210,7 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
let uniform_buffer = SubbufferAllocator::new( let uniform_buffer_allocator = SubbufferAllocator::new(
memory_allocator.clone(), memory_allocator.clone(),
SubbufferAllocatorCreateInfo { SubbufferAllocatorCreateInfo {
buffer_usage: BufferUsage::UNIFORM_BUFFER, buffer_usage: BufferUsage::UNIFORM_BUFFER,
@ -210,8 +220,65 @@ 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!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), self.device.clone(),
attachments: { attachments: {
color: { color: {
format: swapchain.image_format(), format: swapchain.image_format(),
@ -233,93 +300,97 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let (mut pipeline, mut framebuffers) = window_size_dependent_setup( let (framebuffers, pipeline) = window_size_dependent_setup(
memory_allocator.clone(), window_size,
vs.clone(),
fs.clone(),
&images, &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 rotation_start = Instant::now();
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( self.rcx = Some(RenderContext {
device.clone(), window,
Default::default(), swapchain,
)); render_pass,
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( framebuffers,
device.clone(), vs,
Default::default(), fs,
)); pipeline,
recreate_swapchain: false,
previous_frame_end,
rotation_start,
});
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
event_loop.run(move |event, elwt| {
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
let (new_pipeline, new_framebuffers) = window_size_dependent_setup( (rcx.framebuffers, rcx.pipeline) = window_size_dependent_setup(
memory_allocator.clone(), window_size,
vs.clone(),
fs.clone(),
&new_images, &new_images,
render_pass.clone(), &rcx.render_pass,
&self.memory_allocator,
&rcx.vs,
&rcx.fs,
); );
pipeline = new_pipeline; rcx.recreate_swapchain = false;
framebuffers = new_framebuffers;
recreate_swapchain = false;
} }
let uniform_buffer_subbuffer = { let uniform_buffer = {
let elapsed = rotation_start.elapsed(); let elapsed = rcx.rotation_start.elapsed();
let rotation = let rotation =
elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0;
let rotation = Mat3::from_rotation_y(rotation as f32); let rotation = Mat3::from_rotation_y(rotation as f32);
// NOTE: This teapot was meant for OpenGL where the origin is at the lower left // 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. // instead the origin is at the upper left in Vulkan, so we reverse the Y axis.
let aspect_ratio = let aspect_ratio = rcx.swapchain.image_extent()[0] as f32
swapchain.image_extent()[0] as f32 / swapchain.image_extent()[1] as f32; / rcx.swapchain.image_extent()[1] as f32;
let proj = Mat4::perspective_rh_gl( let proj = Mat4::perspective_rh_gl(
std::f32::consts::FRAC_PI_2, std::f32::consts::FRAC_PI_2,
@ -340,38 +411,42 @@ fn main() -> Result<(), impl Error> {
proj: proj.to_cols_array_2d(), proj: proj.to_cols_array_2d(),
}; };
let subbuffer = uniform_buffer.allocate_sized().unwrap(); let buffer = self.uniform_buffer_allocator.allocate_sized().unwrap();
*subbuffer.write().unwrap() = uniform_data; *buffer.write().unwrap() = uniform_data;
subbuffer buffer
}; };
let layout = &pipeline.layout().set_layouts()[0]; let layout = &rcx.pipeline.layout().set_layouts()[0];
let set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
descriptor_set_allocator.clone(), self.descriptor_set_allocator.clone(),
layout.clone(), layout.clone(),
[WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)], [WriteDescriptorSet::buffer(0, uniform_buffer)],
[], [],
) )
.unwrap(); .unwrap();
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -388,81 +463,92 @@ fn main() -> Result<(), impl Error> {
Some(1f32.into()), Some(1f32.into()),
], ],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), rcx.pipeline.layout().clone(),
0, 0,
set, descriptor_set,
) )
.unwrap() .unwrap()
.bind_vertex_buffers(0, (vertex_buffer.clone(), normals_buffer.clone())) .bind_vertex_buffers(
0,
(self.vertex_buffer.clone(), self.normals_buffer.clone()),
)
.unwrap() .unwrap()
.bind_index_buffer(index_buffer.clone()) .bind_index_buffer(self.index_buffer.clone())
.unwrap(); .unwrap();
unsafe { unsafe {
builder 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(); .unwrap();
} }
builder.end_render_pass(Default::default()).unwrap(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
memory_allocator: Arc<StandardMemoryAllocator>, window_size: PhysicalSize<u32>,
vs: EntryPoint,
fs: EntryPoint,
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
) -> (Arc<GraphicsPipeline>, Vec<Arc<Framebuffer>>) { memory_allocator: &Arc<StandardMemoryAllocator>,
let device = memory_allocator.device().clone(); vs: &EntryPoint,
let extent = images[0].extent(); fs: &EntryPoint,
) -> (Vec<Arc<Framebuffer>>, Arc<GraphicsPipeline>) {
let device = memory_allocator.device();
let depth_buffer = ImageView::new_default( let depth_buffer = ImageView::new_default(
Image::new( Image::new(
memory_allocator, memory_allocator.clone(),
ImageCreateInfo { ImageCreateInfo {
image_type: ImageType::Dim2d, image_type: ImageType::Dim2d,
format: Format::D16_UNORM, format: Format::D16_UNORM,
@ -480,6 +566,7 @@ fn window_size_dependent_setup(
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {
@ -497,11 +584,11 @@ fn window_size_dependent_setup(
// https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport // https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport
let pipeline = { let pipeline = {
let vertex_input_state = [Position::per_vertex(), Normal::per_vertex()] let vertex_input_state = [Position::per_vertex(), Normal::per_vertex()]
.definition(&vs) .definition(vs)
.unwrap(); .unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs.clone()),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs.clone()),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), device.clone(),
@ -510,10 +597,10 @@ fn window_size_dependent_setup(
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass, 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device, device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -522,7 +609,7 @@ fn window_size_dependent_setup(
viewport_state: Some(ViewportState { viewport_state: Some(ViewportState {
viewports: [Viewport { viewports: [Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [extent[0] as f32, extent[1] as f32], extent: window_size.into(),
depth_range: 0.0..=1.0, depth_range: 0.0..=1.0,
}] }]
.into_iter() .into_iter()
@ -546,7 +633,7 @@ fn window_size_dependent_setup(
.unwrap() .unwrap()
}; };
(pipeline, framebuffers) (framebuffers, pipeline)
} }
mod vs { mod vs {

View File

@ -14,14 +14,14 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures,
QueueCreateInfo, QueueFlags, Queue, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -48,11 +48,482 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, 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<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
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,
})
.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()
},
)
.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.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];
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(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 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,
});
}
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 => {
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");
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(
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;
}
let mut builder = RecordingCommandBuffer::new(
self.command_buffer_allocator.clone(),
self.queue.queue_family_index(),
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit,
..Default::default()
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer(
rcx.framebuffers[image_index as usize].clone(),
)
},
Default::default(),
)
.unwrap()
.set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap()
.bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap();
unsafe {
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 = rcx
.previous_frame_end
.take()
.unwrap()
.join(acquire_future)
.then_execute(self.queue.clone(), command_buffer)
.unwrap()
.then_swapchain_present(
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) => {
rcx.previous_frame_end = Some(future.boxed());
}
Err(VulkanError::OutOfDate) => {
rcx.recreate_swapchain = true;
rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
}
Err(e) => {
println!("failed to flush future: {e}");
rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
}
}
}
_ => {}
}
}
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<Image>],
render_pass: &Arc<RenderPass>,
) -> Vec<Arc<Framebuffer>> {
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::<Vec<_>>()
}
mod vs { mod vs {
vulkano_shaders::shader! { vulkano_shaders::shader! {
ty: "vertex", ty: "vertex",
@ -119,15 +590,15 @@ mod tes {
void main(void) { void main(void) {
// Retrieve the vertex positions set by the TCS. // Retrieve the vertex positions set by the TCS.
vec4 vert_x = gl_in[0].gl_Position; vec4 v1 = gl_in[0].gl_Position;
vec4 vert_y = gl_in[1].gl_Position; vec4 v2 = gl_in[1].gl_Position;
vec4 vert_z = gl_in[2].gl_Position; vec4 v3 = gl_in[2].gl_Position;
// Convert `gl_TessCoord` from Barycentric coordinates to Cartesian coordinates. // Convert `gl_TessCoord` from Barycentric coordinates to Cartesian coordinates.
gl_Position = vec4( gl_Position = vec4(
gl_TessCoord.x * vert_x.x + gl_TessCoord.y * vert_y.x + gl_TessCoord.z * vert_z.x, gl_TessCoord.x * v1.x + gl_TessCoord.y * v2.x + gl_TessCoord.z * v3.x,
gl_TessCoord.x * vert_x.y + gl_TessCoord.y * vert_y.y + gl_TessCoord.z * vert_z.y, gl_TessCoord.x * v1.y + gl_TessCoord.y * v2.y + gl_TessCoord.z * v3.y,
gl_TessCoord.x * vert_x.z + gl_TessCoord.y * vert_y.z + gl_TessCoord.z * vert_z.z, gl_TessCoord.x * v1.z + gl_TessCoord.y * v2.z + gl_TessCoord.z * v3.z,
1.0 1.0
); );
} }
@ -149,410 +620,3 @@ mod fs {
", ",
} }
} }
fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().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: required_extensions,
..Default::default()
},
)
.unwrap();
let device_extensions = DeviceExtensions {
khr_swapchain: true,
..DeviceExtensions::empty()
};
let 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(&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: 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 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],
},
Vertex {
position: [0.9, 0.9],
},
Vertex {
position: [0.9, 0.8],
},
Vertex {
position: [0.8, 0.8],
},
Vertex {
position: [-0.9, 0.9],
},
Vertex {
position: [-0.7, 0.6],
},
Vertex {
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();
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())
.unwrap()
.entry_point("main")
.unwrap();
let tcs = tcs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let tes = tes::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap();
let stages = [
PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(tcs),
PipelineShaderStageCreateInfo::new(tes),
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::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 mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
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 command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
device.clone(),
Default::default(),
));
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] = window.inner_size().into();
if image_extent.contains(&0) {
return;
}
previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain {
let (new_swapchain, new_images) = swapchain
.recreate(SwapchainCreateInfo {
image_extent,
..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;
}
let (image_index, suboptimal, acquire_future) =
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) {
Ok(r) => r,
Err(VulkanError::OutOfDate) => {
recreate_swapchain = true;
return;
}
Err(e) => panic!("failed to acquire next image: {e}"),
};
if suboptimal {
recreate_swapchain = true;
}
let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(),
queue.queue_family_index(),
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit,
..Default::default()
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(),
)
},
Default::default(),
)
.unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect())
.unwrap()
.bind_pipeline_graphics(pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap();
unsafe {
builder.draw(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)
.then_execute(queue.clone(), command_buffer)
.unwrap()
.then_swapchain_present(
queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index),
)
.then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) {
Ok(future) => {
previous_frame_end = Some(future.boxed());
}
Err(VulkanError::OutOfDate) => {
recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("failed to flush future: {e}");
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}
}
Event::AboutToWait => window.request_redraw(),
_ => (),
}
})
}
/// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup(
images: &[Arc<Image>],
render_pass: Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> {
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 {
attachments: vec![view],
..Default::default()
},
)
.unwrap()
})
.collect::<Vec<_>>()
}

View File

@ -1,6 +1,6 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer, RenderPassBeginInfo,
@ -9,8 +9,8 @@ use vulkano::{
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
format::Format, format::Format,
image::{ image::{
@ -42,9 +42,10 @@ use vulkano::{
DeviceSize, Validated, VulkanError, VulkanLibrary, DeviceSize, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
@ -54,9 +55,39 @@ fn main() -> Result<(), impl Error> {
// uniform sampler2D array_of_textures[42]; // uniform sampler2D array_of_textures[42];
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
texture: Arc<ImageView>,
sampler: Arc<Sampler>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
descriptor_set: Arc<DescriptorSet>,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); 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( let instance = Instance::new(
library, library,
InstanceCreateInfo { InstanceCreateInfo {
@ -81,7 +112,7 @@ fn main() -> Result<(), impl Error> {
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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)) .map(|i| (p, i as u32))
}) })
@ -113,61 +144,30 @@ fn main() -> Result<(), impl Error> {
}, },
) )
.unwrap(); .unwrap();
let queue = queues.next().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())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
#[derive(BufferContents, Vertex)] device.clone(),
#[repr(C)] Default::default(),
struct Vertex { ));
#[format(R32G32_SFLOAT)] let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
position: [f32; 2], device.clone(),
} Default::default(),
));
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.2, -0.5], position: [-0.2, -0.5],
}, },
Vertex { MyVertex {
position: [-0.5, 0.8], position: [-0.5, 0.8],
}, },
Vertex { MyVertex {
position: [0.4, -0.5], position: [0.4, -0.5],
}, },
Vertex { MyVertex {
position: [0.5, 0.2], position: [0.5, 0.2],
}, },
]; ];
@ -186,32 +186,6 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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( let mut uploads = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), command_buffer_allocator.clone(),
queue.queue_family_index(), queue.queue_family_index(),
@ -290,33 +264,110 @@ fn main() -> Result<(), impl Error> {
ImageView::new_default(image).unwrap() ImageView::new_default(image).unwrap()
}; };
let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap(); 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 pipeline = {
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap(); let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
let stages = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
@ -343,97 +394,99 @@ fn main() -> Result<(), impl Error> {
.unwrap() .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 layout = &pipeline.layout().set_layouts()[0];
let set = DescriptorSet::new( let descriptor_set = DescriptorSet::new(
descriptor_set_allocator, self.descriptor_set_allocator.clone(),
layout.clone(), layout.clone(),
[ [
WriteDescriptorSet::sampler(0, sampler), WriteDescriptorSet::sampler(0, self.sampler.clone()),
WriteDescriptorSet::image_view(1, texture), WriteDescriptorSet::image_view(1, self.texture.clone()),
], ],
[], [],
) )
.unwrap(); .unwrap();
let mut viewport = Viewport { let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
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; self.rcx = Some(RenderContext {
let mut previous_frame_end = Some( window,
uploads swapchain,
.end() render_pass,
.unwrap() framebuffers,
.execute(queue.clone()) pipeline,
.unwrap() viewport,
.boxed(), descriptor_set,
); recreate_swapchain: false,
previous_frame_end,
});
}
event_loop.run(move |event, elwt| { fn window_event(
elwt.set_control_flow(ControlFlow::Poll); &mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let rcx = self.rcx.as_mut().unwrap();
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: WindowEvent::RedrawRequested, let window_size = rcx.window.inner_size();
..
} => {
let image_extent: [u32; 2] = window.inner_size().into();
if image_extent.contains(&0) { if window_size.width == 0 || window_size.height == 0 {
return; return;
} }
previous_frame_end.as_mut().unwrap().cleanup_finished(); rcx.previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images, rcx.viewport.extent = window_size.into();
render_pass.clone(), rcx.recreate_swapchain = false;
&mut viewport,
);
recreate_swapchain = false;
} }
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
}; };
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -447,78 +500,92 @@ fn main() -> Result<(), impl Error> {
RenderPassBeginInfo { RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
Default::default(), Default::default(),
) )
.unwrap() .unwrap()
.set_viewport(0, [viewport.clone()].into_iter().collect()) .set_viewport(0, [rcx.viewport.clone()].into_iter().collect())
.unwrap() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_descriptor_sets( .bind_descriptor_sets(
PipelineBindPoint::Graphics, PipelineBindPoint::Graphics,
pipeline.layout().clone(), rcx.pipeline.layout().clone(),
0, 0,
set.clone(), rcx.descriptor_set.clone(),
) )
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { 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(); builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -9,7 +9,7 @@
use std::{error::Error, sync::Arc, time::Duration}; use std::{error::Error, sync::Arc, time::Duration};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo,
@ -38,17 +38,40 @@ use vulkano_util::{
window::VulkanoWindows, window::VulkanoWindows,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::WindowId,
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let context = VulkanoContext::new(VulkanoConfig::default());
let event_loop = EventLoop::new().unwrap(); 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<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
rcx: Option<RenderContext>,
}
struct RenderContext {
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
}
impl App {
fn new(_event_loop: &EventLoop<()>) -> Self {
let context = VulkanoContext::new(VulkanoConfig::default());
// Manages any windows and their rendering. // Manages any windows and their rendering.
let mut windows_manager = VulkanoWindows::default(); let windows = VulkanoWindows::default();
windows_manager.create_window(&event_loop, &context, &Default::default(), |_| {});
let window_renderer = windows_manager.get_primary_renderer_mut().unwrap();
// Some little debug infos. // Some little debug infos.
println!( println!(
@ -57,24 +80,23 @@ fn main() -> Result<(), impl Error> {
context.device().physical_device().properties().device_type, 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 // Before we can start creating and recording command buffers, we need a way of allocating
// to force rustc to use a defined layout for our data, as the default representation has *no // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command
// guarantees*. // pools underneath and provides a safe interface for them.
#[derive(BufferContents, Vertex)] let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
#[repr(C)] context.device().clone(),
struct Vertex { Default::default(),
#[format(R32G32_SFLOAT)] ));
position: [f32; 2],
}
// We now create a buffer that will store the shape of our triangle.
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.5, -0.25], position: [-0.5, -0.25],
}, },
Vertex { MyVertex {
position: [0.0, 0.5], position: [0.0, 0.5],
}, },
Vertex { MyVertex {
position: [0.25, -0.1], position: [0.25, -0.1],
}, },
]; ];
@ -93,17 +115,39 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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 next step is to create the shaders.
// //
// The raw shader creation API provided by the vulkano library is unsafe for various reasons, // The raw shader creation API provided by the vulkano library is unsafe for various
// so The `shader!` macro provides a way to generate a Rust module from GLSL source - in the // reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL
// example below, the source is provided as a string input directly to the shader, but a path // source - in the example below, the source is provided as a string input directly to the
// to a source file can be provided as well. Note that the user must specify the type of shader // shader, but a path to a source file can be provided as well. Note that the user must
// (e.g. "vertex", "fragment", etc.) using the `ty` option of the macro. // 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 // The items generated by the `shader!` macro include a `load` function which loads the
// using an input logical device. The module also includes type definitions for layout // shader using an input logical device. The module also includes type definitions for
// structures defined in the shader source, for example uniforms and push constants. // 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 // 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/ // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/
@ -137,33 +181,29 @@ 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 // 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 // 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. // colors, depth and/or stencil information will be written.
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
context.device().clone(), self.context.device().clone(),
attachments: { attachments: {
// `color` is a custom name we give to the first and only attachment. // `color` is a custom name we give to the first and only attachment.
color: { color: {
// `format: <ty>` indicates the type of the format of the image. This has to be one // `format: <ty>` indicates the type of the format of the image. This has to be
// of the types of the `vulkano::format` module (or alternatively one of your // one of the types of the `vulkano::format` module (or alternatively one of
// structs that implements the `FormatDesc` trait). Here we use the same format as // your structs that implements the `FormatDesc` trait). Here we use the same
// the swapchain. // format as the swapchain.
format: window_renderer.swapchain_format(), format: window_renderer.swapchain_format(),
// `samples: 1` means that we ask the GPU to use one sample to determine the value // `samples: 1` means that we ask the GPU to use one sample to determine the
// of each pixel in the color attachment. We could use a larger value // 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 // (multisampling) for antialiasing. An example of this can be found in
// msaa-renderpass.rs. // msaa-renderpass.rs.
samples: 1, samples: 1,
// `load_op: Clear` means that we ask the GPU to clear the content of this // `load_op: Clear` means that we ask the GPU to clear the content of this
// attachment at the start of the drawing. // attachment at the start of the drawing.
load_op: Clear, load_op: Clear,
// `store_op: Store` means that we ask the GPU to store the output of the draw in // `store_op: Store` means that we ask the GPU to store the output of the draw
// the actual image. We could also ask it to discard the result. // in the actual image. We could also ask it to discard the result.
store_op: Store, store_op: Store,
}, },
}, },
@ -176,28 +216,36 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// Before we draw, we have to create what is called a **pipeline**. A pipeline describes how // The render pass we created above only describes the layout of our framebuffers. Before
// a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains // we can draw we also need to create the actual framebuffers.
// 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 // Since we need to draw to multiple images, we are going to create a different framebuffer
// one. // for each image.
let vs = vs::load(context.device().clone()) 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() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(context.device().clone()) let fs = fs::load(self.context.device().clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
// Automatically generate a vertex input state from the vertex shader's input interface, // Automatically generate a vertex input state from the vertex shader's input
// that takes a single vertex buffer containing `Vertex` structs. // 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. // Make a list of the shader stages that the pipeline will have.
let stages = [ let stages = [
@ -205,61 +253,61 @@ fn main() -> Result<(), impl Error> {
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
// We must now create a **pipeline layout** object, which describes the locations and types // We must now create a **pipeline layout** object, which describes the locations and
// of descriptor sets and push constants used by the shaders in the pipeline. // 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. // Multiple pipelines can share a common layout object, which is more efficient. The
// The shaders in a pipeline must use a subset of the resources described in its pipeline // 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 // layout, but the pipeline layout is allowed to contain resources that are not present
// the shaders; they can be used by shaders in other pipelines that share the same // 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 // layout. Thus, it is a good idea to design shaders so that many pipelines have common
// common resource locations, which allows them to share pipeline layouts. // resource locations, which allows them to share pipeline layouts.
let layout = PipelineLayout::new( 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, // 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 // we automatically generate the creation info for it from the resources used in
// shaders. In a real application, you would specify this information manually so that // the shaders. In a real application, you would specify this information manually
// you can re-use one layout in multiple pipelines. // so that you can re-use one layout in multiple pipelines.
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(context.device().clone()) .into_pipeline_layout_create_info(self.context.device().clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
// We have to indicate which subpass of which render pass this pipeline is going to be used // We have to indicate which subpass of which render pass this pipeline is going to be
// in. The pipeline will only be usable from this particular subpass. // used in. The pipeline will only be usable from this particular subpass.
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
// Finally, create the pipeline. // Finally, create the pipeline.
GraphicsPipeline::new( GraphicsPipeline::new(
context.device().clone(), self.context.device().clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
// How vertex data is read from the vertex buffers into the vertex shader. // How vertex data is read from the vertex buffers into the vertex shader.
vertex_input_state: Some(vertex_input_state), vertex_input_state: Some(vertex_input_state),
// How vertices are arranged into primitive shapes. // How vertices are arranged into primitive shapes. The default primitive shape
// The default primitive shape is a triangle. // is a triangle.
input_assembly_state: Some(InputAssemblyState::default()), input_assembly_state: Some(InputAssemblyState::default()),
// How primitives are transformed and clipped to fit the framebuffer. // How primitives are transformed and clipped to fit the framebuffer. We use a
// We use a resizable viewport, set to draw over the entire window. // resizable viewport, set to draw over the entire window.
viewport_state: Some(ViewportState::default()), viewport_state: Some(ViewportState::default()),
// How polygons are culled and converted into a raster of pixels. // How polygons are culled and converted into a raster of pixels. The default
// The default value does not perform any culling. // value does not perform any culling.
rasterization_state: Some(RasterizationState::default()), rasterization_state: Some(RasterizationState::default()),
// How multiple fragment shader samples are converted to a single pixel value. // How multiple fragment shader samples are converted to a single pixel value.
// The default value does not perform any multisampling. // The default value does not perform any multisampling.
multisample_state: Some(MultisampleState::default()), multisample_state: Some(MultisampleState::default()),
// How pixel values are combined with the values already present in the framebuffer. // How pixel values are combined with the values already present in the
// The default value overwrites the old value with the new one, without any // framebuffer. The default value overwrites the old value with the new one,
// blending. // without any blending.
color_blend_state: Some(ColorBlendState::with_attachment_states( color_blend_state: Some(ColorBlendState::with_attachment_states(
subpass.num_color_attachments(), subpass.num_color_attachments(),
ColorBlendAttachmentState::default(), ColorBlendAttachmentState::default(),
)), )),
// Dynamic states allows us to specify parts of the pipeline settings when // Dynamic states allows us to specify parts of the pipeline settings when
// recording the command buffer, before we perform drawing. // recording the command buffer, before we perform drawing. Here, we specify
// Here, we specify that the viewport should be dynamic. // that the viewport should be dynamic.
dynamic_state: [DynamicState::Viewport].into_iter().collect(), dynamic_state: [DynamicState::Viewport].into_iter().collect(),
subpass: Some(subpass.into()), subpass: Some(subpass.into()),
..GraphicsPipelineCreateInfo::layout(layout) ..GraphicsPipelineCreateInfo::layout(layout)
@ -270,62 +318,46 @@ fn main() -> Result<(), impl Error> {
// Dynamic viewports allow us to recreate just the viewport when the window is resized. // Dynamic viewports allow us to recreate just the viewport when the window is resized.
// Otherwise we would have to recreate the whole pipeline. // Otherwise we would have to recreate the whole pipeline.
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.0, depth_range: 0.0..=1.0,
}; };
// The render pass we created above only describes the layout of our framebuffers. Before we // In the `window_event` handler below we are going to submit commands to the GPU.
// can draw we also need to create the actual framebuffers. // 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.
// 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 self.rcx = Some(RenderContext {
// them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools render_pass,
// underneath and provides a safe interface for them. framebuffers,
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( pipeline,
context.device().clone(), viewport,
Default::default(), });
)); }
// Initialization is finally finished! fn window_event(
&mut self,
// In the loop below we are going to submit commands to the GPU. Submitting a command produces event_loop: &ActiveEventLoop,
// an object that implements the `GpuFuture` trait, which holds the resources for as long as _window_id: WindowId,
// they are in use by the GPU. event: WindowEvent,
) {
event_loop.run(move |event, elwt| { let window_renderer = self.windows.get_primary_renderer_mut().unwrap();
elwt.set_control_flow(ControlFlow::Poll); let rcx = self.rcx.as_mut().unwrap();
match event { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_),
..
} => {
window_renderer.resize(); window_renderer.resize();
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: 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 // Do not draw the frame when the screen size is zero. On Windows, this can
// occur when minimizing the application. // occur when minimizing the application.
let image_extent: [u32; 2] = window_renderer.window().inner_size().into(); if window_size.width == 0 || window_size.height == 0 {
if image_extent.contains(&0) {
return; return;
} }
@ -336,16 +368,13 @@ fn main() -> Result<(), impl Error> {
// on the window size. In this example that // on the window size. In this example that
// includes the swapchain, the framebuffers // includes the swapchain, the framebuffers
// and the dynamic state viewport. // and the dynamic state viewport.
framebuffers = window_size_dependent_setup( rcx.framebuffers =
swapchain_images, window_size_dependent_setup(swapchain_images, &rcx.render_pass);
render_pass.clone(),
&mut viewport,
);
}) })
.unwrap(); .unwrap();
// In order to draw, we have to record a *command buffer*. The command buffer object // In order to draw, we have to record a *command buffer*. The command buffer
// holds the list of commands that are going to be executed. // object holds the list of commands that are going to be executed.
// //
// Recording a command buffer is an expensive operation (usually a few hundred // 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 // microseconds), but it is known to be a hot path in the driver and is expected to
@ -354,8 +383,8 @@ fn main() -> Result<(), impl Error> {
// Note that we have to pass a queue family when we create the command buffer. The // 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. // command buffer will only be executable on that given queue family.
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
context.graphics_queue().queue_family_index(), self.context.graphics_queue().queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -377,13 +406,13 @@ fn main() -> Result<(), impl Error> {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[window_renderer.image_index() as usize].clone(), rcx.framebuffers[window_renderer.image_index() as usize].clone(),
) )
}, },
SubpassBeginInfo { SubpassBeginInfo {
// The contents of the first (and only) subpass. // The contents of the first (and only) subpass. This can be either
// This can be either `Inline` or `SecondaryCommandBuffers`. // `Inline` or `SecondaryCommandBuffers`. The latter is a bit more
// The latter is a bit more advanced and is not covered here. // advanced and is not covered here.
contents: SubpassContents::Inline, contents: SubpassContents::Inline,
..Default::default() ..Default::default()
}, },
@ -392,17 +421,17 @@ fn main() -> Result<(), impl Error> {
// We are now inside the first subpass of the render pass. // We are now inside the first subpass of the render pass.
// //
// TODO: Document state setting and how it affects subsequent draw commands. // 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() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { unsafe {
builder builder
// We add a draw command. // 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(); .unwrap();
} }
@ -416,35 +445,44 @@ fn main() -> Result<(), impl Error> {
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = previous_frame_end
.then_execute(context.graphics_queue().clone(), command_buffer) .then_execute(self.context.graphics_queue().clone(), command_buffer)
.unwrap() .unwrap()
.boxed(); .boxed();
// The color output is now expected to contain our triangle. But in order to // The color output is now expected to contain our triangle. But in order to show
// show it on the screen, we have to *present* the image by calling // it on the screen, we have to *present* the image by calling `present` on the
// `present` on the window renderer. // window renderer.
// //
// This function does not actually present the image immediately. Instead it // 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 // submits a present command at the end of the queue. This means that it will only
// only be presented once the GPU has finished executing the command buffer // be presented once the GPU has finished executing the command buffer that draws
// that draws the triangle. // the triangle.
window_renderer.present(future, false); 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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
swapchain_images: &[Arc<ImageView>], swapchain_images: &[Arc<ImageView>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = swapchain_images[0].image().extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
swapchain_images swapchain_images
.iter() .iter()
.map(|swapchain_image| { .map(|swapchain_image| {

View File

@ -14,14 +14,14 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderingAttachmentInfo, RenderingInfo, CommandBufferUsage, RecordingCommandBuffer, RenderingAttachmentInfo, RenderingInfo,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures,
QueueCreateInfo, QueueFlags, Queue, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -48,14 +48,40 @@ use vulkano::{
Validated, Version, VulkanError, VulkanLibrary, Validated, Version, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
attachment_image_views: Vec<Arc<ImageView>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); let library = VulkanLibrary::new().unwrap();
// The first step of any Vulkan program is to create an instance. // The first step of any Vulkan program is to create an instance.
@ -63,9 +89,9 @@ fn main() -> Result<(), impl Error> {
// When we create an instance, we have to pass a list of extensions that we want to enable. // 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 // 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 // enable manually. To do so, we ask `Surface` for the list of extensions required to draw
// a window. // 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. // Now creating the instance.
let instance = Instance::new( let instance = Instance::new(
@ -80,15 +106,16 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// Choose device extensions that we're going to use. In order to present images to a surface, // Choose device extensions that we're going to use. In order to present images to a
// we need a `Swapchain`, which is provided by the `khr_swapchain` extension. // surface, we need a `Swapchain`, which is provided by the `khr_swapchain` extension.
let mut device_extensions = DeviceExtensions { let mut device_extensions = DeviceExtensions {
khr_swapchain: true, khr_swapchain: true,
..DeviceExtensions::empty() ..DeviceExtensions::empty()
}; };
// We then choose which physical device to use. First, we enumerate all the available physical // We then choose which physical device to use. First, we enumerate all the available
// devices, then apply filters to narrow them down to those that can support our needs. // physical devices, then apply filters to narrow them down to those that can support our
// needs.
let (physical_device, queue_family_index) = instance let (physical_device, queue_family_index) = instance
.enumerate_physical_devices() .enumerate_physical_devices()
.unwrap() .unwrap()
@ -98,46 +125,48 @@ fn main() -> Result<(), impl Error> {
p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering
}) })
.filter(|p| { .filter(|p| {
// Some devices may not support the extensions or features that your application, or // Some devices may not support the extensions or features that your application,
// report properties and limits that are not sufficient for your application. These // or report properties and limits that are not sufficient for your application.
// should be filtered out here. // These should be filtered out here.
p.supported_extensions().contains(&device_extensions) p.supported_extensions().contains(&device_extensions)
}) })
.filter_map(|p| { .filter_map(|p| {
// For each physical device, we try to find a suitable queue family that will execute // For each physical device, we try to find a suitable queue family that will
// our draw commands. // execute our draw commands.
// //
// Devices can provide multiple queues to run commands in parallel (for example a draw // Devices can provide multiple queues to run commands in parallel (for example a
// queue and a compute queue), similar to CPU threads. This is something you have to // draw queue and a compute queue), similar to CPU threads. This is something you
// have to manage manually in Vulkan. Queues of the same type belong to the same queue // have to have to manage manually in Vulkan. Queues of the same type belong to the
// family. // same queue family.
// //
// Here, we look for a single queue family that is suitable for our purposes. In a // 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 // real-world application, you may want to use a separate dedicated transfer queue
// handle data transfers in parallel with graphics operations. You may also need a // to handle data transfers in parallel with graphics operations. You may also need
// separate queue for compute operations, if your application uses those. // a separate queue for compute operations, if your application uses those.
p.queue_family_properties() p.queue_family_properties()
.iter() .iter()
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
// We select a queue family that supports graphics operations. When drawing to // We select a queue family that supports graphics operations. When drawing
// a window surface, as we do in this example, we also need to check that // to a window surface, as we do in this example, we also need to check
// queues in this queue family are capable of presenting images to the surface. // that queues in this queue family are capable of presenting images to the
// surface.
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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 // The code here searches for the first queue family that is suitable. If none
// found, `None` is returned to `filter_map`, which disqualifies this physical // is found, `None` is returned to `filter_map`, which disqualifies this
// device. // physical device.
.map(|i| (p, i as u32)) .map(|i| (p, i as u32))
}) })
// All the physical devices that pass the filters above are suitable for the application. // All the physical devices that pass the filters above are suitable for the
// However, not every device is equal, some are preferred over others. Now, we assign each // application. However, not every device is equal, some are preferred over others.
// physical device a score, and pick the device with the lowest ("best") score. // 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 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" // In a real-world setting, you may want to use the best-scoring device only as a
// or "recommended" device, and let the user choose the device themself. // "default" or "recommended" device, and let the user choose the device themself.
.min_by_key(|(p, _)| { .min_by_key(|(p, _)| {
// We assign a lower score to device types that are likely to be faster/better. // We assign a lower score to device types that are likely to be faster/better.
match p.properties().device_type { match p.properties().device_type {
@ -160,9 +189,9 @@ fn main() -> Result<(), impl Error> {
// If the selected device doesn't have Vulkan 1.3 available, then we need to enable the // 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 // `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. // in version 1.3 and later, so it's always available then and it does not need to be
// We can be sure that this extension will be available on the selected physical device, // enabled. We can be sure that this extension will be available on the selected physical
// because we filtered out unsuitable devices in the device selection code above. // device, because we filtered out unsuitable devices in the device selection code above.
if physical_device.api_version() < Version::V1_3 { if physical_device.api_version() < Version::V1_3 {
device_extensions.khr_dynamic_rendering = true; device_extensions.khr_dynamic_rendering = true;
} }
@ -174,25 +203,25 @@ fn main() -> Result<(), impl Error> {
// Which physical device to connect to. // Which physical device to connect to.
physical_device, physical_device,
DeviceCreateInfo { DeviceCreateInfo {
// The list of queues that we are going to use. Here we only use one queue, from the // The list of queues that we are going to use. Here we only use one queue, from
// previously chosen queue family. // the previously chosen queue family.
queue_create_infos: vec![QueueCreateInfo { queue_create_infos: vec![QueueCreateInfo {
queue_family_index, queue_family_index,
..Default::default() ..Default::default()
}], }],
// A list of optional features and extensions that our program needs to work correctly. // A list of optional features and extensions that our program needs to work
// Some parts of the Vulkan specs are optional and must be enabled manually at device // correctly. Some parts of the Vulkan specs are optional and must be enabled
// creation. In this example the only things we are going to need are the // manually at device creation. In this example the only things we are going to
// `khr_swapchain` extension that allows us to draw to a window, and // 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. // `khr_dynamic_rendering` if we don't have Vulkan 1.3 available.
enabled_extensions: device_extensions, enabled_extensions: device_extensions,
// In order to render with Vulkan 1.3's dynamic rendering, we need to enable it here. // In order to render with Vulkan 1.3's dynamic rendering, we need to enable it
// Otherwise, we are only allowed to render with a render pass object, as in the // here. Otherwise, we are only allowed to render with a render pass object, as in
// standard triangle example. The feature is required to be supported by the device if // the standard triangle example. The feature is required to be supported by the
// it supports Vulkan 1.3 and higher, or if the `khr_dynamic_rendering` extension is // device if it supports Vulkan 1.3 and higher, or if the `khr_dynamic_rendering`
// available, so we don't need to check for support. // extension is available, so we don't need to check for support.
enabled_features: DeviceFeatures { enabled_features: DeviceFeatures {
dynamic_rendering: true, dynamic_rendering: true,
..DeviceFeatures::empty() ..DeviceFeatures::empty()
@ -203,101 +232,30 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// Since we can request multiple queues, the `queues` variable is in fact an iterator. We only // Since we can request multiple queues, the `queues` variable is in fact an iterator. We
// use one queue in this example, so we just retrieve the first and only element of the // only use one queue in this example, so we just retrieve the first and only element of
// iterator. // the iterator.
let queue = queues.next().unwrap(); let queue = queues.next().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(WindowBuilder::new().build(&event_loop).unwrap());
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap();
// 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) = {
// 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
.physical_device()
.surface_capabilities(&surface, Default::default())
.unwrap();
// Choosing the internal format that the images will have.
let image_format = device
.physical_device()
.surface_formats(&surface, Default::default())
.unwrap()[0]
.0;
// Please take a look at the docs for the meaning of the parameters we didn't mention.
Swapchain::new(
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.inner_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()
};
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); 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 // Before we can start creating and recording command buffers, we need a way of allocating
// to force rustc to use a defined layout for our data, as the default representation has *no // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command
// guarantees*. // pools underneath and provides a safe interface for them.
#[derive(BufferContents, Vertex)] let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
#[repr(C)] device.clone(),
struct Vertex { Default::default(),
#[format(R32G32_SFLOAT)] ));
position: [f32; 2],
}
// We now create a buffer that will store the shape of our triangle.
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.5, -0.25], position: [-0.5, -0.25],
}, },
Vertex { MyVertex {
position: [0.0, 0.5], position: [0.0, 0.5],
}, },
Vertex { MyVertex {
position: [0.25, -0.1], position: [0.25, -0.1],
}, },
]; ];
@ -316,17 +274,115 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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();
// 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()
};
// 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 next step is to create the shaders.
// //
// The raw shader creation API provided by the vulkano library is unsafe for various reasons, // The raw shader creation API provided by the vulkano library is unsafe for various
// so The `shader!` macro provides a way to generate a Rust module from GLSL source - in the // reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL
// example below, the source is provided as a string input directly to the shader, but a path // source - in the example below, the source is provided as a string input directly to the
// to a source file can be provided as well. Note that the user must specify the type of shader // shader, but a path to a source file can be provided as well. Note that the user must
// (e.g. "vertex", "fragment", etc.) using the `ty` option of the macro. // 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 // The items generated by the `shader!` macro include a `load` function which loads the
// using an input logical device. The module also includes type definitions for layout // shader using an input logical device. The module also includes type definitions for
// structures defined in the shader source, for example uniforms and push constants. // 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 // 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/ // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/
@ -360,32 +416,28 @@ fn main() -> Result<(), impl Error> {
} }
} }
// At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL // Before we draw, we have to create what is called a **pipeline**. A pipeline describes
// implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this // how a GPU operation is to be performed. It is similar to an OpenGL program, but it also
// manually. // 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.
// 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 = { let pipeline = {
// First, we load the shaders that the pipeline will use: // First, we load the shaders that the pipeline will use: the vertex shader and the
// the vertex shader and the fragment shader. // fragment shader.
// //
// A Vulkan shader can in theory contain multiple entry points, so we have to specify which // A Vulkan shader can in theory contain multiple entry points, so we have to specify
// one. // which one.
let vs = vs::load(device.clone()) let vs = vs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
// Automatically generate a vertex input state from the vertex shader's input interface, // Automatically generate a vertex input state from the vertex shader's input
// that takes a single vertex buffer containing `Vertex` structs. // 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. // Make a list of the shader stages that the pipeline will have.
let stages = [ let stages = [
@ -393,23 +445,23 @@ fn main() -> Result<(), impl Error> {
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
// We must now create a **pipeline layout** object, which describes the locations and types // We must now create a **pipeline layout** object, which describes the locations and
// of descriptor sets and push constants used by the shaders in the pipeline. // 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. // Multiple pipelines can share a common layout object, which is more efficient. The
// The shaders in a pipeline must use a subset of the resources described in its pipeline // 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 // layout, but the pipeline layout is allowed to contain resources that are not present
// the shaders; they can be used by shaders in other pipelines that share the same // 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 // layout. Thus, it is a good idea to design shaders so that many pipelines have common
// common resource locations, which allows them to share pipeline layouts. // resource locations, which allows them to share pipeline layouts.
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
// Since we only have one pipeline in this example, and thus one pipeline layout, // 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 // we automatically generate the creation info for it from the resources used in
// shaders. In a real application, you would specify this information manually so that // the shaders. In a real application, you would specify this information manually
// you can re-use one layout in multiple pipelines. // so that you can re-use one layout in multiple pipelines.
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
@ -419,42 +471,42 @@ fn main() -> Result<(), impl Error> {
// configuration of the attachment images. // configuration of the attachment images.
let subpass = PipelineRenderingCreateInfo { let subpass = PipelineRenderingCreateInfo {
// We specify a single color attachment that will be rendered to. When we begin // 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 // rendering, we will specify a swapchain image to be used as this attachment, so
// we set its format to be the same format as the swapchain. // here we set its format to be the same format as the swapchain.
color_attachment_formats: vec![Some(swapchain.image_format())], color_attachment_formats: vec![Some(swapchain.image_format())],
..Default::default() ..Default::default()
}; };
// Finally, create the pipeline. // Finally, create the pipeline.
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
// How vertex data is read from the vertex buffers into the vertex shader. // How vertex data is read from the vertex buffers into the vertex shader.
vertex_input_state: Some(vertex_input_state), vertex_input_state: Some(vertex_input_state),
// How vertices are arranged into primitive shapes. // How vertices are arranged into primitive shapes. The default primitive shape
// The default primitive shape is a triangle. // is a triangle.
input_assembly_state: Some(InputAssemblyState::default()), input_assembly_state: Some(InputAssemblyState::default()),
// How primitives are transformed and clipped to fit the framebuffer. // How primitives are transformed and clipped to fit the framebuffer. We use a
// We use a resizable viewport, set to draw over the entire window. // resizable viewport, set to draw over the entire window.
viewport_state: Some(ViewportState::default()), viewport_state: Some(ViewportState::default()),
// How polygons are culled and converted into a raster of pixels. // How polygons are culled and converted into a raster of pixels. The default
// The default value does not perform any culling. // value does not perform any culling.
rasterization_state: Some(RasterizationState::default()), rasterization_state: Some(RasterizationState::default()),
// How multiple fragment shader samples are converted to a single pixel value. // How multiple fragment shader samples are converted to a single pixel value.
// The default value does not perform any multisampling. // The default value does not perform any multisampling.
multisample_state: Some(MultisampleState::default()), multisample_state: Some(MultisampleState::default()),
// How pixel values are combined with the values already present in the framebuffer. // How pixel values are combined with the values already present in the
// The default value overwrites the old value with the new one, without any // framebuffer. The default value overwrites the old value with the new one,
// blending. // without any blending.
color_blend_state: Some(ColorBlendState::with_attachment_states( color_blend_state: Some(ColorBlendState::with_attachment_states(
subpass.color_attachment_formats.len() as u32, subpass.color_attachment_formats.len() as u32,
ColorBlendAttachmentState::default(), ColorBlendAttachmentState::default(),
)), )),
// Dynamic states allows us to specify parts of the pipeline settings when // Dynamic states allows us to specify parts of the pipeline settings when
// recording the command buffer, before we perform drawing. // recording the command buffer, before we perform drawing. Here, we specify
// Here, we specify that the viewport should be dynamic. // that the viewport should be dynamic.
dynamic_state: [DynamicState::Viewport].into_iter().collect(), dynamic_state: [DynamicState::Viewport].into_iter().collect(),
subpass: Some(subpass.into()), subpass: Some(subpass.into()),
..GraphicsPipelineCreateInfo::layout(layout) ..GraphicsPipelineCreateInfo::layout(layout)
@ -465,73 +517,63 @@ fn main() -> Result<(), impl Error> {
// Dynamic viewports allow us to recreate just the viewport when the window is resized. // Dynamic viewports allow us to recreate just the viewport when the window is resized.
// Otherwise we would have to recreate the whole pipeline. // Otherwise we would have to recreate the whole pipeline.
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.0, depth_range: 0.0..=1.0,
}; };
// When creating the swapchain, we only created plain images. To use them as an attachment for // In some situations, the swapchain will become invalid by itself. This includes for
// rendering, we must wrap then in an image view. // 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
// Since we need to draw to multiple images, we are going to create a different image view for // to the foreground.
// 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
// foreground.
// //
// In this situation, acquiring a swapchain image or presenting it will return an error. // 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. // Rendering to an image of that swapchain will not produce any error, but may or may not
// To continue rendering, we need to recreate the swapchain by creating a new swapchain. Here, // work. To continue rendering, we need to recreate the swapchain by creating a new
// we remember that we need to do this for the next loop iteration. // 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 loop below we are going to submit commands to the GPU. Submitting a command
// an object that implements the `GpuFuture` trait, which holds the resources for as long as // produces an object that implements the `GpuFuture` trait, which holds the resources for
// they are in use by the GPU. // 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 // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to
// that, we store the submission of the previous frame here. // 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());
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); window,
swapchain,
attachment_image_views,
pipeline,
viewport,
recreate_swapchain,
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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: 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) { // 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; return;
} }
@ -539,27 +581,29 @@ fn main() -> Result<(), impl Error> {
// will keep accumulating and you will eventually reach an out of memory 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 // 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. // 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 // Whenever the window resizes we need to recreate everything dependent on the
// window size. In this example that includes the swapchain, the framebuffers and // window size. In this example that includes the swapchain, the framebuffers and
// the dynamic state viewport. // the dynamic state viewport.
if recreate_swapchain { if rcx.recreate_swapchain {
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .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 // Now that we have new swapchain images, we must create new image views from
// them as well. // them as well.
attachment_image_views = rcx.attachment_image_views = window_size_dependent_setup(&new_images);
window_size_dependent_setup(&new_images, &mut viewport);
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 // Before we can draw on the output, we have to *acquire* an image from the
@ -569,11 +613,15 @@ fn main() -> Result<(), impl Error> {
// //
// This function can block if no image is available. The parameter is an optional // This function can block if no image is available. The parameter is an optional
// timeout after which the function call will return an error. // timeout after which the function call will return an error.
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
@ -584,11 +632,11 @@ fn main() -> Result<(), impl Error> {
// drivers this can be when the window resizes, but it may not cause the swapchain // drivers this can be when the window resizes, but it may not cause the swapchain
// to become out of date. // to become out of date.
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
// In order to draw, we have to record a *command buffer*. The command buffer object // In order to draw, we have to record a *command buffer*. The command buffer
// holds the list of commands that are going to be executed. // object holds the list of commands that are going to be executed.
// //
// Recording a command buffer is an expensive operation (usually a few hundred // 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 // microseconds), but it is known to be a hot path in the driver and is expected to
@ -597,8 +645,8 @@ fn main() -> Result<(), impl Error> {
// Note that we have to pass a queue family when we create the command buffer. The // 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. // command buffer will only be executable on that given queue family.
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -630,7 +678,7 @@ fn main() -> Result<(), impl Error> {
..RenderingAttachmentInfo::image_view( ..RenderingAttachmentInfo::image_view(
// We specify image view corresponding to the currently acquired // We specify image view corresponding to the currently acquired
// swapchain image, to use for this attachment. // 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() ..Default::default()
@ -639,17 +687,17 @@ fn main() -> Result<(), impl Error> {
// We are now inside the first subpass of the render pass. // We are now inside the first subpass of the render pass.
// //
// TODO: Document state setting and how it affects subsequent draw commands. // 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() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { unsafe {
builder builder
// We add a draw command. // 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(); .unwrap();
} }
@ -661,11 +709,12 @@ fn main() -> Result<(), impl Error> {
// Finish recording the command buffer by calling `end`. // Finish recording the command buffer by calling `end`.
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
// The color output is now expected to contain our triangle. But in order to // 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 // show it on the screen, we have to *present* the image by calling
@ -676,39 +725,49 @@ fn main() -> Result<(), impl Error> {
// only be presented once the GPU has finished executing the command buffer // only be presented once the GPU has finished executing the command buffer
// that draws the triangle. // that draws the triangle.
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
println!("failed to flush future: {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();
}
}
// 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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(images: &[Arc<Image>]) -> Vec<Arc<ImageView>> {
images: &[Arc<Image>],
viewport: &mut Viewport,
) -> Vec<Arc<ImageView>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| ImageView::new_default(image.clone()).unwrap()) .map(|image| ImageView::new_default(image.clone()).unwrap())

View File

@ -9,15 +9,15 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
command_buffer::{ command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel, allocator::StandardCommandBufferAllocator, CommandBufferBeginInfo, CommandBufferLevel,
CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, CommandBufferUsage, RecordingCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo,
SubpassContents, SubpassContents,
}, },
device::{ device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueFlags, QueueCreateInfo, QueueFlags,
}, },
image::{view::ImageView, Image, ImageUsage}, image::{view::ImageView, Image, ImageUsage},
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
@ -43,14 +43,41 @@ use vulkano::{
Validated, VulkanError, VulkanLibrary, Validated, VulkanError, VulkanLibrary,
}; };
use winit::{ use winit::{
event::{Event, WindowEvent}, application::ApplicationHandler,
event_loop::{ControlFlow, EventLoop}, event::WindowEvent,
window::WindowBuilder, event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
}; };
fn main() -> Result<(), impl Error> { fn main() -> Result<(), impl Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = App::new(&event_loop);
event_loop.run_app(&mut app)
}
struct App {
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
vertex_buffer: Subbuffer<[MyVertex]>,
rcx: Option<RenderContext>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
pipeline: Arc<GraphicsPipeline>,
viewport: Viewport,
recreate_swapchain: bool,
previous_frame_end: Option<Box<dyn GpuFuture>>,
}
impl App {
fn new(event_loop: &EventLoop<()>) -> Self {
let library = VulkanLibrary::new().unwrap(); let library = VulkanLibrary::new().unwrap();
// The first step of any Vulkan program is to create an instance. // The first step of any Vulkan program is to create an instance.
@ -58,9 +85,9 @@ fn main() -> Result<(), impl Error> {
// When we create an instance, we have to pass a list of extensions that we want to enable. // 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 // 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 // enable manually. To do so, we ask `Surface` for the list of extensions required to draw
// a window. // 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. // Now creating the instance.
let instance = Instance::new( let instance = Instance::new(
@ -75,59 +102,63 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// Choose device extensions that we're going to use. In order to present images to a surface, // Choose device extensions that we're going to use. In order to present images to a
// we need a `Swapchain`, which is provided by the `khr_swapchain` extension. // surface, we need a `Swapchain`, which is provided by the `khr_swapchain` extension.
let device_extensions = DeviceExtensions { let device_extensions = DeviceExtensions {
khr_swapchain: true, khr_swapchain: true,
..DeviceExtensions::empty() ..DeviceExtensions::empty()
}; };
// We then choose which physical device to use. First, we enumerate all the available physical // We then choose which physical device to use. First, we enumerate all the available
// devices, then apply filters to narrow them down to those that can support our needs. // physical devices, then apply filters to narrow them down to those that can support our
// needs.
let (physical_device, queue_family_index) = instance let (physical_device, queue_family_index) = instance
.enumerate_physical_devices() .enumerate_physical_devices()
.unwrap() .unwrap()
.filter(|p| { .filter(|p| {
// Some devices may not support the extensions or features that your application, or // Some devices may not support the extensions or features that your application,
// report properties and limits that are not sufficient for your application. These // or report properties and limits that are not sufficient for your application.
// should be filtered out here. // These should be filtered out here.
p.supported_extensions().contains(&device_extensions) p.supported_extensions().contains(&device_extensions)
}) })
.filter_map(|p| { .filter_map(|p| {
// For each physical device, we try to find a suitable queue family that will execute // For each physical device, we try to find a suitable queue family that will
// our draw commands. // execute our draw commands.
// //
// Devices can provide multiple queues to run commands in parallel (for example a draw // Devices can provide multiple queues to run commands in parallel (for example a
// queue and a compute queue), similar to CPU threads. This is something you have to // draw queue and a compute queue), similar to CPU threads. This is
// have to manage manually in Vulkan. Queues of the same type belong to the same queue // something you have to have to manage manually in Vulkan. Queues
// family. // 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 // 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 // real-world application, you may want to use a separate dedicated transfer queue
// handle data transfers in parallel with graphics operations. You may also need a // to handle data transfers in parallel with graphics operations.
// separate queue for compute operations, if your application uses those. // You may also need a separate queue for compute operations, if
// your application uses those.
p.queue_family_properties() p.queue_family_properties()
.iter() .iter()
.enumerate() .enumerate()
.position(|(i, q)| { .position(|(i, q)| {
// We select a queue family that supports graphics operations. When drawing to // We select a queue family that supports graphics operations. When drawing
// a window surface, as we do in this example, we also need to check that // to a window surface, as we do in this example, we also need to check
// queues in this queue family are capable of presenting images to the surface. // that queues in this queue family are capable of presenting images to the
// surface.
q.queue_flags.intersects(QueueFlags::GRAPHICS) 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 // The code here searches for the first queue family that is suitable. If none
// found, `None` is returned to `filter_map`, which disqualifies this physical // is found, `None` is returned to `filter_map`, which
// device. // disqualifies this physical device.
.map(|i| (p, i as u32)) .map(|i| (p, i as u32))
}) })
// All the physical devices that pass the filters above are suitable for the application. // All the physical devices that pass the filters above are suitable for the
// However, not every device is equal, some are preferred over others. Now, we assign each // application. However, not every device is equal, some are preferred over others.
// physical device a score, and pick the device with the lowest ("best") score. // 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 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" // In a real-world setting, you may want to use the best-scoring device only as a
// or "recommended" device, and let the user choose the device themself. // "default" or "recommended" device, and let the user choose the device themself.
.min_by_key(|(p, _)| { .min_by_key(|(p, _)| {
// We assign a lower score to device types that are likely to be faster/better. // We assign a lower score to device types that are likely to be faster/better.
match p.properties().device_type { match p.properties().device_type {
@ -155,14 +186,14 @@ fn main() -> Result<(), impl Error> {
// Which physical device to connect to. // Which physical device to connect to.
physical_device, physical_device,
DeviceCreateInfo { DeviceCreateInfo {
// A list of optional features and extensions that our program needs to work correctly. // A list of optional features and extensions that our program needs to work
// Some parts of the Vulkan specs are optional and must be enabled manually at device // correctly. Some parts of the Vulkan specs are optional and must be enabled
// creation. In this example the only thing we are going to need is the `khr_swapchain` // manually at device creation. In this example the only thing we are going to need
// extension that allows us to draw to a window. // is the `khr_swapchain` extension that allows us to draw to a window.
enabled_extensions: device_extensions, enabled_extensions: device_extensions,
// The list of queues that we are going to use. Here we only use one queue, from the // The list of queues that we are going to use. Here we only use one queue, from
// previously chosen queue family. // the previously chosen queue family.
queue_create_infos: vec![QueueCreateInfo { queue_create_infos: vec![QueueCreateInfo {
queue_family_index, queue_family_index,
..Default::default() ..Default::default()
@ -173,101 +204,30 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// Since we can request multiple queues, the `queues` variable is in fact an iterator. We only // Since we can request multiple queues, the `queues` variable is in fact an iterator. We
// use one queue in this example, so we just retrieve the first and only element of the // only use one queue in this example, so we just retrieve the first and only element of
// iterator. // the iterator.
let queue = queues.next().unwrap(); let queue = queues.next().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(WindowBuilder::new().build(&event_loop).unwrap());
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap();
// 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) = {
// 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
.physical_device()
.surface_capabilities(&surface, Default::default())
.unwrap();
// Choosing the internal format that the images will have.
let image_format = device
.physical_device()
.surface_formats(&surface, Default::default())
.unwrap()[0]
.0;
// Please take a look at the docs for the meaning of the parameters we didn't mention.
Swapchain::new(
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.inner_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()
};
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); 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 // Before we can start creating and recording command buffers, we need a way of allocating
// to force rustc to use a defined layout for our data, as the default representation has *no // them. Vulkano provides a command buffer allocator, which manages raw Vulkan command
// guarantees*. // pools underneath and provides a safe interface for them.
#[derive(BufferContents, Vertex)] let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
#[repr(C)] device.clone(),
struct Vertex { Default::default(),
#[format(R32G32_SFLOAT)] ));
position: [f32; 2],
}
// We now create a buffer that will store the shape of our triangle.
let vertices = [ let vertices = [
Vertex { MyVertex {
position: [-0.5, -0.25], position: [-0.5, -0.25],
}, },
Vertex { MyVertex {
position: [0.0, 0.5], position: [0.0, 0.5],
}, },
Vertex { MyVertex {
position: [0.25, -0.1], position: [0.25, -0.1],
}, },
]; ];
@ -286,17 +246,110 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .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();
// 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()
};
// The next step is to create the shaders. // The next step is to create the shaders.
// //
// The raw shader creation API provided by the vulkano library is unsafe for various reasons, // The raw shader creation API provided by the vulkano library is unsafe for various
// so The `shader!` macro provides a way to generate a Rust module from GLSL source - in the // reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL
// example below, the source is provided as a string input directly to the shader, but a path // source - in the example below, the source is provided as a string input directly to the
// to a source file can be provided as well. Note that the user must specify the type of shader // shader, but a path to a source file can be provided as well. Note that the user must
// (e.g. "vertex", "fragment", etc.) using the `ty` option of the macro. // 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 // The items generated by the `shader!` macro include a `load` function which loads the
// using an input logical device. The module also includes type definitions for layout // shader using an input logical device. The module also includes type definitions for
// structures defined in the shader source, for example uniforms and push constants. // 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 // 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/ // vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/
@ -330,33 +383,29 @@ 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 // 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 // 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. // colors, depth and/or stencil information will be written.
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), self.device.clone(),
attachments: { attachments: {
// `color` is a custom name we give to the first and only attachment. // `color` is a custom name we give to the first and only attachment.
color: { color: {
// `format: <ty>` indicates the type of the format of the image. This has to be one // `format: <ty>` indicates the type of the format of the image. This has to be
// of the types of the `vulkano::format` module (or alternatively one of your // one of the types of the `vulkano::format` module (or alternatively one of
// structs that implements the `FormatDesc` trait). Here we use the same format as // your structs that implements the `FormatDesc` trait). Here we use the same
// the swapchain. // format as the swapchain.
format: swapchain.image_format(), format: swapchain.image_format(),
// `samples: 1` means that we ask the GPU to use one sample to determine the value // `samples: 1` means that we ask the GPU to use one sample to determine the
// of each pixel in the color attachment. We could use a larger value // 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 // (multisampling) for antialiasing. An example of this can be found in
// msaa-renderpass.rs. // msaa-renderpass.rs.
samples: 1, samples: 1,
// `load_op: Clear` means that we ask the GPU to clear the content of this // `load_op: Clear` means that we ask the GPU to clear the content of this
// attachment at the start of the drawing. // attachment at the start of the drawing.
load_op: Clear, load_op: Clear,
// `store_op: Store` means that we ask the GPU to store the output of the draw in // `store_op: Store` means that we ask the GPU to store the output of the draw
// the actual image. We could also ask it to discard the result. // in the actual image. We could also ask it to discard the result.
store_op: Store, store_op: Store,
}, },
}, },
@ -369,28 +418,35 @@ fn main() -> Result<(), impl Error> {
) )
.unwrap(); .unwrap();
// Before we draw, we have to create what is called a **pipeline**. A pipeline describes how // The render pass we created above only describes the layout of our framebuffers. Before
// a GPU operation is to be performed. It is similar to an OpenGL program, but it also contains // we can draw we also need to create the actual framebuffers.
// 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 // Since we need to draw to multiple images, we are going to create a different framebuffer
// one. // for each image.
let vs = vs::load(device.clone()) 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() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
let fs = fs::load(device.clone()) let fs = fs::load(self.device.clone())
.unwrap() .unwrap()
.entry_point("main") .entry_point("main")
.unwrap(); .unwrap();
// Automatically generate a vertex input state from the vertex shader's input interface, // Automatically generate a vertex input state from the vertex shader's input
// that takes a single vertex buffer containing `Vertex` structs. // 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. // Make a list of the shader stages that the pipeline will have.
let stages = [ let stages = [
@ -398,61 +454,61 @@ fn main() -> Result<(), impl Error> {
PipelineShaderStageCreateInfo::new(fs), PipelineShaderStageCreateInfo::new(fs),
]; ];
// We must now create a **pipeline layout** object, which describes the locations and types // We must now create a **pipeline layout** object, which describes the locations and
// of descriptor sets and push constants used by the shaders in the pipeline. // 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. // Multiple pipelines can share a common layout object, which is more efficient. The
// The shaders in a pipeline must use a subset of the resources described in its pipeline // 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 // layout, but the pipeline layout is allowed to contain resources that are not present
// the shaders; they can be used by shaders in other pipelines that share the same // 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 // layout. Thus, it is a good idea to design shaders so that many pipelines have common
// common resource locations, which allows them to share pipeline layouts. // resource locations, which allows them to share pipeline layouts.
let layout = PipelineLayout::new( let layout = PipelineLayout::new(
device.clone(), self.device.clone(),
// Since we only have one pipeline in this example, and thus one pipeline layout, // 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 // we automatically generate the creation info for it from the resources used in
// shaders. In a real application, you would specify this information manually so that // the shaders. In a real application, you would specify this information manually
// you can re-use one layout in multiple pipelines. // so that you can re-use one layout in multiple pipelines.
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
.into_pipeline_layout_create_info(device.clone()) .into_pipeline_layout_create_info(self.device.clone())
.unwrap(), .unwrap(),
) )
.unwrap(); .unwrap();
// We have to indicate which subpass of which render pass this pipeline is going to be used // We have to indicate which subpass of which render pass this pipeline is going to be
// in. The pipeline will only be usable from this particular subpass. // used in. The pipeline will only be usable from this particular subpass.
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
// Finally, create the pipeline. // Finally, create the pipeline.
GraphicsPipeline::new( GraphicsPipeline::new(
device.clone(), self.device.clone(),
None, None,
GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(), stages: stages.into_iter().collect(),
// How vertex data is read from the vertex buffers into the vertex shader. // How vertex data is read from the vertex buffers into the vertex shader.
vertex_input_state: Some(vertex_input_state), vertex_input_state: Some(vertex_input_state),
// How vertices are arranged into primitive shapes. // How vertices are arranged into primitive shapes. The default primitive shape
// The default primitive shape is a triangle. // is a triangle.
input_assembly_state: Some(InputAssemblyState::default()), input_assembly_state: Some(InputAssemblyState::default()),
// How primitives are transformed and clipped to fit the framebuffer. // How primitives are transformed and clipped to fit the framebuffer. We use a
// We use a resizable viewport, set to draw over the entire window. // resizable viewport, set to draw over the entire window.
viewport_state: Some(ViewportState::default()), viewport_state: Some(ViewportState::default()),
// How polygons are culled and converted into a raster of pixels. // How polygons are culled and converted into a raster of pixels. The default
// The default value does not perform any culling. // value does not perform any culling.
rasterization_state: Some(RasterizationState::default()), rasterization_state: Some(RasterizationState::default()),
// How multiple fragment shader samples are converted to a single pixel value. // How multiple fragment shader samples are converted to a single pixel value.
// The default value does not perform any multisampling. // The default value does not perform any multisampling.
multisample_state: Some(MultisampleState::default()), multisample_state: Some(MultisampleState::default()),
// How pixel values are combined with the values already present in the framebuffer. // How pixel values are combined with the values already present in the
// The default value overwrites the old value with the new one, without any // framebuffer. The default value overwrites the old value with the new one,
// blending. // without any blending.
color_blend_state: Some(ColorBlendState::with_attachment_states( color_blend_state: Some(ColorBlendState::with_attachment_states(
subpass.num_color_attachments(), subpass.num_color_attachments(),
ColorBlendAttachmentState::default(), ColorBlendAttachmentState::default(),
)), )),
// Dynamic states allows us to specify parts of the pipeline settings when // Dynamic states allows us to specify parts of the pipeline settings when
// recording the command buffer, before we perform drawing. // recording the command buffer, before we perform drawing. Here, we specify
// Here, we specify that the viewport should be dynamic. // that the viewport should be dynamic.
dynamic_state: [DynamicState::Viewport].into_iter().collect(), dynamic_state: [DynamicState::Viewport].into_iter().collect(),
subpass: Some(subpass.into()), subpass: Some(subpass.into()),
..GraphicsPipelineCreateInfo::layout(layout) ..GraphicsPipelineCreateInfo::layout(layout)
@ -463,73 +519,64 @@ fn main() -> Result<(), impl Error> {
// Dynamic viewports allow us to recreate just the viewport when the window is resized. // Dynamic viewports allow us to recreate just the viewport when the window is resized.
// Otherwise we would have to recreate the whole pipeline. // Otherwise we would have to recreate the whole pipeline.
let mut viewport = Viewport { let viewport = Viewport {
offset: [0.0, 0.0], offset: [0.0, 0.0],
extent: [0.0, 0.0], extent: window_size.into(),
depth_range: 0.0..=1.0, depth_range: 0.0..=1.0,
}; };
// The render pass we created above only describes the layout of our framebuffers. Before we // In some situations, the swapchain will become invalid by itself. This includes for
// can draw we also need to create the actual framebuffers. // 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
// Since we need to draw to multiple images, we are going to create a different framebuffer for // to the foreground.
// 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
// foreground.
// //
// In this situation, acquiring a swapchain image or presenting it will return an error. // 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. // Rendering to an image of that swapchain will not produce any error, but may or may not
// To continue rendering, we need to recreate the swapchain by creating a new swapchain. Here, // work. To continue rendering, we need to recreate the swapchain by creating a new
// we remember that we need to do this for the next loop iteration. // 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.
// an object that implements the `GpuFuture` trait, which holds the resources for as long as // Submitting a command produces an object that implements the `GpuFuture` trait, which
// they are in use by the GPU. // 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 // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to
// that, we store the submission of the previous frame here. // 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());
event_loop.run(move |event, elwt| { self.rcx = Some(RenderContext {
elwt.set_control_flow(ControlFlow::Poll); window,
swapchain,
render_pass,
framebuffers,
pipeline,
viewport,
recreate_swapchain,
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 { match event {
Event::WindowEvent { WindowEvent::CloseRequested => {
event: WindowEvent::CloseRequested, event_loop.exit();
..
} => {
elwt.exit();
} }
Event::WindowEvent { WindowEvent::Resized(_) => {
event: WindowEvent::Resized(_), rcx.recreate_swapchain = true;
..
} => {
recreate_swapchain = true;
} }
Event::WindowEvent { WindowEvent::RedrawRequested => {
event: 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) { // 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; return;
} }
@ -537,32 +584,31 @@ fn main() -> Result<(), impl Error> {
// will keep accumulating and you will eventually reach an out of memory 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 // 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. // 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 // Whenever the window resizes we need to recreate everything dependent on the
// window size. In this example that includes the swapchain, the framebuffers and // window size. In this example that includes the swapchain, the framebuffers and
// the dynamic state viewport. // the dynamic state viewport.
if recreate_swapchain { if rcx.recreate_swapchain {
// Use the new dimensions of the window. // Use the new dimensions of the window.
let (new_swapchain, new_images) = swapchain let (new_swapchain, new_images) = rcx
.swapchain
.recreate(SwapchainCreateInfo { .recreate(SwapchainCreateInfo {
image_extent, image_extent: window_size.into(),
..swapchain.create_info() ..rcx.swapchain.create_info()
}) })
.expect("failed to recreate swapchain"); .expect("failed to recreate swapchain");
swapchain = new_swapchain; rcx.swapchain = new_swapchain;
// Because framebuffers contains a reference to the old swapchain, we need to // Because framebuffers contains a reference to the old swapchain, we need to
// recreate framebuffers as well. // recreate framebuffers as well.
framebuffers = window_size_dependent_setup( rcx.framebuffers = window_size_dependent_setup(&new_images, &rcx.render_pass);
&new_images,
render_pass.clone(),
&mut viewport,
);
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 // Before we can draw on the output, we have to *acquire* an image from the
@ -572,11 +618,15 @@ fn main() -> Result<(), impl Error> {
// //
// This function can block if no image is available. The parameter is an optional // This function can block if no image is available. The parameter is an optional
// timeout after which the function call will return an error. // timeout after which the function call will return an error.
let (image_index, suboptimal, acquire_future) = let (image_index, suboptimal, acquire_future) = match acquire_next_image(
match acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap) { rcx.swapchain.clone(),
None,
)
.map_err(Validated::unwrap)
{
Ok(r) => r, Ok(r) => r,
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
return; return;
} }
Err(e) => panic!("failed to acquire next image: {e}"), Err(e) => panic!("failed to acquire next image: {e}"),
@ -587,11 +637,11 @@ fn main() -> Result<(), impl Error> {
// drivers this can be when the window resizes, but it may not cause the swapchain // drivers this can be when the window resizes, but it may not cause the swapchain
// to become out of date. // to become out of date.
if suboptimal { if suboptimal {
recreate_swapchain = true; rcx.recreate_swapchain = true;
} }
// In order to draw, we have to record a *command buffer*. The command buffer object // In order to draw, we have to record a *command buffer*. The command buffer
// holds the list of commands that are going to be executed. // object holds the list of commands that are going to be executed.
// //
// Recording a command buffer is an expensive operation (usually a few hundred // 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 // microseconds), but it is known to be a hot path in the driver and is expected to
@ -600,8 +650,8 @@ fn main() -> Result<(), impl Error> {
// Note that we have to pass a queue family when we create the command buffer. The // 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. // command buffer will only be executable on that given queue family.
let mut builder = RecordingCommandBuffer::new( let mut builder = RecordingCommandBuffer::new(
command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
queue.queue_family_index(), self.queue.queue_family_index(),
CommandBufferLevel::Primary, CommandBufferLevel::Primary,
CommandBufferBeginInfo { CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit, usage: CommandBufferUsage::OneTimeSubmit,
@ -623,13 +673,13 @@ fn main() -> Result<(), impl Error> {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer( ..RenderPassBeginInfo::framebuffer(
framebuffers[image_index as usize].clone(), rcx.framebuffers[image_index as usize].clone(),
) )
}, },
SubpassBeginInfo { SubpassBeginInfo {
// The contents of the first (and only) subpass. // The contents of the first (and only) subpass. This can be either
// This can be either `Inline` or `SecondaryCommandBuffers`. // `Inline` or `SecondaryCommandBuffers`. The latter is a bit more
// The latter is a bit more advanced and is not covered here. // advanced and is not covered here.
contents: SubpassContents::Inline, contents: SubpassContents::Inline,
..Default::default() ..Default::default()
}, },
@ -638,17 +688,17 @@ fn main() -> Result<(), impl Error> {
// We are now inside the first subpass of the render pass. // We are now inside the first subpass of the render pass.
// //
// TODO: Document state setting and how it affects subsequent draw commands. // 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() .unwrap()
.bind_pipeline_graphics(pipeline.clone()) .bind_pipeline_graphics(rcx.pipeline.clone())
.unwrap() .unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone()) .bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap(); .unwrap();
unsafe { unsafe {
builder builder
// We add a draw command. // 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(); .unwrap();
} }
@ -661,11 +711,12 @@ fn main() -> Result<(), impl Error> {
// Finish recording the command buffer by calling `end`. // Finish recording the command buffer by calling `end`.
let command_buffer = builder.end().unwrap(); let command_buffer = builder.end().unwrap();
let future = previous_frame_end let future = rcx
.previous_frame_end
.take() .take()
.unwrap() .unwrap()
.join(acquire_future) .join(acquire_future)
.then_execute(queue.clone(), command_buffer) .then_execute(self.queue.clone(), command_buffer)
.unwrap() .unwrap()
// The color output is now expected to contain our triangle. But in order to // 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 // show it on the screen, we have to *present* the image by calling
@ -676,18 +727,21 @@ fn main() -> Result<(), impl Error> {
// only be presented once the GPU has finished executing the command buffer // only be presented once the GPU has finished executing the command buffer
// that draws the triangle. // that draws the triangle.
.then_swapchain_present( .then_swapchain_present(
queue.clone(), self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index), SwapchainPresentInfo::swapchain_image_index(
rcx.swapchain.clone(),
image_index,
),
) )
.then_signal_fence_and_flush(); .then_signal_fence_and_flush();
match future.map_err(Validated::unwrap) { match future.map_err(Validated::unwrap) {
Ok(future) => { Ok(future) => {
previous_frame_end = Some(future.boxed()); rcx.previous_frame_end = Some(future.boxed());
} }
Err(VulkanError::OutOfDate) => { Err(VulkanError::OutOfDate) => {
recreate_swapchain = true; rcx.recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed()); rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed());
} }
Err(e) => { Err(e) => {
panic!("failed to flush future: {e}"); panic!("failed to flush future: {e}");
@ -695,25 +749,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. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
images: &[Arc<Image>], images: &[Arc<Image>],
render_pass: Arc<RenderPass>, render_pass: &Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> { ) -> Vec<Arc<Framebuffer>> {
let extent = images[0].extent();
viewport.extent = [extent[0] as f32, extent[1] as f32];
images images
.iter() .iter()
.map(|image| { .map(|image| {
let view = ImageView::new_default(image.clone()).unwrap(); let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new( Framebuffer::new(
render_pass.clone(), render_pass.clone(),
FramebufferCreateInfo { FramebufferCreateInfo {

View File

@ -4,7 +4,7 @@
pub use self::commands::{clear::*, copy::*, dynamic_state::*, pipeline::*, sync::*}; pub use self::commands::{clear::*, copy::*, dynamic_state::*, pipeline::*, sync::*};
use crate::{graph::ResourceMap, resource::DeathRow, Id}; use crate::{graph::ResourceMap, resource::DeathRow, Id};
use ash::vk; use ash::vk;
use std::sync::Arc; use std::{any::Any, sync::Arc};
use vulkano::{ use vulkano::{
buffer::Buffer, buffer::Buffer,
command_buffer::sys::RawRecordingCommandBuffer, command_buffer::sys::RawRecordingCommandBuffer,
@ -53,6 +53,22 @@ impl<'a> RecordingCommandBuffer<'a> {
pub fn as_raw(&mut self) -> &mut RawRecordingCommandBuffer { pub fn as_raw(&mut self) -> &mut RawRecordingCommandBuffer {
self.inner 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<impl Any + Send + Sync>) {
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<Item = Arc<impl Any + Send + Sync>>,
) {
self.death_row
.extend(objects.into_iter().map(|object| object as _));
}
} }
unsafe impl DeviceOwned for RecordingCommandBuffer<'_> { unsafe impl DeviceOwned for RecordingCommandBuffer<'_> {

View File

@ -49,15 +49,9 @@ impl Default for VulkanoConfig {
}; };
VulkanoConfig { VulkanoConfig {
instance_create_info: InstanceCreateInfo { instance_create_info: InstanceCreateInfo {
#[cfg(target_os = "macos")] #[cfg(target_vendor = "apple")]
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
application_version: Version::V1_3, application_version: Version::V1_3,
enabled_extensions: InstanceExtensions {
#[cfg(target_os = "macos")]
khr_portability_enumeration: true,
..InstanceExtensions::empty()
},
..Default::default() ..Default::default()
}, },
debug_create_info: None, debug_create_info: None,

View File

@ -8,6 +8,7 @@ use std::collections::hash_map::{Iter, IterMut};
use vulkano::swapchain::{PresentMode, SwapchainCreateInfo}; use vulkano::swapchain::{PresentMode, SwapchainCreateInfo};
use winit::{ use winit::{
dpi::LogicalSize, dpi::LogicalSize,
event_loop::ActiveEventLoop,
window::{CursorGrabMode, WindowId}, window::{CursorGrabMode, WindowId},
}; };
@ -21,19 +22,14 @@ use winit::{
/// context::{VulkanoConfig, VulkanoContext}, /// context::{VulkanoConfig, VulkanoContext},
/// window::VulkanoWindows, /// window::VulkanoWindows,
/// }; /// };
/// use winit::event_loop::EventLoop;
/// ///
/// fn test() { /// # let event_loop = return;
/// let context = VulkanoContext::new(VulkanoConfig::default()); /// let context = VulkanoContext::new(VulkanoConfig::default());
/// let event_loop = EventLoop::new().unwrap();
/// let mut vulkano_windows = VulkanoWindows::default(); /// let mut vulkano_windows = VulkanoWindows::default();
/// let _id1 = /// let _id1 = vulkano_windows.create_window(event_loop, &context, &Default::default(), |_| {});
/// vulkano_windows.create_window(&event_loop, &context, &Default::default(), |_| {}); /// let _id2 = 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)] #[derive(Default)]
pub struct VulkanoWindows { pub struct VulkanoWindows {
@ -44,25 +40,25 @@ pub struct VulkanoWindows {
impl VulkanoWindows { impl VulkanoWindows {
/// Creates a winit window with [`VulkanoWindowRenderer`] based on the given /// Creates a winit window with [`VulkanoWindowRenderer`] based on the given
/// [`WindowDescriptor`] input and swapchain creation modifications. /// [`WindowDescriptor`] input and swapchain creation modifications.
pub fn create_window<T>( pub fn create_window(
&mut self, &mut self,
event_loop: &winit::event_loop::EventLoopWindowTarget<T>, event_loop: &ActiveEventLoop,
vulkano_context: &VulkanoContext, vulkano_context: &VulkanoContext,
window_descriptor: &WindowDescriptor, window_descriptor: &WindowDescriptor,
swapchain_create_info_modify: fn(&mut SwapchainCreateInfo), swapchain_create_info_modify: fn(&mut SwapchainCreateInfo),
) -> WindowId { ) -> 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 { winit_window_attributes = match window_descriptor.mode {
WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some( WindowMode::BorderlessFullscreen => winit_window_attributes.with_fullscreen(Some(
winit::window::Fullscreen::Borderless(event_loop.primary_monitor()), winit::window::Fullscreen::Borderless(event_loop.primary_monitor()),
)), )),
WindowMode::Fullscreen => { 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()), 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( winit::window::Fullscreen::Exclusive(get_fitting_videomode(
&event_loop.primary_monitor().unwrap(), &event_loop.primary_monitor().unwrap(),
window_descriptor.width as u32, window_descriptor.width as u32,
@ -80,7 +76,7 @@ impl VulkanoWindows {
if let Some(position) = position { if let Some(position) = position {
if let Some(sf) = scale_factor_override { 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( winit::dpi::LogicalPosition::new(
position[0] as f64, position[0] as f64,
position[1] as f64, position[1] as f64,
@ -88,18 +84,20 @@ impl VulkanoWindows {
.to_physical::<f64>(*sf), .to_physical::<f64>(*sf),
); );
} else { } else {
winit_window_builder = winit_window_attributes = winit_window_attributes.with_position(
winit_window_builder.with_position(winit::dpi::LogicalPosition::new( winit::dpi::LogicalPosition::new(
position[0] as f64, position[0] as f64,
position[1] as f64, position[1] as f64,
)); ),
);
} }
} }
if let Some(sf) = scale_factor_override { if let Some(sf) = scale_factor_override {
winit_window_builder winit_window_attributes
.with_inner_size(LogicalSize::new(*width, *height).to_physical::<f64>(*sf)) .with_inner_size(LogicalSize::new(*width, *height).to_physical::<f64>(*sf))
} else { } 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) .with_resizable(window_descriptor.resizable)
@ -117,19 +115,20 @@ impl VulkanoWindows {
height: constraints.max_height, height: constraints.max_height,
}; };
let winit_window_builder = let winit_window_attributes =
if constraints.max_width.is_finite() && constraints.max_height.is_finite() { 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_min_inner_size(min_inner_size)
.with_max_inner_size(max_inner_size) .with_max_inner_size(max_inner_size)
} else { } else {
winit_window_builder.with_min_inner_size(min_inner_size) winit_window_attributes.with_min_inner_size(min_inner_size)
}; };
#[allow(unused_mut)] #[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 { if window_descriptor.cursor_locked {
match winit_window.set_cursor_grab(CursorGrabMode::Confined) { match winit_window.set_cursor_grab(CursorGrabMode::Confined) {
@ -241,7 +240,7 @@ fn get_fitting_videomode(
monitor: &winit::monitor::MonitorHandle, monitor: &winit::monitor::MonitorHandle,
width: u32, width: u32,
height: u32, height: u32,
) -> winit::monitor::VideoMode { ) -> winit::monitor::VideoModeHandle {
let mut modes = monitor.video_modes().collect::<Vec<_>>(); let mut modes = monitor.video_modes().collect::<Vec<_>>();
fn abs_diff(a: u32, b: u32) -> u32 { fn abs_diff(a: u32, b: u32) -> u32 {
@ -269,7 +268,7 @@ fn get_fitting_videomode(
modes.first().unwrap().clone() 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::<Vec<_>>(); let mut modes = monitor.video_modes().collect::<Vec<_>>();
modes.sort_by(|a, b| { modes.sort_by(|a, b| {
use std::cmp::Ordering::*; use std::cmp::Ordering::*;