mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 14:24:18 +00:00
parent
9033311653
commit
f6bc05df94
414
Cargo.lock
generated
414
Cargo.lock
generated
@ -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"
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -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"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,201 +57,51 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
let required_extensions = Surface::required_extensions(&event_loop).unwrap();
|
|
||||||
let instance = Instance::new(
|
|
||||||
library,
|
|
||||||
InstanceCreateInfo {
|
|
||||||
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
|
||||||
enabled_extensions: required_extensions,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut rcx = RenderContext::new(&event_loop, &instance);
|
|
||||||
|
|
||||||
let mut task_graph = TaskGraph::new(&rcx.resources, 3, 2);
|
|
||||||
|
|
||||||
let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default());
|
|
||||||
let virtual_bloom_image_id = task_graph.add_image(&ImageCreateInfo::default());
|
|
||||||
|
|
||||||
let scene_node_id = task_graph
|
|
||||||
.create_task_node("Scene", QueueFamilyType::Graphics, SceneTask::new(&rcx))
|
|
||||||
.image_access(
|
|
||||||
virtual_bloom_image_id,
|
|
||||||
AccessType::ColorAttachmentWrite,
|
|
||||||
ImageLayoutType::Optimal,
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
let bloom_node_id = task_graph
|
|
||||||
.create_task_node(
|
|
||||||
"Bloom",
|
|
||||||
QueueFamilyType::Compute,
|
|
||||||
BloomTask::new(&rcx, virtual_bloom_image_id),
|
|
||||||
)
|
|
||||||
.image_access(
|
|
||||||
virtual_bloom_image_id,
|
|
||||||
AccessType::ComputeShaderSampledRead,
|
|
||||||
ImageLayoutType::General,
|
|
||||||
)
|
|
||||||
.image_access(
|
|
||||||
virtual_bloom_image_id,
|
|
||||||
AccessType::ComputeShaderStorageWrite,
|
|
||||||
ImageLayoutType::General,
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
let tonemap_node_id = task_graph
|
|
||||||
.create_task_node(
|
|
||||||
"Tonemap",
|
|
||||||
QueueFamilyType::Graphics,
|
|
||||||
TonemapTask::new(&rcx, virtual_swapchain_id),
|
|
||||||
)
|
|
||||||
.image_access(
|
|
||||||
virtual_swapchain_id.current_image_id(),
|
|
||||||
AccessType::ColorAttachmentWrite,
|
|
||||||
ImageLayoutType::Optimal,
|
|
||||||
)
|
|
||||||
.image_access(
|
|
||||||
virtual_bloom_image_id,
|
|
||||||
AccessType::FragmentShaderSampledRead,
|
|
||||||
ImageLayoutType::General,
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
task_graph.add_edge(scene_node_id, bloom_node_id).unwrap();
|
|
||||||
task_graph.add_edge(bloom_node_id, tonemap_node_id).unwrap();
|
|
||||||
|
|
||||||
let mut task_graph = unsafe {
|
|
||||||
task_graph.compile(&CompileInfo {
|
|
||||||
queues: &[&rcx.queue],
|
|
||||||
present_queue: Some(&rcx.queue),
|
|
||||||
flight_id: rcx.flight_id,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
|
||||||
|
|
||||||
match event {
|
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::CloseRequested,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
elwt.exit();
|
|
||||||
}
|
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::Resized(_),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
recreate_swapchain = true;
|
|
||||||
}
|
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::RedrawRequested,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let image_extent: [u32; 2] = rcx.window.inner_size().into();
|
|
||||||
|
|
||||||
if image_extent.contains(&0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if recreate_swapchain {
|
|
||||||
rcx.handle_resize();
|
|
||||||
|
|
||||||
task_graph
|
|
||||||
.task_node_mut(scene_node_id)
|
|
||||||
.unwrap()
|
|
||||||
.task_mut()
|
|
||||||
.downcast_mut::<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 {
|
struct App {
|
||||||
|
instance: Arc<Instance>,
|
||||||
device: Arc<Device>,
|
device: Arc<Device>,
|
||||||
queue: Arc<Queue>,
|
queue: Arc<Queue>,
|
||||||
resources: Arc<Resources>,
|
resources: Arc<Resources>,
|
||||||
flight_id: Id<Flight>,
|
flight_id: Id<Flight>,
|
||||||
|
rcx: Option<RenderContext>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RenderContext {
|
||||||
window: Arc<Window>,
|
window: Arc<Window>,
|
||||||
swapchain_id: Id<Swapchain>,
|
swapchain_id: Id<Swapchain>,
|
||||||
swapchain_format: Format,
|
|
||||||
bloom_image_id: Id<Image>,
|
bloom_image_id: Id<Image>,
|
||||||
viewport: Viewport,
|
viewport: Viewport,
|
||||||
pipeline_layout: Arc<PipelineLayout>,
|
pipeline_layout: Arc<PipelineLayout>,
|
||||||
|
recreate_swapchain: bool,
|
||||||
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
sampler: Arc<Sampler>,
|
sampler: Arc<Sampler>,
|
||||||
descriptor_set: DescriptorSetWithOffsets,
|
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 RenderContext {
|
impl App {
|
||||||
fn new(event_loop: &EventLoop<()>, instance: &Arc<Instance>) -> Self {
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
|
let library = VulkanLibrary::new().unwrap();
|
||||||
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
|
let instance = Instance::new(
|
||||||
|
library,
|
||||||
|
InstanceCreateInfo {
|
||||||
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
|
enabled_extensions: required_extensions,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut device_extensions = DeviceExtensions {
|
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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let flight = self.resources.flight(self.flight_id).unwrap();
|
||||||
|
|
||||||
|
if rcx.recreate_swapchain {
|
||||||
|
rcx.swapchain_id = self
|
||||||
|
.resources
|
||||||
|
.recreate_swapchain(rcx.swapchain_id, |create_info| SwapchainCreateInfo {
|
||||||
|
image_extent: window_size.into(),
|
||||||
|
..create_info
|
||||||
|
})
|
||||||
|
.expect("failed to recreate swapchain");
|
||||||
|
|
||||||
|
rcx.viewport.extent = window_size.into();
|
||||||
|
|
||||||
|
unsafe { self.resources.remove_image(rcx.bloom_image_id) }.unwrap();
|
||||||
|
|
||||||
|
(rcx.bloom_image_id, rcx.descriptor_set) = window_size_dependent_setup(
|
||||||
|
&self.resources,
|
||||||
|
rcx.swapchain_id,
|
||||||
|
&rcx.pipeline_layout,
|
||||||
|
&rcx.sampler,
|
||||||
|
&rcx.descriptor_set_allocator,
|
||||||
|
);
|
||||||
|
|
||||||
|
rcx.task_graph
|
||||||
|
.task_node_mut(rcx.scene_node_id)
|
||||||
|
.unwrap()
|
||||||
|
.task_mut()
|
||||||
|
.downcast_mut::<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
flight.wait(None).unwrap();
|
||||||
|
|
||||||
|
let resource_map = resource_map!(
|
||||||
|
&rcx.task_graph,
|
||||||
|
rcx.virtual_swapchain_id => rcx.swapchain_id,
|
||||||
|
rcx.virtual_bloom_image_id => rcx.bloom_image_id,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
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 handle_resize(&mut self) {
|
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||||
let window_size = self.window.inner_size();
|
let rcx = self.rcx.as_mut().unwrap();
|
||||||
|
rcx.window.request_redraw();
|
||||||
self.swapchain_id = self
|
|
||||||
.resources
|
|
||||||
.recreate_swapchain(self.swapchain_id, |create_info| SwapchainCreateInfo {
|
|
||||||
image_extent: window_size.into(),
|
|
||||||
..create_info
|
|
||||||
})
|
|
||||||
.expect("failed to recreate swapchain");
|
|
||||||
|
|
||||||
let flight = self.resources.flight(self.flight_id).unwrap();
|
|
||||||
let bloom_image_state =
|
|
||||||
unsafe { self.resources.remove_image(self.bloom_image_id) }.unwrap();
|
|
||||||
flight.destroy_object(bloom_image_state.image().clone());
|
|
||||||
flight.destroy_object(self.descriptor_set.as_ref().0.clone());
|
|
||||||
|
|
||||||
(self.bloom_image_id, self.descriptor_set) = window_size_dependent_setup(
|
|
||||||
&self.resources,
|
|
||||||
self.swapchain_id,
|
|
||||||
&self.pipeline_layout,
|
|
||||||
&self.sampler,
|
|
||||||
&self.descriptor_set_allocator,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.viewport.extent = window_size.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cleanup(&mut self) {
|
|
||||||
let flight = self.resources.flight(self.flight_id).unwrap();
|
|
||||||
flight.destroy_object(self.descriptor_set.as_ref().0.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
|
@ -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<_>>()
|
|
||||||
}
|
|
||||||
|
@ -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,293 +42,342 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.enumerate_physical_devices()
|
buffer_allocator: SubbufferAllocator,
|
||||||
.unwrap()
|
rcx: Option<RenderContext>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
}
|
||||||
.filter_map(|p| {
|
|
||||||
p.queue_family_properties()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.position(|(i, q)| {
|
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
|
||||||
})
|
|
||||||
.map(|i| (p, i as u32))
|
|
||||||
})
|
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
|
||||||
PhysicalDeviceType::Cpu => 3,
|
|
||||||
PhysicalDeviceType::Other => 4,
|
|
||||||
_ => 5,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
render_pass: Arc<RenderPass>,
|
||||||
);
|
framebuffers: Vec<Arc<Framebuffer>>,
|
||||||
|
pipeline: Arc<GraphicsPipeline>,
|
||||||
|
viewport: Viewport,
|
||||||
|
recreate_swapchain: bool,
|
||||||
|
previous_frame_end: Option<Box<dyn GpuFuture>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[derive(Clone, Copy, BufferContents, Vertex)]
|
println!(
|
||||||
#[repr(C)]
|
"Using device: {} (type: {:?})",
|
||||||
struct Vertex {
|
physical_device.properties().device_name,
|
||||||
#[format(R32G32_SFLOAT)]
|
physical_device.properties().device_type,
|
||||||
position: [f32; 2],
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// Using a buffer allocator allows multiple buffers to be "in-flight" simultaneously and is
|
let (device, mut queues) = Device::new(
|
||||||
// suited to highly dynamic data like vertex, index and uniform buffers.
|
physical_device,
|
||||||
let buffer_allocator = SubbufferAllocator::new(
|
DeviceCreateInfo {
|
||||||
memory_allocator,
|
enabled_extensions: device_extensions,
|
||||||
SubbufferAllocatorCreateInfo {
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
// We want to use the allocated subbuffers as vertex buffers.
|
queue_family_index,
|
||||||
buffer_usage: BufferUsage::VERTEX_BUFFER,
|
..Default::default()
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
}],
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
..Default::default()
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
mod vs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "vertex",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod fs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "fragment",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 f_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
pass: {
|
.unwrap();
|
||||||
color: [color],
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let pipeline = {
|
let queue = queues.next().unwrap();
|
||||||
let vs = vs::load(device.clone())
|
|
||||||
.unwrap()
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
.entry_point("main")
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
.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(),
|
device.clone(),
|
||||||
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
Default::default(),
|
||||||
.into_pipeline_layout_create_info(device.clone())
|
));
|
||||||
|
|
||||||
|
// Using a buffer allocator allows multiple buffers to be "in-flight" simultaneously and is
|
||||||
|
// suited to highly dynamic data like vertex, index and uniform buffers.
|
||||||
|
let buffer_allocator = SubbufferAllocator::new(
|
||||||
|
memory_allocator,
|
||||||
|
SubbufferAllocatorCreateInfo {
|
||||||
|
// We want to use the allocated subbuffers as vertex buffers.
|
||||||
|
buffer_usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
App {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
command_buffer_allocator,
|
||||||
|
buffer_allocator,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for App {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = Arc::new(
|
||||||
|
event_loop
|
||||||
|
.create_window(Window::default_attributes())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
);
|
||||||
.unwrap();
|
let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
|
||||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
let window_size = window.inner_size();
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
let (swapchain, images) = {
|
||||||
device.clone(),
|
let surface_capabilities = self
|
||||||
None,
|
.device
|
||||||
GraphicsPipelineCreateInfo {
|
.physical_device()
|
||||||
stages: stages.into_iter().collect(),
|
.surface_capabilities(&surface, Default::default())
|
||||||
vertex_input_state: Some(vertex_input_state),
|
.unwrap();
|
||||||
input_assembly_state: Some(InputAssemblyState::default()),
|
let (image_format, _) = self
|
||||||
viewport_state: Some(ViewportState::default()),
|
.device
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
.physical_device()
|
||||||
multisample_state: Some(MultisampleState::default()),
|
.surface_formats(&surface, Default::default())
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
.unwrap()[0];
|
||||||
subpass.num_color_attachments(),
|
|
||||||
ColorBlendAttachmentState::default(),
|
Swapchain::new(
|
||||||
)),
|
self.device.clone(),
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
surface,
|
||||||
subpass: Some(subpass.into()),
|
SwapchainCreateInfo {
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
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()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
let framebuffers = window_size_dependent_setup(&images, &render_pass);
|
||||||
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(
|
mod vs {
|
||||||
device.clone(),
|
vulkano_shaders::shader! {
|
||||||
Default::default(),
|
ty: "vertex",
|
||||||
));
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
layout(location = 0) in vec2 position;
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod fs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "fragment",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 f_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pipeline = {
|
||||||
|
let vs = vs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState::default()),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default(),
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
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 {
|
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,188 +30,244 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.enumerate_physical_devices()
|
rcx: Option<RenderContext>,
|
||||||
.unwrap()
|
}
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
|
||||||
.filter_map(|p| {
|
|
||||||
p.queue_family_properties()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.position(|(i, q)| {
|
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
|
||||||
})
|
|
||||||
.map(|i| (p, i as u32))
|
|
||||||
})
|
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
|
||||||
PhysicalDeviceType::Cpu => 3,
|
|
||||||
PhysicalDeviceType::Other => 4,
|
|
||||||
_ => 5,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
render_pass: Arc<RenderPass>,
|
||||||
);
|
framebuffers: Vec<Arc<Framebuffer>>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
recreate_swapchain: bool,
|
||||||
|
previous_frame_end: Option<Box<dyn GpuFuture>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(device.clone(),
|
let device_extensions = DeviceExtensions {
|
||||||
attachments: {
|
khr_swapchain: true,
|
||||||
color: {
|
..DeviceExtensions::empty()
|
||||||
format: swapchain.image_format(),
|
};
|
||||||
samples: 1,
|
let (physical_device, queue_family_index) = instance
|
||||||
load_op: Clear,
|
.enumerate_physical_devices()
|
||||||
store_op: Store,
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Using device: {} (type: {:?})",
|
||||||
|
physical_device.properties().device_name,
|
||||||
|
physical_device.properties().device_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (device, mut queues) = Device::new(
|
||||||
|
physical_device,
|
||||||
|
DeviceCreateInfo {
|
||||||
|
enabled_extensions: device_extensions,
|
||||||
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
|
queue_family_index,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
pass: {
|
.unwrap();
|
||||||
color: [color],
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
let queue = queues.next().unwrap();
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut width = swapchain.image_extent()[0];
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
let mut height = swapchain.image_extent()[1];
|
device.clone(),
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone());
|
Default::default(),
|
||||||
|
));
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
App {
|
||||||
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
command_buffer_allocator,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 = 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 [width, height] = window_size.into();
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(
|
||||||
|
&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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,164 +53,218 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
memory_allocator: Arc<StandardMemoryAllocator>,
|
||||||
.enumerate_physical_devices()
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.unwrap()
|
rcx: Option<RenderContext>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
}
|
||||||
.filter_map(|p| {
|
|
||||||
p.queue_family_properties()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.position(|(i, q)| {
|
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
|
||||||
})
|
|
||||||
.map(|i| (p, i as u32))
|
|
||||||
})
|
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
|
||||||
PhysicalDeviceType::Cpu => 3,
|
|
||||||
PhysicalDeviceType::Other => 4,
|
|
||||||
_ => 5,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
images: Vec<Arc<ImageView>>,
|
||||||
);
|
frame_system: FrameSystem,
|
||||||
|
triangle_draw_system: TriangleDrawSystem,
|
||||||
|
recreate_swapchain: bool,
|
||||||
|
previous_frame_end: Option<Box<dyn GpuFuture>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Using device: {} (type: {:?})",
|
||||||
|
physical_device.properties().device_name,
|
||||||
|
physical_device.properties().device_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (device, mut queues) = Device::new(
|
||||||
|
physical_device,
|
||||||
|
DeviceCreateInfo {
|
||||||
|
enabled_extensions: device_extensions,
|
||||||
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
|
queue_family_index,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue = queues.next().unwrap();
|
||||||
|
|
||||||
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
|
device.clone(),
|
||||||
|
StandardCommandBufferAllocatorCreateInfo {
|
||||||
|
secondary_buffer_count: 32,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
App {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
memory_allocator,
|
||||||
|
command_buffer_allocator,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for App {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = Arc::new(
|
||||||
|
event_loop
|
||||||
|
.create_window(Window::default_attributes())
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let surface_capabilities = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_capabilities(&surface, Default::default())
|
||||||
|
.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
|
let images = images
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|image| ImageView::new_default(image).unwrap())
|
.map(|image| ImageView::new_default(image).unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
(swapchain, images)
|
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
// Here is the basic initialization for the deferred system.
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
let frame_system = FrameSystem::new(
|
||||||
device.clone(),
|
self.queue.clone(),
|
||||||
StandardCommandBufferAllocatorCreateInfo {
|
swapchain.image_format(),
|
||||||
secondary_buffer_count: 32,
|
self.memory_allocator.clone(),
|
||||||
..Default::default()
|
self.command_buffer_allocator.clone(),
|
||||||
},
|
);
|
||||||
));
|
let triangle_draw_system = TriangleDrawSystem::new(
|
||||||
|
self.queue.clone(),
|
||||||
|
frame_system.deferred_subpass(),
|
||||||
|
self.memory_allocator.clone(),
|
||||||
|
self.command_buffer_allocator.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
// Here is the basic initialization for the deferred system.
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
let mut frame_system = FrameSystem::new(
|
|
||||||
queue.clone(),
|
|
||||||
swapchain.image_format(),
|
|
||||||
memory_allocator.clone(),
|
|
||||||
command_buffer_allocator.clone(),
|
|
||||||
);
|
|
||||||
let triangle_draw_system = TriangleDrawSystem::new(
|
|
||||||
queue.clone(),
|
|
||||||
frame_system.deferred_subpass(),
|
|
||||||
memory_allocator.clone(),
|
|
||||||
command_buffer_allocator,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
self.rcx = Some(RenderContext {
|
||||||
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
window,
|
||||||
|
swapchain,
|
||||||
|
images,
|
||||||
|
frame_system,
|
||||||
|
triangle_draw_system,
|
||||||
|
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");
|
||||||
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,7 @@ fn main() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let queue = queues.next().unwrap();
|
let queue = queues.next().unwrap();
|
||||||
|
|
||||||
mod cs {
|
mod cs {
|
||||||
|
@ -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" }
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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,428 +55,482 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
.enumerate_physical_devices()
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.unwrap()
|
vertex_buffer: Subbuffer<[MyVertex]>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
texture: Arc<ImageView>,
|
||||||
.filter_map(|p| {
|
sampler: Arc<Sampler>,
|
||||||
p.queue_family_properties()
|
rcx: Option<RenderContext>,
|
||||||
.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!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
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>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex)]
|
println!(
|
||||||
#[repr(C)]
|
"Using device: {} (type: {:?})",
|
||||||
struct Vertex {
|
physical_device.properties().device_name,
|
||||||
#[format(R32G32_SFLOAT)]
|
physical_device.properties().device_type,
|
||||||
position: [f32; 2],
|
);
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = [
|
let (device, mut queues) = Device::new(
|
||||||
Vertex {
|
physical_device,
|
||||||
position: [-0.5, -0.5],
|
DeviceCreateInfo {
|
||||||
},
|
enabled_extensions: device_extensions,
|
||||||
Vertex {
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
position: [-0.5, 0.5],
|
queue_family_index,
|
||||||
},
|
..Default::default()
|
||||||
Vertex {
|
}],
|
||||||
position: [0.5, -0.5],
|
..Default::default()
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, 0.5],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
pass: {
|
.unwrap();
|
||||||
color: [color],
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
let queue = queues.next().unwrap();
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut uploads = RecordingCommandBuffer::new(
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
command_buffer_allocator.clone(),
|
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||||
queue.queue_family_index(),
|
device.clone(),
|
||||||
CommandBufferLevel::Primary,
|
Default::default(),
|
||||||
CommandBufferBeginInfo {
|
));
|
||||||
usage: CommandBufferUsage::OneTimeSubmit,
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
..Default::default()
|
device.clone(),
|
||||||
},
|
Default::default(),
|
||||||
)
|
));
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let texture = {
|
let vertices = [
|
||||||
let png_bytes = include_bytes!("image_img.png").as_slice();
|
MyVertex {
|
||||||
let decoder = png::Decoder::new(png_bytes);
|
position: [-0.5, -0.5],
|
||||||
let mut reader = decoder.read_info().unwrap();
|
},
|
||||||
let info = reader.info();
|
MyVertex {
|
||||||
let img_size = [info.width, info.height];
|
position: [-0.5, 0.5],
|
||||||
let extent = [info.width * 2, info.height * 2, 1];
|
},
|
||||||
|
MyVertex {
|
||||||
let upload_buffer = Buffer::new_slice(
|
position: [0.5, -0.5],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.5, 0.5],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
memory_allocator.clone(),
|
memory_allocator.clone(),
|
||||||
BufferCreateInfo {
|
BufferCreateInfo {
|
||||||
usage: BufferUsage::TRANSFER_SRC,
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo {
|
AllocationCreateInfo {
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
(info.width * info.height * 4) as DeviceSize,
|
vertices,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
reader
|
let mut uploads = RecordingCommandBuffer::new(
|
||||||
.next_frame(&mut upload_buffer.write().unwrap())
|
command_buffer_allocator.clone(),
|
||||||
.unwrap();
|
queue.queue_family_index(),
|
||||||
|
CommandBufferLevel::Primary,
|
||||||
let image = Image::new(
|
CommandBufferBeginInfo {
|
||||||
memory_allocator,
|
usage: CommandBufferUsage::OneTimeSubmit,
|
||||||
ImageCreateInfo {
|
|
||||||
format: Format::R8G8B8A8_UNORM,
|
|
||||||
extent,
|
|
||||||
usage: ImageUsage::TRANSFER_SRC | ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Here, we perform image copying and blitting on the same image.
|
let texture = {
|
||||||
uploads
|
let png_bytes = include_bytes!("image_img.png").as_slice();
|
||||||
// Clear the image buffer.
|
let decoder = png::Decoder::new(png_bytes);
|
||||||
.clear_color_image(ClearColorImageInfo::image(image.clone()))
|
let mut reader = decoder.read_info().unwrap();
|
||||||
.unwrap()
|
let info = reader.info();
|
||||||
// Put our image in the top left corner.
|
let img_size = [info.width, info.height];
|
||||||
.copy_buffer_to_image(CopyBufferToImageInfo {
|
let extent = [info.width * 2, info.height * 2, 1];
|
||||||
regions: [BufferImageCopy {
|
|
||||||
image_subresource: image.subresource_layers(),
|
let upload_buffer = Buffer::new_slice(
|
||||||
image_extent: [img_size[0], img_size[1], 1],
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::TRANSFER_SRC,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}]
|
},
|
||||||
.into(),
|
AllocationCreateInfo {
|
||||||
..CopyBufferToImageInfo::buffer_image(upload_buffer, image.clone())
|
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||||
})
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
.unwrap()
|
|
||||||
// Copy from the top left corner to the bottom right corner.
|
|
||||||
.copy_image(CopyImageInfo {
|
|
||||||
// Copying within the same image requires the General layout if the source and
|
|
||||||
// destination subresources overlap.
|
|
||||||
src_image_layout: ImageLayout::General,
|
|
||||||
dst_image_layout: ImageLayout::General,
|
|
||||||
regions: [ImageCopy {
|
|
||||||
src_subresource: image.subresource_layers(),
|
|
||||||
src_offset: [0, 0, 0],
|
|
||||||
dst_subresource: image.subresource_layers(),
|
|
||||||
dst_offset: [img_size[0], img_size[1], 0],
|
|
||||||
extent: [img_size[0], img_size[1], 1],
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}]
|
},
|
||||||
.into(),
|
(info.width * info.height * 4) as DeviceSize,
|
||||||
..CopyImageInfo::images(image.clone(), image.clone())
|
)
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
// Blit from the bottom right corner to the top right corner (flipped).
|
|
||||||
.blit_image(BlitImageInfo {
|
|
||||||
// Same as above applies for blitting.
|
|
||||||
src_image_layout: ImageLayout::General,
|
|
||||||
dst_image_layout: ImageLayout::General,
|
|
||||||
regions: [ImageBlit {
|
|
||||||
src_subresource: image.subresource_layers(),
|
|
||||||
src_offsets: [
|
|
||||||
[img_size[0], img_size[1], 0],
|
|
||||||
[img_size[0] * 2, img_size[1] * 2, 1],
|
|
||||||
],
|
|
||||||
dst_subresource: image.subresource_layers(),
|
|
||||||
// Swapping the two corners results in flipped image.
|
|
||||||
dst_offsets: [
|
|
||||||
[img_size[0] * 2 - 1, img_size[1] - 1, 0],
|
|
||||||
[img_size[0], 0, 1],
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
}]
|
|
||||||
.into(),
|
|
||||||
filter: Filter::Nearest,
|
|
||||||
..BlitImageInfo::images(image.clone(), image.clone())
|
|
||||||
})
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
ImageView::new_default(image).unwrap()
|
reader
|
||||||
};
|
.next_frame(&mut upload_buffer.write().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let sampler = Sampler::new(
|
let image = Image::new(
|
||||||
device.clone(),
|
memory_allocator,
|
||||||
SamplerCreateInfo {
|
ImageCreateInfo {
|
||||||
mag_filter: Filter::Linear,
|
format: Format::R8G8B8A8_UNORM,
|
||||||
min_filter: Filter::Linear,
|
extent,
|
||||||
address_mode: [SamplerAddressMode::Repeat; 3],
|
usage: ImageUsage::TRANSFER_SRC
|
||||||
..Default::default()
|
| ImageUsage::TRANSFER_DST
|
||||||
},
|
| ImageUsage::SAMPLED,
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let pipeline = {
|
|
||||||
let vs = vs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let fs = fs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let vertex_input_state = 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.clone(), 0).unwrap();
|
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
|
||||||
device.clone(),
|
|
||||||
None,
|
|
||||||
GraphicsPipelineCreateInfo {
|
|
||||||
stages: stages.into_iter().collect(),
|
|
||||||
vertex_input_state: Some(vertex_input_state),
|
|
||||||
input_assembly_state: Some(InputAssemblyState {
|
|
||||||
topology: PrimitiveTopology::TriangleStrip,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
},
|
||||||
viewport_state: Some(ViewportState::default()),
|
AllocationCreateInfo::default(),
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
)
|
||||||
multisample_state: Some(MultisampleState::default()),
|
.unwrap();
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
|
||||||
subpass.num_color_attachments(),
|
// Here, we perform image copying and blitting on the same image.
|
||||||
ColorBlendAttachmentState {
|
uploads
|
||||||
blend: Some(AttachmentBlend::alpha()),
|
// Clear the image buffer.
|
||||||
|
.clear_color_image(ClearColorImageInfo::image(image.clone()))
|
||||||
|
.unwrap()
|
||||||
|
// Put our image in the top left corner.
|
||||||
|
.copy_buffer_to_image(CopyBufferToImageInfo {
|
||||||
|
regions: [BufferImageCopy {
|
||||||
|
image_subresource: image.subresource_layers(),
|
||||||
|
image_extent: [img_size[0], img_size[1], 1],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
}]
|
||||||
)),
|
.into(),
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
..CopyBufferToImageInfo::buffer_image(upload_buffer, image.clone())
|
||||||
subpass: Some(subpass.into()),
|
})
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
.unwrap()
|
||||||
|
// Copy from the top left corner to the bottom right corner.
|
||||||
|
.copy_image(CopyImageInfo {
|
||||||
|
// Copying within the same image requires the General layout if the source and
|
||||||
|
// destination subresources overlap.
|
||||||
|
src_image_layout: ImageLayout::General,
|
||||||
|
dst_image_layout: ImageLayout::General,
|
||||||
|
regions: [ImageCopy {
|
||||||
|
src_subresource: image.subresource_layers(),
|
||||||
|
src_offset: [0, 0, 0],
|
||||||
|
dst_subresource: image.subresource_layers(),
|
||||||
|
dst_offset: [img_size[0], img_size[1], 0],
|
||||||
|
extent: [img_size[0], img_size[1], 1],
|
||||||
|
..Default::default()
|
||||||
|
}]
|
||||||
|
.into(),
|
||||||
|
..CopyImageInfo::images(image.clone(), image.clone())
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
// Blit from the bottom right corner to the top right corner (flipped).
|
||||||
|
.blit_image(BlitImageInfo {
|
||||||
|
// Same as above applies for blitting.
|
||||||
|
src_image_layout: ImageLayout::General,
|
||||||
|
dst_image_layout: ImageLayout::General,
|
||||||
|
regions: [ImageBlit {
|
||||||
|
src_subresource: image.subresource_layers(),
|
||||||
|
src_offsets: [
|
||||||
|
[img_size[0], img_size[1], 0],
|
||||||
|
[img_size[0] * 2, img_size[1] * 2, 1],
|
||||||
|
],
|
||||||
|
dst_subresource: image.subresource_layers(),
|
||||||
|
// Swapping the two corners results in flipped image.
|
||||||
|
dst_offsets: [
|
||||||
|
[img_size[0] * 2 - 1, img_size[1] - 1, 0],
|
||||||
|
[img_size[0], 0, 1],
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
}]
|
||||||
|
.into(),
|
||||||
|
filter: Filter::Nearest,
|
||||||
|
..BlitImageInfo::images(image.clone(), image.clone())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
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()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let layout = &pipeline.layout().set_layouts()[0];
|
let _ = uploads.end().unwrap().execute(queue.clone()).unwrap();
|
||||||
let set = DescriptorSet::new(
|
|
||||||
descriptor_set_allocator,
|
|
||||||
layout.clone(),
|
|
||||||
[
|
|
||||||
WriteDescriptorSet::sampler(0, sampler),
|
|
||||||
WriteDescriptorSet::image_view(1, texture),
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
App {
|
||||||
offset: [0.0, 0.0],
|
instance,
|
||||||
extent: [0.0, 0.0],
|
device,
|
||||||
depth_range: 0.0..=1.0,
|
queue,
|
||||||
};
|
descriptor_set_allocator,
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
|
command_buffer_allocator,
|
||||||
|
vertex_buffer,
|
||||||
|
texture,
|
||||||
|
sampler,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
impl ApplicationHandler for App {
|
||||||
let mut previous_frame_end = Some(
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
uploads
|
let window = Arc::new(
|
||||||
.end()
|
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()
|
.unwrap()
|
||||||
.execute(queue.clone())
|
};
|
||||||
.unwrap()
|
|
||||||
.boxed(),
|
|
||||||
);
|
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
let render_pass = vulkano::single_pass_renderpass!(
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
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 fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState {
|
||||||
|
topology: PrimitiveTopology::TriangleStrip,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState {
|
||||||
|
blend: Some(AttachmentBlend::alpha()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let layout = &pipeline.layout().set_layouts()[0];
|
||||||
|
let descriptor_set = DescriptorSet::new(
|
||||||
|
self.descriptor_set_allocator.clone(),
|
||||||
|
layout.clone(),
|
||||||
|
[
|
||||||
|
WriteDescriptorSet::sampler(0, self.sampler.clone()),
|
||||||
|
WriteDescriptorSet::image_view(1, self.texture.clone()),
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
descriptor_set,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,377 +53,427 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
.enumerate_physical_devices()
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.unwrap()
|
vertex_buffer: Subbuffer<[MyVertex]>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
texture: Arc<ImageView>,
|
||||||
.filter_map(|p| {
|
sampler: Arc<Sampler>,
|
||||||
p.queue_family_properties()
|
rcx: Option<RenderContext>,
|
||||||
.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!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
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>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex)]
|
println!(
|
||||||
#[repr(C)]
|
"Using device: {} (type: {:?})",
|
||||||
struct Vertex {
|
physical_device.properties().device_name,
|
||||||
#[format(R32G32_SFLOAT)]
|
physical_device.properties().device_type,
|
||||||
position: [f32; 2],
|
);
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = [
|
let (device, mut queues) = Device::new(
|
||||||
Vertex {
|
physical_device,
|
||||||
position: [-0.5, -0.5],
|
DeviceCreateInfo {
|
||||||
},
|
enabled_extensions: device_extensions,
|
||||||
Vertex {
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
position: [-0.5, 0.5],
|
queue_family_index,
|
||||||
},
|
..Default::default()
|
||||||
Vertex {
|
}],
|
||||||
position: [0.5, -0.5],
|
..Default::default()
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, 0.5],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
pass: {
|
.unwrap();
|
||||||
color: [color],
|
let queue = queues.next().unwrap();
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
device.clone(),
|
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||||
Default::default(),
|
device.clone(),
|
||||||
));
|
Default::default(),
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
));
|
||||||
device.clone(),
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
Default::default(),
|
device.clone(),
|
||||||
));
|
Default::default(),
|
||||||
|
));
|
||||||
|
|
||||||
let mut uploads = RecordingCommandBuffer::new(
|
let vertices = [
|
||||||
command_buffer_allocator.clone(),
|
MyVertex {
|
||||||
queue.queue_family_index(),
|
position: [-0.5, -0.5],
|
||||||
CommandBufferLevel::Primary,
|
},
|
||||||
CommandBufferBeginInfo {
|
MyVertex {
|
||||||
usage: CommandBufferUsage::OneTimeSubmit,
|
position: [-0.5, 0.5],
|
||||||
..Default::default()
|
},
|
||||||
},
|
MyVertex {
|
||||||
)
|
position: [0.5, -0.5],
|
||||||
.unwrap();
|
},
|
||||||
|
MyVertex {
|
||||||
let texture = {
|
position: [0.5, 0.5],
|
||||||
let png_bytes = include_bytes!("image_img.png").as_slice();
|
},
|
||||||
let decoder = png::Decoder::new(png_bytes);
|
];
|
||||||
let mut reader = decoder.read_info().unwrap();
|
let vertex_buffer = Buffer::from_iter(
|
||||||
let info = reader.info();
|
|
||||||
let extent = [info.width, info.height, 1];
|
|
||||||
|
|
||||||
let upload_buffer = Buffer::new_slice(
|
|
||||||
memory_allocator.clone(),
|
memory_allocator.clone(),
|
||||||
BufferCreateInfo {
|
BufferCreateInfo {
|
||||||
usage: BufferUsage::TRANSFER_SRC,
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo {
|
AllocationCreateInfo {
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
(info.width * info.height * 4) as DeviceSize,
|
vertices,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
reader
|
let mut uploads = RecordingCommandBuffer::new(
|
||||||
.next_frame(&mut upload_buffer.write().unwrap())
|
command_buffer_allocator.clone(),
|
||||||
.unwrap();
|
queue.queue_family_index(),
|
||||||
|
CommandBufferLevel::Primary,
|
||||||
let image = Image::new(
|
CommandBufferBeginInfo {
|
||||||
memory_allocator,
|
usage: CommandBufferUsage::OneTimeSubmit,
|
||||||
ImageCreateInfo {
|
|
||||||
image_type: ImageType::Dim2d,
|
|
||||||
format: Format::R8G8B8A8_SRGB,
|
|
||||||
extent,
|
|
||||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
uploads
|
let texture = {
|
||||||
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
let png_bytes = include_bytes!("image_img.png").as_slice();
|
||||||
upload_buffer,
|
let decoder = png::Decoder::new(png_bytes);
|
||||||
image.clone(),
|
let mut reader = decoder.read_info().unwrap();
|
||||||
))
|
let info = reader.info();
|
||||||
.unwrap();
|
let extent = [info.width, info.height, 1];
|
||||||
|
|
||||||
ImageView::new_default(image).unwrap()
|
let upload_buffer = Buffer::new_slice(
|
||||||
};
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
let sampler = Sampler::new(
|
usage: BufferUsage::TRANSFER_SRC,
|
||||||
device.clone(),
|
|
||||||
SamplerCreateInfo {
|
|
||||||
mag_filter: Filter::Linear,
|
|
||||||
min_filter: Filter::Linear,
|
|
||||||
address_mode: [SamplerAddressMode::Repeat; 3],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let pipeline = {
|
|
||||||
let vs = vs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let fs = fs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let vertex_input_state = 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.clone(), 0).unwrap();
|
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
|
||||||
device.clone(),
|
|
||||||
None,
|
|
||||||
GraphicsPipelineCreateInfo {
|
|
||||||
stages: stages.into_iter().collect(),
|
|
||||||
vertex_input_state: Some(vertex_input_state),
|
|
||||||
input_assembly_state: Some(InputAssemblyState {
|
|
||||||
topology: PrimitiveTopology::TriangleStrip,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
},
|
||||||
viewport_state: Some(ViewportState::default()),
|
AllocationCreateInfo {
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||||
multisample_state: Some(MultisampleState::default()),
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
..Default::default()
|
||||||
subpass.num_color_attachments(),
|
},
|
||||||
ColorBlendAttachmentState {
|
(info.width * info.height * 4) as DeviceSize,
|
||||||
blend: Some(AttachmentBlend::alpha()),
|
)
|
||||||
..Default::default()
|
.unwrap();
|
||||||
},
|
|
||||||
)),
|
reader
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
.next_frame(&mut upload_buffer.write().unwrap())
|
||||||
subpass: Some(subpass.into()),
|
.unwrap();
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
|
||||||
|
let image = Image::new(
|
||||||
|
memory_allocator,
|
||||||
|
ImageCreateInfo {
|
||||||
|
image_type: ImageType::Dim2d,
|
||||||
|
format: Format::R8G8B8A8_SRGB,
|
||||||
|
extent,
|
||||||
|
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
uploads
|
||||||
|
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
||||||
|
upload_buffer,
|
||||||
|
image.clone(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ImageView::new_default(image).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let sampler = Sampler::new(
|
||||||
|
device.clone(),
|
||||||
|
SamplerCreateInfo {
|
||||||
|
mag_filter: Filter::Linear,
|
||||||
|
min_filter: Filter::Linear,
|
||||||
|
address_mode: [SamplerAddressMode::Repeat; 3],
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let layout = &pipeline.layout().set_layouts()[0];
|
let _ = uploads.end().unwrap().execute(queue.clone()).unwrap();
|
||||||
let set = DescriptorSet::new(
|
|
||||||
descriptor_set_allocator,
|
|
||||||
layout.clone(),
|
|
||||||
[
|
|
||||||
WriteDescriptorSet::sampler(0, sampler),
|
|
||||||
WriteDescriptorSet::image_view(1, texture),
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
App {
|
||||||
offset: [0.0, 0.0],
|
instance,
|
||||||
extent: [0.0, 0.0],
|
device,
|
||||||
depth_range: 0.0..=1.0,
|
queue,
|
||||||
};
|
descriptor_set_allocator,
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
|
command_buffer_allocator,
|
||||||
|
vertex_buffer,
|
||||||
|
texture,
|
||||||
|
sampler,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
impl ApplicationHandler for App {
|
||||||
let mut previous_frame_end = Some(
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
uploads
|
let window = Arc::new(
|
||||||
.end()
|
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()
|
.unwrap()
|
||||||
.execute(queue.clone())
|
};
|
||||||
.unwrap()
|
|
||||||
.boxed(),
|
|
||||||
);
|
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
let render_pass = vulkano::single_pass_renderpass!(
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
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 fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState {
|
||||||
|
topology: PrimitiveTopology::TriangleStrip,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState {
|
||||||
|
blend: Some(AttachmentBlend::alpha()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let layout = &pipeline.layout().set_layouts()[0];
|
||||||
|
let descriptor_set = DescriptorSet::new(
|
||||||
|
self.descriptor_set_allocator.clone(),
|
||||||
|
layout.clone(),
|
||||||
|
[
|
||||||
|
WriteDescriptorSet::sampler(0, self.sampler.clone()),
|
||||||
|
WriteDescriptorSet::image_view(1, self.texture.clone()),
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
descriptor_set,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,396 +51,448 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
.enumerate_physical_devices()
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.unwrap()
|
vertex_buffer: Subbuffer<[MyVertex]>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
texture: Arc<ImageView>,
|
||||||
.filter_map(|p| {
|
sampler: Arc<Sampler>,
|
||||||
p.queue_family_properties()
|
rcx: Option<RenderContext>,
|
||||||
.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!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
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>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex)]
|
println!(
|
||||||
#[repr(C)]
|
"Using device: {} (type: {:?})",
|
||||||
struct Vertex {
|
physical_device.properties().device_name,
|
||||||
#[format(R32G32_SFLOAT)]
|
physical_device.properties().device_type,
|
||||||
position: [f32; 2],
|
);
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = [
|
let (device, mut queues) = Device::new(
|
||||||
Vertex {
|
physical_device,
|
||||||
position: [-0.5, -0.5],
|
DeviceCreateInfo {
|
||||||
},
|
enabled_extensions: device_extensions,
|
||||||
Vertex {
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
position: [-0.5, 0.5],
|
queue_family_index,
|
||||||
},
|
..Default::default()
|
||||||
Vertex {
|
}],
|
||||||
position: [0.5, -0.5],
|
..Default::default()
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, 0.5],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
pass: {
|
.unwrap();
|
||||||
color: [color],
|
let queue = queues.next().unwrap();
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
device.clone(),
|
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||||
Default::default(),
|
device.clone(),
|
||||||
));
|
Default::default(),
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
));
|
||||||
device.clone(),
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
Default::default(),
|
device.clone(),
|
||||||
));
|
Default::default(),
|
||||||
|
));
|
||||||
|
|
||||||
let mut uploads = RecordingCommandBuffer::new(
|
let vertices = [
|
||||||
command_buffer_allocator.clone(),
|
MyVertex {
|
||||||
queue.queue_family_index(),
|
position: [-0.5, -0.5],
|
||||||
CommandBufferLevel::Primary,
|
},
|
||||||
CommandBufferBeginInfo {
|
MyVertex {
|
||||||
usage: CommandBufferUsage::OneTimeSubmit,
|
position: [-0.5, 0.5],
|
||||||
..Default::default()
|
},
|
||||||
},
|
MyVertex {
|
||||||
)
|
position: [0.5, -0.5],
|
||||||
.unwrap();
|
},
|
||||||
|
MyVertex {
|
||||||
let texture = {
|
position: [0.5, 0.5],
|
||||||
let png_bytes = include_bytes!("image_img.png").as_slice();
|
},
|
||||||
let decoder = png::Decoder::new(png_bytes);
|
];
|
||||||
let mut reader = decoder.read_info().unwrap();
|
let vertex_buffer = Buffer::from_iter(
|
||||||
let info = reader.info();
|
|
||||||
let extent = [info.width, info.height, 1];
|
|
||||||
|
|
||||||
let upload_buffer = Buffer::new_slice(
|
|
||||||
memory_allocator.clone(),
|
memory_allocator.clone(),
|
||||||
BufferCreateInfo {
|
BufferCreateInfo {
|
||||||
usage: BufferUsage::TRANSFER_SRC,
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo {
|
AllocationCreateInfo {
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
(info.width * info.height * 4) as DeviceSize,
|
vertices,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
reader
|
let mut uploads = RecordingCommandBuffer::new(
|
||||||
.next_frame(&mut upload_buffer.write().unwrap())
|
command_buffer_allocator.clone(),
|
||||||
.unwrap();
|
queue.queue_family_index(),
|
||||||
|
CommandBufferLevel::Primary,
|
||||||
let image = Image::new(
|
CommandBufferBeginInfo {
|
||||||
memory_allocator,
|
usage: CommandBufferUsage::OneTimeSubmit,
|
||||||
ImageCreateInfo {
|
|
||||||
image_type: ImageType::Dim2d,
|
|
||||||
format: Format::R8G8B8A8_SRGB,
|
|
||||||
extent,
|
|
||||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
uploads
|
let texture = {
|
||||||
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
let png_bytes = include_bytes!("image_img.png").as_slice();
|
||||||
upload_buffer,
|
let decoder = png::Decoder::new(png_bytes);
|
||||||
image.clone(),
|
let mut reader = decoder.read_info().unwrap();
|
||||||
))
|
let info = reader.info();
|
||||||
|
let extent = [info.width, info.height, 1];
|
||||||
|
|
||||||
|
let upload_buffer = Buffer::new_slice(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::TRANSFER_SRC,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
(info.width * info.height * 4) as DeviceSize,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
ImageView::new_default(image).unwrap()
|
reader
|
||||||
};
|
.next_frame(&mut upload_buffer.write().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let sampler = Sampler::new(
|
let image = Image::new(
|
||||||
device.clone(),
|
memory_allocator,
|
||||||
SamplerCreateInfo {
|
ImageCreateInfo {
|
||||||
mag_filter: Filter::Linear,
|
image_type: ImageType::Dim2d,
|
||||||
min_filter: Filter::Linear,
|
format: Format::R8G8B8A8_SRGB,
|
||||||
address_mode: [SamplerAddressMode::Repeat; 3],
|
extent,
|
||||||
..Default::default()
|
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
||||||
},
|
..Default::default()
|
||||||
)
|
},
|
||||||
.unwrap();
|
AllocationCreateInfo::default(),
|
||||||
|
)
|
||||||
let pipeline = {
|
|
||||||
let vs = vs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
.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 = {
|
|
||||||
let mut layout_create_info =
|
|
||||||
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages);
|
|
||||||
|
|
||||||
// Modify the auto-generated layout by setting an immutable sampler to set 0 binding 0.
|
uploads
|
||||||
layout_create_info.set_layouts[0]
|
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
||||||
.bindings
|
upload_buffer,
|
||||||
.get_mut(&0)
|
image.clone(),
|
||||||
.unwrap()
|
))
|
||||||
.immutable_samplers = vec![sampler];
|
.unwrap();
|
||||||
|
|
||||||
PipelineLayout::new(
|
ImageView::new_default(image).unwrap()
|
||||||
device.clone(),
|
};
|
||||||
layout_create_info
|
|
||||||
.into_pipeline_layout_create_info(device.clone())
|
let sampler = Sampler::new(
|
||||||
.unwrap(),
|
device.clone(),
|
||||||
|
SamplerCreateInfo {
|
||||||
|
mag_filter: Filter::Linear,
|
||||||
|
min_filter: Filter::Linear,
|
||||||
|
address_mode: [SamplerAddressMode::Repeat; 3],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let _ = uploads.end().unwrap().execute(queue.clone()).unwrap();
|
||||||
|
|
||||||
|
App {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
descriptor_set_allocator,
|
||||||
|
command_buffer_allocator,
|
||||||
|
vertex_buffer,
|
||||||
|
texture,
|
||||||
|
sampler,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for App {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = Arc::new(
|
||||||
|
event_loop
|
||||||
|
.create_window(Window::default_attributes())
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let surface_capabilities = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_capabilities(&surface, Default::default())
|
||||||
|
.unwrap();
|
||||||
|
let (image_format, _) = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_formats(&surface, Default::default())
|
||||||
|
.unwrap()[0];
|
||||||
|
|
||||||
|
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()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
let render_pass = vulkano::single_pass_renderpass!(
|
||||||
device.clone(),
|
self.device.clone(),
|
||||||
None,
|
attachments: {
|
||||||
GraphicsPipelineCreateInfo {
|
color: {
|
||||||
stages: stages.into_iter().collect(),
|
format: swapchain.image_format(),
|
||||||
vertex_input_state: Some(vertex_input_state),
|
samples: 1,
|
||||||
input_assembly_state: Some(InputAssemblyState {
|
load_op: Clear,
|
||||||
topology: PrimitiveTopology::TriangleStrip,
|
store_op: Store,
|
||||||
..Default::default()
|
},
|
||||||
}),
|
},
|
||||||
viewport_state: Some(ViewportState::default()),
|
pass: {
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
color: [color],
|
||||||
multisample_state: Some(MultisampleState::default()),
|
depth_stencil: {},
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
|
||||||
subpass.num_color_attachments(),
|
|
||||||
ColorBlendAttachmentState {
|
|
||||||
blend: Some(AttachmentBlend::alpha()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let layout = &pipeline.layout().set_layouts()[0];
|
let framebuffers = window_size_dependent_setup(&images, &render_pass);
|
||||||
|
|
||||||
// Use `image_view` instead of `image_view_sampler`, since the sampler is already in the
|
let pipeline = {
|
||||||
// layout.
|
let vs = vs::load(self.device.clone())
|
||||||
let set = DescriptorSet::new(
|
.unwrap()
|
||||||
descriptor_set_allocator,
|
.entry_point("main")
|
||||||
layout.clone(),
|
.unwrap();
|
||||||
[WriteDescriptorSet::image_view(1, texture)],
|
let fs = fs::load(self.device.clone())
|
||||||
[],
|
.unwrap()
|
||||||
)
|
.entry_point("main")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = {
|
||||||
|
let mut layout_create_info =
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages);
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
// Modify the auto-generated layout by setting an immutable sampler to set 0
|
||||||
offset: [0.0, 0.0],
|
// binding 0.
|
||||||
extent: [0.0, 0.0],
|
layout_create_info.set_layouts[0]
|
||||||
depth_range: 0.0..=1.0,
|
.bindings
|
||||||
};
|
.get_mut(&0)
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
|
.unwrap()
|
||||||
|
.immutable_samplers = vec![self.sampler.clone()];
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
PipelineLayout::new(
|
||||||
let mut previous_frame_end = Some(
|
self.device.clone(),
|
||||||
uploads
|
layout_create_info
|
||||||
.end()
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState {
|
||||||
|
topology: PrimitiveTopology::TriangleStrip,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState {
|
||||||
|
blend: Some(AttachmentBlend::alpha()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute(queue.clone())
|
};
|
||||||
.unwrap()
|
|
||||||
.boxed(),
|
|
||||||
);
|
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
let layout = &pipeline.layout().set_layouts()[0];
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
|
||||||
|
// Use `image_view` instead of `image_view_sampler`, since the sampler is already in the
|
||||||
|
// layout.
|
||||||
|
let descriptor_set = DescriptorSet::new(
|
||||||
|
self.descriptor_set_allocator.clone(),
|
||||||
|
layout.clone(),
|
||||||
|
[WriteDescriptorSet::image_view(1, self.texture.clone())],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
descriptor_set,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,373 +58,426 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
khr_storage_buffer_storage_class: true,
|
device: Arc<Device>,
|
||||||
..DeviceExtensions::empty()
|
queue: Arc<Queue>,
|
||||||
};
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
let (physical_device, queue_family_index) = instance
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.enumerate_physical_devices()
|
indirect_buffer_allocator: SubbufferAllocator,
|
||||||
.unwrap()
|
vertex_buffer_allocator: SubbufferAllocator,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
compute_pipeline: Arc<ComputePipeline>,
|
||||||
.filter_map(|p| {
|
rcx: Option<RenderContext>,
|
||||||
p.queue_family_properties()
|
}
|
||||||
.iter()
|
|
||||||
.enumerate()
|
struct RenderContext {
|
||||||
.position(|(i, q)| {
|
window: Arc<Window>,
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
swapchain: Arc<Swapchain>,
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
render_pass: Arc<RenderPass>,
|
||||||
})
|
framebuffers: Vec<Arc<Framebuffer>>,
|
||||||
.map(|i| (p, i as u32))
|
pipeline: Arc<GraphicsPipeline>,
|
||||||
})
|
viewport: Viewport,
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
recreate_swapchain: bool,
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
previous_frame_end: Option<Box<dyn GpuFuture>>,
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
}
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
|
||||||
PhysicalDeviceType::Cpu => 3,
|
impl App {
|
||||||
PhysicalDeviceType::Other => 4,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
_ => 5,
|
let library = VulkanLibrary::new().unwrap();
|
||||||
})
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
|
let instance = Instance::new(
|
||||||
|
library,
|
||||||
|
InstanceCreateInfo {
|
||||||
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
|
enabled_extensions: required_extensions,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
println!(
|
let device_extensions = DeviceExtensions {
|
||||||
"Using device: {} (type: {:?})",
|
khr_swapchain: true,
|
||||||
physical_device.properties().device_name,
|
khr_storage_buffer_storage_class: true,
|
||||||
physical_device.properties().device_type,
|
..DeviceExtensions::empty()
|
||||||
);
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
let (device, mut queues) = Device::new(
|
.enumerate_physical_devices()
|
||||||
physical_device,
|
.unwrap()
|
||||||
DeviceCreateInfo {
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
enabled_extensions: device_extensions,
|
.filter_map(|p| {
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
p.queue_family_properties()
|
||||||
queue_family_index,
|
.iter()
|
||||||
..Default::default()
|
.enumerate()
|
||||||
}],
|
.position(|(i, q)| {
|
||||||
..Default::default()
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
},
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
)
|
})
|
||||||
.unwrap();
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
let queue = queues.next().unwrap();
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap();
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
let (mut swapchain, images) = {
|
PhysicalDeviceType::Other => 4,
|
||||||
let surface_capabilities = device
|
_ => 5,
|
||||||
.physical_device()
|
})
|
||||||
.surface_capabilities(&surface, Default::default())
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let image_format = device
|
|
||||||
.physical_device()
|
|
||||||
.surface_formats(&surface, Default::default())
|
|
||||||
.unwrap()[0]
|
|
||||||
.0;
|
|
||||||
|
|
||||||
Swapchain::new(
|
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(),
|
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(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
SubbufferAllocatorCreateInfo {
|
||||||
|
buffer_usage: BufferUsage::INDIRECT_BUFFER | BufferUsage::STORAGE_BUFFER,
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let vertex_buffer_allocator = SubbufferAllocator::new(
|
||||||
|
memory_allocator,
|
||||||
|
SubbufferAllocatorCreateInfo {
|
||||||
|
buffer_usage: BufferUsage::STORAGE_BUFFER | BufferUsage::VERTEX_BUFFER,
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// A simple compute shader that generates vertices. It has two buffers bound: the first is
|
||||||
|
// where we output the vertices, the second is the `IndirectDrawArgs` struct we passed the
|
||||||
|
// `draw_indirect` so we can set the number to vertices to draw.
|
||||||
|
mod cs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "compute",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(local_size_x = 16, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) buffer Output {
|
||||||
|
vec2 pos[];
|
||||||
|
} triangles;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 1) buffer IndirectDrawArgs {
|
||||||
|
uint vertices;
|
||||||
|
uint unused0;
|
||||||
|
uint unused1;
|
||||||
|
uint unused2;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint idx = gl_GlobalInvocationID.x;
|
||||||
|
|
||||||
|
// Each invocation of the compute shader is going to increment the counter,
|
||||||
|
// so we need to use atomic operations for safety. The previous value of
|
||||||
|
// the counter is returned so that gives us the offset into the vertex
|
||||||
|
// buffer this thread can write it's vertices into.
|
||||||
|
uint offset = atomicAdd(vertices, 6);
|
||||||
|
|
||||||
|
vec2 center = vec2(-0.8, -0.8) + idx * vec2(0.1, 0.1);
|
||||||
|
triangles.pos[offset + 0] = center + vec2(0.0, 0.0375);
|
||||||
|
triangles.pos[offset + 1] = center + vec2(0.025, -0.01725);
|
||||||
|
triangles.pos[offset + 2] = center + vec2(-0.025, -0.01725);
|
||||||
|
triangles.pos[offset + 3] = center + vec2(0.0, -0.0375);
|
||||||
|
triangles.pos[offset + 4] = center + vec2(0.025, 0.01725);
|
||||||
|
triangles.pos[offset + 5] = center + vec2(-0.025, 0.01725);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let compute_pipeline = {
|
||||||
|
let cs = cs::load(device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let stage = PipelineShaderStageCreateInfo::new(cs);
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage])
|
||||||
|
.into_pipeline_layout_create_info(device.clone())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
..Default::default()
|
)
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
mod vs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "vertex",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
// The triangle vertex positions.
|
|
||||||
layout(location = 0) in vec2 position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod fs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "fragment",
|
|
||||||
src: r#"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 f_color;
|
|
||||||
|
|
||||||
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
|
|
||||||
// where we output the vertices, the second is the `IndirectDrawArgs` struct we passed the
|
|
||||||
// `draw_indirect` so we can set the number to vertices to draw.
|
|
||||||
mod cs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "compute",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(local_size_x = 16, local_size_y = 1, local_size_z = 1) in;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) buffer Output {
|
|
||||||
vec2 pos[];
|
|
||||||
} triangles;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 1) buffer IndirectDrawArgs {
|
|
||||||
uint vertices;
|
|
||||||
uint unused0;
|
|
||||||
uint unused1;
|
|
||||||
uint unused2;
|
|
||||||
};
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
uint idx = gl_GlobalInvocationID.x;
|
|
||||||
|
|
||||||
// Each invocation of the compute shader is going to increment the counter, so
|
|
||||||
// we need to use atomic operations for safety. The previous value of the
|
|
||||||
// counter is returned so that gives us the offset into the vertex buffer this
|
|
||||||
// thread can write it's vertices into.
|
|
||||||
uint offset = atomicAdd(vertices, 6);
|
|
||||||
|
|
||||||
vec2 center = vec2(-0.8, -0.8) + idx * vec2(0.1, 0.1);
|
|
||||||
triangles.pos[offset + 0] = center + vec2(0.0, 0.0375);
|
|
||||||
triangles.pos[offset + 1] = center + vec2(0.025, -0.01725);
|
|
||||||
triangles.pos[offset + 2] = center + vec2(-0.025, -0.01725);
|
|
||||||
triangles.pos[offset + 3] = center + vec2(0.0, -0.0375);
|
|
||||||
triangles.pos[offset + 4] = center + vec2(0.025, 0.01725);
|
|
||||||
triangles.pos[offset + 5] = center + vec2(-0.025, 0.01725);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
|
||||||
|
|
||||||
// Each frame we generate a new set of vertices and each frame we need a new
|
|
||||||
// DrawIndirectCommand struct to set the number of vertices to draw.
|
|
||||||
let indirect_args_pool = SubbufferAllocator::new(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
SubbufferAllocatorCreateInfo {
|
|
||||||
buffer_usage: BufferUsage::INDIRECT_BUFFER | BufferUsage::STORAGE_BUFFER,
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let vertex_pool = SubbufferAllocator::new(
|
|
||||||
memory_allocator,
|
|
||||||
SubbufferAllocatorCreateInfo {
|
|
||||||
buffer_usage: BufferUsage::STORAGE_BUFFER | BufferUsage::VERTEX_BUFFER,
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let compute_pipeline = {
|
|
||||||
let cs = cs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let stage = PipelineShaderStageCreateInfo::new(cs);
|
ComputePipeline::new(
|
||||||
let layout = PipelineLayout::new(
|
device.clone(),
|
||||||
device.clone(),
|
None,
|
||||||
PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage])
|
ComputePipelineCreateInfo::stage_layout(stage, layout),
|
||||||
.into_pipeline_layout_create_info(device.clone())
|
)
|
||||||
|
.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(),
|
.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();
|
.unwrap();
|
||||||
ComputePipeline::new(
|
|
||||||
device.clone(),
|
|
||||||
None,
|
|
||||||
ComputePipelineCreateInfo::stage_layout(stage, layout),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let render_pass = single_pass_renderpass!(
|
let framebuffers = window_size_dependent_setup(&images, &render_pass);
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pass: {
|
|
||||||
color: [color],
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// `Vertex` is the vertex type that will be output from the compute shader and be input to the
|
mod vs {
|
||||||
// vertex shader.
|
vulkano_shaders::shader! {
|
||||||
#[derive(BufferContents, Vertex)]
|
ty: "vertex",
|
||||||
#[repr(C)]
|
src: r"
|
||||||
struct Vertex {
|
#version 450
|
||||||
#[format(R32G32_SFLOAT)]
|
|
||||||
position: [f32; 2],
|
// The triangle vertex positions.
|
||||||
|
layout(location = 0) in vec2 position;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod fs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "fragment",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 f_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pipeline = {
|
||||||
|
let vs = vs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState::default()),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default(),
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let render_pipeline = {
|
fn window_event(
|
||||||
let vs = vs::load(device.clone())
|
&mut self,
|
||||||
.unwrap()
|
event_loop: &ActiveEventLoop,
|
||||||
.entry_point("main")
|
_window_id: WindowId,
|
||||||
.unwrap();
|
event: WindowEvent,
|
||||||
let fs = fs::load(device.clone())
|
) {
|
||||||
.unwrap()
|
let rcx = self.rcx.as_mut().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.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::default()),
|
|
||||||
viewport_state: Some(ViewportState::default()),
|
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
|
||||||
multisample_state: Some(MultisampleState::default()),
|
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
|
||||||
subpass.num_color_attachments(),
|
|
||||||
ColorBlendAttachmentState::default(),
|
|
||||||
)),
|
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
|
||||||
offset: [0.0, 0.0],
|
|
||||||
extent: [0.0, 0.0],
|
|
||||||
depth_range: 0.0..=1.0,
|
|
||||||
};
|
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
|
|
||||||
let mut recreate_swapchain = false;
|
|
||||||
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
|
||||||
|
|
||||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
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 {
|
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,11 +39,496 @@ 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<[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 required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
|
let instance = Instance::new(
|
||||||
|
library,
|
||||||
|
InstanceCreateInfo {
|
||||||
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
|
enabled_extensions: required_extensions,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Using device: {} (type: {:?})",
|
||||||
|
physical_device.properties().device_name,
|
||||||
|
physical_device.properties().device_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (device, mut queues) = Device::new(
|
||||||
|
physical_device,
|
||||||
|
DeviceCreateInfo {
|
||||||
|
enabled_extensions: device_extensions,
|
||||||
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
|
queue_family_index,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue = queues.next().unwrap();
|
||||||
|
|
||||||
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
|
device.clone(),
|
||||||
|
Default::default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// We now create a buffer that will store the shape of our triangle. This triangle is
|
||||||
|
// identical to the one in the `triangle.rs` example.
|
||||||
|
let vertices = [
|
||||||
|
TriangleVertex {
|
||||||
|
position: [-0.5, -0.25],
|
||||||
|
},
|
||||||
|
TriangleVertex {
|
||||||
|
position: [0.0, 0.5],
|
||||||
|
},
|
||||||
|
TriangleVertex {
|
||||||
|
position: [0.25, -0.1],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vertices,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Now we create another buffer that will store the unique data per instance. For this
|
||||||
|
// example, we'll have the instances form a 10x10 grid that slowly gets larger.
|
||||||
|
let instances = {
|
||||||
|
let rows = 10;
|
||||||
|
let cols = 10;
|
||||||
|
let n_instances = rows * cols;
|
||||||
|
let mut data = Vec::new();
|
||||||
|
for c in 0..cols {
|
||||||
|
for r in 0..rows {
|
||||||
|
let half_cell_w = 0.5 / cols as f32;
|
||||||
|
let half_cell_h = 0.5 / rows as f32;
|
||||||
|
let x = half_cell_w + (c as f32 / cols as f32) * 2.0 - 1.0;
|
||||||
|
let y = half_cell_h + (r as f32 / rows as f32) * 2.0 - 1.0;
|
||||||
|
let position_offset = [x, y];
|
||||||
|
let scale = (2.0 / rows as f32) * (c * rows + r) as f32 / n_instances as f32;
|
||||||
|
data.push(InstanceData {
|
||||||
|
position_offset,
|
||||||
|
scale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data
|
||||||
|
};
|
||||||
|
let instance_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator,
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
instances,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
App {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
command_buffer_allocator,
|
||||||
|
vertex_buffer,
|
||||||
|
instance_buffer,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for App {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = Arc::new(
|
||||||
|
event_loop
|
||||||
|
.create_window(Window::default_attributes())
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let surface_capabilities = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_capabilities(&surface, Default::default())
|
||||||
|
.unwrap();
|
||||||
|
let (image_format, _) = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_formats(&surface, Default::default())
|
||||||
|
.unwrap()[0];
|
||||||
|
|
||||||
|
Swapchain::new(
|
||||||
|
self.device.clone(),
|
||||||
|
surface,
|
||||||
|
SwapchainCreateInfo {
|
||||||
|
min_image_count: surface_capabilities.min_image_count.max(2),
|
||||||
|
image_format,
|
||||||
|
image_extent: window_size.into(),
|
||||||
|
image_usage: ImageUsage::COLOR_ATTACHMENT,
|
||||||
|
composite_alpha: surface_capabilities
|
||||||
|
.supported_composite_alpha
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let render_pass = single_pass_renderpass!(
|
||||||
|
self.device.clone(),
|
||||||
|
attachments: {
|
||||||
|
color: {
|
||||||
|
format: swapchain.image_format(),
|
||||||
|
samples: 1,
|
||||||
|
load_op: Clear,
|
||||||
|
store_op: Store,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pass: {
|
||||||
|
color: [color],
|
||||||
|
depth_stencil: {},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let framebuffers = window_size_dependent_setup(&images, &render_pass);
|
||||||
|
|
||||||
|
mod vs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "vertex",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
// The triangle vertex positions.
|
||||||
|
layout(location = 0) in vec2 position;
|
||||||
|
|
||||||
|
// The per-instance data.
|
||||||
|
layout(location = 1) in vec2 position_offset;
|
||||||
|
layout(location = 2) in float scale;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Apply the scale and offset for the instance.
|
||||||
|
gl_Position = vec4(position * scale + position_offset, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod fs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "fragment",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
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()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = [TriangleVertex::per_vertex(), InstanceData::per_instance()]
|
||||||
|
.definition(&vs)
|
||||||
|
.unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
// Use the implementations of the `Vertex` trait to describe to vulkano how the
|
||||||
|
// two vertex types are expected to be used.
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState::default()),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default(),
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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()
|
||||||
|
// We pass both our lists of vertices here.
|
||||||
|
.bind_vertex_buffers(
|
||||||
|
0,
|
||||||
|
(self.vertex_buffer.clone(), self.instance_buffer.clone()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
builder
|
||||||
|
.draw(
|
||||||
|
self.vertex_buffer.len() as u32,
|
||||||
|
self.instance_buffer.len() as u32,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The vertex type that we will be used to describe the triangle's geometry.
|
/// The vertex type that we will be used to describe the triangle's geometry.
|
||||||
#[derive(BufferContents, Vertex)]
|
#[derive(BufferContents, Vertex)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -62,435 +547,16 @@ struct InstanceData {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (physical_device, queue_family_index) = instance
|
|
||||||
.enumerate_physical_devices()
|
|
||||||
.unwrap()
|
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
|
||||||
.filter_map(|p| {
|
|
||||||
p.queue_family_properties()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.position(|(i, q)| {
|
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
|
||||||
})
|
|
||||||
.map(|i| (p, i as u32))
|
|
||||||
})
|
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
|
||||||
PhysicalDeviceType::Cpu => 3,
|
|
||||||
PhysicalDeviceType::Other => 4,
|
|
||||||
_ => 5,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Using device: {} (type: {:?})",
|
|
||||||
physical_device.properties().device_name,
|
|
||||||
physical_device.properties().device_type,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
|
||||||
physical_device,
|
|
||||||
DeviceCreateInfo {
|
|
||||||
enabled_extensions: device_extensions,
|
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
|
||||||
queue_family_index,
|
|
||||||
..Default::default()
|
|
||||||
}],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let queue = queues.next().unwrap();
|
|
||||||
|
|
||||||
let 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()));
|
|
||||||
|
|
||||||
// We now create a buffer that will store the shape of our triangle. This triangle is identical
|
|
||||||
// to the one in the `triangle.rs` example.
|
|
||||||
let vertices = [
|
|
||||||
TriangleVertex {
|
|
||||||
position: [-0.5, -0.25],
|
|
||||||
},
|
|
||||||
TriangleVertex {
|
|
||||||
position: [0.0, 0.5],
|
|
||||||
},
|
|
||||||
TriangleVertex {
|
|
||||||
position: [0.25, -0.1],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Now we create another buffer that will store the unique data per instance. For this example,
|
|
||||||
// we'll have the instances form a 10x10 grid that slowly gets larger.
|
|
||||||
let instances = {
|
|
||||||
let rows = 10;
|
|
||||||
let cols = 10;
|
|
||||||
let n_instances = rows * cols;
|
|
||||||
let mut data = Vec::new();
|
|
||||||
for c in 0..cols {
|
|
||||||
for r in 0..rows {
|
|
||||||
let half_cell_w = 0.5 / cols as f32;
|
|
||||||
let half_cell_h = 0.5 / rows as f32;
|
|
||||||
let x = half_cell_w + (c as f32 / cols as f32) * 2.0 - 1.0;
|
|
||||||
let y = half_cell_h + (r as f32 / rows as f32) * 2.0 - 1.0;
|
|
||||||
let position_offset = [x, y];
|
|
||||||
let scale = (2.0 / rows as f32) * (c * rows + r) as f32 / n_instances as f32;
|
|
||||||
data.push(InstanceData {
|
|
||||||
position_offset,
|
|
||||||
scale,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data
|
|
||||||
};
|
|
||||||
let instance_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator,
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
instances,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
mod vs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "vertex",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
// The triangle vertex positions.
|
|
||||||
layout(location = 0) in vec2 position;
|
|
||||||
|
|
||||||
// The per-instance data.
|
|
||||||
layout(location = 1) in vec2 position_offset;
|
|
||||||
layout(location = 2) in float scale;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// Apply the scale and offset for the instance.
|
|
||||||
gl_Position = vec4(position * scale + position_offset, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod fs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "fragment",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 f_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let render_pass = single_pass_renderpass!(
|
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pass: {
|
|
||||||
color: [color],
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let pipeline = {
|
|
||||||
let vs = vs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let fs = fs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let vertex_input_state = [TriangleVertex::per_vertex(), InstanceData::per_instance()]
|
|
||||||
.definition(&vs)
|
|
||||||
.unwrap();
|
|
||||||
let stages = [
|
|
||||||
PipelineShaderStageCreateInfo::new(vs),
|
|
||||||
PipelineShaderStageCreateInfo::new(fs),
|
|
||||||
];
|
|
||||||
let layout = PipelineLayout::new(
|
|
||||||
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(),
|
|
||||||
// Use the implementations of the `Vertex` trait to describe to vulkano how the two
|
|
||||||
// vertex types are expected to be used.
|
|
||||||
vertex_input_state: Some(vertex_input_state),
|
|
||||||
input_assembly_state: Some(InputAssemblyState::default()),
|
|
||||||
viewport_state: Some(ViewportState::default()),
|
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
|
||||||
multisample_state: Some(MultisampleState::default()),
|
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
|
||||||
subpass.num_color_attachments(),
|
|
||||||
ColorBlendAttachmentState::default(),
|
|
||||||
)),
|
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
|
||||||
offset: [0.0, 0.0],
|
|
||||||
extent: [0.0, 0.0],
|
|
||||||
depth_range: 0.0..=1.0,
|
|
||||||
};
|
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
|
|
||||||
let mut recreate_swapchain = false;
|
|
||||||
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
|
||||||
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
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, 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()
|
|
||||||
// We pass both our lists of vertices here.
|
|
||||||
.bind_vertex_buffers(0, (vertex_buffer.clone(), instance_buffer.clone()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
builder
|
|
||||||
.draw(
|
|
||||||
vertex_buffer.len() as u32,
|
|
||||||
instance_buffer.len() as u32,
|
|
||||||
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.
|
/// 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 {
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 {
|
||||||
|
124
examples/interactive-fractal/input.rs
Normal file
124
examples/interactive-fractal/input.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,168 +10,383 @@
|
|||||||
// - 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 context = VulkanoContext::new(VulkanoConfig::default());
|
let mut app = App::new(&event_loop);
|
||||||
let mut windows = VulkanoWindows::default();
|
|
||||||
let _id = windows.create_window(
|
println!(
|
||||||
&event_loop,
|
"\
|
||||||
&context,
|
Usage:
|
||||||
&WindowDescriptor {
|
WASD: Pan view
|
||||||
title: "Fractal".to_string(),
|
Scroll: Zoom in/out
|
||||||
present_mode: PresentMode::Fifo,
|
Space: Toggle between Mandelbrot and Julia
|
||||||
..Default::default()
|
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\
|
||||||
|
",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add our render target image onto which we'll be rendering our fractals.
|
event_loop.run_app(&mut app)
|
||||||
let render_target_id = 0;
|
|
||||||
let primary_window_renderer = windows.get_primary_renderer_mut().unwrap();
|
|
||||||
|
|
||||||
// Make sure the image usage is correct (based on your pipeline).
|
|
||||||
primary_window_renderer.add_additional_image_view(
|
|
||||||
render_target_id,
|
|
||||||
DEFAULT_IMAGE_FORMAT,
|
|
||||||
ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create app to hold the logic of our fractal explorer.
|
|
||||||
let gfx_queue = context.graphics_queue();
|
|
||||||
|
|
||||||
// We intend to eventually render on our swapchain, thus we use that format when creating the
|
|
||||||
// app here.
|
|
||||||
let mut app = FractalApp::new(
|
|
||||||
gfx_queue.clone(),
|
|
||||||
primary_window_renderer.swapchain_format(),
|
|
||||||
primary_window_renderer.swapchain_image_views(),
|
|
||||||
);
|
|
||||||
app.print_guide();
|
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
|
||||||
|
|
||||||
let renderer = windows.get_primary_renderer_mut().unwrap();
|
|
||||||
|
|
||||||
if process_event(renderer, &event, &mut app, render_target_id) {
|
|
||||||
elwt.exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass event for the app to handle our inputs.
|
|
||||||
app.handle_input(renderer.window_size(), &event);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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,
|
||||||
renderer: &mut VulkanoWindowRenderer,
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
event: &Event<()>,
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
app: &mut FractalApp,
|
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,
|
render_target_id: usize,
|
||||||
) -> bool {
|
}
|
||||||
match &event {
|
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::CloseRequested,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. },
|
|
||||||
..
|
|
||||||
} => renderer.resize(),
|
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::RedrawRequested,
|
|
||||||
..
|
|
||||||
} => 'redraw: {
|
|
||||||
// Tasks for redrawing:
|
|
||||||
// 1. Update state based on events
|
|
||||||
// 2. Compute & Render
|
|
||||||
// 3. Reset input state
|
|
||||||
// 4. Update time & title
|
|
||||||
|
|
||||||
// The rendering part goes here:
|
impl App {
|
||||||
match renderer.window_size() {
|
fn new(_event_loop: &EventLoop<()>) -> Self {
|
||||||
[w, h] => {
|
let context = VulkanoContext::new(VulkanoConfig::default());
|
||||||
// Skip this frame when minimized.
|
let windows = VulkanoWindows::default();
|
||||||
if w == 0.0 || h == 0.0 {
|
|
||||||
break 'redraw;
|
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||||
}
|
context.device().clone(),
|
||||||
}
|
Default::default(),
|
||||||
}
|
));
|
||||||
app.update_state_after_inputs(renderer);
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
compute_then_render(renderer, app, render_target_id);
|
context.device().clone(),
|
||||||
app.reset_input_state();
|
StandardCommandBufferAllocatorCreateInfo {
|
||||||
app.update_time();
|
secondary_buffer_count: 32,
|
||||||
renderer.window().set_title(&format!(
|
..Default::default()
|
||||||
"{} fps: {:.2} dt: {:.2}, Max Iterations: {}",
|
},
|
||||||
if app.is_julia { "Julia" } else { "Mandelbrot" },
|
));
|
||||||
app.avg_fps(),
|
|
||||||
app.dt(),
|
App {
|
||||||
app.max_iters
|
context,
|
||||||
));
|
windows,
|
||||||
|
descriptor_set_allocator,
|
||||||
|
command_buffer_allocator,
|
||||||
|
rcx: None,
|
||||||
}
|
}
|
||||||
Event::AboutToWait => renderer.window().request_redraw(),
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
!app.is_running()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Orchestrates rendering.
|
impl ApplicationHandler for App {
|
||||||
fn compute_then_render(
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
renderer: &mut VulkanoWindowRenderer,
|
let _id = self.windows.create_window(
|
||||||
app: &mut FractalApp,
|
event_loop,
|
||||||
target_image_id: usize,
|
&self.context,
|
||||||
) {
|
&WindowDescriptor {
|
||||||
// Start the frame.
|
title: "Fractal".to_string(),
|
||||||
let before_pipeline_future =
|
present_mode: PresentMode::Fifo,
|
||||||
match renderer.acquire(Some(Duration::from_millis(1000)), |swapchain_image_views| {
|
..Default::default()
|
||||||
app.place_over_frame
|
},
|
||||||
.recreate_framebuffers(swapchain_image_views)
|
|_| {},
|
||||||
}) {
|
);
|
||||||
Err(e) => {
|
|
||||||
println!("{e}");
|
// Add our render target image onto which we'll be rendering our fractals.
|
||||||
return;
|
let render_target_id = 0;
|
||||||
|
let window_renderer = self.windows.get_primary_renderer_mut().unwrap();
|
||||||
|
|
||||||
|
// Make sure the image usage is correct (based on your pipeline).
|
||||||
|
window_renderer.add_additional_image_view(
|
||||||
|
render_target_id,
|
||||||
|
DEFAULT_IMAGE_FORMAT,
|
||||||
|
ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST,
|
||||||
|
);
|
||||||
|
|
||||||
|
let gfx_queue = self.context.graphics_queue();
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
render_target_id,
|
||||||
|
fractal_pipeline: FractalComputePipeline::new(
|
||||||
|
gfx_queue.clone(),
|
||||||
|
self.context.memory_allocator().clone(),
|
||||||
|
self.command_buffer_allocator.clone(),
|
||||||
|
self.descriptor_set_allocator.clone(),
|
||||||
|
),
|
||||||
|
place_over_frame: RenderPassPlaceOverFrame::new(
|
||||||
|
gfx_queue.clone(),
|
||||||
|
self.command_buffer_allocator.clone(),
|
||||||
|
self.descriptor_set_allocator.clone(),
|
||||||
|
window_renderer.swapchain_format(),
|
||||||
|
window_renderer.swapchain_image_views(),
|
||||||
|
),
|
||||||
|
is_julia: false,
|
||||||
|
is_c_paused: false,
|
||||||
|
c: Vec2::new(0.0, 0.0),
|
||||||
|
scale: Vec2::new(4.0, 4.0),
|
||||||
|
translation: Vec2::new(0.0, 0.0),
|
||||||
|
max_iters: MAX_ITERS_INIT,
|
||||||
|
time: Instant::now(),
|
||||||
|
dt: 0.0,
|
||||||
|
dt_sum: 0.0,
|
||||||
|
frame_count: 0.0,
|
||||||
|
avg_fps: 0.0,
|
||||||
|
input_state: InputState::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(
|
||||||
|
&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();
|
||||||
|
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
event_loop.exit();
|
||||||
}
|
}
|
||||||
Ok(future) => future,
|
WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. } => {
|
||||||
};
|
renderer.resize();
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
// Tasks for redrawing:
|
||||||
|
// 1. Update state based on events
|
||||||
|
// 2. Compute & Render
|
||||||
|
// 3. Reset input state
|
||||||
|
// 4. Update time & title
|
||||||
|
|
||||||
// Retrieve the target image.
|
// Skip this frame when minimized.
|
||||||
let image = renderer.get_additional_image_view(target_image_id);
|
if window_size.width == 0 || window_size.height == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Compute our fractal (writes to target image). Join future with `before_pipeline_future`.
|
rcx.update_state_after_inputs(renderer);
|
||||||
let after_compute = app.compute(image.clone()).join(before_pipeline_future);
|
|
||||||
|
|
||||||
// Render the image over the swapchain image, inputting the previous future.
|
// Start the frame.
|
||||||
let after_renderpass_future = app.place_over_frame.render(
|
let before_pipeline_future = match renderer.acquire(
|
||||||
after_compute,
|
Some(Duration::from_millis(1000)),
|
||||||
image,
|
|swapchain_image_views| {
|
||||||
renderer.swapchain_image_view(),
|
rcx.place_over_frame
|
||||||
renderer.image_index(),
|
.recreate_framebuffers(swapchain_image_views)
|
||||||
);
|
},
|
||||||
|
) {
|
||||||
|
Err(e) => {
|
||||||
|
println!("{e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(future) => future,
|
||||||
|
};
|
||||||
|
|
||||||
// Finish the frame (which presents the view), inputting the last future. Wait for the future
|
// Retrieve the target image.
|
||||||
// so resources are not in use when we render.
|
let image = renderer.get_additional_image_view(rcx.render_target_id);
|
||||||
renderer.present(after_renderpass_future, true);
|
|
||||||
|
// Compute our fractal (writes to target image). Join future with
|
||||||
|
// `before_pipeline_future`.
|
||||||
|
let after_compute = rcx
|
||||||
|
.fractal_pipeline
|
||||||
|
.compute(
|
||||||
|
image.clone(),
|
||||||
|
rcx.c,
|
||||||
|
rcx.scale,
|
||||||
|
rcx.translation,
|
||||||
|
rcx.max_iters,
|
||||||
|
rcx.is_julia,
|
||||||
|
)
|
||||||
|
.join(before_pipeline_future);
|
||||||
|
|
||||||
|
// Render the image over the swapchain image, inputting the previous future.
|
||||||
|
let after_renderpass_future = rcx.place_over_frame.render(
|
||||||
|
after_compute,
|
||||||
|
image,
|
||||||
|
renderer.swapchain_image_view(),
|
||||||
|
renderer.image_index(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Finish the frame (which presents the view), inputting the last future. Wait for
|
||||||
|
// the future so resources are not in use when we render.
|
||||||
|
renderer.present(after_renderpass_future, true);
|
||||||
|
|
||||||
|
rcx.input_state.reset();
|
||||||
|
rcx.update_time();
|
||||||
|
renderer.window().set_title(&format!(
|
||||||
|
"{} fps: {:.2} dt: {:.2}, Max Iterations: {}",
|
||||||
|
if rcx.is_julia { "Julia" } else { "Mandelbrot" },
|
||||||
|
rcx.avg_fps(),
|
||||||
|
rcx.dt(),
|
||||||
|
rcx.max_iters
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Pass event for the app to handle our inputs.
|
||||||
|
rcx.input_state.handle_input(window_size, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rcx.input_state.should_quit {
|
||||||
|
event_loop.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,11 +52,482 @@ 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> {
|
||||||
|
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 required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
|
let instance = Instance::new(
|
||||||
|
library,
|
||||||
|
InstanceCreateInfo {
|
||||||
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
|
enabled_extensions: required_extensions,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
ext_mesh_shader: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Using device: {} (type: {:?})",
|
||||||
|
physical_device.properties().device_name,
|
||||||
|
physical_device.properties().device_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (device, mut queues) = Device::new(
|
||||||
|
physical_device,
|
||||||
|
DeviceCreateInfo {
|
||||||
|
enabled_extensions: device_extensions,
|
||||||
|
enabled_features: DeviceFeatures {
|
||||||
|
mesh_shader: true,
|
||||||
|
..DeviceFeatures::default()
|
||||||
|
},
|
||||||
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
|
queue_family_index,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue = queues.next().unwrap();
|
||||||
|
|
||||||
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
|
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||||
|
device.clone(),
|
||||||
|
Default::default(),
|
||||||
|
));
|
||||||
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
|
device.clone(),
|
||||||
|
Default::default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// We now create a buffer that will store the shape of our triangle. This triangle is
|
||||||
|
// identical to the one in the `triangle.rs` example.
|
||||||
|
let vertices = [
|
||||||
|
TriangleVertex {
|
||||||
|
position: [-0.5, -0.25],
|
||||||
|
},
|
||||||
|
TriangleVertex {
|
||||||
|
position: [0.0, 0.5],
|
||||||
|
},
|
||||||
|
TriangleVertex {
|
||||||
|
position: [0.25, -0.1],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::STORAGE_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vertices,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Now we create another buffer that will store the unique data per instance. For this
|
||||||
|
// example, we'll have the instances form a 10x10 grid that slowly gets larger.
|
||||||
|
let rows = 10;
|
||||||
|
let cols = 10;
|
||||||
|
let instances = {
|
||||||
|
let n_instances = rows * cols;
|
||||||
|
let mut data = Vec::new();
|
||||||
|
for c in 0..cols {
|
||||||
|
for r in 0..rows {
|
||||||
|
let half_cell_w = 0.5 / cols as f32;
|
||||||
|
let half_cell_h = 0.5 / rows as f32;
|
||||||
|
let x = half_cell_w + (c as f32 / cols as f32) * 2.0 - 1.0;
|
||||||
|
let y = half_cell_h + (r as f32 / rows as f32) * 2.0 - 1.0;
|
||||||
|
let position_offset = [x, y];
|
||||||
|
let scale = (2.0 / rows as f32) * (c * rows + r) as f32 / n_instances as f32;
|
||||||
|
data.push(InstanceData {
|
||||||
|
position_offset,
|
||||||
|
scale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data
|
||||||
|
};
|
||||||
|
let instance_buffer = Buffer::new_unsized::<mesh::InstanceBuffer>(
|
||||||
|
memory_allocator,
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::STORAGE_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
instances.len() as DeviceSize,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
{
|
||||||
|
let mut guard = instance_buffer.write().unwrap();
|
||||||
|
for (i, instance) in instances.iter().enumerate() {
|
||||||
|
guard.instance[i] = Padded(*instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
vertex_buffer,
|
||||||
|
instance_buffer,
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
descriptor_set_allocator,
|
||||||
|
command_buffer_allocator,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
let pipeline = {
|
||||||
|
let mesh = mesh::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(mesh),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default(),
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let descriptor_set = DescriptorSet::new(
|
||||||
|
self.descriptor_set_allocator.clone(),
|
||||||
|
pipeline.layout().set_layouts()[0].clone(),
|
||||||
|
[
|
||||||
|
WriteDescriptorSet::buffer(0, self.vertex_buffer.clone()),
|
||||||
|
WriteDescriptorSet::buffer(1, self.instance_buffer.clone()),
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
descriptor_set,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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()
|
||||||
|
// Instead of binding vertex attributes, bind buffers as descriptor sets
|
||||||
|
.bind_descriptor_sets(
|
||||||
|
PipelineBindPoint::Graphics,
|
||||||
|
rcx.pipeline.layout().clone(),
|
||||||
|
0,
|
||||||
|
rcx.descriptor_set.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
builder.draw_mesh_tasks([self.cols, self.rows, 1]).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.end_render_pass(Default::default()).unwrap();
|
||||||
|
|
||||||
|
let command_buffer = builder.end().unwrap();
|
||||||
|
let future = 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The vertex type that we will be used to describe the triangle's geometry.
|
/// The vertex type that we will be used to describe the triangle's geometry.
|
||||||
#[derive(BufferContents)]
|
#[derive(BufferContents)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -67,6 +538,28 @@ struct TriangleVertex {
|
|||||||
/// The vertex type that describes the unique data per instance.
|
/// The vertex type that describes the unique data per instance.
|
||||||
type InstanceData = mesh::Instance;
|
type InstanceData = mesh::Instance;
|
||||||
|
|
||||||
|
/// This function is called once during initialization, then again whenever the window is resized.
|
||||||
|
fn window_size_dependent_setup(
|
||||||
|
images: &[Arc<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 mesh {
|
mod mesh {
|
||||||
vulkano_shaders::shader! {
|
vulkano_shaders::shader! {
|
||||||
ty: "mesh",
|
ty: "mesh",
|
||||||
@ -81,425 +574,3 @@ mod fs {
|
|||||||
path: "frag.glsl",
|
path: "frag.glsl",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
ext_mesh_shader: true,
|
|
||||||
..DeviceExtensions::empty()
|
|
||||||
};
|
|
||||||
let (physical_device, queue_family_index) = instance
|
|
||||||
.enumerate_physical_devices()
|
|
||||||
.unwrap()
|
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
|
||||||
.filter_map(|p| {
|
|
||||||
p.queue_family_properties()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.position(|(i, q)| {
|
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
|
||||||
})
|
|
||||||
.map(|i| (p, i as u32))
|
|
||||||
})
|
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
|
||||||
PhysicalDeviceType::Cpu => 3,
|
|
||||||
PhysicalDeviceType::Other => 4,
|
|
||||||
_ => 5,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Using device: {} (type: {:?})",
|
|
||||||
physical_device.properties().device_name,
|
|
||||||
physical_device.properties().device_type,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
|
||||||
physical_device,
|
|
||||||
DeviceCreateInfo {
|
|
||||||
enabled_extensions: device_extensions,
|
|
||||||
enabled_features: DeviceFeatures {
|
|
||||||
mesh_shader: true,
|
|
||||||
..DeviceFeatures::default()
|
|
||||||
},
|
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
|
||||||
queue_family_index,
|
|
||||||
..Default::default()
|
|
||||||
}],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let queue = queues.next().unwrap();
|
|
||||||
|
|
||||||
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
|
||||||
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap();
|
|
||||||
|
|
||||||
let (mut swapchain, images) = {
|
|
||||||
let surface_capabilities = device
|
|
||||||
.physical_device()
|
|
||||||
.surface_capabilities(&surface, Default::default())
|
|
||||||
.unwrap();
|
|
||||||
let image_format = device
|
|
||||||
.physical_device()
|
|
||||||
.surface_formats(&surface, Default::default())
|
|
||||||
.unwrap()[0]
|
|
||||||
.0;
|
|
||||||
|
|
||||||
Swapchain::new(
|
|
||||||
device.clone(),
|
|
||||||
surface,
|
|
||||||
SwapchainCreateInfo {
|
|
||||||
min_image_count: surface_capabilities.min_image_count.max(2),
|
|
||||||
image_format,
|
|
||||||
image_extent: window.inner_size().into(),
|
|
||||||
image_usage: ImageUsage::COLOR_ATTACHMENT,
|
|
||||||
composite_alpha: surface_capabilities
|
|
||||||
.supported_composite_alpha
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.unwrap(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
|
||||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
// We now create a buffer that will store the shape of our triangle. This triangle is identical
|
|
||||||
// to the one in the `triangle.rs` example.
|
|
||||||
let vertices = [
|
|
||||||
TriangleVertex {
|
|
||||||
position: [-0.5, -0.25],
|
|
||||||
},
|
|
||||||
TriangleVertex {
|
|
||||||
position: [0.0, 0.5],
|
|
||||||
},
|
|
||||||
TriangleVertex {
|
|
||||||
position: [0.25, -0.1],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::STORAGE_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Now we create another buffer that will store the unique data per instance. For this example,
|
|
||||||
// we'll have the instances form a 10x10 grid that slowly gets larger.
|
|
||||||
let rows = 10;
|
|
||||||
let cols = 10;
|
|
||||||
let instances = {
|
|
||||||
let n_instances = rows * cols;
|
|
||||||
let mut data = Vec::new();
|
|
||||||
for c in 0..cols {
|
|
||||||
for r in 0..rows {
|
|
||||||
let half_cell_w = 0.5 / cols as f32;
|
|
||||||
let half_cell_h = 0.5 / rows as f32;
|
|
||||||
let x = half_cell_w + (c as f32 / cols as f32) * 2.0 - 1.0;
|
|
||||||
let y = half_cell_h + (r as f32 / rows as f32) * 2.0 - 1.0;
|
|
||||||
let position_offset = [x, y];
|
|
||||||
let scale = (2.0 / rows as f32) * (c * rows + r) as f32 / n_instances as f32;
|
|
||||||
data.push(InstanceData {
|
|
||||||
position_offset,
|
|
||||||
scale,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data
|
|
||||||
};
|
|
||||||
let instance_buffer = Buffer::new_unsized::<mesh::InstanceBuffer>(
|
|
||||||
memory_allocator,
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::STORAGE_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
instances.len() as DeviceSize,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
{
|
|
||||||
let mut guard = instance_buffer.write().unwrap();
|
|
||||||
for (i, instance) in instances.iter().enumerate() {
|
|
||||||
guard.instance[i] = Padded(*instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 mesh = mesh::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let fs = fs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let stages = [
|
|
||||||
PipelineShaderStageCreateInfo::new(mesh),
|
|
||||||
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(),
|
|
||||||
viewport_state: Some(ViewportState::default()),
|
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
|
||||||
multisample_state: Some(MultisampleState::default()),
|
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
|
||||||
subpass.num_color_attachments(),
|
|
||||||
ColorBlendAttachmentState::default(),
|
|
||||||
)),
|
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let descriptor_set = DescriptorSet::new(
|
|
||||||
descriptor_set_allocator,
|
|
||||||
pipeline.layout().set_layouts()[0].clone(),
|
|
||||||
[
|
|
||||||
WriteDescriptorSet::buffer(0, vertex_buffer.clone()),
|
|
||||||
WriteDescriptorSet::buffer(1, instance_buffer.clone()),
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
|
||||||
offset: [0.0, 0.0],
|
|
||||||
extent: [0.0, 0.0],
|
|
||||||
depth_range: 0.0..=1.0,
|
|
||||||
};
|
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
|
|
||||||
let mut recreate_swapchain = false;
|
|
||||||
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
|
||||||
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
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, 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()
|
|
||||||
// Instead of binding vertex attributes, bind buffers as descriptor sets
|
|
||||||
.bind_descriptor_sets(
|
|
||||||
PipelineBindPoint::Graphics,
|
|
||||||
pipeline.layout().clone(),
|
|
||||||
0,
|
|
||||||
descriptor_set.clone(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
builder.draw_mesh_tasks([cols, rows, 1]).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<_>>()
|
|
||||||
}
|
|
||||||
|
@ -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()));
|
||||||
|
@ -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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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::{
|
||||||
|
@ -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: {
|
||||||
|
@ -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,406 +42,389 @@ 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 {
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
enabled_extensions: required_extensions,
|
||||||
enabled_extensions: required_extensions,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let device_extensions = DeviceExtensions {
|
|
||||||
khr_swapchain: true,
|
|
||||||
..DeviceExtensions::empty()
|
|
||||||
};
|
|
||||||
let (physical_device, queue_family_index) = instance
|
|
||||||
.enumerate_physical_devices()
|
|
||||||
.unwrap()
|
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
|
||||||
.filter_map(|p| {
|
|
||||||
p.queue_family_properties()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.position(|(i, q)| {
|
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
|
||||||
})
|
|
||||||
.map(|i| (p, i as u32))
|
|
||||||
})
|
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
|
||||||
PhysicalDeviceType::Cpu => 3,
|
|
||||||
PhysicalDeviceType::Other => 4,
|
|
||||||
_ => 5,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Using device: {} (type: {:?})",
|
|
||||||
physical_device.properties().device_name,
|
|
||||||
physical_device.properties().device_type,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
|
||||||
physical_device,
|
|
||||||
DeviceCreateInfo {
|
|
||||||
enabled_extensions: device_extensions,
|
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
|
||||||
queue_family_index,
|
|
||||||
..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();
|
|
||||||
|
|
||||||
// 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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex)]
|
println!(
|
||||||
#[repr(C)]
|
"Using device: {} (type: {:?})",
|
||||||
struct Vertex {
|
physical_device.properties().device_name,
|
||||||
#[format(R32G32_SFLOAT)]
|
physical_device.properties().device_type,
|
||||||
position: [f32; 2],
|
);
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = [
|
let (device, mut queues) = Device::new(
|
||||||
Vertex {
|
physical_device,
|
||||||
position: [-0.5, -0.25],
|
DeviceCreateInfo {
|
||||||
},
|
enabled_extensions: device_extensions,
|
||||||
Vertex {
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
position: [0.0, 0.5],
|
queue_family_index,
|
||||||
},
|
..Default::default()
|
||||||
Vertex {
|
}],
|
||||||
position: [0.25, -0.1],
|
..Default::default()
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator,
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
mod vs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "vertex",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod fs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "fragment",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 f_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
pass: {
|
.unwrap();
|
||||||
color: [color],
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let pipeline = {
|
let queue = queues.next().unwrap();
|
||||||
let vs = vs::load(device.clone())
|
|
||||||
.unwrap()
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
.entry_point("main")
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
.unwrap();
|
device.clone(),
|
||||||
let fs = fs::load(device.clone())
|
Default::default(),
|
||||||
.unwrap()
|
));
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
let vertices = [
|
||||||
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap();
|
MyVertex {
|
||||||
let stages = [
|
position: [-0.5, -0.25],
|
||||||
PipelineShaderStageCreateInfo::new(vs),
|
},
|
||||||
PipelineShaderStageCreateInfo::new(fs),
|
MyVertex {
|
||||||
|
position: [0.0, 0.5],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.25, -0.1],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let layout = PipelineLayout::new(
|
let vertex_buffer = Buffer::from_iter(
|
||||||
device.clone(),
|
memory_allocator,
|
||||||
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
BufferCreateInfo {
|
||||||
.into_pipeline_layout_create_info(device.clone())
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
.unwrap(),
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vertices,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
// A hashmap that contains all of our created windows and their resources.
|
||||||
device.clone(),
|
let rcxs = HashMap::new();
|
||||||
None,
|
|
||||||
GraphicsPipelineCreateInfo {
|
App {
|
||||||
stages: stages.into_iter().collect(),
|
instance,
|
||||||
vertex_input_state: Some(vertex_input_state),
|
device,
|
||||||
input_assembly_state: Some(InputAssemblyState::default()),
|
queue,
|
||||||
viewport_state: Some(ViewportState::default()),
|
command_buffer_allocator,
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
vertex_buffer,
|
||||||
multisample_state: Some(MultisampleState::default()),
|
rcxs,
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
}
|
||||||
subpass.num_color_attachments(),
|
}
|
||||||
ColorBlendAttachmentState::default(),
|
|
||||||
)),
|
fn create_rcx(&self, window: Window) -> RenderContext {
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
let window = Arc::new(window);
|
||||||
subpass: Some(subpass.into()),
|
let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
let window_size = window.inner_size();
|
||||||
|
|
||||||
|
// The swapchain and framebuffer images for this particular window.
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let surface_capabilities = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_capabilities(&surface, Default::default())
|
||||||
|
.unwrap();
|
||||||
|
let (image_format, _) = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_formats(&surface, Default::default())
|
||||||
|
.unwrap()[0];
|
||||||
|
|
||||||
|
Swapchain::new(
|
||||||
|
self.device.clone(),
|
||||||
|
surface.clone(),
|
||||||
|
SwapchainCreateInfo {
|
||||||
|
min_image_count: surface_capabilities.min_image_count.max(2),
|
||||||
|
image_format,
|
||||||
|
image_extent: window_size.into(),
|
||||||
|
image_usage: ImageUsage::COLOR_ATTACHMENT,
|
||||||
|
composite_alpha: surface_capabilities
|
||||||
|
.supported_composite_alpha
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
mod vs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "vertex",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 position;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod fs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "fragment",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 f_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let render_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()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
let framebuffers = window_size_dependent_setup(&images, &render_pass);
|
||||||
offset: [0.0, 0.0],
|
|
||||||
extent: [0.0, 0.0],
|
|
||||||
depth_range: 0.0..=1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
let pipeline = {
|
||||||
device.clone(),
|
let vs = vs::load(self.device.clone())
|
||||||
Default::default(),
|
.unwrap()
|
||||||
));
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
window_surfaces.insert(
|
GraphicsPipeline::new(
|
||||||
window_id,
|
self.device.clone(),
|
||||||
WindowSurface {
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState::default()),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default(),
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
RenderContext {
|
||||||
window,
|
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 {
|
WindowEvent::KeyboardInput {
|
||||||
event:
|
event:
|
||||||
WindowEvent::KeyboardInput {
|
KeyEvent {
|
||||||
event:
|
state: ElementState::Pressed,
|
||||||
KeyEvent {
|
|
||||||
state: ElementState::Pressed,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
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())
|
||||||
|
.unwrap();
|
||||||
let window_id = window.id();
|
let window_id = window.id();
|
||||||
let (swapchain, images) = {
|
let rcx = self.create_rcx(window);
|
||||||
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(
|
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
*recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,393 +40,447 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
memory_allocator: Arc<StandardMemoryAllocator>,
|
||||||
.enumerate_physical_devices()
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.unwrap()
|
triangle1: Subbuffer<[MyVertex]>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
triangle2: Subbuffer<[MyVertex]>,
|
||||||
.filter_map(|p| {
|
triangle3: Subbuffer<[MyVertex]>,
|
||||||
p.queue_family_properties()
|
query_pool: Arc<QueryPool>,
|
||||||
.iter()
|
query_results: [u32; 3],
|
||||||
.enumerate()
|
rcx: Option<RenderContext>,
|
||||||
.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!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
render_pass: Arc<RenderPass>,
|
||||||
);
|
framebuffers: Vec<Arc<Framebuffer>>,
|
||||||
|
pipeline: Arc<GraphicsPipeline>,
|
||||||
|
viewport: Viewport,
|
||||||
|
recreate_swapchain: bool,
|
||||||
|
previous_frame_end: Option<Box<dyn GpuFuture>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Vertex {
|
|
||||||
#[format(R32G32B32_SFLOAT)]
|
|
||||||
position: [f32; 3],
|
|
||||||
#[format(R32G32B32_SFLOAT)]
|
|
||||||
color: [f32; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = [
|
|
||||||
// The first triangle (red) is the same one as in the triangle example.
|
|
||||||
Vertex {
|
|
||||||
position: [-0.5, -0.25, 0.5],
|
|
||||||
color: [1.0, 0.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.0, 0.5, 0.5],
|
|
||||||
color: [1.0, 0.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.25, -0.1, 0.5],
|
|
||||||
color: [1.0, 0.0, 0.0],
|
|
||||||
},
|
|
||||||
// The second triangle (cyan) is the same shape and position as the first, but smaller, and
|
|
||||||
// moved behind a bit. It should be completely occluded by the first triangle. (You can
|
|
||||||
// lower its z value to put it in front.)
|
|
||||||
Vertex {
|
|
||||||
position: [-0.25, -0.125, 0.6],
|
|
||||||
color: [0.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.0, 0.25, 0.6],
|
|
||||||
color: [0.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.125, -0.05, 0.6],
|
|
||||||
color: [0.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
// The third triangle (green) is the same shape and size as the first, but moved to the
|
|
||||||
// left and behind the second. It is partially occluded by the first two.
|
|
||||||
Vertex {
|
|
||||||
position: [-0.25, -0.25, 0.7],
|
|
||||||
color: [0.0, 1.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.25, 0.5, 0.7],
|
|
||||||
color: [0.0, 1.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, -0.1, 0.7],
|
|
||||||
color: [0.0, 1.0, 0.0],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Create three buffer slices, one for each triangle.
|
|
||||||
let triangle1 = vertex_buffer.clone().slice(0..3);
|
|
||||||
let triangle2 = vertex_buffer.clone().slice(3..6);
|
|
||||||
let triangle3 = vertex_buffer.slice(6..9);
|
|
||||||
|
|
||||||
// Create a query pool for occlusion queries, with 3 slots.
|
|
||||||
let query_pool = QueryPool::new(
|
|
||||||
device.clone(),
|
|
||||||
QueryPoolCreateInfo {
|
|
||||||
query_count: 3,
|
|
||||||
..QueryPoolCreateInfo::query_type(QueryType::Occlusion)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Create a buffer on the CPU to hold the results of the three queries. Query results are
|
|
||||||
// always represented as either `u32` or `u64`. For occlusion queries, you always need one
|
|
||||||
// element per query. You can ask for the number of elements needed at runtime by calling
|
|
||||||
// `QueryType::result_len`. If you retrieve query results with `with_availability` enabled,
|
|
||||||
// then this array needs to be 6 elements long instead of 3.
|
|
||||||
let mut query_results = [0u32; 3];
|
|
||||||
|
|
||||||
mod vs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "vertex",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 position;
|
|
||||||
layout(location = 1) in vec3 color;
|
|
||||||
|
|
||||||
layout(location = 0) out vec3 v_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
v_color = color;
|
|
||||||
gl_Position = vec4(position, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod fs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "fragment",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 v_color;
|
|
||||||
layout(location = 0) out vec4 f_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
f_color = vec4(v_color, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
|
||||||
depth_stencil: {
|
|
||||||
format: Format::D16_UNORM,
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: DontCare,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pass: {
|
|
||||||
color: [color],
|
|
||||||
depth_stencil: {depth_stencil},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let pipeline = {
|
|
||||||
let vs = vs::load(device.clone())
|
|
||||||
.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();
|
.unwrap();
|
||||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
let device_extensions = DeviceExtensions {
|
||||||
device.clone(),
|
khr_swapchain: true,
|
||||||
None,
|
..DeviceExtensions::empty()
|
||||||
GraphicsPipelineCreateInfo {
|
};
|
||||||
stages: stages.into_iter().collect(),
|
let (physical_device, queue_family_index) = instance
|
||||||
vertex_input_state: Some(vertex_input_state),
|
.enumerate_physical_devices()
|
||||||
input_assembly_state: Some(InputAssemblyState::default()),
|
.unwrap()
|
||||||
viewport_state: Some(ViewportState::default()),
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
.filter_map(|p| {
|
||||||
multisample_state: Some(MultisampleState::default()),
|
p.queue_family_properties()
|
||||||
// Enable depth testing, which is needed for occlusion queries to make sense at
|
.iter()
|
||||||
// all. If you disable depth testing, every pixel is considered to pass the depth
|
.enumerate()
|
||||||
// test, so every query will return a nonzero result.
|
.position(|(i, q)| {
|
||||||
depth_stencil_state: Some(DepthStencilState {
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
depth: Some(DepthState::simple()),
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Using device: {} (type: {:?})",
|
||||||
|
physical_device.properties().device_name,
|
||||||
|
physical_device.properties().device_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (device, mut queues) = Device::new(
|
||||||
|
physical_device,
|
||||||
|
DeviceCreateInfo {
|
||||||
|
enabled_extensions: device_extensions,
|
||||||
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
|
queue_family_index,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}],
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
..Default::default()
|
||||||
subpass.num_color_attachments(),
|
|
||||||
ColorBlendAttachmentState::default(),
|
|
||||||
)),
|
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
let queue = queues.next().unwrap();
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
offset: [0.0, 0.0],
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
extent: [0.0, 0.0],
|
device.clone(),
|
||||||
depth_range: 0.0..=1.0,
|
Default::default(),
|
||||||
};
|
));
|
||||||
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
let vertices = [
|
||||||
device.clone(),
|
// The first triangle (red) is the same one as in the triangle example.
|
||||||
Default::default(),
|
MyVertex {
|
||||||
));
|
position: [-0.5, -0.25, 0.5],
|
||||||
|
color: [1.0, 0.0, 0.0],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.0, 0.5, 0.5],
|
||||||
|
color: [1.0, 0.0, 0.0],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.25, -0.1, 0.5],
|
||||||
|
color: [1.0, 0.0, 0.0],
|
||||||
|
},
|
||||||
|
// The second triangle (cyan) is the same shape and position as the first, but smaller,
|
||||||
|
// and moved behind a bit. It should be completely occluded by the first triangle. (You
|
||||||
|
// can lower its z value to put it in front.)
|
||||||
|
MyVertex {
|
||||||
|
position: [-0.25, -0.125, 0.6],
|
||||||
|
color: [0.0, 1.0, 1.0],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.0, 0.25, 0.6],
|
||||||
|
color: [0.0, 1.0, 1.0],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.125, -0.05, 0.6],
|
||||||
|
color: [0.0, 1.0, 1.0],
|
||||||
|
},
|
||||||
|
// The third triangle (green) is the same shape and size as the first, but moved to the
|
||||||
|
// left and behind the second. It is partially occluded by the first two.
|
||||||
|
MyVertex {
|
||||||
|
position: [-0.25, -0.25, 0.7],
|
||||||
|
color: [0.0, 1.0, 0.0],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.25, 0.5, 0.7],
|
||||||
|
color: [0.0, 1.0, 0.0],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.5, -0.1, 0.7],
|
||||||
|
color: [0.0, 1.0, 0.0],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vertices,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut framebuffers = window_size_dependent_setup(
|
// Create three buffer slices, one for each triangle.
|
||||||
&images,
|
let triangle1 = vertex_buffer.clone().slice(0..3);
|
||||||
render_pass.clone(),
|
let triangle2 = vertex_buffer.clone().slice(3..6);
|
||||||
&mut viewport,
|
let triangle3 = vertex_buffer.slice(6..9);
|
||||||
memory_allocator.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
// Create a query pool for occlusion queries, with 3 slots.
|
||||||
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
let query_pool = QueryPool::new(
|
||||||
|
device.clone(),
|
||||||
|
QueryPoolCreateInfo {
|
||||||
|
query_count: 3,
|
||||||
|
..QueryPoolCreateInfo::query_type(QueryType::Occlusion)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
// Create a buffer on the CPU to hold the results of the three queries. Query results are
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
// always represented as either `u32` or `u64`. For occlusion queries, you always need one
|
||||||
|
// element per query. You can ask for the number of elements needed at runtime by calling
|
||||||
|
// `QueryType::result_len`. If you retrieve query results with `with_availability` enabled,
|
||||||
|
// then this array needs to be 6 elements long instead of 3.
|
||||||
|
let query_results = [0u32; 3];
|
||||||
|
|
||||||
|
App {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
memory_allocator,
|
||||||
|
command_buffer_allocator,
|
||||||
|
triangle1,
|
||||||
|
triangle2,
|
||||||
|
triangle3,
|
||||||
|
query_pool,
|
||||||
|
query_results,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for App {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = Arc::new(
|
||||||
|
event_loop
|
||||||
|
.create_window(Window::default_attributes())
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let surface_capabilities = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_capabilities(&surface, Default::default())
|
||||||
|
.unwrap();
|
||||||
|
let (image_format, _) = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_formats(&surface, Default::default())
|
||||||
|
.unwrap()[0];
|
||||||
|
|
||||||
|
Swapchain::new(
|
||||||
|
self.device.clone(),
|
||||||
|
surface,
|
||||||
|
SwapchainCreateInfo {
|
||||||
|
min_image_count: surface_capabilities.min_image_count.max(2),
|
||||||
|
image_format,
|
||||||
|
image_extent: window_size.into(),
|
||||||
|
image_usage: ImageUsage::COLOR_ATTACHMENT,
|
||||||
|
composite_alpha: surface_capabilities
|
||||||
|
.supported_composite_alpha
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let render_pass = vulkano::single_pass_renderpass!(
|
||||||
|
self.device.clone(),
|
||||||
|
attachments: {
|
||||||
|
color: {
|
||||||
|
format: swapchain.image_format(),
|
||||||
|
samples: 1,
|
||||||
|
load_op: Clear,
|
||||||
|
store_op: Store,
|
||||||
|
},
|
||||||
|
depth_stencil: {
|
||||||
|
format: Format::D16_UNORM,
|
||||||
|
samples: 1,
|
||||||
|
load_op: Clear,
|
||||||
|
store_op: DontCare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pass: {
|
||||||
|
color: [color],
|
||||||
|
depth_stencil: {depth_stencil},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let framebuffers =
|
||||||
|
window_size_dependent_setup(&images, &render_pass, &self.memory_allocator);
|
||||||
|
|
||||||
|
mod vs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "vertex",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 position;
|
||||||
|
layout(location = 1) in vec3 color;
|
||||||
|
|
||||||
|
layout(location = 0) out vec3 v_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_color = color;
|
||||||
|
gl_Position = vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod fs {
|
||||||
|
vulkano_shaders::shader! {
|
||||||
|
ty: "fragment",
|
||||||
|
src: r"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 v_color;
|
||||||
|
layout(location = 0) out vec4 f_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_color = vec4(v_color, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pipeline = {
|
||||||
|
let vs = vs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState::default()),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
// Enable depth testing, which is needed for occlusion queries to make sense at
|
||||||
|
// all. If you disable depth testing, every pixel is considered to pass the
|
||||||
|
// depth test, so every query will return a nonzero result.
|
||||||
|
depth_stencil_state: Some(DepthStencilState {
|
||||||
|
depth: Some(DepthState::simple()),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default(),
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
&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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,376 +40,425 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
khr_push_descriptor: true,
|
device: Arc<Device>,
|
||||||
..DeviceExtensions::empty()
|
queue: Arc<Queue>,
|
||||||
};
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
let (physical_device, queue_family_index) = instance
|
vertex_buffer: Subbuffer<[MyVertex]>,
|
||||||
.enumerate_physical_devices()
|
texture: Arc<ImageView>,
|
||||||
.unwrap()
|
sampler: Arc<Sampler>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
rcx: Option<RenderContext>,
|
||||||
.filter_map(|p| {
|
}
|
||||||
p.queue_family_properties()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.position(|(i, q)| {
|
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
|
||||||
})
|
|
||||||
.map(|i| (p, i as u32))
|
|
||||||
})
|
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
|
||||||
PhysicalDeviceType::Cpu => 3,
|
|
||||||
PhysicalDeviceType::Other => 4,
|
|
||||||
_ => 5,
|
|
||||||
})
|
|
||||||
.expect("no suitable physical device found");
|
|
||||||
|
|
||||||
println!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
render_pass: Arc<RenderPass>,
|
||||||
);
|
framebuffers: Vec<Arc<Framebuffer>>,
|
||||||
|
pipeline: Arc<GraphicsPipeline>,
|
||||||
|
viewport: Viewport,
|
||||||
|
recreate_swapchain: bool,
|
||||||
|
previous_frame_end: Option<Box<dyn GpuFuture>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
khr_push_descriptor: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.expect("no suitable physical device found");
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex)]
|
println!(
|
||||||
#[repr(C)]
|
"Using device: {} (type: {:?})",
|
||||||
struct Vertex {
|
physical_device.properties().device_name,
|
||||||
#[format(R32G32_SFLOAT)]
|
physical_device.properties().device_type,
|
||||||
position: [f32; 2],
|
);
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = [
|
let (device, mut queues) = Device::new(
|
||||||
Vertex {
|
physical_device,
|
||||||
position: [-0.5, -0.5],
|
DeviceCreateInfo {
|
||||||
},
|
enabled_extensions: device_extensions,
|
||||||
Vertex {
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
position: [-0.5, 0.5],
|
queue_family_index,
|
||||||
},
|
..Default::default()
|
||||||
Vertex {
|
}],
|
||||||
position: [0.5, -0.5],
|
..Default::default()
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, 0.5],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
pass: {
|
.unwrap();
|
||||||
color: [color],
|
let queue = queues.next().unwrap();
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
device.clone(),
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
Default::default(),
|
device.clone(),
|
||||||
));
|
Default::default(),
|
||||||
let mut uploads = RecordingCommandBuffer::new(
|
));
|
||||||
command_buffer_allocator.clone(),
|
|
||||||
queue.queue_family_index(),
|
|
||||||
CommandBufferLevel::Primary,
|
|
||||||
CommandBufferBeginInfo {
|
|
||||||
usage: CommandBufferUsage::OneTimeSubmit,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let texture = {
|
let vertices = [
|
||||||
let png_bytes = include_bytes!("image_img.png").as_slice();
|
MyVertex {
|
||||||
let decoder = png::Decoder::new(png_bytes);
|
position: [-0.5, -0.5],
|
||||||
let mut reader = decoder.read_info().unwrap();
|
},
|
||||||
let info = reader.info();
|
MyVertex {
|
||||||
let extent = [info.width, info.height, 1];
|
position: [-0.5, 0.5],
|
||||||
|
},
|
||||||
let upload_buffer = Buffer::new_slice(
|
MyVertex {
|
||||||
|
position: [0.5, -0.5],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.5, 0.5],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
memory_allocator.clone(),
|
memory_allocator.clone(),
|
||||||
BufferCreateInfo {
|
BufferCreateInfo {
|
||||||
usage: BufferUsage::TRANSFER_SRC,
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo {
|
AllocationCreateInfo {
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
(info.width * info.height * 4) as DeviceSize,
|
vertices,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
reader
|
let mut uploads = RecordingCommandBuffer::new(
|
||||||
.next_frame(&mut upload_buffer.write().unwrap())
|
command_buffer_allocator.clone(),
|
||||||
.unwrap();
|
queue.queue_family_index(),
|
||||||
|
CommandBufferLevel::Primary,
|
||||||
let image = Image::new(
|
CommandBufferBeginInfo {
|
||||||
memory_allocator,
|
usage: CommandBufferUsage::OneTimeSubmit,
|
||||||
ImageCreateInfo {
|
|
||||||
image_type: ImageType::Dim2d,
|
|
||||||
format: Format::R8G8B8A8_SRGB,
|
|
||||||
extent,
|
|
||||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
uploads
|
let texture = {
|
||||||
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
let png_bytes = include_bytes!("image_img.png").as_slice();
|
||||||
upload_buffer,
|
let decoder = png::Decoder::new(png_bytes);
|
||||||
image.clone(),
|
let mut reader = decoder.read_info().unwrap();
|
||||||
))
|
let info = reader.info();
|
||||||
|
let extent = [info.width, info.height, 1];
|
||||||
|
|
||||||
|
let upload_buffer = Buffer::new_slice(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::TRANSFER_SRC,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
(info.width * info.height * 4) as DeviceSize,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
ImageView::new_default(image).unwrap()
|
reader
|
||||||
};
|
.next_frame(&mut upload_buffer.write().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let sampler = Sampler::new(
|
let image = Image::new(
|
||||||
device.clone(),
|
memory_allocator,
|
||||||
SamplerCreateInfo {
|
ImageCreateInfo {
|
||||||
mag_filter: Filter::Linear,
|
image_type: ImageType::Dim2d,
|
||||||
min_filter: Filter::Linear,
|
format: Format::R8G8B8A8_SRGB,
|
||||||
address_mode: [SamplerAddressMode::Repeat; 3],
|
extent,
|
||||||
..Default::default()
|
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
||||||
},
|
..Default::default()
|
||||||
)
|
},
|
||||||
.unwrap();
|
AllocationCreateInfo::default(),
|
||||||
|
)
|
||||||
let pipeline = {
|
|
||||||
let vs = vs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
.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 = {
|
|
||||||
let mut layout_create_info =
|
|
||||||
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages);
|
|
||||||
let set_layout = &mut layout_create_info.set_layouts[0];
|
|
||||||
set_layout.flags |= DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR;
|
|
||||||
set_layout.bindings.get_mut(&0).unwrap().immutable_samplers = vec![sampler];
|
|
||||||
|
|
||||||
PipelineLayout::new(
|
uploads
|
||||||
device.clone(),
|
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
||||||
layout_create_info
|
upload_buffer,
|
||||||
.into_pipeline_layout_create_info(device.clone())
|
image.clone(),
|
||||||
.unwrap(),
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ImageView::new_default(image).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let sampler = Sampler::new(
|
||||||
|
device.clone(),
|
||||||
|
SamplerCreateInfo {
|
||||||
|
mag_filter: Filter::Linear,
|
||||||
|
min_filter: Filter::Linear,
|
||||||
|
address_mode: [SamplerAddressMode::Repeat; 3],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let _ = uploads.end().unwrap().execute(queue.clone()).unwrap();
|
||||||
|
|
||||||
|
App {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
command_buffer_allocator,
|
||||||
|
vertex_buffer,
|
||||||
|
texture,
|
||||||
|
sampler,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for App {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = Arc::new(
|
||||||
|
event_loop
|
||||||
|
.create_window(Window::default_attributes())
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let surface_capabilities = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_capabilities(&surface, Default::default())
|
||||||
|
.unwrap();
|
||||||
|
let (image_format, _) = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_formats(&surface, Default::default())
|
||||||
|
.unwrap()[0];
|
||||||
|
|
||||||
|
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()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
let render_pass = vulkano::single_pass_renderpass!(
|
||||||
device.clone(),
|
self.device.clone(),
|
||||||
None,
|
attachments: {
|
||||||
GraphicsPipelineCreateInfo {
|
color: {
|
||||||
stages: stages.into_iter().collect(),
|
format: swapchain.image_format(),
|
||||||
vertex_input_state: Some(vertex_input_state),
|
samples: 1,
|
||||||
input_assembly_state: Some(InputAssemblyState {
|
load_op: Clear,
|
||||||
topology: PrimitiveTopology::TriangleStrip,
|
store_op: Store,
|
||||||
..Default::default()
|
},
|
||||||
}),
|
},
|
||||||
viewport_state: Some(ViewportState::default()),
|
pass: {
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
color: [color],
|
||||||
multisample_state: Some(MultisampleState::default()),
|
depth_stencil: {},
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
|
||||||
subpass.num_color_attachments(),
|
|
||||||
ColorBlendAttachmentState {
|
|
||||||
blend: Some(AttachmentBlend::alpha()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
let framebuffers = window_size_dependent_setup(&images, &render_pass);
|
||||||
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 pipeline = {
|
||||||
let mut previous_frame_end = Some(
|
let vs = vs::load(self.device.clone())
|
||||||
uploads
|
.unwrap()
|
||||||
.end()
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = {
|
||||||
|
let mut layout_create_info =
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages);
|
||||||
|
let set_layout = &mut layout_create_info.set_layouts[0];
|
||||||
|
set_layout.flags |= DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR;
|
||||||
|
set_layout.bindings.get_mut(&0).unwrap().immutable_samplers =
|
||||||
|
vec![self.sampler.clone()];
|
||||||
|
|
||||||
|
PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
layout_create_info
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState {
|
||||||
|
topology: PrimitiveTopology::TriangleStrip,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState {
|
||||||
|
blend: Some(AttachmentBlend::alpha()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute(queue.clone())
|
};
|
||||||
.unwrap()
|
|
||||||
.boxed(),
|
|
||||||
);
|
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
let viewport = Viewport {
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
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 {
|
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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,307 +48,351 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.enumerate_physical_devices()
|
vertex_buffer: Subbuffer<[MyVertex]>,
|
||||||
.unwrap()
|
rcx: Option<RenderContext>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
}
|
||||||
.filter_map(|p| {
|
|
||||||
p.queue_family_properties()
|
struct RenderContext {
|
||||||
.iter()
|
window: Arc<Window>,
|
||||||
.enumerate()
|
swapchain: Arc<Swapchain>,
|
||||||
.position(|(i, q)| {
|
render_pass: Arc<RenderPass>,
|
||||||
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
framebuffers: Vec<Arc<Framebuffer>>,
|
||||||
&& p.presentation_support(i as u32, &event_loop).unwrap()
|
pipeline: Arc<GraphicsPipeline>,
|
||||||
})
|
viewport: Viewport,
|
||||||
.map(|i| (p, i as u32))
|
recreate_swapchain: bool,
|
||||||
})
|
previous_frame_end: Option<Box<dyn GpuFuture>>,
|
||||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
}
|
||||||
PhysicalDeviceType::DiscreteGpu => 0,
|
|
||||||
PhysicalDeviceType::IntegratedGpu => 1,
|
impl App {
|
||||||
PhysicalDeviceType::VirtualGpu => 2,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
PhysicalDeviceType::Cpu => 3,
|
let library = VulkanLibrary::new().unwrap();
|
||||||
PhysicalDeviceType::Other => 4,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
_ => 5,
|
let instance = Instance::new(
|
||||||
})
|
library,
|
||||||
|
InstanceCreateInfo {
|
||||||
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
|
enabled_extensions: required_extensions,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
println!(
|
let device_extensions = DeviceExtensions {
|
||||||
"Using device: {} (type: {:?})",
|
khr_swapchain: true,
|
||||||
physical_device.properties().device_name,
|
..DeviceExtensions::empty()
|
||||||
physical_device.properties().device_type,
|
};
|
||||||
);
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
let (device, mut queues) = Device::new(
|
.unwrap()
|
||||||
physical_device,
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
DeviceCreateInfo {
|
.filter_map(|p| {
|
||||||
enabled_extensions: device_extensions,
|
p.queue_family_properties()
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
.iter()
|
||||||
queue_family_index,
|
.enumerate()
|
||||||
..Default::default()
|
.position(|(i, q)| {
|
||||||
}],
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
..Default::default()
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
},
|
})
|
||||||
)
|
.map(|i| (p, i as u32))
|
||||||
.unwrap();
|
})
|
||||||
let queue = queues.next().unwrap();
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap();
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
let (mut swapchain, images) = {
|
PhysicalDeviceType::Other => 4,
|
||||||
let surface_capabilities = device
|
_ => 5,
|
||||||
.physical_device()
|
})
|
||||||
.surface_capabilities(&surface, Default::default())
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let image_format = device
|
|
||||||
.physical_device()
|
|
||||||
.surface_formats(&surface, Default::default())
|
|
||||||
.unwrap()[0]
|
|
||||||
.0;
|
|
||||||
|
|
||||||
Swapchain::new(
|
println!(
|
||||||
device.clone(),
|
"Using device: {} (type: {:?})",
|
||||||
surface,
|
physical_device.properties().device_name,
|
||||||
SwapchainCreateInfo {
|
physical_device.properties().device_type,
|
||||||
min_image_count: surface_capabilities.min_image_count.max(2),
|
);
|
||||||
image_format,
|
|
||||||
image_extent: window.inner_size().into(),
|
let (device, mut queues) = Device::new(
|
||||||
image_usage: ImageUsage::COLOR_ATTACHMENT,
|
physical_device,
|
||||||
composite_alpha: surface_capabilities
|
DeviceCreateInfo {
|
||||||
.supported_composite_alpha
|
enabled_extensions: device_extensions,
|
||||||
.into_iter()
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
.next()
|
queue_family_index,
|
||||||
.unwrap(),
|
..Default::default()
|
||||||
|
}],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
let queue = queues.next().unwrap();
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pass: {
|
|
||||||
color: [color],
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let graphics_pipeline = {
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
let vs = {
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
let code = read_spirv_words_from_file("vert.spv");
|
|
||||||
|
|
||||||
// Create a ShaderModule on a device the same Shader::load does it.
|
|
||||||
// NOTE: You will have to verify correctness of the data by yourself!
|
|
||||||
let module = unsafe {
|
|
||||||
ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&code)).unwrap()
|
|
||||||
};
|
|
||||||
module.entry_point("main").unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let fs = {
|
|
||||||
let code = read_spirv_words_from_file("frag.spv");
|
|
||||||
|
|
||||||
let module = unsafe {
|
|
||||||
ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&code)).unwrap()
|
|
||||||
};
|
|
||||||
module.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(),
|
device.clone(),
|
||||||
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
Default::default(),
|
||||||
.into_pipeline_layout_create_info(device.clone())
|
));
|
||||||
.unwrap(),
|
|
||||||
|
let vertices = [
|
||||||
|
MyVertex {
|
||||||
|
position: [-1.0, 1.0],
|
||||||
|
color: [1.0, 0.0, 0.0],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.0, -1.0],
|
||||||
|
color: [0.0, 1.0, 0.0],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [1.0, 1.0],
|
||||||
|
color: [0.0, 0.0, 1.0],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator,
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vertices,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
App {
|
||||||
device.clone(),
|
instance,
|
||||||
None,
|
device,
|
||||||
GraphicsPipelineCreateInfo {
|
queue,
|
||||||
stages: stages.into_iter().collect(),
|
command_buffer_allocator,
|
||||||
vertex_input_state: Some(vertex_input_state),
|
vertex_buffer,
|
||||||
input_assembly_state: Some(InputAssemblyState::default()),
|
rcx: None,
|
||||||
viewport_state: Some(ViewportState::default()),
|
}
|
||||||
rasterization_state: Some(RasterizationState {
|
}
|
||||||
cull_mode: CullMode::Front,
|
}
|
||||||
front_face: FrontFace::CounterClockwise,
|
|
||||||
|
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()
|
..Default::default()
|
||||||
}),
|
},
|
||||||
multisample_state: Some(MultisampleState::default()),
|
)
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
.unwrap()
|
||||||
subpass.num_color_attachments(),
|
};
|
||||||
ColorBlendAttachmentState::default(),
|
|
||||||
)),
|
let render_pass = vulkano::single_pass_renderpass!(
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
self.device.clone(),
|
||||||
subpass: Some(subpass.into()),
|
attachments: {
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
color: {
|
||||||
|
format: swapchain.image_format(),
|
||||||
|
samples: 1,
|
||||||
|
load_op: Clear,
|
||||||
|
store_op: Store,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pass: {
|
||||||
|
color: [color],
|
||||||
|
depth_stencil: {},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
let framebuffers = window_size_dependent_setup(&images, &render_pass);
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
let pipeline = {
|
||||||
|
let vs = {
|
||||||
|
let code = read_spirv_words_from_file("vert.spv");
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex)]
|
// Create a ShaderModule on a device the same Shader::load does it.
|
||||||
#[repr(C)]
|
// NOTE: You will have to verify correctness of the data by yourself!
|
||||||
pub struct Vertex {
|
let module = unsafe {
|
||||||
#[format(R32G32_SFLOAT)]
|
ShaderModule::new(self.device.clone(), ShaderModuleCreateInfo::new(&code))
|
||||||
pub position: [f32; 2],
|
.unwrap()
|
||||||
#[format(R32G32B32_SFLOAT)]
|
};
|
||||||
pub color: [f32; 3],
|
module.entry_point("main").unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let fs = {
|
||||||
|
let code = read_spirv_words_from_file("frag.spv");
|
||||||
|
|
||||||
|
let module = unsafe {
|
||||||
|
ShaderModule::new(self.device.clone(), ShaderModuleCreateInfo::new(&code))
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
module.entry_point("main").unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState::default()),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState {
|
||||||
|
cull_mode: CullMode::Front,
|
||||||
|
front_face: FrontFace::CounterClockwise,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default(),
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let vertices = [
|
fn window_event(
|
||||||
Vertex {
|
&mut self,
|
||||||
position: [-1.0, 1.0],
|
event_loop: &ActiveEventLoop,
|
||||||
color: [1.0, 0.0, 0.0],
|
_window_id: WindowId,
|
||||||
},
|
event: WindowEvent,
|
||||||
Vertex {
|
) {
|
||||||
position: [0.0, -1.0],
|
let rcx = self.rcx.as_mut().unwrap();
|
||||||
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],
|
|
||||||
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 previous_frame_end = Some(sync::now(device.clone()).boxed());
|
|
||||||
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
|
||||||
|
|
||||||
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||||
|
|
||||||
|
@ -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,266 +62,335 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
memory_allocator: Arc<StandardMemoryAllocator>,
|
||||||
.enumerate_physical_devices()
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
.unwrap()
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
vertex_buffer: Subbuffer<[Position]>,
|
||||||
.filter_map(|p| {
|
normals_buffer: Subbuffer<[Normal]>,
|
||||||
p.queue_family_properties()
|
index_buffer: Subbuffer<[u16]>,
|
||||||
.iter()
|
uniform_buffer_allocator: SubbufferAllocator,
|
||||||
.enumerate()
|
rcx: Option<RenderContext>,
|
||||||
.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!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
|
||||||
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
POSITIONS,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let normals_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
NORMALS,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let index_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::INDEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
INDICES,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let uniform_buffer = SubbufferAllocator::new(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
SubbufferAllocatorCreateInfo {
|
|
||||||
buffer_usage: BufferUsage::UNIFORM_BUFFER,
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
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 vs = vs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let fs = fs::load(device.clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (mut pipeline, mut framebuffers) = window_size_dependent_setup(
|
let device_extensions = DeviceExtensions {
|
||||||
memory_allocator.clone(),
|
khr_swapchain: true,
|
||||||
vs.clone(),
|
..DeviceExtensions::empty()
|
||||||
fs.clone(),
|
};
|
||||||
&images,
|
let (physical_device, queue_family_index) = instance
|
||||||
render_pass.clone(),
|
.enumerate_physical_devices()
|
||||||
);
|
.unwrap()
|
||||||
let mut recreate_swapchain = false;
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
println!(
|
||||||
let rotation_start = Instant::now();
|
"Using device: {} (type: {:?})",
|
||||||
|
physical_device.properties().device_name,
|
||||||
|
physical_device.properties().device_type,
|
||||||
|
);
|
||||||
|
|
||||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
let (device, mut queues) = Device::new(
|
||||||
device.clone(),
|
physical_device,
|
||||||
Default::default(),
|
DeviceCreateInfo {
|
||||||
));
|
enabled_extensions: device_extensions,
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
device.clone(),
|
queue_family_index,
|
||||||
Default::default(),
|
..Default::default()
|
||||||
));
|
}],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let queue = queues.next().unwrap();
|
||||||
|
|
||||||
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
|
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||||
|
device.clone(),
|
||||||
|
Default::default(),
|
||||||
|
));
|
||||||
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
|
device.clone(),
|
||||||
|
Default::default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
POSITIONS,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let normals_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
NORMALS,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let index_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::INDEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
INDICES,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let uniform_buffer_allocator = SubbufferAllocator::new(
|
||||||
|
memory_allocator.clone(),
|
||||||
|
SubbufferAllocatorCreateInfo {
|
||||||
|
buffer_usage: BufferUsage::UNIFORM_BUFFER,
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
App {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
memory_allocator,
|
||||||
|
descriptor_set_allocator,
|
||||||
|
command_buffer_allocator,
|
||||||
|
vertex_buffer,
|
||||||
|
normals_buffer,
|
||||||
|
index_buffer,
|
||||||
|
uniform_buffer_allocator,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for App {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = Arc::new(
|
||||||
|
event_loop
|
||||||
|
.create_window(Window::default_attributes())
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap();
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let surface_capabilities = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_capabilities(&surface, Default::default())
|
||||||
|
.unwrap();
|
||||||
|
let (image_format, _) = self
|
||||||
|
.device
|
||||||
|
.physical_device()
|
||||||
|
.surface_formats(&surface, Default::default())
|
||||||
|
.unwrap()[0];
|
||||||
|
|
||||||
|
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 vs = vs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (framebuffers, pipeline) = window_size_dependent_setup(
|
||||||
|
window_size,
|
||||||
|
&images,
|
||||||
|
&render_pass,
|
||||||
|
&self.memory_allocator,
|
||||||
|
&vs,
|
||||||
|
&fs,
|
||||||
|
);
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
let rotation_start = Instant::now();
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
vs,
|
||||||
|
fs,
|
||||||
|
pipeline,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
rotation_start,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(
|
||||||
|
&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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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",
|
||||||
@ -75,21 +546,21 @@ mod tcs {
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// A value of 3 means a patch consists of a single triangle.
|
// A value of 3 means a patch consists of a single triangle.
|
||||||
layout(vertices = 3) out;
|
layout(vertices = 3) out;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
// Save the position of the patch, so the TES can access it. We could define our
|
// Save the position of the patch, so the TES can access it. We could define our
|
||||||
// own output variables for this, but `gl_out` is handily provided.
|
// own output variables for this, but `gl_out` is handily provided.
|
||||||
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
||||||
|
|
||||||
// Many triangles are generated in the center.
|
// Many triangles are generated in the center.
|
||||||
gl_TessLevelInner[0] = 10;
|
gl_TessLevelInner[0] = 10;
|
||||||
// No triangles are generated for this edge.
|
// No triangles are generated for this edge.
|
||||||
gl_TessLevelOuter[0] = 1;
|
gl_TessLevelOuter[0] = 1;
|
||||||
// Many triangles are generated for this edge.
|
// Many triangles are generated for this edge.
|
||||||
gl_TessLevelOuter[1] = 10;
|
gl_TessLevelOuter[1] = 10;
|
||||||
// Many triangles are generated for this edge.
|
// Many triangles are generated for this edge.
|
||||||
gl_TessLevelOuter[2] = 10;
|
gl_TessLevelOuter[2] = 10;
|
||||||
|
|
||||||
// These are only used when TES uses `layout(quads)`.
|
// These are only used when TES uses `layout(quads)`.
|
||||||
// gl_TessLevelInner[1] = ...;
|
// gl_TessLevelInner[1] = ...;
|
||||||
@ -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<_>>()
|
|
||||||
}
|
|
||||||
|
@ -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,386 +55,438 @@ 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);
|
||||||
|
|
||||||
let library = VulkanLibrary::new().unwrap();
|
event_loop.run_app(&mut app)
|
||||||
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 {
|
struct App {
|
||||||
khr_swapchain: true,
|
instance: Arc<Instance>,
|
||||||
..DeviceExtensions::empty()
|
device: Arc<Device>,
|
||||||
};
|
queue: Arc<Queue>,
|
||||||
let (physical_device, queue_family_index) = instance
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
.enumerate_physical_devices()
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
.unwrap()
|
vertex_buffer: Subbuffer<[MyVertex]>,
|
||||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
texture: Arc<ImageView>,
|
||||||
.filter_map(|p| {
|
sampler: Arc<Sampler>,
|
||||||
p.queue_family_properties()
|
rcx: Option<RenderContext>,
|
||||||
.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!(
|
struct RenderContext {
|
||||||
"Using device: {} (type: {:?})",
|
window: Arc<Window>,
|
||||||
physical_device.properties().device_name,
|
swapchain: Arc<Swapchain>,
|
||||||
physical_device.properties().device_type,
|
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>>,
|
||||||
|
}
|
||||||
|
|
||||||
let (device, mut queues) = Device::new(
|
impl App {
|
||||||
physical_device,
|
fn new(event_loop: &EventLoop<()>) -> Self {
|
||||||
DeviceCreateInfo {
|
let library = VulkanLibrary::new().unwrap();
|
||||||
enabled_extensions: device_extensions,
|
let required_extensions = Surface::required_extensions(event_loop).unwrap();
|
||||||
queue_create_infos: vec![QueueCreateInfo {
|
let instance = Instance::new(
|
||||||
queue_family_index,
|
library,
|
||||||
..Default::default()
|
InstanceCreateInfo {
|
||||||
}],
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
..Default::default()
|
enabled_extensions: required_extensions,
|
||||||
},
|
|
||||||
)
|
|
||||||
.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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..DeviceExtensions::empty()
|
||||||
|
};
|
||||||
|
let (physical_device, queue_family_index) = instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.unwrap()
|
||||||
|
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||||
|
.filter_map(|p| {
|
||||||
|
p.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.intersects(QueueFlags::GRAPHICS)
|
||||||
|
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||||
|
})
|
||||||
|
.map(|i| (p, i as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex)]
|
println!(
|
||||||
#[repr(C)]
|
"Using device: {} (type: {:?})",
|
||||||
struct Vertex {
|
physical_device.properties().device_name,
|
||||||
#[format(R32G32_SFLOAT)]
|
physical_device.properties().device_type,
|
||||||
position: [f32; 2],
|
);
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = [
|
let (device, mut queues) = Device::new(
|
||||||
Vertex {
|
physical_device,
|
||||||
position: [-0.2, -0.5],
|
DeviceCreateInfo {
|
||||||
},
|
enabled_extensions: device_extensions,
|
||||||
Vertex {
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
position: [-0.5, 0.8],
|
queue_family_index,
|
||||||
},
|
..Default::default()
|
||||||
Vertex {
|
}],
|
||||||
position: [0.4, -0.5],
|
..Default::default()
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, 0.2],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
|
||||||
device.clone(),
|
|
||||||
attachments: {
|
|
||||||
color: {
|
|
||||||
format: swapchain.image_format(),
|
|
||||||
samples: 1,
|
|
||||||
load_op: Clear,
|
|
||||||
store_op: Store,
|
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
pass: {
|
.unwrap();
|
||||||
color: [color],
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
let queue = queues.next().unwrap();
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
|
||||||
device.clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut uploads = RecordingCommandBuffer::new(
|
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
command_buffer_allocator.clone(),
|
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||||
queue.queue_family_index(),
|
device.clone(),
|
||||||
CommandBufferLevel::Primary,
|
Default::default(),
|
||||||
CommandBufferBeginInfo {
|
));
|
||||||
usage: CommandBufferUsage::OneTimeSubmit,
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
..Default::default()
|
device.clone(),
|
||||||
},
|
Default::default(),
|
||||||
)
|
));
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let texture = {
|
let vertices = [
|
||||||
// Replace with your actual image array dimensions.
|
MyVertex {
|
||||||
let format = Format::R8G8B8A8_SRGB;
|
position: [-0.2, -0.5],
|
||||||
let extent: [u32; 3] = [128, 128, 1];
|
},
|
||||||
let array_layers = 3u32;
|
MyVertex {
|
||||||
|
position: [-0.5, 0.8],
|
||||||
let buffer_size = format.block_size()
|
},
|
||||||
* extent
|
MyVertex {
|
||||||
.into_iter()
|
position: [0.4, -0.5],
|
||||||
.map(|e| e as DeviceSize)
|
},
|
||||||
.product::<DeviceSize>()
|
MyVertex {
|
||||||
* array_layers as DeviceSize;
|
position: [0.5, 0.2],
|
||||||
let upload_buffer = Buffer::new_slice(
|
},
|
||||||
|
];
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
memory_allocator.clone(),
|
memory_allocator.clone(),
|
||||||
BufferCreateInfo {
|
BufferCreateInfo {
|
||||||
usage: BufferUsage::TRANSFER_SRC,
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo {
|
AllocationCreateInfo {
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
buffer_size,
|
vertices,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
{
|
let mut uploads = RecordingCommandBuffer::new(
|
||||||
let mut image_data = &mut *upload_buffer.write().unwrap();
|
command_buffer_allocator.clone(),
|
||||||
|
queue.queue_family_index(),
|
||||||
for png_bytes in [
|
CommandBufferLevel::Primary,
|
||||||
include_bytes!("square.png").as_slice(),
|
CommandBufferBeginInfo {
|
||||||
include_bytes!("star.png").as_slice(),
|
usage: CommandBufferUsage::OneTimeSubmit,
|
||||||
include_bytes!("asterisk.png").as_slice(),
|
|
||||||
] {
|
|
||||||
let decoder = png::Decoder::new(png_bytes);
|
|
||||||
let mut reader = decoder.read_info().unwrap();
|
|
||||||
reader.next_frame(image_data).unwrap();
|
|
||||||
let info = reader.info();
|
|
||||||
image_data = &mut image_data[(info.width * info.height * 4) as usize..];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = Image::new(
|
|
||||||
memory_allocator,
|
|
||||||
ImageCreateInfo {
|
|
||||||
image_type: ImageType::Dim2d,
|
|
||||||
format,
|
|
||||||
extent,
|
|
||||||
array_layers,
|
|
||||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
uploads
|
let texture = {
|
||||||
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
// Replace with your actual image array dimensions.
|
||||||
upload_buffer,
|
let format = Format::R8G8B8A8_SRGB;
|
||||||
image.clone(),
|
let extent: [u32; 3] = [128, 128, 1];
|
||||||
))
|
let array_layers = 3u32;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
ImageView::new_default(image).unwrap()
|
let buffer_size = format.block_size()
|
||||||
};
|
* extent
|
||||||
|
.into_iter()
|
||||||
let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap();
|
.map(|e| e as DeviceSize)
|
||||||
|
.product::<DeviceSize>()
|
||||||
let pipeline = {
|
* array_layers as DeviceSize;
|
||||||
let vs = vs::load(device.clone())
|
let upload_buffer = Buffer::new_slice(
|
||||||
.unwrap()
|
memory_allocator.clone(),
|
||||||
.entry_point("main")
|
BufferCreateInfo {
|
||||||
.unwrap();
|
usage: BufferUsage::TRANSFER_SRC,
|
||||||
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.clone(), 0).unwrap();
|
|
||||||
|
|
||||||
GraphicsPipeline::new(
|
|
||||||
device.clone(),
|
|
||||||
None,
|
|
||||||
GraphicsPipelineCreateInfo {
|
|
||||||
stages: stages.into_iter().collect(),
|
|
||||||
vertex_input_state: Some(vertex_input_state),
|
|
||||||
input_assembly_state: Some(InputAssemblyState {
|
|
||||||
topology: PrimitiveTopology::TriangleStrip,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
},
|
||||||
viewport_state: Some(ViewportState::default()),
|
AllocationCreateInfo {
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||||
multisample_state: Some(MultisampleState::default()),
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
..Default::default()
|
||||||
subpass.num_color_attachments(),
|
},
|
||||||
ColorBlendAttachmentState {
|
buffer_size,
|
||||||
blend: Some(AttachmentBlend::alpha()),
|
)
|
||||||
..Default::default()
|
.unwrap();
|
||||||
},
|
|
||||||
)),
|
{
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
let mut image_data = &mut *upload_buffer.write().unwrap();
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
for png_bytes in [
|
||||||
|
include_bytes!("square.png").as_slice(),
|
||||||
|
include_bytes!("star.png").as_slice(),
|
||||||
|
include_bytes!("asterisk.png").as_slice(),
|
||||||
|
] {
|
||||||
|
let decoder = png::Decoder::new(png_bytes);
|
||||||
|
let mut reader = decoder.read_info().unwrap();
|
||||||
|
reader.next_frame(image_data).unwrap();
|
||||||
|
let info = reader.info();
|
||||||
|
image_data = &mut image_data[(info.width * info.height * 4) as usize..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = Image::new(
|
||||||
|
memory_allocator,
|
||||||
|
ImageCreateInfo {
|
||||||
|
image_type: ImageType::Dim2d,
|
||||||
|
format,
|
||||||
|
extent,
|
||||||
|
array_layers,
|
||||||
|
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
uploads
|
||||||
|
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
||||||
|
upload_buffer,
|
||||||
|
image.clone(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ImageView::new_default(image).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let sampler =
|
||||||
|
Sampler::new(device.clone(), SamplerCreateInfo::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()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let layout = &pipeline.layout().set_layouts()[0];
|
let framebuffers = window_size_dependent_setup(&images, &render_pass);
|
||||||
let set = DescriptorSet::new(
|
|
||||||
descriptor_set_allocator,
|
|
||||||
layout.clone(),
|
|
||||||
[
|
|
||||||
WriteDescriptorSet::sampler(0, sampler),
|
|
||||||
WriteDescriptorSet::image_view(1, texture),
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut viewport = Viewport {
|
let pipeline = {
|
||||||
offset: [0.0, 0.0],
|
let vs = vs::load(self.device.clone())
|
||||||
extent: [0.0, 0.0],
|
.unwrap()
|
||||||
depth_range: 0.0..=1.0,
|
.entry_point("main")
|
||||||
};
|
.unwrap();
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
|
let fs = fs::load(self.device.clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.device.clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
let mut recreate_swapchain = false;
|
GraphicsPipeline::new(
|
||||||
let mut previous_frame_end = Some(
|
self.device.clone(),
|
||||||
uploads
|
None,
|
||||||
.end()
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState {
|
||||||
|
topology: PrimitiveTopology::TriangleStrip,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState {
|
||||||
|
blend: Some(AttachmentBlend::alpha()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute(queue.clone())
|
};
|
||||||
.unwrap()
|
|
||||||
.boxed(),
|
|
||||||
);
|
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
let viewport = Viewport {
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let layout = &pipeline.layout().set_layouts()[0];
|
||||||
|
let descriptor_set = DescriptorSet::new(
|
||||||
|
self.descriptor_set_allocator.clone(),
|
||||||
|
layout.clone(),
|
||||||
|
[
|
||||||
|
WriteDescriptorSet::sampler(0, self.sampler.clone()),
|
||||||
|
WriteDescriptorSet::image_view(1, self.texture.clone()),
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let previous_frame_end = Some(sync::now(self.device.clone()).boxed());
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
window,
|
||||||
|
swapchain,
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
descriptor_set,
|
||||||
|
recreate_swapchain: false,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
Ok(r) => r,
|
None,
|
||||||
Err(VulkanError::OutOfDate) => {
|
)
|
||||||
recreate_swapchain = true;
|
.map_err(Validated::unwrap)
|
||||||
return;
|
{
|
||||||
}
|
Ok(r) => r,
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(VulkanError::OutOfDate) => {
|
||||||
};
|
rcx.recreate_swapchain = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
@ -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,294 +38,326 @@ 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();
|
||||||
// Manages any windows and their rendering.
|
let mut app = App::new(&event_loop);
|
||||||
let mut windows_manager = VulkanoWindows::default();
|
|
||||||
windows_manager.create_window(&event_loop, &context, &Default::default(), |_| {});
|
|
||||||
let window_renderer = windows_manager.get_primary_renderer_mut().unwrap();
|
|
||||||
|
|
||||||
// Some little debug infos.
|
event_loop.run_app(&mut app)
|
||||||
println!(
|
}
|
||||||
"Using device: {} (type: {:?})",
|
|
||||||
context.device().physical_device().properties().device_name,
|
|
||||||
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
|
struct App {
|
||||||
// to force rustc to use a defined layout for our data, as the default representation has *no
|
context: VulkanoContext,
|
||||||
// guarantees*.
|
windows: VulkanoWindows,
|
||||||
#[derive(BufferContents, Vertex)]
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
#[repr(C)]
|
vertex_buffer: Subbuffer<[MyVertex]>,
|
||||||
struct Vertex {
|
rcx: Option<RenderContext>,
|
||||||
#[format(R32G32_SFLOAT)]
|
}
|
||||||
position: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = [
|
struct RenderContext {
|
||||||
Vertex {
|
render_pass: Arc<RenderPass>,
|
||||||
position: [-0.5, -0.25],
|
framebuffers: Vec<Arc<Framebuffer>>,
|
||||||
},
|
pipeline: Arc<GraphicsPipeline>,
|
||||||
Vertex {
|
viewport: Viewport,
|
||||||
position: [0.0, 0.5],
|
}
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.25, -0.1],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let vertex_buffer = Buffer::from_iter(
|
|
||||||
context.memory_allocator().clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vertices,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// The next step is to create the shaders.
|
impl App {
|
||||||
//
|
fn new(_event_loop: &EventLoop<()>) -> Self {
|
||||||
// The raw shader creation API provided by the vulkano library is unsafe for various reasons,
|
let context = VulkanoContext::new(VulkanoConfig::default());
|
||||||
// so The `shader!` macro provides a way to generate a Rust module from GLSL source - in the
|
|
||||||
// example below, the source is provided as a string input directly to the shader, but a path
|
|
||||||
// to a source file can be provided as well. Note that the user must specify the type of shader
|
|
||||||
// (e.g. "vertex", "fragment", etc.) using the `ty` option of the macro.
|
|
||||||
//
|
|
||||||
// The items generated by the `shader!` macro include a `load` function which loads the shader
|
|
||||||
// using an input logical device. The module also includes type definitions for layout
|
|
||||||
// structures defined in the shader source, for example uniforms and push constants.
|
|
||||||
//
|
|
||||||
// A more detailed overview of what the `shader!` macro generates can be found in the
|
|
||||||
// vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/
|
|
||||||
mod vs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "vertex",
|
|
||||||
src: r"
|
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 position;
|
// Manages any windows and their rendering.
|
||||||
|
let windows = VulkanoWindows::default();
|
||||||
|
|
||||||
void main() {
|
// Some little debug infos.
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
println!(
|
||||||
}
|
"Using device: {} (type: {:?})",
|
||||||
",
|
context.device().physical_device().properties().device_name,
|
||||||
}
|
context.device().physical_device().properties().device_type,
|
||||||
}
|
);
|
||||||
|
|
||||||
mod fs {
|
// Before we can start creating and recording command buffers, we need a way of allocating
|
||||||
vulkano_shaders::shader! {
|
// them. Vulkano provides a command buffer allocator, which manages raw Vulkan command
|
||||||
ty: "fragment",
|
// pools underneath and provides a safe interface for them.
|
||||||
src: r"
|
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 f_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL
|
|
||||||
// implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this
|
|
||||||
// manually.
|
|
||||||
|
|
||||||
// The next step is to create a *render pass*, which is an object that describes where the
|
|
||||||
// output of the graphics pipeline will go. It describes the layout of the images where the
|
|
||||||
// colors, depth and/or stencil information will be written.
|
|
||||||
let render_pass = vulkano::single_pass_renderpass!(
|
|
||||||
context.device().clone(),
|
|
||||||
attachments: {
|
|
||||||
// `color` is a custom name we give to the first and only attachment.
|
|
||||||
color: {
|
|
||||||
// `format: <ty>` indicates the type of the format of the image. This has to be one
|
|
||||||
// of the types of the `vulkano::format` module (or alternatively one of your
|
|
||||||
// structs that implements the `FormatDesc` trait). Here we use the same format as
|
|
||||||
// the swapchain.
|
|
||||||
format: window_renderer.swapchain_format(),
|
|
||||||
// `samples: 1` means that we ask the GPU to use one sample to determine the value
|
|
||||||
// of each pixel in the color attachment. We could use a larger value
|
|
||||||
// (multisampling) for antialiasing. An example of this can be found in
|
|
||||||
// msaa-renderpass.rs.
|
|
||||||
samples: 1,
|
|
||||||
// `load_op: Clear` means that we ask the GPU to clear the content of this
|
|
||||||
// attachment at the start of the drawing.
|
|
||||||
load_op: Clear,
|
|
||||||
// `store_op: Store` means that we ask the GPU to store the output of the draw in
|
|
||||||
// the actual image. We could also ask it to discard the result.
|
|
||||||
store_op: Store,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pass: {
|
|
||||||
// We use the attachment named `color` as the one and only color attachment.
|
|
||||||
color: [color],
|
|
||||||
// No depth-stencil attachment is indicated with empty brackets.
|
|
||||||
depth_stencil: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// 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(context.device().clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
let fs = fs::load(context.device().clone())
|
|
||||||
.unwrap()
|
|
||||||
.entry_point("main")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Automatically generate a vertex input state from the vertex shader's input interface,
|
|
||||||
// that takes a single vertex buffer containing `Vertex` structs.
|
|
||||||
let vertex_input_state = Vertex::per_vertex().definition(&vs).unwrap();
|
|
||||||
|
|
||||||
// Make a list of the shader stages that the pipeline will have.
|
|
||||||
let stages = [
|
|
||||||
PipelineShaderStageCreateInfo::new(vs),
|
|
||||||
PipelineShaderStageCreateInfo::new(fs),
|
|
||||||
];
|
|
||||||
|
|
||||||
// We must now create a **pipeline layout** object, which describes the locations and types
|
|
||||||
// of descriptor sets and push constants used by the shaders in the pipeline.
|
|
||||||
//
|
|
||||||
// Multiple pipelines can share a common layout object, which is more efficient.
|
|
||||||
// The shaders in a pipeline must use a subset of the resources described in its pipeline
|
|
||||||
// layout, but the pipeline layout is allowed to contain resources that are not present in
|
|
||||||
// the shaders; they can be used by shaders in other pipelines that share the same
|
|
||||||
// layout. Thus, it is a good idea to design shaders so that many pipelines have
|
|
||||||
// common resource locations, which allows them to share pipeline layouts.
|
|
||||||
let layout = PipelineLayout::new(
|
|
||||||
context.device().clone(),
|
context.device().clone(),
|
||||||
// Since we only have one pipeline in this example, and thus one pipeline layout,
|
Default::default(),
|
||||||
// we automatically generate the creation info for it from the resources used in the
|
));
|
||||||
// shaders. In a real application, you would specify this information manually so that
|
|
||||||
// you can re-use one layout in multiple pipelines.
|
// We now create a buffer that will store the shape of our triangle.
|
||||||
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
let vertices = [
|
||||||
.into_pipeline_layout_create_info(context.device().clone())
|
MyVertex {
|
||||||
.unwrap(),
|
position: [-0.5, -0.25],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.0, 0.5],
|
||||||
|
},
|
||||||
|
MyVertex {
|
||||||
|
position: [0.25, -0.1],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let vertex_buffer = Buffer::from_iter(
|
||||||
|
context.memory_allocator().clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vertices,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// We have to indicate which subpass of which render pass this pipeline is going to be used
|
App {
|
||||||
// in. The pipeline will only be usable from this particular subpass.
|
context,
|
||||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
windows,
|
||||||
|
command_buffer_allocator,
|
||||||
|
vertex_buffer,
|
||||||
|
rcx: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Finally, create the pipeline.
|
impl ApplicationHandler for App {
|
||||||
GraphicsPipeline::new(
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
context.device().clone(),
|
if let Some(primary_window_id) = self.windows.primary_window_id() {
|
||||||
None,
|
self.windows.remove_renderer(primary_window_id);
|
||||||
GraphicsPipelineCreateInfo {
|
}
|
||||||
stages: stages.into_iter().collect(),
|
|
||||||
// How vertex data is read from the vertex buffers into the vertex shader.
|
self.windows
|
||||||
vertex_input_state: Some(vertex_input_state),
|
.create_window(event_loop, &self.context, &Default::default(), |_| {});
|
||||||
// How vertices are arranged into primitive shapes.
|
let window_renderer = self.windows.get_primary_renderer_mut().unwrap();
|
||||||
// The default primitive shape is a triangle.
|
let window_size = window_renderer.window().inner_size();
|
||||||
input_assembly_state: Some(InputAssemblyState::default()),
|
|
||||||
// How primitives are transformed and clipped to fit the framebuffer.
|
// The next step is to create the shaders.
|
||||||
// We use a resizable viewport, set to draw over the entire window.
|
//
|
||||||
viewport_state: Some(ViewportState::default()),
|
// The raw shader creation API provided by the vulkano library is unsafe for various
|
||||||
// How polygons are culled and converted into a raster of pixels.
|
// reasons, so The `shader!` macro provides a way to generate a Rust module from GLSL
|
||||||
// The default value does not perform any culling.
|
// source - in the example below, the source is provided as a string input directly to the
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
// shader, but a path to a source file can be provided as well. Note that the user must
|
||||||
// How multiple fragment shader samples are converted to a single pixel value.
|
// specify the type of shader (e.g. "vertex", "fragment", etc.) using the `ty` option of
|
||||||
// The default value does not perform any multisampling.
|
// the macro.
|
||||||
multisample_state: Some(MultisampleState::default()),
|
//
|
||||||
// How pixel values are combined with the values already present in the framebuffer.
|
// The items generated by the `shader!` macro include a `load` function which loads the
|
||||||
// The default value overwrites the old value with the new one, without any
|
// shader using an input logical device. The module also includes type definitions for
|
||||||
// blending.
|
// layout structures defined in the shader source, for example uniforms and push constants.
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
//
|
||||||
subpass.num_color_attachments(),
|
// A more detailed overview of what the `shader!` macro generates can be found in the
|
||||||
ColorBlendAttachmentState::default(),
|
// vulkano-shaders crate docs. You can view them at https://docs.rs/vulkano-shaders/
|
||||||
)),
|
mod vs {
|
||||||
// Dynamic states allows us to specify parts of the pipeline settings when
|
vulkano_shaders::shader! {
|
||||||
// recording the command buffer, before we perform drawing.
|
ty: "vertex",
|
||||||
// Here, we specify that the viewport should be dynamic.
|
src: r"
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
#version 450
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
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);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next step is to create a *render pass*, which is an object that describes where the
|
||||||
|
// output of the graphics pipeline will go. It describes the layout of the images where the
|
||||||
|
// colors, depth and/or stencil information will be written.
|
||||||
|
let render_pass = vulkano::single_pass_renderpass!(
|
||||||
|
self.context.device().clone(),
|
||||||
|
attachments: {
|
||||||
|
// `color` is a custom name we give to the first and only attachment.
|
||||||
|
color: {
|
||||||
|
// `format: <ty>` indicates the type of the format of the image. This has to be
|
||||||
|
// one of the types of the `vulkano::format` module (or alternatively one of
|
||||||
|
// your structs that implements the `FormatDesc` trait). Here we use the same
|
||||||
|
// format as the swapchain.
|
||||||
|
format: window_renderer.swapchain_format(),
|
||||||
|
// `samples: 1` means that we ask the GPU to use one sample to determine the
|
||||||
|
// value of each pixel in the color attachment. We could use a larger value
|
||||||
|
// (multisampling) for antialiasing. An example of this can be found in
|
||||||
|
// msaa-renderpass.rs.
|
||||||
|
samples: 1,
|
||||||
|
// `load_op: Clear` means that we ask the GPU to clear the content of this
|
||||||
|
// attachment at the start of the drawing.
|
||||||
|
load_op: Clear,
|
||||||
|
// `store_op: Store` means that we ask the GPU to store the output of the draw
|
||||||
|
// in the actual image. We could also ask it to discard the result.
|
||||||
|
store_op: Store,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pass: {
|
||||||
|
// We use the attachment named `color` as the one and only color attachment.
|
||||||
|
color: [color],
|
||||||
|
// No depth-stencil attachment is indicated with empty brackets.
|
||||||
|
depth_stencil: {},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
// Dynamic viewports allow us to recreate just the viewport when the window is resized.
|
// The render pass we created above only describes the layout of our framebuffers. Before
|
||||||
// Otherwise we would have to recreate the whole pipeline.
|
// we can draw we also need to create the actual framebuffers.
|
||||||
let mut viewport = Viewport {
|
//
|
||||||
offset: [0.0, 0.0],
|
// Since we need to draw to multiple images, we are going to create a different framebuffer
|
||||||
extent: [0.0, 0.0],
|
// for each image.
|
||||||
depth_range: 0.0..=1.0,
|
let framebuffers =
|
||||||
};
|
window_size_dependent_setup(window_renderer.swapchain_image_views(), &render_pass);
|
||||||
|
|
||||||
// The render pass we created above only describes the layout of our framebuffers. Before we
|
// Before we draw, we have to create what is called a **pipeline**. A pipeline describes
|
||||||
// can draw we also need to create the actual framebuffers.
|
// 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,
|
||||||
// Since we need to draw to multiple images, we are going to create a different framebuffer for
|
// we create a **graphics** pipeline, but there are also other types of pipeline.
|
||||||
// each image.
|
let pipeline = {
|
||||||
let mut framebuffers = window_size_dependent_setup(
|
// First, we load the shaders that the pipeline will use: the vertex shader and the
|
||||||
window_renderer.swapchain_image_views(),
|
// fragment shader.
|
||||||
render_pass.clone(),
|
//
|
||||||
&mut viewport,
|
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
|
||||||
);
|
// which one.
|
||||||
|
let vs = vs::load(self.context.device().clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
let fs = fs::load(self.context.device().clone())
|
||||||
|
.unwrap()
|
||||||
|
.entry_point("main")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Before we can start creating and recording command buffers, we need a way of allocating
|
// Automatically generate a vertex input state from the vertex shader's input
|
||||||
// them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools
|
// interface, that takes a single vertex buffer containing `Vertex` structs.
|
||||||
// underneath and provides a safe interface for them.
|
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
|
||||||
context.device().clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Initialization is finally finished!
|
// Make a list of the shader stages that the pipeline will have.
|
||||||
|
let stages = [
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
|
||||||
// In the loop below we are going to submit commands to the GPU. Submitting a command produces
|
// We must now create a **pipeline layout** object, which describes the locations and
|
||||||
// an object that implements the `GpuFuture` trait, which holds the resources for as long as
|
// types of descriptor sets and push constants used by the shaders in the pipeline.
|
||||||
// they are in use by the GPU.
|
//
|
||||||
|
// Multiple pipelines can share a common layout object, which is more efficient. The
|
||||||
|
// shaders in a pipeline must use a subset of the resources described in its pipeline
|
||||||
|
// layout, but the pipeline layout is allowed to contain resources that are not present
|
||||||
|
// in the shaders; they can be used by shaders in other pipelines that share the same
|
||||||
|
// layout. Thus, it is a good idea to design shaders so that many pipelines have common
|
||||||
|
// resource locations, which allows them to share pipeline layouts.
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
self.context.device().clone(),
|
||||||
|
// Since we only have one pipeline in this example, and thus one pipeline layout,
|
||||||
|
// we automatically generate the creation info for it from the resources used in
|
||||||
|
// the shaders. In a real application, you would specify this information manually
|
||||||
|
// so that you can re-use one layout in multiple pipelines.
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(self.context.device().clone())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| {
|
// We have to indicate which subpass of which render pass this pipeline is going to be
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
// used in. The pipeline will only be usable from this particular subpass.
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||||
|
|
||||||
|
// Finally, create the pipeline.
|
||||||
|
GraphicsPipeline::new(
|
||||||
|
self.context.device().clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
// How vertex data is read from the vertex buffers into the vertex shader.
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
// How vertices are arranged into primitive shapes. The default primitive shape
|
||||||
|
// is a triangle.
|
||||||
|
input_assembly_state: Some(InputAssemblyState::default()),
|
||||||
|
// How primitives are transformed and clipped to fit the framebuffer. We use a
|
||||||
|
// resizable viewport, set to draw over the entire window.
|
||||||
|
viewport_state: Some(ViewportState::default()),
|
||||||
|
// How polygons are culled and converted into a raster of pixels. The default
|
||||||
|
// value does not perform any culling.
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
// How multiple fragment shader samples are converted to a single pixel value.
|
||||||
|
// The default value does not perform any multisampling.
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
// How pixel values are combined with the values already present in the
|
||||||
|
// framebuffer. The default value overwrites the old value with the new one,
|
||||||
|
// without any blending.
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default(),
|
||||||
|
)),
|
||||||
|
// Dynamic states allows us to specify parts of the pipeline settings when
|
||||||
|
// recording the command buffer, before we perform drawing. Here, we specify
|
||||||
|
// that the viewport should be dynamic.
|
||||||
|
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dynamic viewports allow us to recreate just the viewport when the window is resized.
|
||||||
|
// Otherwise we would have to recreate the whole pipeline.
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// In the `window_event` handler below we are going to submit commands to the GPU.
|
||||||
|
// Submitting a command produces an object that implements the `GpuFuture` trait, which
|
||||||
|
// holds the resources for as long as they are in use by the GPU.
|
||||||
|
|
||||||
|
self.rcx = Some(RenderContext {
|
||||||
|
render_pass,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
viewport,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
_window_id: WindowId,
|
||||||
|
event: WindowEvent,
|
||||||
|
) {
|
||||||
|
let window_renderer = self.windows.get_primary_renderer_mut().unwrap();
|
||||||
|
let rcx = self.rcx.as_mut().unwrap();
|
||||||
|
|
||||||
match event {
|
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| {
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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<'_> {
|
||||||
|
@ -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,
|
||||||
|
@ -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 = vulkano_windows.create_window(event_loop, &context, &Default::default(), |_| {});
|
||||||
/// let _id1 =
|
/// let _id2 = 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(), |_| {});
|
|
||||||
///
|
///
|
||||||
/// // 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::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user